打印存储为字符串的十六进制值会产生意外的输出

Posted

技术标签:

【中文标题】打印存储为字符串的十六进制值会产生意外的输出【英文标题】:Printing the hex value stored as a string gives unexpected output 【发布时间】:2015-10-29 10:28:14 【问题描述】:

我在 C 语言中定义了字符串中的十六进制数字:

char chars[] = "\xfb\x54\x9c\xb2\x10\xef\x89\x51\x2f\x0b\xea\xbb\x1d\xaf\xad\xf8";

然后我想将这些值与另一个值进行比较。它不起作用,如果我打印如下值:

printf("%02x\n", chars[0]);

它写fffffffb。为什么会这样以及如何准确获取 fb 值?

【问题讨论】:

处理二进制数据时,您总是希望使用unsigned char。我会更改声明而不是添加演员表。 【参考方案1】:

这是因为符号扩展。

改变

printf("%02x\n", chars[0]);

printf("%02x\n", (unsigned char)chars[0]);

%x 格式说明符将在 32 位机器上读取 4 bytes。由于您已将chars 声明为字符数组,因此在获取值fb(负值)时将符号扩展为fffffffb,其中fb 的MSB 设置为它之前的所有其他位。

更多详情请参考sign extension

如果您将char chars[] 声明为unsigned char chars[],则打印结果将符合预期。

【讨论】:

【参考方案2】:

根据标准提及 %x 格式说明符和 fprintf()

o,u,x,X

无符号整数参数转换为无符号八进制(o),无符号 dddd 风格的十进制 (u) 或 无符号十六进制表示法(x 或 X); [...]

因此,%x 的预期参数类型是 unsigned int

现在,printf() 是一个可变参数函数,只有默认提升规则应用于其参数。在您的代码中,charschar 类型的数组(其符号取决于实现),以防

printf("%02x\n", chars[0]);

chars[0] 的值被提升为 int,这不是 %x 的预期类型。因此,输出是错误的,因为 intunsigned int 不是同一类型。 [请参阅 §6.7.2,C11]。所以,没有像

这样的明确演员表
printf("%02x\n", (unsigned int)chars[0]);

它调用undefined behaviour。

FWIW,如果你有一个支持 C99 的编译器,你可以使用 hh 长度修饰符来解决这个问题,比如

 printf("%02hhx\n", (unsigned char)chars[0]);

【讨论】:

[次要旁注]:我认为,C 开发人员之间有一个共同的共识,即 %x 格式说明符对 int 对象有效,它持有 nonnegative价值。例如 int i = 100; printf("%x\n", i); 是可以接受的,即使类型不是unsigned int。但是,严格来说是UB,对吧? @GrzegorzSzpetkowski 是的,这就是我所知道的。 :-)【参考方案3】:

这是因为符号扩展。

这将按您的预期工作:

printf("%02x\n", (unsigned char)chars[0]);

【讨论】:

以上是关于打印存储为字符串的十六进制值会产生意外的输出的主要内容,如果未能解决你的问题,请参考以下文章

打印获取 MAC 地址函数的 std::string 时出现意外输出?

Powershell:将八位字节字符串 (SNMP) 输出转换为十六进制(Mac 地址)

读取 uart 十六进制值会忽略某些字节/字符

为什么以太网密钥库密文 以十六进制字符串格式存储,长度仅为64?

Console.WriteLine 为十六进制

如何将枚举的十六进制值打印到C++中的字符串