在C语言中,我们通常用指针指向动态分配的内存来实现动态数组,即用n重指针指向动态n维数组。
众所周知,静态一维数组和一重指针的访问方式相同,即若
int *p, a[M];//M为常量
p = malloc(sizeof(int) * m);//m不一定为常量
则
p[i] == *(p + i)//实际上这里访问的地址大小是((int)p + sizeof(int) * i)
a[i] == *(a + i)//同理,访问的地址大小是((int)a + sizeof(int) * i)
因此我们可以将一重指针和静态一维数组都作为一重指针传入函数统一处理。
但静态二维数组和二重指针的访问方式则不同,即若
int **p, a[M][N];//M,N为常量
则
p[i][j] == *(*(p + i) + j)//方式一
a[i][j] == *(a + i * N + j)//方式二,C语言是行优先存储,也有用列优先存储的其他语言
假如把静态二维数组a作为二重指针p传入函数,这个函数会采用第一种方式访问p,有很大可能导致段错误。(所以编译器会警告)
关于动态分配二维数组,以下是本人拙见:
如果想动态分配一个二维数组p,又想让p[ i ][ j ]正确访问数组元素,就要额外分配一个一维数组来保存每一列的头指针,例如:
int **p, i;
p = (int**)malloc(sizeof(int*) * m);//m不一定为常量,申请用于存储每一列头指针
for(i = 0; i < m; i++)
{
p[i] = (int*)malloc(sizeof(int) * n);//n不一定为常量,申请每一列的内存
}
通过p[ i ][ j ]访问时,先根据行数 i 访问 p 指向的指针数组,得到列的头指针t = p[ i ],再根据列数访问t指向列数组,得到要访问的元素p[ i ][ j ] = t[ j ]。
可以看出,这样申请的内存为sizeof(int*) * m + sizeof(int) * m * n,大于静态分配的二维数组占用大小sizeof(int) * m * n,而且这样申请的内存不一定连续,只能用p[ i ] [ j ] == *(*(p + i) + j)的方式访问。
静态分配的二维数组本质上和一维数组相同,内存连续且没有额外空间。
如果想动态分配和静态分配的内存一致,可以分配一个一维数组当作二维数组处理:
int *p;
p = (int*)malloc(sizeof(int) * m * n);
但此时访问 i 行 j 列的数组元素的方式就应该写成下面两种方式之一:
*(p + i * n + j)//方式一
p[i * n + j]//方式二
这里采用了行优先存储,也可以采用列优先存储,写法稍有不同:
*(p + i + j * m)//方式一
p[i + j * m]//方式二
2023-1-5 被力扣的733.图像渲染创晕,在本地gdb调试的过程中想到这些问题,经过查找和验证后记录下来