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

时间:2015-06-27 15:55:50

标签: c arrays printf

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

我得到额外的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;
}

3 个答案:

答案 0 :(得分:13)

签署扩展名。您的编译器正在将int power(int a,int b) { if (b != 1) return a * power(a, b-1); } 实现为char。当你将字符传递给signed char时,他们在晋升到printf期间都会被签名。当第一位为0时,这并不重要,因为它会以int s扩展。

二进制文件中的

00xAF由于第一位是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中所述。