一、二维数组的基本概念
二维数组的内存映像
从内存角度看,二维数组和一维数组一样,在内存中都是连续分布的多个内存单元,并没有本质差别,只是内存的管理方式不一样,如下图所示
一维数组int a[10]与二维数组int b[2][5]的对应关系为
一维数组 | a[0] | a[1] | a[4] | a[5] | a[9] |
二维数组 | b[0][0] | b[0][1] | b[0][4] | b[1][0] | b[1][4] |
一个一维数组a[m]可以表示为二维数组a[x][y](m = x * y),其中m是数组总元素个数,x和y分别是二维数组的行数和列数
任一二维数组中的元素a[i][j]在一维数组中对应的k为:k = i * y + j,即a[i][j] == a[k] == a[i * y + j]
二维数组和一维数组在内存使用效率、访问效率上是没有差别的(理想情况下)。在一些特定情况下(比如说矩阵、平面、显示器)用二维数组而不用一维数组,原因在于二维数组好理解、代码好写、利于组织。
二维数组的访问方式
下标式访问(便于理解):a[i][j]
指针式访问(数组本质):*(*(a + i) + j)
二维数组的动态内存分配
首先需要注意的是,不要这样一次性分配为数组内存空间:int **arr = (int**)malloc(n * m * sizeof(int));
正确的做法是先分配行,再分配列。释放内存的时候,要先释放列,再释放行。
注意,顺序反了的话,会把列的地址擦除,导致释放列时内存时找不到地址,程序崩溃。
正确的分配空间代码如下:
int **arr = (int **)malloc(m*sizeof(int *));
for(i=0;i<m;i++)
{arr[i] = (int *)malloc(n*sizeof(int));}
正确的释放空间代码如下:
for (i = 0; i < m; i++)
free(arr[i]);/*释放列*/
free(arr);/*释放行*/
注:这种分配方式得到的其实并不是真正意义上的二维数组,因为其行与行之间的内存并不连续,虽然可以用下标arr[i][j]的方式访问,但当用指向该二维数组的指针来访问时候,不能通过指针值的增加来跨行获取元素,对所有元素清零:memset(arr, 0, m * n);是不可行的,必须每行分别清零。不过这种情况一般用的也不多,因此上述分配方式在大多数情况下的操作都能得到正确的结果。
当列数也不定时,需要多一个数组存放每一行的列数
int **arr = malloc(m * sizeof(int*)//分配指针数组的内存空间
*returnSize = m;//指针数组的长度,也即行数
*returnColumnSizes = malloc(sizeof(int) * m)//列数组,用来存放每一行的列数
for(int i = 0; i < m; i++){ //为每一行分配内存空间
arr[i] = malloc(sizeof(int) * n); //为第i行分配内存空间
memset(arr[i], 0, sizeof(int) * n); //初始化第i行全为零
(*returnColumnSizes)[i] = n; //记录每一行的列数,也即元素个数
}