请考虑以下简化代码.我想从文件中提取一些二进制数据/流,并以十六进制格式将其打印到标准输出.
我有额外的3个字节0xFFFFFF
.怎么了?额外的字节来自哪里?
产量
in:
2000FFFFFFAF00690033005A00
out:
2000FFFFFFAF00690033005A00
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
Rya*_*ing 14
签名扩展.你的编译器实现char
的signed char
.当你通过角色时,printf
他们在升级到int
s 期间都会被标记延长.当第一位为0时,这无关紧要,因为它随0
s 扩展.
0xAF
在二进制中是10101111
因为第一个位是a 1
,当它传递给printf
它时,会1
在转换中使用所有s进行扩展,int
使其成为11111111111111111111111110101111
十六进制值.
解决方案:而是unsigned char
用来防止在调用中出现符号扩展形式
const unsigned char raw[] = {0x20,0x00,0xAF,0x00,0x69,0x00,0x33,0x00,0x5A,0x00};
Run Code Online (Sandbox Code Playgroud)
原始示例中的所有这些值都是符号扩展的,它只是0xAF
第一个中带有a的唯一值1
.
另一个更简单的相同行为示例
signed char c = 0xAF; // probably gives an overflow warning
int i = c; // extra 24 bits are all 1
assert( i == 0xFFFFFFAF );
Run Code Online (Sandbox Code Playgroud)
这是因为从有符号字符转换为有符号整数时0xAF为负(它是有符号扩展),并且%02X
格式用于无符号参数,并将转换后的值打印为FFFFFFAF
。
之所以%x
会出现多余的字符,是因为printf 永远不会默默地截断某个值的数字。非负值也将进行符号扩展,但这只是添加零位,并且该值适合2个十六进制数字,因此printf %02
可以处理两位数字。
请注意,有2种C语言方言:一种是普通char
符号,另一种是普通符号。在你的签名。您可以使用选项更改它,例如gcc和clang支持-funsigned-char
和-fsigned-char
。