十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C语言进制转换全解析:从原理到内存操作与位运算实战

C语言进制转换全解析:从原理到内存操作与位运算实战 1. 项目概述从“数”的本质到程序中的表达在计算机的世界里我们每天都在和数字打交道。无论是屏幕上显示的一个像素颜色还是后台处理的一条用户数据其最底层的表达都离不开“进制”这个基础概念。对于C语言初学者甚至是一些已经写过几行代码的朋友来说进制转换常常是一个“知道很重要但总觉得隔着一层纱”的知识点。我们可能背下了“除2取余逆序排列”的口诀也能在纸上完成十进制转二进制的计算但一旦要把这套逻辑用C语言实现或者去理解一个十六进制数0xFF在内存中究竟如何存放就有点犯迷糊了。这个内容的核心就是把这层“纱”彻底揭开。它不仅仅是数学课上的进制换算练习更是理解计算机如何存储、运算和展示数据的关键一步。当你掌握了不同进制在C语言中的表示、转换和底层逻辑很多之前模糊的概念会瞬间清晰起来为什么char类型的范围是-128到127为什么用%x打印出的地址是一串十六进制数为什么位操作 | ~ ^如此高效答案都藏在进制与内存的映射关系里。本文将从最朴素的“数”的概念出发带你重新审视十进制、二进制、八进制和十六进制。我们会先建立清晰的数学转换逻辑然后重点落在C语言如何实现这些转换——既有用于人机交互的“格式化输入输出”也有用于程序内部处理的“算法实现”。最后我们会深入到内存层面看看这些进制表示如何与变量、指针、位运算产生联系。无论你是刚接触C语言的新手还是想夯实基础的中级开发者相信这些围绕“进制”展开的讨论都能让你对编程有更“底层”的认识。2. 核心需求解析为什么程序员必须懂进制转换在开始敲代码之前我们得先想明白在高级语言如此发达的今天为什么我们还要关心二进制、十六进制这些“底层”的东西直接让编译器处理不就好了吗这里有几个无法回避的刚性需求。2.1 需求一精准的内存操作与调试C语言被誉为“高级语言中的汇编语言”其强大之处在于它提供了直接操作内存的能力。当你使用指针、处理数组、或者进行位运算时你实际上是在和内存中一个个的二进制位打交道。场景举例你定义了一个整型变量int a 10;在大多数32位系统上它在内存中占4个字节32位。10的二进制是1010但实际存储会是00000000 00000000 00000000 00001010大端序为例。如果你用调试器如GDB去查看变量a在内存中的值或者用printf(“%p”, a);打印其地址显示出来的通常是一串十六进制数比如0x7ffeeb4d2c9c。如果你不懂十六进制就无法直观理解这个地址的范围和意义。更进一步如果你需要检查某块内存的具体内容例如分析一个网络数据包或文件头十六进制查看器Hex Viewer是你的主要工具里面的数据全是十六进制表示。核心价值理解进制特别是二进制和十六进制是读懂内存布局、进行低级调试和性能分析的“视觉语言”。没有它你就像在黑暗中摸索。2.2 需求二高效的位运算与标志位管理很多底层系统、协议和算法都依赖于位运算来提高效率、节省空间。而位运算的直接操作对象就是二进制位。场景举例在操作系统的文件权限管理中如Linux的chmodrwx权限就是用二进制位来表示的读r 4 (100) 写w 2 (010) 执行x 1 (001)。给一个文件设置rw-r--r--权限对应的八进制数字就是644二进制110 100 100。在C语言中我们可以用位掩码Bitmask来检查和设置这些标志位#define READ_PERM 0x4 // 十六进制表示二进制 0100 #define WRITE_PERM 0x2 // 0010 #define EXEC_PERM 0x1 // 0001 int permissions 0; permissions | READ_PERM | WRITE_PERM; // 添加读和写权限 if (permissions READ_PERM) { // 检查是否有读权限 printf(可读\n); }核心价值二进制是位运算的天然土壤。理解进制转换能让你设计出更优雅、更高效的位操作逻辑这在嵌入式开发、网络编程、图形处理等领域至关重要。2.3 需求三理解数据编码与跨系统交互计算机中所有的数据最终都是二进制。但为了人类可读或满足特定协议需要不同的进制表示。场景举例颜色表示。在Web开发中颜色常用十六进制RGB表示如#FF5733红色FF绿色57蓝色33。在C语言中处理图像时你可能需要将这样的字符串解析成单独的R、G、B分量每个分量0-255正好对应8位二进制即2位十六进制。又比如硬件寄存器配置、通信协议帧如CAN总线中的数据段其说明书上几乎清一色使用十六进制或二进制来定义每一位的含义。核心价值进制是连接人类抽象思维与机器物理实现的桥梁。掌握进制转换意味着你能准确无误地在协议文档、数据手册和你的C语言代码之间进行翻译。2.4 需求四夯实基础触类旁通进制转换是计算机科学最基础的知识之一。它不仅是C语言的问题更是所有编程语言、乃至整个计算机体系的基石。深刻理解它对于后续学习汇编语言、计算机组成原理、操作系统、编译原理等课程有极大的帮助。它帮你建立了一种“计算机是如何思考”的思维模型。注意很多初学者觉得进制转换枯燥是因为只停留在“手工计算”层面。我们的目标是将这种计算思维转化为编程思维用C语言作为工具去自动化、可视化地处理进制问题从而解决上述的实际需求。3. 进制基础与手工转换原理在让计算机帮我们转换之前我们必须自己先彻底搞懂转换的数学原理。这就像学开车得先知道方向盘、油门、刹车的功能才能谈自动驾驶。3.1 各进制定义与表示法十进制 (Decimal)这是我们最熟悉的进制基数为10使用数字0-9。每一位的权重是10的幂次。例如123.45 1*10^2 2*10^1 3*10^0 4*10^-1 5*10^-2。二进制 (Binary)计算机的母语基数为2使用数字0和1。每一位的权重是2的幂次。例如1011.01(B) 1*2^3 0*2^2 1*2^1 1*2^0 0*2^-1 1*2^-2 11.25。八进制 (Octal)基数为8使用数字0-7。早期计算机系统如PDP-8常用现在多作为一种简便的二进制缩写形式因为1位八进制正好对应3位二进制。例如157(O) 1*8^2 5*8^1 7*8^0 111。十六进制 (Hexadecimal)基数为16使用数字0-9和字母A-F或a-f代表10-15。它是目前最常用的二进制缩写形式因为1位十六进制正好对应4位二进制非常紧凑。例如A3F(H) 10*16^2 3*16^1 15*16^0 2623。在C语言中为了区分不同进制有特定的前缀二进制C语言标准本身不支持二进制字面量常量。但一些编译器如GCC提供了扩展支持使用0b或0B前缀如0b1011。可移植代码中应避免使用。八进制以数字0开头如0123表示八进制的123等于十进制的83。十进制无前缀如123。十六进制以0x或0X开头如0xA3F。3.2 核心转换算法除基取余法与乘基取整法所有整数部分的转换都遵循“除基取余逆序排列”小数部分遵循“乘基取整顺序排列”。我们以十进制转二进制为例彻底走一遍流程。目标将十进制数29.375转换为二进制。步骤1整数部分 (29) 转换 - 除2取余逆序排列29 ÷ 2 14 ... 余 1(最低位)14 ÷ 2 7 ... 余 07 ÷ 2 3 ... 余 13 ÷ 2 1 ... 余 11 ÷ 2 0 ... 余 1(最高位)将余数从最后一次计算逆序向上排列得到11101。所以29(10) 11101(2)。步骤2小数部分 (0.375) 转换 - 乘2取整顺序排列0.375 × 2 0.75 ... 整数部分 0(小数点后第一位)0.75 × 2 1.5 ... 整数部分 10.5 × 2 1.0 ... 整数部分 1将整数部分顺序排列得到011。所以0.375(10) 0.011(2)。步骤3合并29.375(10) 11101.011(2)实操心得小数转换时有时会出现无限循环的情况如十进制0.1转二进制。这时需要根据精度要求决定取多少位。这也是为什么在金融计算等对精度要求极高的场景中要使用特殊的十进制浮点数库如Java的BigDecimal而非原生的二进制浮点数如C的float/double以避免舍入误差。八进制、十六进制与二进制的快捷转换由于82^3162^4它们之间的转换可以“分组对应”这是手工转换的利器。二进制转八进制从二进制小数点开始向左整数部分和向右小数部分每3位一组不足3位的用0补足。然后将每组二进制数转换为对应的八进制数。例11101.011(2)-011101.011-35.3-35.3(8)二进制转十六进制同上但每4位一组。例11101.011(2)-00011101.0110(补零) -1D.6-1D.6(16)反向转换八/十六进制转二进制将每一位八进制数展开为3位二进制每一位十六进制数展开为4位二进制即可。掌握了这些手工方法我们就能验证程序输出的正确性做到心中有数。4. C语言中的进制表示与格式化输入输出理解了原理我们来看C语言如何“原生”地支持不同进制。这主要依赖于printf和scanf系列函数的格式化占位符。4.1 格式化输出printf的进制控制printf函数通过格式说明符来控制输出的进制。#include stdio.h int main() { int num 255; // 十进制输出 (无前缀) printf(十进制: %d\n, num); // 输出: 255 // 八进制输出 (无前缀或带0前缀取决于格式) printf(八进制 (不带0): %o\n, num); // 输出: 377 printf(八进制 (带0): %#o\n, num); // 输出: 0377 // 十六进制输出 (小写字母) printf(十六进制 (小写): %x\n, num); // 输出: ff printf(十六进制 (小写带0x): %#x\n, num); // 输出: 0xff // 十六进制输出 (大写字母) printf(十六进制 (大写): %X\n, num); // 输出: FF printf(十六进制 (大写带0X): %#X\n, num); // 输出: 0xFF // 注意C标准没有二进制的格式说明符 // 输出二进制需要自己编写函数见下一章。 return 0; }关键点解析%d: 十进制 (decimal)。%o: 八进制 (octal)。%#o会添加前导0更符合八进制书写习惯。%x/%X: 十六进制 (hexadecimal)。小写x输出如ff大写X输出如FF。%#x和%#X会添加前导0x或0X。#标志符用于%o,%x,%X时会输出该进制的标准前缀0, 0x, 0X。4.2 格式化输入scanf的进制解析scanf函数同样能识别不同进制的输入这为程序提供了灵活性。#include stdio.h int main() { int num1, num2, num3; printf(请输入一个十进制数: ); scanf(%d, num1); // 输入 255 printf(请输入一个八进制数 (可带0前缀): ); scanf(%o, num2); // 输入 0377 或 377 printf(请输入一个十六进制数 (可带0x前缀): ); scanf(%x, num3); // 输入 0xff 或 FF printf(\n你输入的数在内存中都是同一个值: \n); printf(十进制: %d, %d, %d\n, num1, num2, num3); printf(八进制: %#o, %#o, %#o\n, num1, num2, num3); printf(十六进制: %#x, %#x, %#x\n, num1, num2, num3); return 0; }运行上述程序无论你以255十进制、0377八进制还是0xff十六进制输入程序都会正确地将它们解析为同一个整数值在大多数系统上是255并以三种格式打印出来。这证明了在内存中数据是以统一的二进制形式存储的进制的不同只是我们“看待”和“输入输出”它的方式。注意事项使用scanf(“%o”, num)时如果用户输入了带前导0的数字如0123scanf会正确识别为八进制。但如果用户输入了123它也会按八进制解析123(8)等于83(10)这可能不是用户的本意用户可能想输入十进制123。因此在要求用户输入时清晰的提示至关重要。更好的做法是统一用%d或%x读取字符串然后使用strtol等函数进行更安全、更可控的转换可以指定基数并检测错误。5. 算法实现编写进制转换函数虽然printf/scanf能处理基本的输入输出但很多时候我们需要在程序内部进行进制转换例如将十进制数转换为二进制字符串保存或者解析用户输入的特定格式。这时就需要我们自己实现转换算法。5.1 十进制整数转任意进制2-16字符串这是一个经典问题。思路就是反复执行“除基取余”将余数转换为对应字符存储到字符串中最后将字符串反转。#include stdio.h #include string.h /** * 将十进制整数转换为指定进制的字符串 * param num 待转换的十进制整数 * param base 目标进制 (2-16) * param result 存储结果的字符数组调用者需保证其空间足够 */ void intToBase(int num, int base, char* result) { // 处理0的特殊情况 if (num 0) { result[0] 0; result[1] \0; return; } int index 0; int isNegative 0; // 处理负数先转成正数处理最后添加负号 if (num 0 base 10) { // 通常只在十进制输出中显示负号 isNegative 1; num -num; } char digits[] 0123456789ABCDEF; // 映射表 // 除基取余存储余数对应的字符 while (num 0) { int remainder num % base; result[index] digits[remainder]; num / base; } // 如果是负数添加负号 if (isNegative) { result[index] -; } result[index] \0; // 字符串结束符 // 反转字符串因为我们是逆序存储余数的 int i 0, j index - 1; while (i j) { char temp result[i]; result[i] result[j]; result[j] temp; i; j--; } } int main() { char buffer[33]; // 32位整数二进制最长为32位结束符 int number 255; intToBase(number, 2, buffer); printf(%d 的二进制是: %s\n, number, buffer); // 11111111 intToBase(number, 8, buffer); printf(%d 的八进制是: 0%s\n, number, buffer); // 377 intToBase(number, 16, buffer); printf(%d 的十六进制是: 0x%s\n, number, buffer); // FF intToBase(-10, 10, buffer); printf(-10 的十进制是: %s\n, buffer); // -10 return 0; }代码要点解析字符映射表digits这是关键技巧。余数remainder是一个0-15的数字通过digits[remainder]可以直接得到对应的字符0-9或A-F避免了繁琐的if-else判断。负数处理这里做了一个简化处理仅当目标进制为10时才添加负号。因为其他进制如二进制补码的负数表示更复杂通常我们转换的是整数的无符号形式。在实际应用中如果需要处理负数的二进制补码表示应对unsigned int类型进行操作。字符串反转由于计算余数是从低位到高位的而字符串存储是从高位到低位所以最后必须反转。缓冲区大小对于32位整数二进制形式最长可能为32位-2^31的补码表示加上符号位和结束符\0分配33字节是安全的。5.2 任意进制2-16字符串转十进制整数这是逆过程。思路是从字符串的高位左端开始遍历将每个字符根据映射表转换回数字然后套用“按权展开”的公式result result * base digit_value。#include stdio.h #include ctype.h #include string.h /** * 将指定进制的字符串转换为十进制整数 * param str 待转换的字符串 * param base 字符串的进制 (2-16) * return 转换后的十进制整数如果遇到非法字符返回0简单处理生产代码需更健壮 */ int baseToInt(const char* str, int base) { int result 0; int isNegative 0; int i 0; // 处理可能的正负号通常只对十进制有效 if (str[0] -) { isNegative 1; i; } else if (str[0] ) { i; } // 处理可能的进制前缀 0, 0x, 0X if (base 16) { if (str[i] 0 (str[i1] x || str[i1] X)) { i 2; } } else if (base 8) { if (str[i] 0) { i; } } // 核心转换逻辑 for (; str[i] ! \0; i) { char c str[i]; int digit; if (c 0 c 9) { digit c - 0; } else if (c A c F) { digit c - A 10; } else if (c a c f) { digit c - a 10; } else { // 非法字符 printf(错误非法字符 %c\n, c); return 0; } // 检查数字是否在进制范围内 if (digit base) { printf(错误数字 %d 超出 %d 进制范围\n, digit, base); return 0; } result result * base digit; } return isNegative ? -result : result; } int main() { printf(二进制 \1101\ 转十进制: %d\n, baseToInt(1101, 2)); // 13 printf(八进制 \173\ 转十进制: %d\n, baseToInt(173, 8)); // 123 printf(十六进制 \FF\ 转十进制: %d\n, baseToInt(FF, 16)); // 255 printf(十六进制 \0x1A\ 转十进制: %d\n, baseToInt(0x1A, 16)); // 26 printf(十进制 \-42\ 转十进制: %d\n, baseToInt(-42, 10)); // -42 return 0; }代码要点解析字符到数字的转换通过判断字符范围将其转换为对应的数值。c - 0是经典技巧利用了ASCII码中数字字符连续的特性。前缀处理为了增强鲁棒性函数尝试识别并跳过常见的进制前缀0、0x。这在处理用户输入时非常有用。核心算法result result * base digit这是迭代式的“按权展开”。例如对于二进制1101处理过程是((((0*21)*21)*20)*21) 13。错误处理简单的示例中遇到非法字符或数字超出范围就返回0。在实际项目中应该设置错误码或使用errno来更精确地报告错误类型。实操心得自己实现转换函数是理解算法最好的方式。但在生产代码中更推荐使用C标准库函数strtolstring to long它功能更强大、更安全能自动处理不同进制、检测溢出、跳过空白字符等。例如long num strtol(“0xff”, NULL, 16);。自己实现的函数更适合于教学和理解原理。6. 深入内存进制、字节序与位运算实战理解了进制的表示和转换我们终于可以深入到最有趣的部分看看这些数字在计算机内存中究竟是如何“生活”的以及我们如何通过C语言直接与它们互动。6.1 窥探内存使用指针和十六进制查看器内存中的数据本质上是连续的字节序列。每个字节由8个二进制位组成用两个十六进制数表示非常方便因为0xFF正好是一个字节的最大值。#include stdio.h int main() { int num 0x12345678; // 一个32位整数 unsigned char *p (unsigned char*)# // 指向num首字节的指针 printf(整数 0x%X 在内存中的字节序列是\n, num); for(int i 0; i sizeof(num); i) { printf(地址 %p: 0x%02X\n, (void*)(pi), *(pi)); } // 浮点数示例 float f 3.14159f; unsigned char *pf (unsigned char*)f; printf(\n浮点数 %f 在内存中的字节序列是\n, f); for(int i 0; i sizeof(f); i) { printf(地址 %p: 0x%02X\n, (void*)(pfi), *(pfi)); } return 0; }运行这段代码你可能会看到类似这样的输出具体地址和字节顺序因机器而异整数 0x12345678 在内存中的字节序列是 地址 0x7ffeed3a5c9c: 0x78 地址 0x7ffeed3a5c9d: 0x56 地址 0x7ffeed3a5c9e: 0x34 地址 0x7ffeed3a5c9f: 0x12注意看我们定义的数是0x12345678高位是12低位是78但在我的机器上低地址存放的是0x78低位字节。这种存储方式称为小端序Little Endian即数据的低位字节存放在内存的低地址处。另一种是大端序Big Endian即高位字节在低地址。网络传输通常使用大端序网络字节序。理解字节序对于网络编程、文件格式解析和跨平台数据传输至关重要。6.2 位运算直接操纵二进制位位运算允许我们直接操作整数在内存中的二进制位效率极高。它们是进制知识最直接的应用。运算符名称描述示例 (假设 a0b1100, b0b1010)按位与两位都为1时结果才为1a b 0b1000(0b1100 0b1010)|按位或两位有一个为1时结果就为1a | b 0b1110(0b1100 | 0b1010)^按位异或两位不同时结果为1a ^ b 0b0110(0b1100 ^ 0b1010)~按位取反对每一位取反~a 0b...11110011(取决于位数)左移所有位左移低位补0a 2 0b110000(相当于 a * 4)右移所有位右移高位补符号位(算术右移)或0(逻辑右移)a 2 0b0011(相当于 a / 4)实战场景1标志位Flag管理假设我们有一个系统状态变量status用其不同的位代表不同的状态1为真0为假。#define STATUS_A (1 0) // 第0位: 0b0001 #define STATUS_B (1 1) // 第1位: 0b0010 #define STATUS_C (1 2) // 第2位: 0b0100 #define STATUS_D (1 3) // 第3位: 0b1000 unsigned char status 0; // 初始状态全0 // 设置状态B和D为真 status | (STATUS_B | STATUS_D); // status 0b1010 // 检查状态C是否为真 if (status STATUS_C) { printf(状态C为真\n); } else { printf(状态C为假\n); // 会执行这里 } // 清除状态B status ~STATUS_B; // ~STATUS_B 0b1101, status 0b1000 // 切换状态A如果为真则变假反之亦然 status ^ STATUS_A;实战场景2快速乘除与取特定位unsigned int x 10; // 左移一位相当于乘以2 x x 1; // x 20 // 右移一位相当于除以2对于无符号数 x x 1; // x 10 // 提取一个32位数低16位 unsigned int data 0xABCD1234; unsigned short low16 data 0xFFFF; // low16 0x1234 // 提取高16位 unsigned short high16 (data 16) 0xFFFF; // high16 0xABCD注意事项位运算的优先级通常低于算术运算符。在复杂表达式中务必使用括号来明确运算顺序例如(a 0xFF) 8。另外对有符号数进行右移操作 () 是“算术右移”高位补符号位这可能导致负数右移后保持负数。对无符号数右移是“逻辑右移”高位补0。这个细节需要牢记。7. 常见问题与排查技巧实录在实际学习和使用中关于进制转换和位操作总会遇到一些“坑”。这里记录了几个最常见的问题和我的排查思路。7.1 问题一printf输出八进制或十六进制时结果和预期不符典型症状int a 65; printf(“%o”, a);你期望输出101八进制但可能输出了101或0101或其他。排查思路检查变量值首先确认变量a的值确实是65。可以在输出前用printf(“a%d”, a);验证。理解格式说明符%o输出的是无符号八进制整数。如果a是负数输出的是其补码形式的八进制表示这会是一个很大的数。例如a -1在32位系统上输出可能是37777777777。检查前缀%o不会自动添加前导0。如果你需要标准格式应使用%#o。验证计算手动计算65的八进制65 / 8 8 ... 1,8 / 8 1 ... 0,1 / 8 0 ... 1逆序101。与程序输出对比。解决方案确保你理解格式说明符的含义对于负数要特别注意。使用%#o和%#x可以获得带标准前缀的输出便于阅读。7.2 问题二自己实现的进制转换函数对于负数或大数处理错误典型症状转换-10为二进制字符串得到的是乱码或正数的二进制。排查思路区分有符号与无符号计算机存储负数使用的是补码。你的转换函数很可能直接对负数进行了“除基取余”操作这在数学上对于负整数是未定义的余数可能为负。确定转换目标你想得到的是数值的绝对值的二进制表示还是其补码表示前者用于显示后者才是内存中的真实形态。使用无符号类型如果要得到补码表示最安全的方法是先将有符号整数转换为对应的无符号类型然后对无符号数进行转换。例如unsigned int u (unsigned int)num;然后再转换u。缓冲区溢出对于32位整数二进制字符串最长需要33字节32位符号位\0。你分配的缓冲区够大吗解决方案对于显示用途可以像我们前面示例那样单独处理负号然后对绝对值进行转换。这适用于十进制输出。对于内存/位操作用途转换为无符号类型后再处理。例如要打印一个整数的二进制补码void printBinary(int num) { unsigned int mask 1 (sizeof(int)*8 - 1); // 最高位的掩码 for(int i 0; i sizeof(int)*8; i) { putchar((num mask) ? 1 : 0); mask 1; if ((i1) % 8 0) putchar( ); // 每8位加空格 } putchar(\n); }7.3 问题三位运算结果诡异尤其是移位操作典型症状int x -8; x x 1;结果x是-4而不是-4等等这好像是对的或者unsigned int y 0x80000000; y y 1;结果不是0x40000000。排查思路有符号右移是算术右移对于有符号数右移操作 () 会保持符号位不变即高位补符号位。-8的二进制补码假设8位是11111000右移一位补符号位1得到11111100这是-4的补码。所以结果是正确的但可能和“除以2”的直觉不符对于负数算术右移一位确实是除以2并向下取整。无符号右移是逻辑右移对于无符号数右移操作高位补0。0x80000000 1结果是0x40000000。如果结果不对检查变量类型是否真的是unsigned。移位位数超过类型宽度在C语言中如果移位的位数大于或等于操作数的位宽行为是未定义的。int a 1; a a 33;在32位系统上可能导致任何结果。注意运算符优先级a 0xFF 8等价于a (0xFF 8)这可能不是你想要的。应该写成(a 0xFF) 8。解决方案明确你的操作对象是有符号数还是无符号数。对于有符号数的右移牢记它是算术右移。避免移位位数过大。在复杂的位运算表达式中大量使用括号。7.4 问题速查表问题现象可能原因快速检查与解决printf(“%o”, -1)输出一长串数字%o以无符号形式输出负数的补码理解补码概念或先取绝对值再转换转换函数对0输出为空或错误循环条件while(num 0)忽略了0在函数开始处单独处理num 0的情况十六进制字符串转整数”0x1G”也转换了未做有效的字符合法性检查在转换循环中增加digit base的判断1 31得到负数在32位系统上1是int左移31位后符号位被置1使用1U 31(无符号整数) 或1L 31(长整型)位运算设置/清除标志位无效混淆了、|、^的用法或掩码值错误复习真值表用|设置位用 ~mask清除位内存查看时字节顺序和写的不一样主机字节序小端/大端问题理解字节序概念网络传输时使用htonl/ntohl等函数转换掌握进制转换远不止于通过考试。它是你打开计算机系统底层大门的第一把钥匙。从理解一个简单变量在内存中的模样到设计高效的位操作算法再到解析复杂的网络数据包这项基础技能无处不在。我个人的体会是初期多用手工计算来强化概念中期多用C语言编写转换函数来理解过程后期则在调试和位操作中反复应用。当你看到一段内存数据能立刻在脑中将其转化为十六进制和二进制并理解其含义时那种对程序的掌控感是完全不同的。最后分享一个小技巧在阅读底层代码或协议文档时身边常备一个程序员计算器如Windows自带的计算器切换到“程序员”模式或使用在线的进制转换工具可以随时进行快速的进制换算和位查看能极大提升效率。
返回列表