【发布时间】:2017-12-12 17:47:57
【问题描述】:
以下程序的行为是否未定义?
#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:
此外,如果表达式 P 指向一个 数组对象,表达式 (P)+1 指向最后一个 数组对象的元素......如果指针操作数和 结果指向同一个数组对象的元素,或者一个过去的元素 数组对象的最后一个元素,评估不应产生 溢出;否则,行为未定义。 如果结果点 超过数组对象的最后一个元素,不得用作 计算的一元 * 运算符的操作数。
由此看来,上述程序的行为是未定义的; ptr3_plus_1 指向从其派生的数组对象末尾之后的地址,取消引用该地址会导致未定义的行为。
此外,Annex J.2 表明这是未定义的行为:
一个数组下标超出范围,即使一个对象显然是 使用给定的下标可访问(如在左值表达式中 a[1][7] 给出声明 int a[4][5]) (6.5.6)。
堆栈溢出问题One-dimensional access to a multidimensional array: well-defined C? 对此问题进行了一些讨论。这里的共识似乎是,这种通过一维下标访问二维数组的任意元素确实是未定义的行为。
在我看来,问题在于形成指针ptr3_plus_2 的地址甚至是不合法的,因此以这种方式访问任意二维数组元素是不合法的。但是,使用这种指针算法形成指针ptr3_plus_1 的地址是合法的。另外,比较ptr3_plus_1和ptr4这两个指针是合法的,根据§6.5.9 ¶6:
当且仅当两个指针都是空指针时,两个指针比较相等 是指向同一对象的指针(包括指向对象的指针和 开头的子对象)或函数,两者都是指向一个的指针 过去同一个数组对象的最后一个元素,或者一个是指针 一个超过一个数组对象的末尾,另一个是指向 立即发生的不同数组对象的开始 跟随地址空间中的第一个数组对象。
所以,如果ptr3_plus_1 和ptr4 都是比较相等的有效指针并且必须指向相同的地址(ptr4 指向的对象在内存中必须与@ 指向的对象相邻987654334@ 无论如何,因为数组存储必须是连续的),看起来*ptr3_plus_1 与*ptr4 一样有效。
这是未定义的行为,如 §6.5.6 ¶8 和附件 J.2 中所述,还是例外情况?
澄清
尝试访问二维数组的 final 行末尾之后的元素是未定义的行为,这似乎很明确。我感兴趣的问题是,通过使用指向前一行元素的指针和指针算法形成一个新指针来访问中间行的第一个元素是否合法。在我看来,附件 J.2 中的不同示例可以更清楚地说明这一点。
是否可以调和第 6.5.6 节 ¶8 中的明确声明,即尝试取消引用指向数组末尾之后位置的指针会导致未定义的行为,认为指针超出数组末尾T[][] 类型的二维数组的第一行也是 T* 类型的指针,它指向 T 类型的对象strong>,即 T[]?
类型数组的第一个元素【问题讨论】:
-
"是否是 UB 访问二维数组行尾的一个元素?"当然是最后一排。我看不到前面行的 UB - 它都是连续的空间。
-
您的 6.5.6 引用清楚地表明这是 UB。指针的出处很重要,而不仅仅是它所代表的地址。
-
我不知道你在问什么。您的问题已经明确说明了分析器。它显然没有列出任何例外。
-
据我了解,这是一个悬而未决的问题,尚未得到令人满意的解决,可能会在未来版本的标准中得到澄清。
-
@Olaf--
ptr3_plus_1是一个有效的指针,它指向数组末尾的一个位置,因此不能被取消引用;但ptr3_plus_1也是指向数组第一个元素的有效指针,因此应该可以取消引用。我试图调和在我看来明显矛盾的东西。或许答案是ptr3_plus_1根本不能说是指向第二个数组的第一个元素的指针。
标签: c arrays pointers multidimensional-array language-lawyer