在 C 中打印十六进制字符

Posted

技术标签:

【中文标题】在 C 中打印十六进制字符【英文标题】:Printing hexadecimal characters in C 【发布时间】:2011-12-25 00:01:15 【问题描述】:

我正在尝试读入一行字符,然后打印出这些字符的十六进制等价物。

例如,如果我有一个字符串是"0xc0 0xc0 abc123",其中前两个字符是十六进制的c0,其余字符是ASCII 的abc123,那么我应该得到

c0 c0 61 62 63 31 32 33

但是,printf 使用 %x 给了我

ffffffc0 ffffffc0 61 62 63 31 32 33

如果没有"ffffff",我如何获得我想要的输出?为什么只有c0(和80)有ffffff,而其他字符没有?

【问题讨论】:

与您的字节数组匹配的字符串将是... "\xc0\xc0abc123" 【参考方案1】:

您看到的是ffffff,因为char 已在您的系统上签名。在 C 中,printf 等可变参数函数会将所有小于int 的整数提升为int。由于char 是一个整数(在您的情况下为8 位有符号整数),您的字符将通过符号扩展提升为int

由于 c080 具有前导 1 位(并且作为 8 位整数为负数),因此它们会进行符号扩展,而您的示例中的其他则没有。

char    int
c0 -> ffffffc0
80 -> ffffff80
61 -> 00000061

这里有一个解决方案:

char ch = 0xC0;
printf("%x", ch & 0xff);

这将屏蔽高位并仅保留您想要的低 8 位。

【讨论】:

我使用转换为unsigned char 的解决方案是gcc4.6 for x86-64 中的一条更小的指令... 也许我可以帮忙。这是(技术上)未定义的行为,因为说明符 x 需要无符号类型,但 ch 被提升为 int。正确的代码只需将 ch 强制转换为 unsigned,或使用强制转换为 unsigned char 和说明符:hhx 如果我有printf("%x", 0),则不会打印任何内容。 它不打印任何内容,因为最小值设置为 0。要解决此问题,请尝试使用 printf("%.2x", 0);,这会将绘制的最小字符增加到 2。要设置最大值,请在前面加上 .有一个数字。例如,您可以通过 printf("%2.2x", 0); 强制仅绘制 2 个字符 有什么理由让printf("%x", ch & 0xff) 比在@brutal_lobster 的answer 中使用printf("%02hhX", a) 更好?【参考方案2】:

确实,有到 int 的类型转换。 您还可以使用 %hhx 说明符将类型强制为 char。

printf("%hhX", a);

在大多数情况下,您还需要设置最小长度以用零填充第二个字符:

printf("%02hhX", a);

ISO/IEC 9899:201x 说:

7 长度修饰符及其含义是: hh 指定后面的 d、i、o、u、x 或 X 转换说明符适用于 有符号字符或无符号字符参数(参数将有 根据整数提升进行提升,但其值应为 打印前转换为有符号字符或无符号字符);或者那个 追随者

【讨论】:

【参考方案3】:

你可以创建一个无符号字符:

unsigned char c = 0xc5;

打印它会给出C5 而不是ffffffc5

只有大于 127 的字符使用ffffff 打印,因为它们是负数(char 已签名)。

或者您可以在打印时投射char

char c = 0xc5; 
printf("%x", (unsigned char)c);

【讨论】:

+1 真正的最佳答案,显式键入尽可能接近数据声明(但不接近)。【参考方案4】:

您可以使用hh 告诉printf 该参数是一个无符号字符。使用0 获得零填充,使用2 将宽度设置为2。xX 用于小写/大写十六进制字符。

uint8_t a = 0x0a;
printf("%02hhX", a); // Prints "0A"
printf("0x%02hhx", a); // Prints "0x0a"

编辑:如果读者担心 2501 声称这不是“正确”的格式说明符,我建议他们再次阅读printf link。具体来说:

尽管 %c 需要 int 参数,但传递 char 是安全的,因为调用可变参数函数时会发生整数提升。

在标题<cinttypes>(C++) 或<inttypes.h> (C) 中定义了固定宽度字符类型(int8_t 等)的正确转换规范(尽管 PRIdMAX、PRIuMAX 等与%jd、%ju 等).

至于他关于有符号与无符号的观点,在这种情况下,这无关紧要,因为值必须始终为正并且很容易适合有符号的 int。反正没有带符号的十六进制格式说明符。

编辑 2:(“何时承认你错了”版本):

如果您阅读第 311 页(PDF 的 329)上的 the actual C11 standard,您会发现:

hh:指定以下 diouxX 转换说明符适用于 signed charunsigned char 参数(参数将已按整数提升,但其值在打印前需转换为signed charunsigned char);或者后面的n 转换说明符适用于指向signed char 参数的指针。

【讨论】:

说明符对于 uint8_t 类型不正确。固定宽度类型使用特殊的打印说明符。见:inttypes.h 是的,但所有 varargs 整数都被隐式提升为 int。 可能是这样,但就 C 的定义而言,如果您不使用正确的说明符,则行为是未定义的。 但是 %x 正确的说明符。 (charunsigned char 被提升为 int)[en.cppreference.com/w/cpp/language/variadic_arguments]。您只需将 PRI 说明符用于不适合您的平台的事物 int - 例如unsigned int. %x 对于 unsigned int 而不是 int 是正确的。 char 和 unsigned char 类型被提升为 int。此外,不保证 uint8_t 被定义为 unsigned char。【参考方案5】:

您可能将值 0xc0 存储在 char 变量中,这可能是有符号类型,并且您的值是负数(最高有效位设置)。然后,在打印时,它被转换为int,并且为了保持语义等价,编译器用 0xff 填充额外的字节,因此负数 int 将具有与负数 char 相同的数值。要解决此问题,只需在打印时转换为 unsigned char

printf("%x", (unsigned char)variable);

【讨论】:

【参考方案6】:

您可能正在从有符号字符数组打印。从无符号字符数组打印或使用 0xff 屏蔽值:例如ar[i] & 0xFF。由于设置了高(符号)位,因此 c0 值正在符号扩展。

【讨论】:

【参考方案7】:

试试这样的:

int main()

    printf("%x %x %x %x %x %x %x %x\n",
        0xC0, 0xC0, 0x61, 0x62, 0x63, 0x31, 0x32, 0x33);

产生这个:

$ ./foo 
c0 c0 61 62 63 31 32 33

【讨论】:

以上是关于在 C 中打印十六进制字符的主要内容,如果未能解决你的问题,请参考以下文章

由c ++ ofstream打印到文件的神秘十六进制字符

C语言。编写一个程序,输入一个二进制的字符串(长度不超过32),然后计算出相应的十进制整数,并打印。

如何在C中将十六进制字符串转换为二进制字符串?

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

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

c语言输入数字转换成2进制字符串原理