本文来源:声明int a36, a09越界吗?
1. 问题
看下面的程序:
#include <stdio.h>
int main(void)
{
int a[3][6];
for(int i=0; i<3; i++) {
for(int j=0; j<6; j++){
a[i][j] =i * 6 + j;
}
}
printf("%d\n",a[0][9]);
return 0;
}
第10行中的数组元素a09中的下标9越界吗?
这是一个关于二维数组的问题,下面先了解一下二维数组的相关知识。
2. 二维及多维数组定义和存储方式
二维数组通常用于表示由固定多个同类型的、具有行列结构的数据所构成的复合数据。
二维数组定义的一般形式是:
类型说明符 数组名元素长度1元素长度2;
其中类型说明符、数组名的含义和一维数组完全相同。元素长度1表示数组第一维(高维)的长度,元素长度2 表示第二维的长度。二维数组经常用来表示行列式,因此第一维也称为行,第二维也称为列。例如:
int mat34;
定义了一个三行四列的二维数组,数组名为mat,该数组共有3×4=12个int类型元素,每个元素的名称、前后顺序如图1所示。

图1 二维数组mat
二维数组的两个下标在横向和纵向两个方向上变化,而不像一维数组只是一个方向。但是,计算机中存储器是一维编址的,或者说存储器单元是按一维线性排列的,只有一个方向。那么如何在一维结构的存储器中存放二维数组呢,一般有两种方式存储二维数组**:一种是按行优先存放** ,即存放完二维数组第一行之后再存放入第二行;另一种是按列优先存放,即存放完二维数组第一列之后再存放第二列。C实现都是按行优先存放。
因此图1所示二维数组mat,各元素在存储器中存放的先后次序为:
mat00,mat01,mat02,mat03,mat10,mat11,mat12,mat13,mat20,mat21,mat22,mat23
和一维数组一样,二维数组定义后,系统会在内存中为二维数组分配一块连续区域,从内存的低地址开始依次存放二维数组的各元素数据。因此mat34定义以后,12个整型元素存放在一块连续的内存中。这一原则对任何类型的数组都成立。
3. C标准的规定
C标准规定,多维数组按行优先存放。 在附录J.2 未定义行为一节中列出了下标越界,这说明,下标越界属于未定义行为。
An array subscript is out of range, even if an object is apparently accessible with the given subscript (as in the lvalue expression a17 given the declaration int a45) (6.5.6).
给定声明int a45,显然左值表达式a17给定的下标是可以访问的,但其数组下标越界了(6.5.6)。
4. 结论
声明int a36, 则a09的下标越界。
对于数组越界来说,它属于未定义行为。任何未定义行为都是潜在的bug。但C编译器对数组越界不进行判断,对于a09仍可以正常访问,我们可以认为这是一个假越界。
对于声明int a36来说,aij等价于 *(a+(i*6+j)*sizeof(int))。
如果i*6+j<3*6,且0<=i<3, j>=6,我们定义这种情况为假越界,程序可以正常访问,这是一个假bug。
如果i*6+j>=3*6,且 i>=0, j>=0,我们定义这种情况为真越界。可以判断这是一个真的bug。