首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【C语言基石】一文吃透进制转换:原理、公式、代码全解析

【C语言基石】一文吃透进制转换:原理、公式、代码全解析

作者头像
Extreme35
发布2025-12-23 18:09:00
发布2025-12-23 18:09:00
80
举报
文章被收录于专栏:DLDL

进制转换是计算机科学中的基石之一。无论是程序设计、底层开发还是算法学习,都离不开对二进制、八进制、十进制和十六进制的理解与操作。本文将深入浅出地讲解C语言中进制转换的核心概念、数学原理,并提供使用C语言实现常见进制转换的完整代码示例。

一、核心概念:为什么需要进制转换?

在计算机内部,所有数据都以**二进制(Base-2)**形式存储,因为电子元件只有“开”和“关”两种状态(1和0)。然而,直接操作长串的二进制数字对人类来说非常不便,容易出错。

  • 十进制(Decimal, Base-10): 人类日常使用的计数系统。
  • 二进制(Binary, Base-2): 计算机底层存储和运算的系统。
  • 八进制(Octal, Base-8): 早期计算机中常用,现在较少,但在文件权限等场景仍有应用(C语言中以0开头)。
  • 十六进制(Hexadecimal, Base-16): 用于简洁表示二进制数据(每4位二进制对应1位十六进制),在内存地址、颜色编码、数据包等领域广泛使用(C语言中以0x0X开头)。

进制转换的目的: 是在人类习惯的表示法(十进制/十六进制)和计算机内部的存储方式(二进制)之间建立桥梁。

二、原理详解:进制转换的数学基础 (基于位权)

所有的进制转换都基于一个核心概念——位权(Place Value)。位权,即每个位置的数字乘以它所代表的权重。理解位权是掌握进制转换的关键。

位权(权重值)的确定规则是:从小数点(或最低位)开始,向左,第

i

位的权重是进制数

N

i

次方 (

N^i

)。

2.1 N进制转十进制(位权展开法)

这是最简单的转换方式。只需将 N 进制数的每一位数字,乘以进制数的相应幂次方(位权),再求和即可。

✅ 规则: 将 N 进制数

(d_{n}d_{n-1}...d_{1}d_{0})_N

的每一位数字

d_i

,乘以它对应的位权

N^i

,然后相加。

【公式】

(d_{n}d_{n-1}...d_{1}d_{0})_N = d_{n} \times N^{n} + ... + d_{1} \times N^{1} + d_{0} \times N^{0}
示例 1:十六进制
1A_{16}

转十进制

  1. 确定位权:
    A

    位于

    16^0

    位(个位),权重为

    16^0 = 1

    1

    位于

    16^1

    位(十六位),权重为

    16^1 = 16

  2. 展开求和:
1 \times 16^1 + A(10) \times 16^0
= 16 + 10
= 26_{10}

注意:十六进制中的

A

对应十进制的

10

示例 2:二进制
1101_{2}

转十进制

  1. 确定位权: 从右向左,位权依次为
2^0, 2^1, 2^2, 2^3

  1. 展开求和:
1 \times 2^3 + 1 \times 2^2 + 0 \times 2^1 + 1 \times 2^0
= 8 + 4 + 0 + 1
= 13_{10}

2.2 十进制转N进制(除N取余法)

将十进制数连续除以目标进制

N

,直到商为 0。每次得到的余数从下往上(逆序)排列,即为目标进制数。

示例 1:十进制
26_{10}

转十六进制

  • 第一次除法:
26 \div 16 = 1

10 (A)

  • 第二次除法:
1 \div 16 = 0

1

  • 结果: 从下往上依次读取余数
\rightarrow 1A

示例 2:十进制
125_{10}

转二进制

运算

余数 (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​

125 \div 2

621

62 \div 2

310

31 \div 2

151

15 \div 2

71

7 \div 2

31

3 \div 2

11

1 \div 2

01最终结果

\uparrow

(由下往上读)

1111101_2

2.3 二进制与其他进制的快速转换(分组法)

八进制和十六进制的引入,就是为了更简洁地表示冗长的二进制。

2.3.1 二进制转八进制(3位一组)

8进制的数字每⼀位是0~7的,0~7的数字,各⾃写成2进制,最多有3个2进制位就⾜够了,⽐如7的⼆进制是111,所以在2进制转8进制数的时候,从2进制序列中右边低位开始向左每3个2进制位会换算⼀个8进制位,剩余不够3个2进制位的直接换算。

✅ 规则: 将二进制数从右边低位向左,每 3 位划分为一组,不足 3 位的在左侧补 0,然后将每组转换为对应的八进制数字(0~7)。

示例:二进制
01101011_2

转八进制

2进制

110

101

011

分组

110

101

011

十进制

6

5

3

8进制

1

5

3

结果:

01101011_2 \rightarrow 0153_8

2.3.2 二进制转十六进制(4位一组)

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)。

示例:二进制
01101011_2

转十六进制

2进制

0110

1011

分组

0110

1011

十进制

6

11

16进制

6

B

结果:

01101011_2 \rightarrow 0x6B_{16}

三、代码实现

3.1 十进制转N进制

3.1.1 十进制转二进制
代码语言:javascript
复制
//十进制转二进制
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;
}

【代码理解】

  • 核心算法: 遵循除2取余法。循环体内通过num % 2得到当前位的余数(即二进制位
0

1

),并通过num /= 2更新商。

  • 存储机制: 余数被依次存入数组bin[]中。由于除法是从低位向高位进行的(余数先得到个位,再得到十位…),所以数组bin[]中存储的是逆序的二进制序列。
  • 返回值: 函数返回i,即转换后得到的二进制序列的长度。
3.1.2 十进制转八进制
代码语言:javascript
复制
//十进制转八进制
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;
}

【代码理解】

  • 核心算法: 与转二进制类似,遵循除8取余法。每次迭代通过num % 8获取八进制位,并通过 num /= 8更新商。
  • 通用性: 这种模N除N的结构体现了进制转换算法的通用性,可以很容易地扩展到任意进制转换。
  • 结果存储: 数组oct[]同样存储的是逆序的八进制数字序列。
3.1.3 十进制转十六进制
代码语言:javascript
复制
//将余数映射为十六进制中的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;
}

【代码理解】

  • Reflect 函数(关键): 十六进制中,数字
10

15

用字符

A

F

表示。

  • 当余数
num \le 9

时,直接加上 ‘0’ 转换为对应的数字字符(利用 ASCII 码连续性)。

  • 当余数
num > 9

时,通过'A' + (num - 10)

10, 11, \dots, 15

映射为 ‘A’, ‘B’, \dots, ‘F’ 字符。

  • DecToHex 函数: 遵循除16取余法。每次取余后,立即调用 Reflect 函数将余数转换为十六进制字符,并存储到字符数组 hex[] 中。

3.2 二进制转N进制

3.2.1 二进制转十进制
代码语言:javascript
复制
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;
}

【代码理解】

  • 核心算法: 遵循位权展开法。从二进制字符串的最低位(最右侧
i = len - 1

)开始向左遍历。

  • 位权计算: 变量 k 从
0

开始递增,表示当前的位权是

2^k

  • 累加求和: 只有当当前位是 ‘1’ 时,才将
2^k

的值(即该位的权重)累加到 decimal 结果中。

3.2.2 二进制转八进制

这里有两个方法:

  • 使用十进制数当桥梁,二进制
\rightarrow

十进制

\rightarrow

八进制。

  • 使用分组法直接转换:二进制
\rightarrow

八进制。

那这里就以第二个方法进行演示吧。

代码语言:javascript
复制
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;
}

【代码理解】

  • 核心算法: 遵循3位分组法。外层循环控制从右向左,每隔 3 位进行一次处理。
  • 内层计算: 内层循环从当前组的右侧 (j = i) 向左遍历 3 位。
    • count 充当该组内部的位权指数(
    0, 1, 2

    ),即

    2^0, 2^1, 2^2

    • sum 累加这 3 位对应的十进制值,即完成了3位二进制
    \rightarrow

    1位八进制的转换。

  • 结果转换: 得到的 sum (范围 0-7) 加上字符 ‘0’ 即可转换为八进制字符,并存入 oct[]。注意: 这里的 oct[] 也是逆序存储的八进制序列。
3.2.3 二进制转十六进制

这里有两个方法:

  • 使用十进制数当桥梁,二进制
\rightarrow

十进制

\rightarrow

十六进制。

  • 使用分组法直接转换:二进制
\rightarrow

十六进制。

那这里还是以第二个方法进行演示,第一个方法就只需要把上面的函数按照桥梁进行按顺序调用即可。

代码语言:javascript
复制
//二进制转十六进制(分组法)
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;
}

【代码理解】

  • 核心算法: 遵循4位分组法。与八进制转换类似,但外层循环是 i -= 4,内层循环限制 count < 4。
  • 内层计算: 4 位二进制求和得到的结果 sum 范围是
0

15

  • 结果转换: 得到的 sum 调用之前定义的 Reflect 函数,将
10-15

映射为字符

A-F

,从而得到十六进制字符。

3.3 八进制转N进制

3.3.1 八进制转十进制
代码语言:javascript
复制
//八进制转十进制
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;
}

【代码理解】

  • 核心算法: 再次使用位权展开法
  • 位权基数: 这里的位权基数是
8

,因此使用 pow(8, k)。

  • 数字提取: (oct[i] - ‘0’) 用于将字符形式的八进制数字(例如字符 ‘7’)转换为整数值
7

3.3.2 八进制转十六进制

这里有两个方法:

  • 使用十进制数当桥梁,八进制
\rightarrow

十进制

\rightarrow

十六进制。

  • 使用分组法直接转换:八进制
\rightarrow

十六进制。

那这里以第一个方法进行演示,就只需要把上面的函数按照桥梁进行按顺序调用即可。

代码语言:javascript
复制
//八进制转十六进制(分组法)
char* OctToHex(char* oct,char* hex,int* count)
{
    int num = OctToDec(oct);
    *count = DecToHex(hex,num);
    return hex;
}

【代码理解】

  • 该函数采用十进制桥梁法,将复杂的跨进制转换分解为两个已实现的简单步骤:
    • 调用 OctToDec 将八进制字符串转换为整数(十进制)。
    • 调用 DecToHex 将得到的十进制整数转换为十六进制字符串。
3.3.2 八进制转二进制
代码语言:javascript
复制
//八进制转二进制
char* OctToBin(char* oct,int* bin,int* count)
{
    int num = OctToDec(oct);
    *count = DecToBin(bin, num);
    return bin;
}

【代码理解】 同理,该函数使用十进制作为桥梁,将八进制转换为二进制,实现了代码的复用性。

3.3 十六进制转十进制

代码语言:javascript
复制
//十六进制转十进制
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;
}

【代码理解】

  • 标准库函数: 该函数使用了 C 语言标准库 <stdlib.h> 中的 strtol(String to Long)函数。strtol 是处理字符串转数字时最健壮的方法,特别是涉及到不同进制和错误检测时。
  • 参数 16: 传入的第三个参数 16 告诉 strtol 函数将输入字符串 hex 视为十六进制进行解析。
  • 错误检测(健壮性):
    • errno == ERANGE:检查结果是否超出了 long 类型的表示范围(溢出)。
    • end == hex:检查字符串是否包含有效数字。
    • *end != ‘\0’:检查字符串末尾是否包含无法解析的“垃圾”字符。
    • val < INT_MIN || val > INT_MAX:检查转换后的 long 值是否在目标 int 类型的范围内。
  • 实际应用: 在实际工程中,对于复杂的字符串进制解析,推荐使用 strtol 系列函数而非手动实现,以确保代码的健壮性和准确性。

四、总结

运行结果展示:

在这里插入图片描述
在这里插入图片描述

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、核心概念:为什么需要进制转换?
  • 二、原理详解:进制转换的数学基础 (基于位权)
    • 2.1 N进制转十进制(位权展开法)
      • 示例 1:十六进制
      • 示例 2:二进制
    • 2.2 十进制转N进制(除N取余法)
      • 示例 1:十进制
      • 示例 2:十进制
    • 2.3 二进制与其他进制的快速转换(分组法)
      • 2.3.1 二进制转八进制(3位一组)
      • 2.3.2 二进制转十六进制(4位一组)
  • 三、代码实现
    • 3.1 十进制转N进制
      • 3.1.1 十进制转二进制
      • 3.1.2 十进制转八进制
      • 3.1.3 十进制转十六进制
    • 3.2 二进制转N进制
      • 3.2.1 二进制转十进制
      • 3.2.2 二进制转八进制
      • 3.2.3 二进制转十六进制
    • 3.3 八进制转N进制
      • 3.3.1 八进制转十进制
      • 3.3.2 八进制转十六进制
      • 3.3.2 八进制转二进制
    • 3.3 十六进制转十进制
  • 四、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档