为什么在打印十六进制数时,printf不能只打印一个字节?

42

pixel_data是一个char类型的vector

当我执行printf(" 0x%1x ", pixel_data[0] )时,我希望看到的结果是0xf5

但是我得到的结果是0xfffffff5,好像我正在输出4字节整数而不是1字节。

为什么会这样?我已经给printf传递了一个char类型的变量进行打印 - 它只有1字节,那么为什么printf会打印出4个字节的结果?

NB. 这个printf实现被封装在第三方API中,但是我想知道这是否是标准printf的特性?


2
参见:https://dev59.com/D0_Ta4cB1Zd3GeqPEd27 - CB Bailey
5个回答

59

你可能会得到一种良性的未定义行为,因为 %x 修饰符期望一个 unsigned int 参数,而当传递给 可变参数 函数时,char 通常会被升级为 int

你应该显式地将 char 强制转换为 unsigned int 以获得可预测的结果:

printf(" 0x%1x ", (unsigned)pixel_data[0] );
请注意,字段宽度为1并不是非常有用的。它只是指定要显示的最小数字数,并且在任何情况下至少需要一个数字。
如果您平台上的char是有符号的,则此转换将把负char值转换为大型unsigned int值(例如fffffff5)。如果您想将字节值视为无符号值,并在转换为unsigned int时仅进行零扩展,您应该使用unsigned char代替pixel_data,或通过unsigned char进行转换或在升级后使用掩码操作。
例如:
printf(" 0x%x ", (unsigned)(unsigned char)pixel_data[0] );

或者

printf(" 0x%x ", (unsigned)pixel_data[0] & 0xffU );

1
@BeeBand:小心,仅仅更改为“unsigned char”可能不够。在大多数平台上,“unsigned char”会提升为“int”,而不是“unsigned int”。 - CB Bailey
@BeeBand:我已更新了我的答案,这是否更全面地回答了你的问题? - CB Bailey
@R..:很抱歉,我不认为我理解你们两个的观点。无论pixel_data的自然类型是什么,printf%x的正确类型都是unsigned int。如果这与pixel_data的类型不同(0-255和unsigned char并不是一定的),那么至少需要进行一次转换。是的,使用unsigned进行按位与操作将强制进行该转换,但这并不是唯一的选择。其次,如果实现有更大的char类型,你将没有其他选择,根据定义,char是任何实现中最小类型的大小。 - CB Bailey
@Charles Bailey:我认为使用'int'是必须的,以便对于两种类型都可表示的值,使用相同的内部存储表示。我肯定看过并编写了很多使用“%02X”说明符打印无符号字符的代码。我想标准可能会指定将malloc的指针强制转换为int指针并在那里存储某些内容将产生与将其强制转换为无符号指针并存储相同值的字节序列相同,而不指定使用可变参数函数的等效性。它说什么? - supercat
这个答案已经被另一个回答所取代,可以在这里找到新的答案。 - 303
显示剩余11条评论

7

建议使用标准格式标志

printf(" %#1x ", pixel_data[0] );

然后您的编译器会为您添加十六进制前缀。

6

使用 %hhx

printf("%#04hhx ", foo);

很遗憾,我正在使用第三方API实现的printf,似乎不允许使用hh格式化符号。但是还是很好知道这个信息,谢谢。 - BeeBand
这是正确的答案,如果你的printf实现支持它。如果你无论如何都要使用第三方的printf,请考虑使用nanoprintf,它支持此功能 https://github.com/charlesnicholson/nanoprintf - Charles Nicholson

3
然后,length修饰符是最小长度。

所以1是最小长度?我以为长度修饰符是以字节为单位的。 - BeeBand
3
这是指字符长度。 - leppie
抱歉,我不明白你的意思。为什么发生了什么事情就意味着 length 修饰符是最小长度? - BeeBand
@BeeBand:这是文档上的要求。您说您希望至少有1个字符长度。 - leppie
我了解了,谢谢!这非常有用,我这些年一直错了。但是,我决定选择Charles的答案,因为他解释了为什么我看到了前导"f"。 - BeeBand

2

printf 中的宽度指示符实际上是最小宽度。您可以使用 printf(" 0x%2x ", pixel_data[0] & 0xff) 来打印最低字节(注意 2,如果 pixel_data[0] 是例如 0xffffff02,则实际上会打印两个字符)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接