是“ int * ptr = *((&a)+ 1);” “ a”在标准中是否明确定义为int [5]?

时间:2018-06-26 17:46:43

标签: c arrays pointers language-lawyer pointer-arithmetic

基于此问题(strange output issue in c),有关于此行的答案(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.

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

3 个答案:

答案 0 :(得分:6)

由于解除引用运算符*,此表达式调用未定义的行为:

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

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

C standard详细说明加法运算符的第6.5.6节中,第7段指出:

  

就这些运算符而言,指向对象的指针是   不是数组的元素的行为与指向第一个的指针的行为相同   长度为1且对象类型为其的数组的元素   元素类型。

还允许创建一个指针,该指针指向数组末尾的一个元素。因此,允许&a + 1

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

  

将具有整数类型的表达式添加或减去时   从指针开始,结果具有指针操作数的类型。如果   指针操作数指向数组对象的元素,并且该数组   足够大,结果指向的元素与   原始元素,使得下标的差异   结果数组元素和原始数组元素等于整数表达式。   换句话说,如果表达式P指向   数组对象,表达式(P)+ N (相当于 N +(P))和(P)-N   (其中 N 的值为n)分别指向第i + n和   数组对象的第i-n个元素(如果存在)。而且,如果   表达式 P 指向数组对象的最后一个元素,即   表达式(P)+1 指向数组对象的最后一个元素,   并且如果表达式 q 指向数组的最后一个元素   对象,表达式(Q)-1 指向数组的最后一个元素   宾语。如果指针操作数和结果都指向元素   相同的数组对象,或者在数组的最后一个元素之后   目标,评估不得产生溢出;否则,   行为是不确定的。 如果结果指向最后一个元素   数组对象的值,不得用作一元*的操作数   被评估的运算符。

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

返回引用的帖子中的表达式:

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内部或一个元素过去。

答案 1 :(得分:3)

*( ( &a ) + 1 )之所以是UB

  

...如果结果指向数组对象的最后一个元素之后,它将   不得用作被评估的一元*运算符的操作数。 C11§6.5.68

( &a ) + 1指向“过去”。在该位置使用*违反了“禁止”。

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

即使aint a,由于

  

出于这些运算符的目的,指向不是数组元素的对象的指针的行为与指向长度为1且对象类型为其元素类型的数组的第一个元素的指针相同。 §6.5.67

答案 2 :(得分:1)

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

C11-§6.5.6“加法运算符”(P8):

  

将具有整数类型的表达式添加到指针或从指针中减去时,   结果具有指针操作数的类型。 如果指针操作数指向的元素   一个数组对象,并且数组足够大,结果指向一个元素偏移量   原始元素,使得结果和原始下标的差   数组元素等于整数表达式。换句话说,如果表达式P指向   数组对象的第i个元素,表达式(P)+N(相当于N+(P))和   (P)-N(其中N的值为n)分别指向第i+n和第i−n个元素   数组对象(如果存在)。此外,如果表达式P指向最后一个   数组对象的元素,表达式(P)+1指向数组对象的最后一个元素   数组对象,如果表达式Q指向数组对象的最后一个元素,   表达式(Q)-1指向数组对象的最后一个元素。[...]

相关问题