以下代码
#include <stdio.h>
int main()
{
long long data = 0xFFFEABCD11112345;
char *pData = (char *)&data;
printf("Value at address %p is %x\n", pData, *pData);
pData = pData + 5;
printf("Value at address %p is %x\n", pData, *pData);
return 0;
}
产生类似于
的输出Value at address 00000023515FFC00 is 45
Value at address 00000023515FFC05 is ffffffab
鉴于
pData
是 char *
,我预计第二个值是 ab
而不是 ffffffab
。我相信 %x
格式说明符可能是罪魁祸首,但我并不完全理解它。领先的 f
来自哪里?
char
可以是有符号的也可以是无符号的,具体取决于编译器。 char 默认是有符号的还是无符号的? 在本例中它似乎是有符号的。
在主流计算机上,有符号
char
只能保存值 -128 到 127。0xAB
在这样的计算机上将是十进制负数 2 的补码表示 -85
。
C 有各种形式的隐式类型提升,当在大多数表达式中使用像
char
这样的小类型时,或者在本例中,当传递给可变参数函数 printf
时,就会发生这种情况。可变参数函数的特殊隐式提升规则集称为“默认参数提升”,它们规定小整数类型会提升为 int
,无论符号性如何。
如果我们有一个带符号的
char
,其值为-85,那么在升级到int
期间,该符号受到尊重,这称为符号扩展。这意味着该值仍然是 -85,但升级后的 int
的二进制 2 补码表示可能是 0xFFFFFFAB
(假设为 32 位 int)。
但是,如果我们有无符号
char
,其值为 0xAB/171,则在升级到 int
期间,该值将被保留,并且不存在任何符号。所以我们可以通过转换来避免符号扩展:(unsigned char)*pData
。从有符号到无符号的显式转换是明确定义的。
printf
的格式字符串与此促销活动无关。 %x
需要一个 unsigned int
的参数,因此我们实际上是在撒谎打印,因为我们传递了 char
提升为 int
,严格来说是未定义的行为。然而,在这种情况下,printf
只是读取int
的二进制表示并将其呈现为0xFFFFFFAB
。