为啥这两个指针减法会给出不同的结果?

Posted

技术标签:

【中文标题】为啥这两个指针减法会给出不同的结果?【英文标题】:Why do these two pointer subtractions give different results?为什么这两个指针减法会给出不同的结果? 【发布时间】:2012-02-07 22:44:56 【问题描述】:

考虑以下代码:

char* p = new char[2];
long* pi = (long*) p;
assert(p == pi);         // OK

char* p1 = &p[1];
long* pi1 = (long*) p1;
assert(p1 == pi1);       // OK

int d = p1 - p;
int d1 = pi1 - pi;
assert(d == d1);         // No :(

运行后,我得到d == 1d1 == 0,尽管p1 == pi1p == pi(我在调试器中检查了这个)。这是未定义的行为吗?

【问题讨论】:

如果你看一下反汇编,pi1 - pi 产生一个减法,然后右移 2 位。 (在 MSVC 上)右移显然是除以 sizeof(long),它当然会截断为 0,相差仅为 1。我不知道是否定义了这种行为。 【参考方案1】:

正如其他人所指出的,这是未定义的行为。但是,对于您所看到的内容,有一个非常简单的解释。

指针之间的区别在于元素的数量,而不是它们之间的字节数。

pi 和 pi1 都指向 longs,但 pi1 指向的地址仅比 pi 多一个字节。假设 long 的长度为 4 个字节,则地址差 1 除以元素的大小 4 为 0。

另一种思考方式是,您可以想象编译器会生成与此等效的代码来计算 d1:

int d1 = ((BYTE*)pi1 - (BYTE*)pi)/sizeof(long).

【讨论】:

【参考方案2】:

如果两个指针不指向同一个数组,或者指针是从指向不相关类型的指针类型转换的,则两个指针之间的差异是不确定的。

另外,区别不在于字节,而在于元素的数量。

在您的第二种情况下,差异是 1 个字节,但它被 sizeof(long) 除。请注意,因为这是未定义的行为,所以这里的任何答案都是正确的。

【讨论】:

伙计,我看着你的头像笑了:) 这是一个绝妙的主意:))) 在哪一节中说你不能对从不相关类型转换的指针进行指针运算 @SethCarnegie,我无法指出任何具体的内容,事实上我可能弄错了。我认为类型转换本身会生成 UB,尤其是在这种情况下,因为它与类型未对齐。 啊,你是对的。 (对于任何想知道的人,我认为这在 §5.2.10.7 中有详细说明)【参考方案3】:

重新解释指针的底层类型不会改变它的地址。但是指针算术会根据指针类型产生不同的结果。所以你在这里描述的是完全正确的,这就是我所期望的。见pointer arithmetics。

【讨论】:

【参考方案4】:

它为pi1 - pi做整数(长)指针运算;

如果p1&p[4],您会看到它为d1 打印1,而实际差异是4 个字节。这是因为sizeof (long) = 4 个字节。

【讨论】:

long 的大小取决于平台,因此即使p1 指向&p[4](或者可能是4 或其他),差异也可能是0。事实上,许多 64 位平台都有 8byte long。 IIRC 甚至存在sizeof(long)==1 的平台(char 是例如64bit 在那些)

以上是关于为啥这两个指针减法会给出不同的结果?的主要内容,如果未能解决你的问题,请参考以下文章

Python append() 与列表上的 + 运算符,为啥这些会给出不同的结果?

Python append() 与列表上的 + 运算符,为啥这些会给出不同的结果?

当它的目标应该被删除时,为啥这个智能指针会给出正确的结果?

为啥 innerHTML 和 textContent 给出不同的结果?

为啥 scipy sparse 和 numpy 数组的乘法函数会给出不同的结果?

为啥这段代码(在 Matlab 的 MEX 文件中使用 OpenMP)给出不同的结果?