Char 和 int16 数组元素都显示为 32 位十六进制?

Char and int16 array element both shown as 32bit hex?

在下面的例子中:

int main(int argc, char *argv[])
{
    int16_t array1[] = {0xffff,0xffff,0xffff,0xffff};
    char array2[] = {0xff,0xff,0xff,0xff};
    printf("Char size: %d \nint16_t size: %d \n", sizeof(char), sizeof(int16_t));

    if (*array1 == *array2)
        printf("They are the same \n");
    if (array1[0] == array2[0])
        printf("They are the same \n");

    printf("%x \n", array1[0]);
    printf("%x \n", *array1);

    printf("%x \n", array2[0]);
    printf("%x \n", *array2);
}

输出:

Char size: 1 
int16_t size: 2 
They are the same 
They are the same 
ffffffff 
ffffffff 
ffffffff 
ffffffff

为什么 charint16_t 都打印了 32 位值,为什么可以比较它们并认为它们相同?

它们是相同的,因为它们都是 -1 的不同表示。

它们打印为 32 位的 ff 因为你在 32 位机器上并且你使用了 %d 默认参数 promotions 发生了(基本上,所有较小的东西都被提升为 int)。尝试使用 %hx。 (这可能会让你得到 ffff;除了使用 unsigned char 或使用 & 0xff 掩码之外,我不知道有什么方法可以在这里得到 ffprintf("%x \n", array2[0] & 0xff).)


在 "They're the same because they're all different representations of -1" 上扩展:

int16_t 是有符号的 16 位类型。它可以包含 -32768 到 +32767 范围内的值。
char 是一个 8 位类型,在你的机器上它显然也被签名了。因此它可以包含 -128 到 +127 范围内的值。

0xff 是十进制的 255,一个不能用有符号字符表示的值。如果将 0xff 分配给带符号的字符,则该位模式最终不会被解释为 255,而是 -1。 (同样,如果您指定 0xfe,则不会将其解释为 254,而是 -2。)

0xffff 是十进制的 65535,这个值不能用 int16_t 表示。如果将 0xffff 分配给 int16_t,该位模式最终不会被解释为 65535,而是 -1。 (同样,如果您分配 0xfffe,则不会将其解释为 65534,而是 -2。)

所以当你说

int16_t array1[] = {0xffff,0xffff,0xffff,0xffff};

基本上就像你说的那样

int16_t array1[] = {-1,-1,-1,-1};

当你说

char array2[] = {0xff,0xff,0xff,0xff};

就像你说的一样

char array2[] = {-1,-1,-1,-1};

所以这就是 *array1 == *array2array1[0] == array2[0].

的原因

此外,值得注意的是,所有这一切都与 array1array2 的类型有关。如果你改为说

uint16_t array3[] = {0xffff,0xffff,0xffff,0xffff};
unsigned char array4[] = {0xff,0xff,0xff,0xff};

您会看到打印出不同的值(ffffff),并且 array3array4 的值不会相同。

另一个回答说 "there is no type information in C at runtime"。这是真的,但在这种情况下具有误导性。当编译器生成代码来操作 array1array2array3array4 的值时,它生成的代码(当然 在运行时很重要!)将基于它们的类型。特别是,当生成代码以从 array1array2 获取值时(但 而不是 array3array4),编译器将在分配给更大类型的对象(例如 32 位)时,使用执行 符号扩展 的指令。这就是 0xff 和 0xffff 变成 0xffffffff 的方式。

因为 C 在运行时 中没有类型信息,并且通过使用普通的 %x 进行打印,您告诉 printf 您的指针指向一个 unsigned int。糟糕的库函数只是相信你...请参阅 printf(3) 中的 长度修饰符 以了解如何为 printf 提供所需的信息。

使用 %x 打印负值会导致 undefined behaviour,因此您不应假设您所看到的内容是合理的。

char 的正确格式说明符是 %hhdint16_t 的正确格式说明符是 "%" PRId16。您将需要 #include <inttypes.h> 来获取后一个宏。

由于默认参数促销,将%dcharint16_t一起使用也是正确的1。如果您将代码更改为使用 %d 而不是 %x,它将不再表现出未定义的行为,并且结果将是有意义的。


1 C 标准实际上并没有这么说,但假定那是作者的意图。