观察如下代码,在 x86_64 系统上,你猜会输出什么?
char a = 0xa1;
printf("%02x", a);
你是不是觉得会打印 "a1"?
错!!
实际输出是:ffffffa1
没错,你没看错 —— 它打印的是一个 int 类型负数的十六进制表示。
🔍 为什么会这样?
在 C 语言中,当你使用 printf 的 %x 或 %02x 格式符时,参数会经历“整型提升(integer promotion)” —— 所有比 int 小的整型(如 char、short)都会被自动提升为 int(或在极少数情况下为 unsigned int),然后再传给 printf。这是 C 语言“默认参数提升规则”的一部分。
📜 C 标准规定(C17 §6.3.1.1):
如果int能表示原类型的所有值,则提升为int;否则提升为unsigned int。
在主流平台(如 x86_64、x86_32)上,int 是 32 位(4 字节),而 char 是 8 位 —— 所以 char 总是被提升为 int。
🧪 回到我们的例子:
char a = 0xa1; // 在有符号 char 平台上,0xa1 = 161 > 127 → 被解释为负数(-95)
当它被提升为 int 时,会进行符号扩展 —— 即高位补 1,变成:
0xffffffa1 (32 位)
然后 %x 会把这个 int 当作 unsigned int 来解释,于是打印出 ffffffa1。
💡 注意:
%x格式符总是把参数当作unsigned int来处理,不管它原本是不是负数!
📏 关于 %02x 中的 02
很多人误以为 %02x 会“截断”输出为 2 位,其实不是:
02表示:最小宽度为 2,不足补 0;超过 2 位,照常输出。- 比如:
int a = 0xabc;
printf("%02x", a); // 输出 "abc",不是 "bc",也不是 "0abc"
✅ 正确做法:显式转为 unsigned char
如果你只想打印一个字节对应的 2 位十六进制值,请强制转换为 unsigned char:
char a = 0xa1;
printf("%02x", (unsignedchar)a); // ✅ 输出 "a1"
这样,0xa1 会被当作无符号值 161 处理,提升为 int(161),再被 %x 打印为 "a1" —— 完美!
怎么样,是不是又学到了一个 C 语言的“温柔陷阱”?😄
下次打印字节数据,记得加 (unsigned char) —— 你的调试生活会清爽很多!
👏 欢迎点赞、收藏、转发~
