【发布时间】:2014-10-07 20:16:48
【问题描述】:
有很多这样的代码:
#include <stdio.h>
int main(void)
{
int a[2][2] = {{0, 1}, {2, -1}};
int *p = &a[0][0];
while (*p != -1) {
printf("%d\n", *p);
p++;
}
return 0;
}
但是基于这个answer,行为是未定义的。
N1570。 6.5.6 p8:
当一个整数类型的表达式被添加或减去时 从一个指针,结果具有指针操作数的类型。如果 指针操作数指向数组对象的一个元素,而数组 足够大,结果指向一个元素偏移量 原始元素使得下标的差异 结果和原始数组元素等于整数表达式。 换句话说,如果表达式 P 指向一个 数组对象,表达式 (P)+N(等价于 N+(P))和 (P)-N (其中 N 的值为 n)分别指向第 i+n 个和 数组对象的第 i-n 个元素,前提是它们存在。而且, 如果表达式 P 指向数组对象的最后一个元素,则 表达式 (P)+1 指向数组对象的最后一个元素, 如果表达式 Q 指向数组的最后一个元素 对象,表达式 (Q)-1 指向数组的最后一个元素 目的。如果指针操作数和结果都指向元素 相同的数组对象,或数组的最后一个元素 对象,评估不应产生溢出;否则, 行为未定义。如果结果指向最后一个元素 数组对象,不得用作一元的操作数 * 被评估的运算符。
谁能详细解释一下?
【问题讨论】:
-
您在链接的问题中阅读了 christoph 的回答吗? stackoverflow.com/a/7787436/3684343我觉得他解释的很好。
-
是的,有大量带有 UB 的代码。
-
与指针 向后 遍历数组并排排列,直到其值“小于”第一个元素地址。它的惊人它的频率有多高,几乎没有人知道他们这样做是在调用 UB。
-
我在 N1570 中找不到任何东西来证明这条规则的合理性,除了“因为标准这么说”。似乎数组订阅和
sizeof规则阻止了不同数组维度之间的任何类型的填充。我想知道是否有任何标准编译系统会破坏上述代码。 -
现在我考虑到这一点,也许这个限制的目的是允许将子阵列放置在不同的内存库上,就像 PIC 一样。所以 a[0] 和 a[1] 可能被放在不同的 bank 上,示例代码会失败,因为编译器假定循环中不需要 bank 切换指令。