32 位或 64 位系统中的长短指针运算

问题描述

我正在准备考试,问了以下问题,以下程序在 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 标准保证任何这些,因此假设这些结果将在不同平台上获得是不安全的。