请考虑以下简化代码。我想从文件中提取一些二进制数据/流,并以十六进制格式将其打印到标准输出。
我得到额外的3个字节0xFFFFFF
。怎么了?多余的字节从哪里来?
输出
in:
2000FFFFFFAF00690033005A00
out:
2000FFFFFFAF00690033005A00
program.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;
}
答案 0 :(得分:13)
签署扩展名。您的编译器正在将int power(int a,int b)
{
if (b != 1)
return a * power(a, b-1);
}
实现为char
。当你将字符传递给signed char
时,他们在晋升到printf
期间都会被签名。当第一位为0时,这并不重要,因为它会以int
s扩展。
0
为0xAF
由于第一位是10101111
,因此在将其传递给1
时,会将其扩展为所有printf
转换为1
使其成为int
,即您拥有的十六进制值。
解决方案:而是使用11111111111111111111111110101111
来防止在通话中出现签名扩展表格
unsigned char
原始示例中的所有这些值都是符号扩展的,只是const unsigned char raw[] = {0x20,0x00,0xAF,0x00,0x69,0x00,0x33,0x00,0x5A,0x00};
是唯一一个在第一位有0xAF
的值。
同一行为的另一个更简单的例子(live link):
1
答案 1 :(得分:6)
那是因为0xAF从有符号字符转换为有符号整数时为负(符号扩展),%02X
格式用于无符号参数,并将转换后的值打印为{{1 }}
出现额外的字符是因为printf FFFFFFAF
从不默默地截断值的数字。非负值的值也会被扩展,但是这只是添加零位,并且值适合2个十六进制数字,因此printf %x
可以使用两位数输出。
请注意,有两种C方言:一种是普通%02
签名,另一种是无符号方言。在你的签名。您可以使用选项更改它,例如gcc和clang支持char
和-funsigned-char
。
答案 2 :(得分:1)
printf()
是可变参数函数,其附加参数(与其原型的...
部分对应)受默认参数提升的约束因此char
被提升为int
。
当您的char
已签署 1 ,two's complement表示时,0xAF
元素的最高有效位设置为1。在促销期间,会传播比特,从而生成0xFFFFFFAF
int
类型,在您的实施中可能是sizeof(int) = 4
。
顺便提一下,您正在调用undefined behaviour,因为%X
格式说明符应该用于unsigned int
类型的对象,或者至少用于int
,而MSB是未设置的(这是常见的,被广泛接受的做法)。
根据建议,您可以考虑使用明确的unsigned char
类型。
1)实现可以选择char
的有符号和无符号表示。 char
签名是相当普遍的,但你不能认为它是地球上其他所有编译器都是理所当然的。其中一些可能允许在这两种模式之间进行选择,如Jens's answer中所述。