我最近进入了一些代码,做了一些有问题的2D数组索引操作。以下面的代码示例为例:
int a[5][5];
a[0][20] = 3;
a[-2][15] = 4;
a[5][-3] = 5;
上面的索引操作是否会受到未定义的行为?
答案 0 :(得分:6)
这是未定义的行为,这就是原因。
多维数组访问可以分解为一系列单维数组访问。换句话说,表达式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)。
答案 1 :(得分:-1)
是的,这是未定义的行为。
答案 2 :(得分:-1)
带负索引的数组索引是未定义的行为。抱歉,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