"int *ptr = *( ( &a ) + 1 );" where "a" is int[5] 是否由标准明确定义?

Is "int *ptr = *( ( &a ) + 1 );" where "a" is int[5] well-defined by the Standard?

基于这个关于这一行的问题(strange output issue in c) there was an Answer ( provided by @Lundin):

int *ptr = (int*)(&a+1);

他说:

the cast (int*) was hiding this bug.

所以我带来了以下内容:

#include <stdio.h>

int main( void ){
    int a[5] = {1,2,3,4,5};

    int *ptr = *( ( &a ) + 1 );
    printf("%d", *(ptr-1) );
}

我想知道这是不是:

int *ptr = *( ( &a ) + 1 );

标准明确定义了吗?

编辑:

在某些时候@chux 指向 §6.3.2.3.7 即:

A pointer to an object type may be converted to a pointer to a different object type. If the
resulting pointer is not correctly aligned68) for the referenced type, the behavior is
undefined. Otherwise, when converted back again, the result shall compare equal to the
original pointer. When a pointer to an object is converted to a pointer to a character type,
the result points to the lowest addressed byte of the object. Successive increments of the
result, up to the size of the object, yield pointers to the remaining bytes of the object.

但是我不确定我是否理解正确。

*( ( &a ) + 1 ) 是 UB 由于

... If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated. C11 §6.5.6 8

( &a ) + 1指向"one past"。使用 * 违背 "shall not".

int a[5] = {1,2,3,4,5};
int *ptr = *( ( &a ) + 1 );

即使 aint a 这也适用,因为

For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type. §6.5.6 7

由于解引用运算符 *:

,此表达式调用未定义的行为
int *ptr = *( ( &a ) + 1 );

首先,让我们从( &a ) + 1开始。这部分是有效的。 &a 的类型为 int (*)[5],即指向大小为 5 的数组的指针。通过加 1 执行指针运算是有效的,即使 a 不是数组的元素。

在详述加法运算符的 C standard 的第 6.5.6 节中,第 7 段指出:

For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

还允许创建指向数组末尾后一个元素的指针。所以 &a + 1 是允许的。

问题出在我们取消引用此表达式时。第 8 段指出:

When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i+n-th and i−n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.

由于不允许取消引用指向数组末尾的指针,因此行为未定义。

回到引用的表达式 post:

int *ptr = (int*)(&a+1);
printf("%d %d", *(a+1), *(ptr-1));

这也是未定义的行为,但原因不同。在这种情况下,int (*)[5] 被转换为 int *,随后使用转换后的值。使用这种转换后的值是合法的唯一情况是将对象指针转换为指向字符类型的指针,例如char *unsigned char * 并随后取消引用以读取对象表示的字节。

编辑:

上面两行好像定义的很清楚。在指针取消引用 *(ptr-1) 发生时,被访问的对象具有有效类型 int,与 ptr-1 的取消引用类型相匹配。将指针值 &a+1int (*)[5] 转换为 int * 是有效的,对转换后的指针值执行指针运算也是有效的,因为它指向 a 内部或一个元素过去了。

int *ptr = *( ( &a ) + 1 ); 调用了未定义的行为。

C11 - §6.5.6 "Additive operators" (P8) :

When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i+n-th and i−n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object.[...]