问题描述
我正在准备考试,问了以下问题,以下程序在 32 位或 64 位中生成什么输出?
#include <stdio.h>
int main(int argc,char **argv)
{
long *p = (long *)8;
short *q = (short *)0;
int c,i,l;
p = p + 1;
q = q + 1;
printf("p = %p q = %p\n",p,q);
c = (char *)p - (char *)q;
i = (int *)p - (int *)q;
l = (long *)p - (long *)q;
printf("c = %d,i = %d,l = %d\n",c,l);
return 0;
}
32 位的结果如下:
p = 0xc q = 0x2
c = 10,i = 2,l = 2
64 位的结果如下:
p = 0x10 q = 0x2
c = 14,i = 3,l = 1
只是我必须承认,我根本无法理解这些结果,因为当我计算一个指针时,它应该指向内存中随机的东西,它总是出现 0xc 或 0x10。
有人可以帮我吗?
解决方法
只是我必须承认,我根本无法理解这些结果,因为当我计算一个指针时,它应该指向内存中随机的东西,它总是出现 0xc 或 0x10。
程序不会访问任何指针的“指针指向的内存中的值”。它只做指针运算(改变指针指向的东西)。
问题的目标可能是测试您是否了解指针运算在 C 中的工作原理。
指针算法在 C 中的工作原理是它隐藏了“乘以 sizeof(无论指针指向什么)”。换句话说,p = p + n;
大致相当于 p = (char *)p + n * sizeof(*p);
,或者 p = &p[n];
。
首先,我应该指出这段代码中有很多未定义的行为,并且它做出了很多标准无法保证的假设。但话虽如此,这个问题可以根据您将在典型实现中看到的内容来回答。
假设两台机器上的数据类型大小(以字节为单位)是:
类型 | 32 位大小 | 64 位大小 |
---|---|---|
长 | 4 | 8 |
内部 | 4 | 4 |
短 | 2 | 2 |
字符 | 1 | 1 |
同样,我不认为这些是有保证的,但它们非常典型。
现在考虑代码在做什么。首先是设置
long *p = (long *)8;
short *q = (short *)0;
这些地址不引用有效数据(至少,不是可移植的),但代码实际上从未真正延迟它们。它所做的只是使用它们来执行指针运算。
首先,它增加它们:
p = p + 1;
q = q + 1;
向指针添加整数时,整数按目标数据类型的大小进行缩放。因此,在 p
的情况下,比例是 4(对于 32 位)或 8(对于 64 位)。在 q 的情况下,尺度是 2(在两种情况下)。
所以 p
变为 12(对于 32 位)或 16(对于 64 位),而 q
变为 2(在这两种情况下)。以十六进制打印时,12 是 0xc
,16 是 0x10
,因此这与您看到的一致。
然后取两个指针之间的差异,首先将它们转换为各种不同的指针类型:
c = (char *)p - (char *)q;
i = (int *)p - (int *)q;
l = (long *)p - (long *)q;
这些结果表现出未定义的行为,但如果您假设所有指针值都是字节地址,那么就会发生这种情况。
首先,当两个相同类型的指针相减时,差值是除以目标数据类型的大小。这给出了分隔两个指针的该类型元素的数量。
所以在 c
(类型 char *
)的情况下,它除以 1,给出原始指针差异,即 (12 - 2) = 10(对于 32 位)和 ( 16 - 2) = 14(64 位)。
对于i
(类型int *
),它除以4(并截断结果),所以差异是10 / 4 = 2(对于32位)和14 / 4 = 3 (对于 64 位)。
最后,对于 l
(类型 long*
),它除以 4(对于 32 位)或 8(对于 64 位),再次截断结果。所以差异是 10 / 4 = 2(对于 32 位)和 14 / 8 = 1(对于 64 位)。
同样,C 标准不保证任何这些,因此假设这些结果将在不同平台上获得是不安全的。