printf从char数组中添加额外的`FFFFFF`到十六进制打印

use*_*.dz 12 c arrays printf

请考虑以下简化代码.我想从文件中提取一些二进制数据/流,并以十六进制格式将其打印到标准输出.

我有额外的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

签名扩展.你的编译器实现charsigned char.当你通过角色时,printf他们在升级到ints 期间都会被标记延长.当第一位为0时,这无关紧要,因为它随0s 扩展.

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)


Jen*_*ens 6

这是因为从有符号字符转换为有符号整数时0xAF为负(它是有符号扩展),并且%02X格式用于无符号参数,并将转换后的值打印为FFFFFFAF

之所以%x会出现多余的字符,是因为printf 永远不会默默地截断某个值的数字。非负值也将进行符号扩展,但这只是添加零位,并且该值适合2个十六进制数字,因此printf %02可以处理两位数字。

请注意,有2种C语言方言:一种是普通char符号,另一种是普通符号。在你的签名。您可以使用选项更改它,例如gcc和clang支持-funsigned-char-fsigned-char