我最近一直在阅读 C 标准 ISO/IEC 9899:2018 规范。其中,第6.5.6节(加法运算符)描述了对
+
运算符的约束。规则 [8] 说:
当整数类型的表达式与指针相加或相减时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向距原始元素的元素偏移量,使得结果数组元素和原始数组元素的下标之差等于整数表达式。换句话说,如果表达式 P 指向数组对象的第 i 个元素,则表达式 (P)+N (相当于 N+(P))和 (P)-N (其中 N 的值为 n)指向分别为数组对象的第 i + n 个和 i − n 个元素(前提是它们存在)。此外,如果表达式 P 指向数组对象的最后一个元素,则表达式 (P)+1 指向数组对象的最后一个元素后一位,如果表达式 Q 指向数组对象的最后一个元素后一位,表达式 (Q)-1 指向数组对象的最后一个元素。如果指针操作数和结果都指向同一个数组对象的元素,或者超过数组对象的最后一个元素,则求值不会产生溢出;否则,行为是未定义的。如果结果指向数组对象的最后一个元素,则不得将其用作所计算的一元 * 运算符的操作数。
除了数组上的算术行为之外,我还理解这条规则也描述了由于空指针不指向任何对象,所以对它们进行算术是未定义的行为。 (我发现与此相关的Reddit 帖子支持了我的结论)
首先:我对上述说法的理解正确吗?
其次:下面的代码怎么样?这是否也被视为 UB,如果是/如果不是,为什么? 第 6.5.6 节中描述的规则是否也适用于 intptr_t
?上面的代码似乎也没有违反第 6.3.2.3 节(指针)中的规则。
int arr[2] = {0};
intptr_t ptr_0 = 0;
intptr_t ptr = (intptr_t) arr;
intptr_t new = ptr_0 + ptr;
int* ptr_int = (int*) new;
提供更多上下文的其他一些部分:
intptr_t
只是一个普通的有符号整数类型。您可以像任何其他整数一样使用它,有关指针的规则不适用于它。它的特别之处(以及
uintptr_t
)是它保证足够大以容纳任何
void*
指针(并且由于任何对象指针都可以转换为
void*
而不会丢失任何信息,这也意味着它是足够大以容纳任何对象指针)。因此,您可以将指针投射到
intptr_t
,然后将结果投射回来,并获取原始指针。但是,如果您修改
intptr_t
变量的值,则没有这样的保证。以下未定义:
int arr[2] = {0, 1};
intptr_t iptr = (intptr_t) arr;
iptr += sizeof(int);
int *ptr = (int *)iptr;
printf("%d\n", *ptr);
在大多数实现中,我希望打印出 1
,但这不是规范所要求的。