c语言中的二维数组要怎么申请内存

看到很多有写malloc的,但是并不是很懂,请问如何用malloc来申请一个二维数组的内存

一、概念定义

1、二级指针

  指针变量也是一种变量,也会占用存储空间,也可以使用&获取它的地址。
  如果一个指针指向另外一个指针,我们就称它为二级指针,或者指向指针的指针。用C语言描述如下:

int x = 1;
int *p1 = &x;
int **p2 = &p1;

  当然,还有三级指针,四级指针,几乎很少用到,这里不再累述。大概是这幅样子:

int ***p3 = &p2;
int ****p4 = &p3;

2、解引用

  在学习一级指针的时候,我们用过*进行解引用。一级指针经过一次解引用,变成普通变量。二级指针经过一次解引用,变成一级指针。

int *p1 = *p2;

3、力扣中的二级指针

  在 LeetCode 中,选择 C语言 进行求解时,往往出现如下的情况,这是什么意思呢?

int** func(int** matrix, int matrixSize, int* matrixColSize, int* returnSize, int** returnColumnSizes){
}
参数名变量类型实际含义
matrix二级指针传入的矩阵首地址
matrixSize普通变量传入的矩阵的行数
matrixColSize一级指针传入矩阵每行的列数(注意是每行,所以是数组)
returnSize一级指针传出矩阵的行数,由于需要作为参数返回,所以用指针取地址
returnColumnSizes二级指针传出矩阵的每行的列数,由于需要作为数组参数返回,所以用二级指针

  对于returnColumnSizes这个参数,补充几点:
  1)它是一个 指向数组 的 指针,这里数组是行数组;
  2)*returnColumnSizes是实际的那个数组,并且数组的每个元素是 (*returnColumnSizes)[i]代表的是列数;
  3)return是个前缀,代表它的定位是 返回值,而不是 函数传参

4、内存申请模板

  所谓模板,就是写完以后,每个题目都能套着用,今天我们就来提炼一个有关于二维数组的内存申请的模板。大致声明如下:

int **myMalloc(int r, int c, int* returnSize, int** returnColumnSizes);

  其中,返回值代表申请的二维数组(为了方便理解,我们叫矩阵吧)的首地址。rc代表矩阵的行和列,returnSize是需要返回给调用方的,实际的矩阵的行,我们会在函数内部将它赋值为r,而returnColumnSizes也是需要返回给调用方的,只不过需要返回的是一个数组。所以需要用二级指针(思考:为什么返回数组要用二级指针)。
  这个函数就可以作为我们的模板函数的,我们接下来来实现一下它:

int **myMalloc(int r, int c, int* returnSize, int** returnColumnSizes) {
    int i;
    int **ret = (int **)malloc( sizeof(int *) * r );        // (1)
    *returnColumnSizes = (int *)malloc( sizeof(int) * r );  // (2)
    *returnSize = r;                                        // (3)
    for(i = 0; i < r; ++i) {
        ret[i] = (int *)malloc( sizeof(int) * c );          // (4)
        (*returnColumnSizes)[i] = c;                        // (5)
    }    
    return ret;
}
  • $(1)$ 申请一个矩阵(二维数组)的内存,行数为 r,首地址为ret,二维数组的类型为int **,二维数组中每个元素的类型为一级指针,即int *,对应sizeof(int *)这个表达式;
  • $(2)$ 为这个矩阵的列申请一个数组来记录它每一行的列数,所以这个列数组的长度应该是行数r,由于需要作为参数返回给调用方,所以这里调用了一次解引用;
  • $(3)$ *returnSize是需要返回的矩阵的行数,调用者不知道这个功能返回的矩阵有多少行,需要实现者告诉他,同样调用一次解引用;
  • $(4)$ 申请矩阵每一行的内存空间,每一行的长度为c,即列数;
  • $(5)$ 每一行的列数长度需要作为返回值返回,所以需要先解引用再索引到行号,即给(*returnColumnSizes)进行赋值;

二、扩展阅读