在下面的例子中:
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
为什么为char
和int16_t
打印32位值,为什么它们可以进行比较并被认为是相同的?
它们是相同的,因为它们都是-1的不同表示。
它们打印为32位的ff
值,因为您在32位机器上使用%d
,并且默认参数提升发生了(基本上,所有较小的参数都被提升为int
)。尝试使用%hx
。(这可能会让你得到ffff
;我不知道有什么方法可以得到ff
,除了使用unsigned char
,或者用& 0xff
屏蔽:printf("%x n", array2[0] & 0xff)
。)
展开"它们是相同的,因为它们都是-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 == *array2
和array1[0] == array2[0]
。
同样,值得注意的是,所有这些都很大程度上是因为array1
和array2
的类型。如果你改成
uint16_t array3[] = {0xffff,0xffff,0xffff,0xffff};
unsigned char array4[] = {0xff,0xff,0xff,0xff};
您将看到打印不同的值(ffff
和ff
),并且array3
和array4
的值不会进行相同的比较。
另一个回答说"在运行时C语言中没有类型信息"。这是事实,但在这种情况下是误导。当编译器生成代码来操作array1
、array2
、array3
和array4
中的值时,它生成的代码(当然在运行时是重要的!)将基于它们的类型。特别是,当生成从array1
和array2
获取值的代码时(但不是 array3
和array4
),编译器将在赋值给更大类型的对象(例如32位)时使用执行符号扩展的指令。这就是为什么0xff和0xffff变成了0xffffff
因为在运行时C 中没有类型信息,并且通过使用普通的%x
进行打印,您告诉printf
您的指针指向unsigned int
。差劲的库函数信任你…参考printf(3)
中的长度修饰符,了解printf
需要的信息
使用%x
打印负数会导致未定义的行为,所以你不应该假设你所看到的有任何合理的东西。
char
的正确格式说明符是%hhd
, int16_t
的正确格式说明符是"%" PRId16
。您将需要#include <inttypes.h>
来获取后一个宏。
由于默认参数提升,因此将%d
与char
和int16_t
1一起使用也是正确的。如果您将代码更改为使用%d
而不是%x
,它将不再显示未定义的行为,并且结果将是有意义的。
1 C标准实际上并没有这么说,但假设这是作者的意图。