带有char的字符的十六进制表示不正确,但带有unsigned char的字符表示正确

时间:2015-05-05 17:21:34

标签: c file hex

我正在编写一个打印" hexdump"一个给定的文件。功能如下:

bool printhexdump (FILE *fp) {
    long unsigned int filesize = 0;
    char c;

    if (fp == NULL) {
        return false;
    }

    while (! feof (fp)) {
        c = fgetc (fp);
        if (filesize % 16 == 0) {
            if (filesize >= 16) {
                printf ("\n");
            }
            printf ("%08lx  ", filesize);
        }
        printf ("%02hx ", c);
        filesize++;
    }

    printf ("\n");
    return true;
}

但是,在某些文件中,似乎会打印某些无效的整数表示形式,例如:

00000000  4d 5a ff90 00 03 00 00 00 04 00 00 00 ffff ffff 00 00
00000010  ffb8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
00000020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000030  00 00 00 00 00 00 00 00 00 00 00 00 ff80 00 00 00
00000040  ffff

除了因ffff字符而导致的上一个EOFff90ffffffb8等都是错误的。但是,如果我将char更改为unsigned char,我会得到正确的表示形式:

00000000  4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00
00000010  b8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
00000020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000030  00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00
00000040  ff

为什么上述行为会发生?

修改cprintf()的处理方式应该相同,因为格式说明符不会更改。所以我不确定在char获胜的情况下,unsigned char如何延长签名?

5 个答案:

答案 0 :(得分:3)

问:cprintf()的处理应该相同,因为格式说明符不会改变。
答:OP是正确的,cprintf()的处理没有改变。改变的是传递给printf()的内容。在charunsigned char时,c通常会通过int整数促销。 char,如果已签名,则会获得一个符号扩展名。像0xFF这样的char值是-1。 {0x}之类的unsigned char值仍为255。

问:所以我不确定在char赢得“{1}}”时,unsigned char如何延长签名? 答:他们都有一个符号扩展名。 char可能是否定的,因此其符号扩展名可以是01位。 unsigned char始终为正,因此其符号扩展名为0位。

解决方案

char c;
printf ("%02x ", (unsigned char) c);
// or
printf ("%02hhx ", c);

// or
unsigned char c;
printf ("%02x ", c);
// or
printf ("%02hhx ", c);

答案 1 :(得分:2)

char可以是签名类型,在这种情况下,值0x800xff会在传递给printf之前进行符号扩展。

(char)0x80符号扩展为-128,无符号短符为0xff80。

[编辑]更清楚宣传;存储在char中的值是8位,并且在该8位表示中,类似0x90的值将表示-112或114,这取决于char是有符号还是无符号。这是因为最高有效位作为有符号类型的符号位,而无符号类型的幅度位。如果该位置位,则它使值为负(通过减去128)或使其变大(通过加128),具体取决于它是否为带符号类型。

从char到int的升级总是会发生,但如果char被签名然后将其转换为int,则需要将符号位展开到int的符号位,以便int表示与char相同的值。

然后printf得到它,但是不知道原始类型是签名还是未签名,并且它不知道它曾经是一个字符。它所知道的是格式说明符是无符号的十六进制短,因此它打印该数字就好像它是无符号短。 16位int中-112的位模式为1111111110010000,格式为十六进制,即ff90。

如果您的char是无符号的,则0x90不表示负值,当您将其转换为int时,无需在int中更改任何内容以使其表示相同的值。位模式的其余部分全为零,printf不需要那些正确显示数字。

答案 2 :(得分:1)

问题只是由格式引起的。 %h02x采用int。当你把一个低于128的字符时,一切都很好,它是正的,转换为int时不会改变。

现在,让我们拿一个128以上的字符,说0x90。作为unsigned char,它的值为144,它将转换为int值144,并打印在90。但作为一个带符号的char,它的值为-112(仍为0x90),它将转换为值为-112的int(对于16位int为0xff90)并打印为ff90

答案 3 :(得分:0)

因为在unsigned char中,最重要的位与signed char的含义不同。

例如,二进制文件中的0x9010010000144十进制,无符号,但签名为-16十进制。

答案 4 :(得分:0)

char是否已签名取决于平台。这意味着根据您的机器可能会或可能不会扩展符号位,因此您可以得到不同的结果。

但是,使用unsigned char可确保没有符号扩展名(因为不再有符号位)。