在C中打印十六进制字符


103

我试图读取一行字符,然后打印出等价的十六进制字符。

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

c0 c0 61 62 63 31 32 33

但是,printf使用%x给了我

ffffffc0 ffffffc0 61 62 63 31 32 33

没有,如何获得所需的输出"ffffff"?为什么只有c0(和80)具有ffffff,而其他字符却没有呢?


匹配您的字节数组的字符串将是..."\xc0\xc0abc123"
burito

Answers:


132

您会看到ffffff因为char已在系统上签名。在C语言中,可变参数的功能,比如printf将促进所有整数小于intint。由于char是整数(在您的情况下为8位带符号整数),因此您的字符将int通过符号扩展提升。

由于c080具有前导1位(并且作为8位整数为负),因此对它们进行符号扩展,而样本中的其他字符则不进行符号扩展。

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

这是一个解决方案:

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

这将屏蔽掉高位,并仅保留所需的低8位。


15
我的解决方案使用unsigned char强制转换为x86-64的gcc4.6中的指令更小...
lvella 2011年

1
也许我可以帮忙。这是(技术上)未定义的行为,因为说明符x需要一个无符号类型,但是ch被提升为int类型。正确的代码只需将ch强制转换为无符号,或者使用强制转换为unsigned char和说明符:hhx
2501年

1
如果我有printf("%x", 0),则什么也不会打印。
古斯塔沃·梅拉

它不会打印任何内容,因为最小值被设置为0。要解决此问题,请尝试printf("%.2x", 0);将将绘制的最小字符提升为2。要设置最大值,请在前面加上。与数字。例如,您只能强制执行以下操作绘制2个字符printf("%2.2x", 0);
user2262111 '19

有什么理由printf("%x", ch & 0xff)比使用printf("%02hhX", a)@brutal_lobster的答案更好?
maxschlepzig

62

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

printf("%hhX", a);

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

printf("%02hhX", a);

ISO / IEC 9899:201x说:

7长度修饰符及其含义是:hh指定以下d,i,o,u,x或X转换说明符适用于带符号的char或无符号的char参数(该参数将根据整数提升进行提升,但其值在打印之前应转换为带符号的字符或无符号的字符);或以下


30

您可以创建一个未签名的字符:

unsigned char c = 0xc5;

打印它会给C5而不是ffffffc5

仅打印大于127的字符,ffffff因为它们为负数(字符已签名)。

或者,您可以char在打印时进行投放:

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

3
+1最佳答案,显式键入尽可能接近数据声明(但不要更接近)。
鲍勃·斯坦

13

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

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

13

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

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

编辑:如果读者担心2501的断言是某种程度上不是“正确”格式说明符,我建议他们再次阅读该printf链接。特别:

即使%c期望使用int参数,也可以安全地传递char,因为调用可变参数函数时会发生整数提升。

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

至于他关于有符号与无符号的观点,在这种情况下没有关系,因为这些值必须始终为正,并且可以轻松地放入带符号的int中。无论如何,没有带符号的十六进制格式说明符。

编辑2:(“当您承认错误时”版):

如果您阅读第311页的实际C11标准(PDF的329),则会发现:

HH:用于指定后续的dioux,或X转换说明适用于一个signed charunsigned char参数(该参数将已经根据整数促销促进,但它的值应被转换成signed charunsigned char打印之前); 或以下n转换说明符适用于指向signed char参数的指针。


对于uint8_t类型,说明符不正确。固定宽度类型使用特殊的打印说明符。请参阅:inttypes.h
2501年

是的,但是所有varargs整数都隐式提升为int。
Timmmm '16

可能是这样,但是就C而言,如果不使用正确的说明符,则行为是不确定的。
2501年

但是%x 正确的说明符。(charunsigned char提升为int)[ en.cppreference.com/w/cpp/language/variadic_arguments]。您只需要对平台中不适合的内容使用PRI规范int-例如unsigned int
Timmmm

%x对于unsigned int不是int是正确的。char和unsigned char类型被提升为int类型。另外,不能保证将uint8_t定义为无符号字符。
2501年

2

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


-1

尝试这样的事情:

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.