在 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
。
由于 c0
和 80
具有前导 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。x
或X
用于小写/大写十六进制字符。
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:指定以下
d
、i
、o
、u
、x
或X
转换说明符适用于signed char
或unsigned char
参数(参数将已按整数提升,但其值在打印前需转换为signed char
或unsigned char
);或者后面的n
转换说明符适用于指向signed char
参数的指针。
【讨论】:
说明符对于 uint8_t 类型不正确。固定宽度类型使用特殊的打印说明符。见:inttypes.h
是的,但所有 varargs 整数都被隐式提升为 int。
可能是这样,但就 C 的定义而言,如果您不使用正确的说明符,则行为是未定义的。
但是 %x 是 正确的说明符。 (char
和 unsigned 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 中打印十六进制字符的主要内容,如果未能解决你的问题,请参考以下文章