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 = &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 位应用程序有啥区别?