以下程序的行为是否未定义?
#include <stdio.h>
int main(void)
{
int arr[2][3] = { { 1, 2, 3 },
{ 4, 5, 6 }
};
int *ptr1 = &arr[0][0]; // pointer to first elem of { 1, 2, 3 }
int *ptr3 = ptr1 + 2; // pointer to last elem of { 1, 2, 3 }
int *ptr3_plus_1 = ptr3 + 1; // pointer to one past last elem of { 1, 2, 3 }
int *ptr4 = &arr[1][0]; // pointer to first elem of { 4, 5, 6 }
// int *ptr_3_plus_2 = ptr3 + 2; // this is not legal
/* It is legal to compare ptr3_plus_1 and ptr4 */
if (ptr3_plus_1 == ptr4) {
puts("ptr3_plus_1 == ptr4");
/* ptr3_plus_1 is a valid address, but is it legal to dereference it? */
printf("*ptr3_plus_1 = %d\n", *ptr3_plus_1);
} else {
puts("ptr3_plus_1 != ptr4");
}
return 0;
}
根据§6.5.6 ¶8 http://port70.net/~nsz/c/c11/n1570.html#6.5.6p8:
此外,如果表达式P指向最后一个元素
数组对象,表达式(P)+1过去了最后一点
数组对象的元素...如果指针操作数和
结果指向同一个数组对象的元素,或者过去的一个
数组对象的最后一个元素,评估不应产生
溢出;否则,行为是未定义的。如果结果点
超过数组对象的最后一个元素后,它不得用作
被求值的一元 * 运算符的操作数。
由此可见,上述程序的行为是未定义的;ptr3_plus_1
指向派生它的数组对象末尾之后的地址,取消引用该地址会导致未定义的行为。
更远,附件J.2 http://port70.net/~nsz/c/c11/n1570.html#J.2表明这是未定义的行为:
数组下标超出范围,即使对象明显是
使用给定的下标可访问(如左值表达式中所示)a[1][7]鉴于声明整数a[4][5])(6.5.6)。
Stack Overflow 问题中有一些关于这个问题的讨论,对多维数组的一维访问:定义良好的 C? https://stackoverflow.com/questions/6290956/one-dimensional-access-to-a-multidimensional-array-well-defined-c。这里的共识似乎是这种访问随意的通过一维下标获取二维数组的元素确实是未定义的行为。
在我看来,问题在于形成指针的地址甚至是不合法的ptr3_plus_2
,因此以这种方式访问任意二维数组元素是不合法的。但它is构成指针地址是合法的ptr3_plus_1
使用这个指针算术。此外,比较两个指针是合法的ptr3_plus_1
and ptr4
, 根据§6.5.9 ¶6 http://port70.net/~nsz/c/c11/n1570.html#6.5.9p6:
两个指针比较相等当且仅当两个指针都是空指针时
是指向同一个对象的指针(包括指向对象的指针和
开头的子对象)或函数,两者都是指向一个的指针
经过同一数组对象的最后一个元素,或者一个是指针
一个指向一个数组对象的末尾,另一个是指向
立即发生的不同数组对象的开始
跟随地址空间中的第一个数组对象。
所以,如果两者兼而有之ptr3_plus_1
and ptr4
是比较相等的有效指针,并且必须指向相同的地址(由ptr4
在内存中必须与所指向的对象相邻ptr3
无论如何,由于数组存储必须是连续的),看起来*ptr3_plus_1
与以下一样有效*ptr4
.
这是未定义的行为,如第 6.5.6 节 ¶8 和附件 J.2 中所述,还是特殊情况?
澄清
尝试访问超过末尾的元素是未定义的行为,这似乎很明确final二维数组的行。我感兴趣的是通过使用指向前一行元素的指针和指针算术形成新指针来访问中间行的第一个元素是否合法的问题。在我看来,附件 J.2 中的另一个例子可以使这一点更加清楚。
是否可以协调第 6.5.6 节中的明确声明¶8 尝试取消引用指向超出数组末尾的位置的指针会导致未定义的行为,而指针超出第一行末尾的想法类型的二维数组T[][]也是一个类型的指针T *指向一个类型的对象T,即类型数组的第一个元素T[]?