2D数组索引 – 未定义的行为?
我最近遇到了一些代码,做了一些有问题的2D数组索引操作。 以下面的代码示例为例:
int a[5][5]; a[0][20] = 3; a[-2][15] = 4; a[5][-3] = 5;
上面的索引操作是否受到未定义的行为的影响?
这是未定义的行为,这就是原因。
多维数组访问可以分解为一系列单维数组访问。 换句话说,表达式a[i][j]
可以被认为是(a[i])[j]
。 引用C11§6.5.2.1/ 2:
下标运算符
[]
的定义是E1[E2]
与(*((E1)+(E2)))
。
这意味着以上与*(*(a + i) + j)
。 遵循C11§6.5.6/ 8关于添加整数和指针(强调我的):
如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出; 否则,行为未定义 。
换句话说,如果a[i]
不是有效索引,则行为立即未定义,即使“直观地” a[i][j]
似乎是有界的。
因此,在第一种情况下, a[0]
有效,但以下[20]
不是,因为a[0]
的类型是int[5]
。 因此,索引20超出范围。
在第二种情况下, a[-1]
已经超出范围,因此已经是UB。
然而,在最后一种情况下,表达式a[5]
指向数组的最后一个元素之后的一个,根据§6.5.6/ 8有效:
…如果表达式
P
指向数组对象的最后一个元素,则表达式(P)+1
指向数组对象的最后一个元素之后的一个…
但是,在同一段后面:
如果结果指向数组对象的最后一个元素之后,则不应将其用作已计算的一元*运算符的操作数。
因此,虽然a[5]
是一个有效的指针,但是解引用它会导致未定义的行为,这是由最终的[-3]
索引引起的(其中,也是越界,因此是UB)。
是的,这是未定义的行为。
具有负索引的数组索引是未定义的行为。 抱歉,在大多数架构/编译器中,[ *(&a - 3)
a[-3]
与*(&a - 3)
相同,并且在没有警告的情况下被接受,但C语言允许您向指针添加负整数,但不使用负值作为数组索引。 诅咒甚至没有在运行时检查。
此外,在将前面的数组定义为指针时,还有一些问题需要解决。 你可以只保留未指定的第一个子索引,而不是更多,如:
int a[][3][2]; /* array of unspecified size, definition is alias of int (*a)[3][2]; */
(实际上,上面是一个指针定义,而不是一个数组,只是打印sizeof a
)
要么
int a [4] [3] [2]; / * 24个整数数组,大小为24 * sizeof(int)* /
当你这样做时,评估偏移的方法对于数组而言不同于指针,所以要小心。 在数组的情况下, int a[I][J][K];
&a[i][j][k]
放在
&a + i*(sizeof(int)*J*K) + j*(sizeof(int)*K) + k*(sizeof(int))
但是当你宣布时
int ***a;
然后a[i][j][k]
与:
*(*(*(&a+i)+j)+k)
,意味着您必须取消引用指针a
,然后将(sizeof(int **))*i
到其值,然后再取消引用,然后添加(sizeof (int *))*j
到该值,然后取消引用它,并将(sizeof(int))*k
到该值以获取数据的确切地址。
BR