当为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。