在 C 中添加整数类型时,指针算术究竟是如何工作的?



在C中,给定

char arr [10];
uint32_t i = -1;
char * p = &arr [1];

(p + i) 是否溢出/未定义或等于 &arr [0]? 为什么C标准(6.5.6.8)中的指针算术规则如此混乱?

语言定义指针可以对任何整数类型做+,-,+=,-=操作,当指针添加负整数值时会发生什么?如果指针的表示形式为 4 个字节,但整数操作数int64_t,该怎么办?

C99标准在数组索引术语(6.5.6.8)中定义了指针算法,根据我的理解,它指出:

char * ptr = …;
char * new_ptr = ptr + int_expr;
assert( (new_ptr - ptr) == (int_expr) );

模糊的间接定义是什么原因?

-1分配给uint32_t将其转换为UINT32_MAX(即4294967295),每减少模数6.2.5p9。

所以你的代码等效于:

char arr [10];
uint32_t i = UINT32_MAX;
char * p = &arr [1];

p指向数组arr中的第二个元素。所以p+i,即p + 4294967295,产生一个指针,该指针肯定不在数组对象内。所以这将是未定义的行为

例如,如果将i类型更改为int32_t,则它可以保存负值(正如您最初所希望的那样)。p + i,即p - 1会发出指向数组arr中第一个元素的指针(相当于&arr[0])。没有未定义的行为 因为生成的指针p + i(==&arr[0]) 仍然指向数组对象并且完全有效。

是的,它会溢出,不,它不会等于 &arr[0]。

因为变量iuint32_t类型,所以它实际上没有值-1,而是非常大的数字429496729511111111 11111111 11111111 11111111二进制或0xFFFFFFFF十六进制。

如果将 i 的类型更改为类似int的值,则i的值将-1(p+i)将引用arr[0]

鉴于您的示例,i无符号数据类型,您肯定会指向数组之外arr因为-1被视为0xFFFFFFFF。 但这里令人困惑的部分可能不是指针算术,而是索引变量类型转换期间的溢出。


另一方面,使用带符号数据类型进行i您将处于安全状态:

只要在一个数据对象的范围内,指针算术就是安全的。您也可以将一个元素指向数组的最后一个元素之后。

在C中,无论您编写*(arr+i)还是arr[i]都完全相同。 这意味着您的示例

char * p = &arr [1];

char * p = arr+1;

由此你可以推导出p + (-1)等于arr+1-1 == arr并且等于&arr[0]这完全可以指向该数组的边界内。

你指的是:

将整数类型的表达式加到或减去时 从指针中,结果具有指针操作数的类型。如果 指针操作数指向数组对象的元素,并且数组 足够大,结果指向 原始元素使得下标的差异 结果和原始数组元素等于整数表达式。

这并不令人困惑,这只是指针算法的定义。并应读作:

E array[N];
assert( (&array[X]+D) == &array[X+D] );

前提是 X 和 X+D 都在 [0,N] 中(您可以将一个指向最后一个元素)。

D可以是任何整数表达式。在您的情况下,它具有无符号整数类型(-1,因为uint32_tUINT32_MAX),因此它是未定义的行为,因为结果超出了数组 (1+UINT32_MAX>10) 的范围。

如果你使用了int32_t,结果将指向数组的第一个元素:

char array[10];
assert( (&array[1]-1) == &array[0] );

最新更新