- 此问题在此处已有答案**:
Why does printf not print out just one byte when printing hex?(5个答案)
六年前关闭了。
考虑下面的简化代码。我想从一个文件中提取一些二进制数据/流,并将其以十六进制格式打印到标准输出。
我得到了额外的3个字节0xFFFFFF
。怎么了?额外的字节是从哪里来的?
- 输出**
in:
2000FFFFFFAF00690033005A00
out:
2000FFFFFFAF00690033005A00
- 程序. c**
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
int i;
char raw[10] = {0x20,0x00,0xAF,0x00,0x69,0x00,0x33,0x00,0x5A,0x00};
FILE *outfile;
char *buf;
printf("in:\n\t");
for( i=0; i<10; i++ )
printf("%02X", raw[i]);
outfile = fopen("raw_data.bin", "w+b");
fwrite(raw, 1, 10, outfile);
buf = (char *) malloc (32 * sizeof(char));
fseek(outfile, 0, SEEK_SET);
fread(buf, 1, 10, outfile);
printf("\nout:\n\t");
for( i=0; i<10; i++ )
printf("%02X", buf[i]);
printf("\n");
fclose(outfile);
return 0;
}
3条答案
按热度按时间r6l8ljro1#
符号扩展。您的编译器将
char
实现为signed char
。当您将字符传递给printf
时,它们在提升为int
s的过程中都会进行符号扩展。当第一位为0时,这并不重要,因为它会使用0
s进行扩展。0xAF
在二进制中是10101111
由于第一位是1
,当将其传递给printf
时,在转换为int
的过程中,它将扩展为所有1
,使其成为11111111111111111111111110101111
,即0xFFFFFFAF
,即您所拥有的十六进制值。**解决方案:**使用
unsigned char
(而不是char
)防止在调用中发生符号扩展原始示例中的所有这些值都进行了符号扩展,只是
0xAF
是唯一一个第一位为1
的值。相同行为的另一个简单示例(live link):
bnl4lu3b2#
这是因为0xAF在从有符号字符转换为有符号整数时是负数(它是符号扩展的),而
%02X
用于无符号参数,并将转换后的值打印为FFFFFFAF
。额外字符的出现是因为printf
%x
* 从不 * 悄悄地从一个值中截断数字。非负值也会得到符号扩展,但这只是添加0位,值适合2个十六进制数字,所以printf%02
可以输出2位数字。请注意,C语言有两种方言:一个是普通的
char
有符号的,另一个是无符号的。在你的中它是有符号的。你可以用一个选项来改变它,例如gcc和clang支持-funsigned-char
和-fsigned-char
。s1ag04yj3#
printf()
是一个 * 可变参数 * 函数,其附加参数(对应于其原型的...
部分)受 * 默认参数提升 * 的影响,因此char
被提升为int
。由于您的
char
具有signed1,two's complement表示形式,因此0xAF
元素的最高有效位设置为1。在升级期间,将传播带符号位,从而生成int
类型的0xFFFFFFAF
,可能与您的实现中的sizeof(int) = 4
相同。顺便说一下,您正在调用undefined behaviour,因为
%X
说明符应该用于unsigned int
类型的对象,或者至少用于MSB未设置的int
(这是常见的、广泛接受的做法)。按照建议,您可以考虑使用明确的
unsigned char
类型。1)实现可以在
char
的有符号和无符号表示之间进行选择。char
有符号是相当常见的,但是你不能认为这对地球上的其他编译器来说是理所当然的。其中一些编译器可以允许在这两种模式之间进行选择,就像Jens's answer中提到的那样。