为什么指针算术中的整数提升在64位代码中的表现与在32位代码中不同

Why does integer promotion in pointer arithmetic behave differently in 64bit code than it does in 32bit code?

本文关键字:代码 32位 指针 整数 为什么 64位      更新时间:2023-10-16

当为64位编译时,以下代码在访问ptr[1-offset]:时因访问违规而崩溃

char* ptr = new char[4];
strcpy(ptr, "bar");
unsigned int offset = 2;
ptr+=offset;
char test0 = (ptr-offset)[2];
char test1 = ptr[2-offset];
char test2 = (ptr-offset)[1];
char test3 = ptr[1-offset];
delete (ptr-offset);

当编译为32位时,代码执行得很好。

当我用ptr[(int)(1-offset)]替换ptr[1-offset]时,或者当我把unsigned int offset = 2;改成int offset = 2;时,代码对64位也执行得很好。

显然,在64位上,1-offset的结果被提升为无符号整数类型,因此ptr[1-offset]不会解析为ptr[-1],而是解析为ptr[maxValueOfSomeUnsignedIntegerType]

然而,为什么这种情况只发生在64位,而1-offset似乎被提升为32位的有符号整数类型?

这似乎不是特定于实现的——我用VC++和G++得到了相同的结果。

首先,1-offset的结果始终是UINT_MAX,与您的体系结构无关。仅凭这一点并不能解释差异。

如果将其强制转换为int,则会得到一个实现定义的结果,但通常是-1。与int offset相同,您只需要得到普通的带符号整数运算,即可得到-1。这样就行了。

现在来解释为什么会出现segfault:显然,在你尝试的系统上,指针上的溢出算术会包裹mod2^32。据我所知,这是UB,但它似乎适用于您的系统。所以你实际上最终得到了ptr[1]

另一方面,如果指针是64位宽的,它可以表示ptr + 2^32 - 1(至少在这种情况下是明显的),因此不会发生包装,并且指针指向分配后大约4GB的某个无意义位置。

1属于int类型。CCD_ 21属于CCD_。

整数提升不适用(仅适用于小于int的类型)。然后,运算符-应用通常的算术转换。因为两个输入都具有相同的秩(int),并且只有符号不同,所以输入被转换为类型unsigned int

所以1-offset总是类型为unsigned int,值为UINT_MAX。这适用于32位和64位构建。

索引指针会导致越界访问,因此代码在32位和64位平台上都有未定义的行为。

对于32位,指针计算恰好是环绕的,因此您可以获得与ptr[-1]相同的效果(假设优化器没有利用UB),而对于64位,指针运算不会溢出,通常会得到segfault。