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

Posted

技术标签:

【中文标题】32位或64位系统中的指针算术长短【英文标题】:Pointer Arithmetic long and short in 32bit or 64bit System 【发布时间】:2021-07-13 18:08:45 【问题描述】:

我刚准备考试,问下面的问题,下面的程序在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, i, l);

 return 0;

在 32bit 上的结果如下:

p = 0xc q = 0x2
c = 10, i = 2, l = 2

64位的结果如下:

p = 0x10 q = 0x2
c = 14, i = 3, l = 1

只有我必须承认,我根本无法理解这些结果,因为当我计算一个指针时,它应该指向内存中随机的东西,它总是出现 0xc 或 0x10。

有人可以帮我吗?

【问题讨论】:

如果将1 添加到指针,它会将类型的大小添加到指针变量持有的值(地址)。因此,当您将 1 加到 short *q 时,它的值会增加 2。请注意,c = (char *)p - (char *)q;未定义的行为,因为它是不同对象之间的指针运算。 你懂十六进制数吗? 是的,现在它更有意义了。所以我添加 8 + 4(32 位长)字节 = 12 0xc 和 8+8(64 位长)字节 = 16 0x10。现在有意义 但第二个打印。简而言之,当我将我的 12 字节长投射时,它是 12 倍 1 字节的镜头,对吗?减法 1 个短字节是 11,但结果是 10 ...这没有意义,或者我错过了什么? 它应该指向内存中随机的东西 - 但是%p 不是打印指针目标的值,而是指针本身。这些语句都没有取消引用指针。在为他们分配了那些硬编码的值之后,无论如何你都会遇到麻烦。 【参考方案1】:

只有我必须承认,我根本无法理解这些结果,因为当我计算一个指针时,它应该指向内存中随机的东西,它总是出现 0xc 或 0x10。

程序不会访问任何指针的“指针指向的内存中的值”。它只做指针运算(改变指针指向的东西)。

这个问题的目标可能是测试你是否了解指针运算在 C 中的工作原理。

指针算法在 C 中的工作原理是它隐藏了“乘以 sizeof(无论指针指向什么)”。换句话说,p = p + n; 大致相当于p = (char *)p + n * sizeof(*p);,或者p = &amp;p[n];

【讨论】:

是的,谢谢,taht 帮助了我,但是在减法中围绕这些指针进行投射时的行为如何? c = (char *)p - (char *)q; @JoeDaniel:强制转换为(char *) 有效地禁用了“乘以 sizeof()”行为(通过将其变为“乘以 1”,它什么都不做,因为 sizeof(char) 是 1)。它还可以防止编译器抱怨不同类型的减法。 我现在玩了一点,所以 c 是有意义的,它基本上是 12-2 = 10 这是正确的但是 i = (int *)p - (int *)q;是2(32bit),是怎么生成的?【参考方案2】:

首先,我应该指出,这段代码中有很多未定义的行为,并且它做出了许多标准无法保证的假设。但话虽如此,这个问题可以根据您在典型实现中看到的内容来回答。

假设两台机器上的数据类型大小(以字节为单位)为:

type 32-bit size 64-bit size
long 4 8
int 4 4
short 2 2
char 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 标准保证任何这一点,因此假设这些结果将在不同平台上获得是不安全的。

【讨论】:

首先,非常感谢您的回答,现在一切都变得更清楚了。 :D 我有一个额外的问题,当我从像 char **p = (char **)5; 这样的指针中获取指针时会发生什么p+=2 ?当我打印它时,它是 0xd (13) 但不应该是 7 就像我在没有秒 * 的情况下做的那样? @JoeDaniel 它再次按指针目标的大小缩放它。在这种情况下,指针的类型为char **,因此目标的类型为char *,即目标本身就是一个指针。所以 2 按sizeof(char *) 缩放。同样,这取决于系统。在具有 4 字节指针的系统上,您将得到 5 + 2*4 = 13。但同样,这是未定义的行为,并且您正在对未对齐的指针进行算术运算,因此无法保证任何这一点。

以上是关于32位或64位系统中的指针算术长短的主要内容,如果未能解决你的问题,请参考以下文章

在 64 位 Windows 上安装 32 位或 64 位应用程序有啥区别?

检测32位或64位Windows

批处理判断操作系统32位或64位,并注册服务

安装的jvm是64位或32位[重复]

linux红帽5.6系统下安装oracle11g 32位或64位

32位与64位系统的不同点