进制转换是计算机科学中的基石之一。无论是程序设计、底层开发还是算法学习,都离不开对二进制、八进制、十进制和十六进制的理解与操作。本文将深入浅出地讲解C语言中进制转换的核心概念、数学原理,并提供使用C语言实现常见进制转换的完整代码示例。
在计算机内部,所有数据都以**二进制(Base-2)**形式存储,因为电子元件只有“开”和“关”两种状态(1和0)。然而,直接操作长串的二进制数字对人类来说非常不便,容易出错。
0开头)。0x或0X开头)。进制转换的目的: 是在人类习惯的表示法(十进制/十六进制)和计算机内部的存储方式(二进制)之间建立桥梁。
所有的进制转换都基于一个核心概念——位权(Place Value)。位权,即每个位置的数字乘以它所代表的权重。理解位权是掌握进制转换的关键。
位权(权重值)的确定规则是:从小数点(或最低位)开始,向左,第
位的权重是进制数
的
次方 (
)。
这是最简单的转换方式。只需将 N 进制数的每一位数字,乘以进制数的相应幂次方(位权),再求和即可。
✅ 规则: 将 N 进制数
的每一位数字
,乘以它对应的位权
,然后相加。
【公式】
转十进制
位于
位(个位),权重为
。
位于
位(十六位),权重为
。
注意:十六进制中的
对应十进制的
。
转十进制
。
将十进制数连续除以目标进制
,直到商为 0。每次得到的余数从下往上(逆序)排列,即为目标进制数。
转十六进制
余 10 (A)。
余 1。
。
转二进制
运算 | 商 | 余数 (Binary Digit) |
|---|---|---|
125 ÷ 2 125 \div 2 125÷2 | 62 | 1 |
62 ÷ 2 62 \div 2 62÷2 | 31 | 0 |
31 ÷ 2 31 \div 2 31÷2 | 15 | 1 |
15 ÷ 2 15 \div 2 15÷2 | 7 | 1 |
7 ÷ 2 7 \div 2 7÷2 | 3 | 1 |
3 ÷ 2 3 \div 2 3÷2 | 1 | 1 |
1 ÷ 2 1 \div 2 1÷2 | 0 | 1 |
最终结果 | ↑ \uparrow ↑ (由下往上读) | 111110 1 2 1111101_2 11111012 |
621
310
151
71
31
11
01最终结果
(由下往上读)
八进制和十六进制的引入,就是为了更简洁地表示冗长的二进制。
8进制的数字每⼀位是0~7的,0~7的数字,各⾃写成2进制,最多有3个2进制位就⾜够了,⽐如7的⼆进制是111,所以在2进制转8进制数的时候,从2进制序列中右边低位开始向左每3个2进制位会换算⼀个8进制位,剩余不够3个2进制位的直接换算。
✅ 规则: 将二进制数从右边低位向左,每 3 位划分为一组,不足 3 位的在左侧补 0,然后将每组转换为对应的八进制数字(0~7)。
转八进制
2进制 | 110 | 101 | 011 |
|---|---|---|---|
分组 | 110 | 101 | 011 |
十进制 | 6 | 5 | 3 |
8进制 | 1 | 5 | 3 |
结果:
。
16进制的数字每⼀位是0~9, a~f 的,0~9, a~f的数字,各⾃写成2进制,最多有4个2进制位就⾜够了,⽐如 f 的⼆进制是1111,所以在2进制转16进制数的时候,从2进制序列中右边低位开始向左每4个2进制位会换算⼀个16进制位,剩余不够4个⼆进制位的直接换算。
✅ 规则: 将二进制数从右边低位向左,每 4 位划分为一组,不足 4 位的在左侧补 0,然后将每组转换为对应的十六进制数字(0~F)。
转十六进制
2进制 | 0110 | 1011 |
|---|---|---|
分组 | 0110 | 1011 |
十进制 | 6 | 11 |
16进制 | 6 | B |
结果:
。
//十进制转二进制
int DecToBin(int bin[], int num)
{
// i表示二进制有多少位
int i = 0;
if (num == 0)
return i;
//模2除2,结果存放在二进制数组中并带回
while (num)
{
bin[i++] = num % 2;
num /= 2;
}
return i;
}【代码理解】
num % 2得到当前位的余数(即二进制位 或
),并通过num /= 2更新商。
bin[]中。由于除法是从低位向高位进行的(余数先得到个位,再得到十位…),所以数组bin[]中存储的是逆序的二进制序列。i,即转换后得到的二进制序列的长度。//十进制转八进制
int DecToOct(int oct[], int num)
{
//统计八进制有多少位
int i = 0;
if (num == 0)
return i;
//模8除8,将结果存储在结果数组中
while (num)
{
oct[i++] = num % 8;
num /= 8;
}
return i;
}【代码理解】
num % 8获取八进制位,并通过 num /= 8更新商。模N除N的结构体现了进制转换算法的通用性,可以很容易地扩展到任意进制转换。oct[]同样存储的是逆序的八进制数字序列。//将余数映射为十六进制中的0-9,A-F字符
char Reflect(int num)
{
if (num > 9)
return 'A' + (num - 10);
else
return num + '0';
}
//十进制转十六进制
int DecToHex(char hex[], int num)
{
int i = 0;
if (num == 0)
return i;
while (num)
{
hex[i++] = Reflect(num % 16);
num /= 16;
}
return i;
}【代码理解】
到
用字符
到
表示。
时,直接加上 ‘0’ 转换为对应的数字字符(利用 ASCII 码连续性)。
时,通过'A' + (num - 10)将
映射为 ‘A’, ‘B’, \dots, ‘F’ 字符。
int BinToDec(char* bin)
{
//求二进制位数
int len = strlen(bin);
//定义结果变量
int decimal = 0;
//从后往前进行位权 相乘相加 求结果
int k = 0;//定义位权变量
for (int i = len - 1; i >= 0; i--)
{
//二进制为1时进行计算
if (bin[i] == '1')
decimal += pow(2, k);
//每次计算一位时,位权自增,无论该位是0还是1
k++;
}
return decimal;
}【代码理解】
)开始向左遍历。
开始递增,表示当前的位权是
。
的值(即该位的权重)累加到 decimal 结果中。
这里有两个方法:
十进制
八进制。
八进制。
那这里就以第二个方法进行演示吧。
char* BinToOct(char* bin)
{
//定义八进制序列
char oct[32] = "0";
int index = 0;
//二进制序列长度
int len = strlen(bin);
//从右端开始 3 位一组
for (int i = len-1; i >= 0; i -= 3)
{
//定义三个一组,控制内层相加次数
int count = 0;
//分组位权求和结果
int sum = 0;
for (int j = i; count < 3 && j >= 0; j--)
{
sum += pow(2, count) * (bin[j] - '0');
count++;
}
oct[index++] = (char)('0' + sum);
}
return oct;
}【代码理解】
count 充当该组内部的位权指数(),即
。
sum 累加这 3 位对应的十进制值,即完成了3位二进制 1位八进制的转换。
这里有两个方法:
十进制
十六进制。
十六进制。
那这里还是以第二个方法进行演示,第一个方法就只需要把上面的函数按照桥梁进行按顺序调用即可。
//二进制转十六进制(分组法)
char* BinToHex(char* bin)
{
//定义十六进制序列
char hex[32] = { 0 };
int index = 0;
//二进制序列长度
int len = strlen(bin);
//从右端开始 4 位一组
for (int i = len - 1; i >= 0; i -= 4)
{
//定义三个一组,控制内层相加次数
int count = 0;
//分组位权求和结果
int sum = 0;
for (int j = i; count < 4 && j >= 0; j--)
{
sum += pow(2, count) * (bin[j] - '0');
count++;
}
hex[index++] = Reflect(sum);
}
return hex;
}【代码理解】
到
。
映射为字符
,从而得到十六进制字符。
//八进制转十进制
int OctToDec(char* oct)
{
int num = 0;
//八进制字符串长度
int len = strlen(oct);
int k = 0;
for (int i = len - 1; i >= 0; i--)
{
num += pow(8, k) * (oct[i] - '0');
k++;
}
return num;
}【代码理解】
,因此使用 pow(8, k)。
。
这里有两个方法:
十进制
十六进制。
十六进制。
那这里以第一个方法进行演示,就只需要把上面的函数按照桥梁进行按顺序调用即可。
//八进制转十六进制(分组法)
char* OctToHex(char* oct,char* hex,int* count)
{
int num = OctToDec(oct);
*count = DecToHex(hex,num);
return hex;
}【代码理解】
//八进制转二进制
char* OctToBin(char* oct,int* bin,int* count)
{
int num = OctToDec(oct);
*count = DecToBin(bin, num);
return bin;
}【代码理解】 同理,该函数使用十进制作为桥梁,将八进制转换为二进制,实现了代码的复用性。
//十六进制转十进制
int HexToDec(const char* hex, int* ok)
{
char* end = NULL;
errno = 0;
long val = strtol(hex, &end, 16); /* 16 表示按十六进制解析 */
if (errno == ERANGE /* 溢出 */
|| end == hex /* 根本没数字 */
|| *end != '\0' /* 后面还有垃圾字符 */
|| val < INT_MIN || val > INT_MAX) {
*ok = 0;
return 0;
}
*ok = 1;
return (int)val;
}【代码理解】
运行结果展示:

以上部分代码比较繁琐,没有做到让代码有很高的复用性,旨在培养编程思维。其中很多小细节,类似整形溢出等问题,没有很好的考虑进去。