例如,如果我有一个字符串是
"0xc0 0xc0 abc123"
,其中前两个字符是十六进制中的c0
,其余字符是ASCII中的abc123
,那么我应该得到c0 c0 61 62 63 31 32 33
然而,使用%x
的printf
给了我一个
ffffffc0 ffffffc0 61 62 63 31 32 33
我如何在输出中去除"ffffff"
?为什么只有c0(和80)具有ffffff
,而其他字符没有?
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
强制转换比原来节省了一条指令。 - lvellax
需要无符号类型,但 ch 被提升为 int。正确的代码应该只需将 ch 强制转换为无符号类型,或使用强制转换为无符号字符和说明符:hhx
。 - 2501printf("%x", 0)
,什么也不会被打印出来。 - Gustavo Meiraprintf("%.2x", 0);
,它将增加绘制的最小字符数到2。要设置最大值,请在.前面添加一个数字。例如,您可以通过执行printf("%2.2x", 0);
来强制绘制仅有2个字符。 - user2262111printf("%x", ch & 0xff)
比只使用 printf("%02hhX", a)
更好,就像 @brutal_lobster 的 答案 中所述的那样? - maxschlepzig实际上,可以将类型转换为int。 此外,您还可以使用%hhx限定符强制类型转换为char。
printf("%hhX", a);
在大多数情况下,您还需要设置最小长度以填充第二个字符为零:printf("%02hhX", a);
ISO/IEC 9899:201x规定:
7 长度修饰符及其含义为: hh指示后面的d、i、o、u、x或X转换说明符适用于signed char或unsigned char参数(根据整数提升,参数将被提升,但在打印之前其值必须转换为signed char或unsigned char);或者指示后续
你可以创建一个无符号字符:
unsigned char c = 0xc5;
打印它将会给出C5
而不是ffffffc5
。
只有大于127的字符会以ffffff
打印,因为它们是负数(char
是带符号的)。
或者您可以在打印时转换char
:
char c = 0xc5;
printf("%x", (unsigned char)c);
您可以使用hh
告诉printf
参数为无符号字符。使用0
进行零填充,2
设置宽度为2。x
或X
表示小写/大写十六进制字符。
uint8_t a = 0x0a;
printf("%02hhX", a); // Prints "0A"
printf("0x%02hhx", a); // Prints "0x0a"
编辑:如果读者对2501的说法有所担忧,认为这不是“正确”的格式说明符,我建议他们再次阅读printf
链接。特别是:
尽管%c期望int参数,但由于可变函数调用时发生的整数提升,传递char是安全的。
固定宽度字符类型(int8_t等)的正确转换规范在头文件
<cinttypes>
(C++)或<inttypes.h>
(C)中定义(虽然PRIdMAX、PRIuMAX等与%jd、%ju等同)。
至于他关于带符号与无符号的观点,在这种情况下并不重要,因为这些值必须始终为正,并且很容易适合有符号int。反正也没有带符号的十六进制格式说明符。
编辑2:(“何时承认错误”版):
如果您读实际的C11标准第311页(PDF的第329页),您会发现:
hh:指定后面的
d
、i
、o
、u
、x
或X
转换规范适用于signed char
或unsigned char
参数(根据整数提升,参数将被升级,但其值在打印之前应转换为signed char
或unsigned char
);或者后面的n
转换规范适用于指向signed char
参数的指针。
inttypes.h
。 - 2501char
和 unsigned char
会被提升为 int
) [http://en.cppreference.com/w/cpp/language/variadic_arguments]。只有在你的平台 int
不适用于某些情况时,才需要使用 PRI 说明符 - 例如 unsigned int
。 - Timmmmchar
变量中存储了值 0xc0,它可能是一个带符号的类型,而你的值是负数(最高位为1)。当打印时,它将被转换为 int
,为保持语义等效性,编译器会用 0xff 填充额外的字节,使得负的 int
具有与你的负的 char
相同的数值。要解决这个问题,只需在打印时将其强制转换为 unsigned char
:printf("%x", (unsigned char)variable);
您可能正在从signed char数组中打印内容。请改为从unsigned char数组中打印或使用掩码0xff:例如ar[i] & 0xFF。由于高(符号)位被设置,c0值被扩展为符号位。
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
unsigned signed hex binary
-----------------------------------------------------------
unsigned char: 127 127 7f 0111 1111
signed char: 127 127 7f 0111 1111
unsigned signed hex binary
---------------------------------------------------------------
unsigned char: 128 128 80 00000000 00000000 00000000 10000000
signed char: ... -128 ffffff80 11111111 11111111 11111111 10000000
程序:
#include <stdio.h>
void print(char c) {
unsigned char uc = c;
printf(" %15s %15s %15s\n", "unsigned", "signed", "hex");
printf("---------------------------------------------------------------\n");
printf("unsigned char: %15u %15i %15x\n", uc, uc, uc);
printf(" signed char: %15u %15i %15x\n\n", c, c, c);
}
void main() {
print(127);
print(128);
}
即使超过127,无符号字符仍会扩展为0,因为您明确告诉它它是一个正数。
打印有符号字符作为有符号整数时,可以看到符号扩展如何保留-128的值。
(编辑:在示例输出中添加了二进制列,稍后将在程序代码中包括此列。)
"\xc0\xc0abc123"
- burito