11 C 语言数据进制详解:常见进制、前缀表示、格式化输出、混合运算、整数转换、小数转换、转换工具

11 C 语言数据进制详解:常见进制、前缀表示、格式化输出、混合运算、整数转换、小数转换、转换工具 1C 语言中的整数进制在计算机科学中所有数据的底层表示与处理都基于二进制系统。无论是简单的数值、字符还是复杂的多媒体信息如图像、音频和视频在计算机内部都会被编码为二进制形式进行存储与运算。然而二进制Base-2表示法通常非常长对于人类开发者来说直接阅读和书写大量的 0 和 1 既不直观也极易出错。为了更方便地观察和操作底层的二进制数据计算机科学中引入了八进制Octal和十六进制Hexadecimal。它们作为二进制的紧凑表示形式或称为 “简写”极大地提高了可读性。因此理解以下几种常见的进制系统对于学习 C 语言至关重要。1.1 常见进制系统二进制Base-2使用数字 0 和 1 表示数值。在二进制中每一位的基数为 2当某一位的值达到 2 时便向高位进位即 “逢二进一”。这是计算机内部存储和处理数据的基本方式。八进制Base-8使用数字 0 到 7 表示数值。在八进制中每一位的基数为 8当某一位的值达到 8 时便向高位进位即 “逢八进一”。这是一种早期用于简化二进制表示的进位制。十进制Base-10使用数字 0 到 9 表示数值。在十进制中每一位的基数为 10当某一位的值达到 10 时便向高位进位即 “逢十进一”。这是人类日常生活中最广泛使用的计数系统。十六进制Base-16使用数字 0 到 9 以及字母 A 到 F或 a 到 f表示数值其中 A – F 分别对应十进制的 10 – 15。在十六进制中每一位的基数为 16当某一位的值达到 16 时便向高位进位即 “逢十六进一”。这是目前最常用的人类可读的二进制 “简写” 形式。以下表格展示了十进制数值 0 到 17 在这四种进制下的对应写法二进制八进制十进制十六进制000011111022211333100444101555110666111777100010881001119910101210A10111311B11001412C11011513D11101614E11111715F10000201610100012117111.2进制的前缀表示在 C 语言中整数常量可以通过特定的前缀来表示其所属的进制。这有助于开发者在代码中清晰地书写不同进制的数值也方便编译器正确解析。具体规则如下二进制Binary以 0b 或 0B 开头后跟 0 和 1 组成的数字序列。例如0b1010表示十进制的10。标准说明请注意此 0b 前缀在 C23 标准中才被正式采纳。在 C99/C11/C17 等早期标准中这是一种非标准的编译器扩展尽管 Gcc、Clang、MSVC 等主流编译器早已支持。八进制Octal以数字 0 开头后跟 0 到 7 的数字。这是 C 语言的传统标准自 C89/C90 起。例如012表示十进制的10。十进制Decimal直接书写数字不带任何前缀。这是 C 语言中最常见的书写方式。例如10表示十进制的10。十六进制Hexadecimal以 0x 或 0X 开头后跟 0 - 9 和 A - Fa - f。与八进制一样这也是 C 语言的传统标准自 C89/C90 起。例如0xA表示十进制的10。以下示例程序演示了如何在 C 语言中使用这些前缀并将它们以十进制形式使用%d格式符输出到控制台以验证它们的值#include stdio.h int main(void) { // 1. 使用不同进制前缀定义同一个数值十进制的 10 int decimal_val 10; // 十进制 (无前缀) int octal_val 012; // 八进制 (以 0 开头) int hex_val 0xA; // 十六进制 (以 0x 开头) // 注意0b 前缀是 C23 标准或编译器扩展 int binary_val 0b1010; // 二进制 (以 0b 开头) printf(--- 演示使用不同进制表示十进制的 10 ---\n); printf(十进制 10 的值是: %d\n, decimal_val); printf(八进制 012 的值是: %d\n, octal_val); printf(十六进制 0xA 的值是: %d\n, hex_val); printf(二进制 0b1010 的值是: %d\n, binary_val); // 2. 演示另一个值 (十进制 31) int hex_complex 0x1F; // 十六进制 1F 1*16 15 31 int oct_complex 037; // 八进制 37 3*8 7 24 7 31 printf(\n--- 演示使用不同进制表示十进制的 31 ---\n); printf(十六进制 0x1F 的值是: %d\n, hex_complex); printf(八进制 037 的值是: %d\n, oct_complex); return 0; }程序运行结果如下环境Win64 VS Code MinGW-w64 UCRT1.3进制的格式化输出在 1.2 节中我们学习了如何在代码中书写不同进制的整数。本节我们将学习如何使用 printf 函数将一个在内存中以二进制存储的整数以不同的进制形式输出打印到控制台。printf 通过 “格式占位符” 来控制输出的进制。常见格式占位符%d或%i以有符号十进制形式输出整数。%o以无符号八进制形式输出整数不含前缀 0。%x以无符号十六进制形式输出整数使用小写字母 a - f不含前缀 0x。%X以无符号十六进制形式输出整数使用大写字母 A - F不含前缀 0X。带前缀的格式化输出我们可以使用 # 标志来强制 printf 输出该进制的标准前缀%#o以无符号八进制输出并自动添加前缀 0。%#x以无符号十六进制小写输出并自动添加前缀 0x。%#X以无符号十六进制大写输出并自动添加前缀 0X。⚠️ 注意%o 与 %x 的无符号特性%o、%x、%X 格式符以及带 # 的版本总是将参数视为无符号整数。若传入一个负数例如 -1printf 会将其解释为该负数在内存中对应的那个巨大的无符号正整数。例如在 32 位系统上将 -1 作为 %x 格式输出会打印出 ffffffff。 扩展理解有符号signed与无符号unsigned你可能想知道为什么 %x 会把 -1 打印成 ffffffff。这涉及到 C 语言中整数的 “有符号” 和 “无符号” 特性有符号signedint 类型默认是 “有符号” 的意味着它可以存储正数、负数和零。它会使用最高的一个位符号位来区分正负。无符号unsignedunsigned int 类型假定数值永远非负0 或正数。因此它将所有的位包括那个本应是 “符号位” 的最高位都用来表示数值大小能表示的最大正数约是 int 的两倍但不能表示负数。这就解释了 printf 的行为在 C 语言的内部规则中-1 在内存中恰好是以所有位都为 1 的二进制模式来表示的当使用 %d有符号时printf 检查符号位理解这个 “全 1” 模式代表 -1。但当你使用 %x无符号时它会忽略 “符号位” 的规则将这个 “全 1” 模式当作一个巨大的正数来打印即 ffffffff。⚠️ 注意printf 不支持二进制输出C 语言的 printf 函数不支持直接输出二进制形式即没有类似 %b 的标准格式符。如果需要以二进制形式输出整数必须自行编写函数来实现。以下示例程序将演示上述所有格式占位符的功能特别是对比 %d有符号和 %x无符号在处理正数与负数-1时的区别#include stdio.h int main(void) { // --- 1. 演示一个正数 (65) --- // 65 (十进制) 101 (八进制) 41 (十六进制) int positive_num 65; printf(--- 演示正数 (65) ---\n); // 使用转义字符 %% 来输出一个百分号 % printf(十进制 (%%d): %d\n, positive_num); printf(八进制 (%%o): %o\n, positive_num); printf(十六进制 (%%x): %x\n, positive_num); printf(十六进制 (%%X): %X\n\n, positive_num); printf(--- 演示带前缀的格式 (65) ---\n); printf(带前缀八进制 (%%#o): %#o\n, positive_num); printf(带前缀十六进制 (%%#x): %#x\n, positive_num); printf(带前缀十六进制 (%%#X): %#X\n\n, positive_num); // --- 2. 演示负数 (-1) --- // (在 32 位系统上, -1 的十六进制内存表示为 0xFFFFFFFF) int negative_num -1; printf(--- 演示负数 (-1)验证无符号特性 ---\n); // %d 是有符号的它会检查符号位正确输出 -1 printf(有符号十进制 (%%d): %d\n, negative_num); // %x 是无符号的它会忽略符号位 // 将内存中 -1 的二进制模式 (全 1) 视为一个巨大的正数打印 printf(无符号十六进制 (%%x): %x\n, negative_num); // %o 也是无符号的 printf(无符号八进制 (%%o): %o\n, negative_num); return 0; }程序运行结果如下环境Win64 VS Code MinGW-w64 UCRT1.4 进制的混合运算在 C 语言中我们可以在源代码中使用多种进制形式来书写整数常量包括二进制0b...、八进制以 0 开头、十进制无前缀和十六进制0x...。一个核心概念是这些进制表示方式仅用于源代码中的 “书写形式”并不会影响整数在计算机内存中的 “实际存储” 和 “处理方式”。运算原理说明统一存储无论你在代码中写的是 10、012 还是 0xA当 C 语言编译器读取它们时都会立即将它们转换为相同的二进制模式存储在内存中都代表十进制的 10。统一运算在计算机内部CPU 进行数学运算时操作的始终是这些统一的二进制数据。结论这意味着不同进制的整数可以在表达式中自由混合使用编译器会自动处理完全不会影响运算结果。例如以下表达式是完全合法的int result 10 015 0x20;10 是十进制整数015 是八进制整数编译器将其解析为十进制的 130x20 是十六进制整数编译器将其解析为十进制的 32CPU 实际执行的运算是 10 13 32最终 result 的值为 55。以下示例程序演示了 C 语言如何处理不同进制的混合运算#include stdio.h int main(void) { // 1. 定义不同进制的整数变量 int decimal_val 10; // 十进制: 10 int octal_val 015; // 八进制: 015 (等于十进制 13) int hex_val 0x20; // 十六进制: 0x20 (等于十进制 32) // C23 标准或编译器扩展 int binary_val 0b1001; // 二进制: 1001 (等于十进制 9) // 2. 混合运算将所有值相加 // 预期结果: 10 13 32 9 64 int result decimal_val octal_val hex_val binary_val; // 3. 打印每个变量的十进制值以证明编译器已正确解析 printf(--- 变量在内存中的十进制值 ---\n); printf(源码 10 (十) %d\n, decimal_val); printf(源码 015 (八) %d\n, octal_val); printf(源码 0x20 (十六) %d\n, hex_val); printf(源码 0b1001 (二) %d\n\n, binary_val); // 4. 打印最终的混合运算结果 printf(--- 混合运算结果 ---\n); printf(源码: result 10 015 0x20 0b1001\n); printf(实际计算: result %d %d %d %d\n, decimal_val, octal_val, hex_val, binary_val); printf(最终结果 (十进制): %d\n, result); return 0; }程序运行结果如下环境Win64 VS Code MinGW-w64 UCRT2 常见进制间的相互转换在第 1 章中我们了解了 C 语言如何表示不同进制。而在实际编程和学习中我们还经常需要在这几种进制之间手动进行换算。本章将聚焦于这些常见进制间的相互转换主要针对整数并逐一讲解最核心的几对转换的具体方法。2.1 二进制 ↔ 十进制二进制→十进制规则采用【按权展开法】从二进制数的最低位最右侧开始将其定为第 0 位每一位的值乘以 2 的对应位数然后将所有乘积累加求和。示例将二进制数 1011 转换为十进制数如下图所示十进制→二进制规则采用【除基取余逆序排列法】将十进制数反复除以基数 2记录每次的余数直到商为 0。然后将所有余数按逆序即从下往上排列即可得到对应的二进制表示。示例将十进制数 56 转换为二进制数如下图所示2.2 八进制 ↔ 十进制八进制→十进制规则采用【按权展开法】从八进制数的最低位最右侧开始第 0 位每一位的值乘以 8 的对应位数然后累加求和。示例将八进制数 101 转换为十进制数如下图所示十进制→八进制规则采用【除基取余逆序排列法】将十进制数反复除以基数 8记录每次的余数直到商为 0然后将余数按逆序排列。示例将十进制数 156 转换为八进制数如下图所示2.3 十六进制 ↔ 十进制十六进制→十进制规则采用【按权展开法】从十六进制数的最低位最右侧开始第 0 位每一位的值A10B11 ... F15乘以 16 的对应位数然后累加求和。示例将十六进制数 0x34A 转换为十进制数如下图所示十进制 →十六进制规则采用【除基取余逆序排列法】将十进制数反复除以基数 16记录余数直到商为 0。若余数大于 9需用对应的十六进制字母A10B11 ... F15表示。最后将余数按逆序排列。示例将十进制数 356 转换为十六进制数如下图所示2.4 二进制 ↔ 八进制由于 8 2^3所以一位八进制数恰好对应三位二进制数。二进制→八进制规则采用【取三合一法】从二进制数的最低位右端起将二进制数每三位一组最高位不足三位时在左侧补零然后将每三位二进制数组合转换为对应的八进制数0 - 7。示例将二进制数 1101011 转换为八进制数如下图所示八进制→二进制规则采用【取一分三法】将八进制数的每一位数字都转换为对应的三位二进制数。最后将所有二进制数组合可省略最终结果最左侧的 0。示例将八进制数 153 转换为二进制数如下图所示2.5 二进制 ↔ 十六进制由于 16 2^4所以一位十六进制数恰好对应四位二进制数。二进制→十六进制规则采用【取四合一法】从二进制数的最低位右端起将二进制数每四位一组最高位不足四位时在左侧补零然后将每四位二进制数组合转换为对应的十六进制数0 - F。示例将二进制数 1001011 转换为十六进制数如下图所示十六进制→二进制规则采用【取一分四法】将十六进制数的每一位数字A10B11 ... F15都转换为对应的四位二进制数。最后将所有二进制数组合可省略最终结果最左侧的 0。示例将十六进制数 0x23B 转换为二进制数如下图所示 提示利用二进制作为高效 “桥梁”在进行非二进制与十进制的转换时例如 八进制 ↔ 十六进制或者在进行十进制→八/十六进制转换时若觉得 “除基取余法” 计算繁琐可始终先转换为二进制再利用分组法三合一、四合一快速得到结果。总结利用二进制作为中转可以显著降低复杂进制转换的计算量是一种高效、直观的转换方法。3 小数与通用进制转换方法在第 2 章中我们演示了最常见的几对整数进制转换。本章我们将深入探究这些转换背后的通用方法即 “原理”并在此基础上将转换规则从整数部分正式扩展到小数领域。3.1 整数部分的通用转换其他进制整数 → 十进制整数规则按权展开法。方法将整数的每一位乘以其对应权值基数的 n 次方然后求和。十进制整数 → 其他进制整数规则除基取余逆序排列。方法将十进制数反复除以目标基数如 2、8、16记录余数直至商为 0最后将余数逆序排列。3.2 小数部分的通用转换其他进制小数 → 十进制小数规则按权展开法使用负指数。方法与整数部分类似但小数部分的权值从小数点后第一位开始分别为基数的 -1-2-3 ... 次方。示例将二进制小数 0.101 转换为十进制小数。十进制小数 → 其他进制小数规则乘基取整顺序排列。方法将十进制小数部分乘以目标基数如 2取其结果的整数部分再将剩余的小数部分继续乘以基数再次取整 ... 以此类推直到小数部分为 0 或达到所需精度最后将取出的所有整数按顺序从上到下排列。示例将十进制小数 0.625 转换为二进制小数。3.3 转换的注意事项混合数处理对于包含整数和小数部分的数如 12.625应将整数部分和小数部分拆开分别使用各自的规则进行转换最后再将结果合并。无限循环小数某些十进制小数在转换为其他进制特别是二进制时无法被精确表示会出现无限循环的情况。这也是 C 语言中浮点数如 float、double产生精度误差的根本原因。精度控制由于存在无限循环小数在实际应用中例如 C 语言 printf 打印小数时程序无法无限计算下去。C 语言中用于处理小数的浮点数类型主要是 float 和 double都会设定一个固定的精度限制。例如float类型大约能保留6 - 7位有效的十进制数字double类型大约能保留15 -16 位超出部分会被截断或舍入。关于 float、double 以及 C 语言浮点数在内存中如何存储的详细知识我们将在后续的教程章节中进行深入讲解。目前你只需要了解 “十进制小数如 0.1在转换为二进制时可能不精确” 这个结论即可。4 进制转换工具在日常开发或技术交流中我们经常会遇到进制转换的问题。对于较小的数值建议熟练掌握基本的转换方法这不仅能提升工作效率也有助于展现专业素养。而对于较大数值手动转换容易出错且耗时。此时可以借助工具简化操作。4.1 计算器程序Windows 系统自带的计算器是一款实用且便捷的工具支持二进制、八进制、十进制与十六进制之间的快速转换操作方法如下所示1.打开计算器按下键盘上的 Win 键在搜索栏中输入 “计算器”然后点击打开程序2.切换为程序员模式点击计算器左上角的 “≡” 菜单图标选择 “程序员” 模式3.输入数据并查看结果在对应进制的输入框中输入数值其他进制区域将自动显示转换后的结果4.2 在线转换工具Windows 计算器程序员模式虽然方便但它有两个主要限制它不支持小数浮点数的转换并且最大只支持到十六进制。如果你需要处理第 3 章中提到的小数进制转换或进行如 36 进制等更大范围的进制转换那么功能更强大的 “在线进制转换工具” 将是更优的选择。其主要优势包括功能强大支持 2 ~ 36 进制之间的任意转换。支持小数它们能处理浮点型数据这对于验证第 3 章的 “乘基取整法”如 0.1 转二进制非常有用。跨平台无需安装有浏览器即可使用对 Mac 和 Linux 用户友好。例如oschina 工具箱https://tool.oschina.net/hexconvert就是一个广受好评的在线工具。它不仅支持 2 ~ 36 进制的任意转换也支持浮点型数据。具体操作界面如下所示