如何在C中正确设置,访问和释放多维数组?

Mik*_*ike 24 c malloc dynamic multidimensional-array

关于C中的多维数组,我已经看过几十个关于"我的代码有什么问题"的问题.出于某种原因,人们似乎无法理解这里发生的事情,所以我决定回答这个问题作为对其他人的参考:

如何在C中正确设置,访问和释放多维数组?

如果其他人有有用的建议,请随时发布!

Jen*_*edt 29

在C99中,自C99以来,即使是动态多维数组也可以轻松地一次性分配,malloc并通过以下方式释放free:

double (*A)[n] = malloc(sizeof(double[n][n]));

for (size_t i = 0; i < n; ++i)
  for (size_t j = 0; j < n; ++j)
      A[i][j] = someinvolvedfunction(i, j);

free(A);
Run Code Online (Sandbox Code Playgroud)

  • @Lundin:有时使用编译时已知的大小动态分配是有意义的,因为多维数组很容易打击堆栈. (2认同)

Ste*_*sop 14

在C89中至少有四种不同的方法来创建或模拟多维数组.

一个是"分别分配每一行",迈克在他的回答中描述.它不是一个多维数组,它只是模仿一个(特别是它模仿了访问元素的语法).它在每行具有不同大小的情况下非常有用,因此您不是表示矩阵而是表示具有"粗糙边缘"的行.

一个是"分配多维数组".它看起来像这样:

int (*rows)[NUM_ROWS][NUM_COLS] = malloc(sizeof *rows);
...
free(rows);
Run Code Online (Sandbox Code Playgroud)

然后访问元素[i,j]的语法是(*rows)[i][j].在C89,都NUM_COLSNUM_ROWS必须在编译时是已知的.这是一个真正的二维数组,并且rows是指向它的指针.

一个是"分配行数组".它看起来像这样:

int (*rows)[NUM_COLS] = malloc(sizeof(*rows) * NUM_ROWS);
...
free(rows);
Run Code Online (Sandbox Code Playgroud)

然后访问元素[i,j]的语法是rows[i][j].在C89中,NUM_COLS必须在编译时知道.这是一个真正的二维数组.

一个是"分配一维阵列并假装".它看起来像这样:

int *matrix = malloc(sizeof(int) * NUM_COLS * NUM_ROWS);
...
free(matrix);
Run Code Online (Sandbox Code Playgroud)

然后访问元素[i,j]的语法是matrix[NUM_COLS * i + j].这(当然)不是真正的二维阵列.在实践中,它具有与一个相同的布局.


Mik*_*ike 7

从静态来说,这很容易理解:

int mtx[3][2] = {{1, 2},
                 {2, 3},
                 {3, 4}};
Run Code Online (Sandbox Code Playgroud)

这里没什么复杂的.3列,2列; 第一栏中的数据:1, 2, 3; 第二栏中的数据:2, 3, 4.我们可以通过相同的构造访问元素:

for(i = 0; i<3; i++){
    for(j = 0; j<2; j++)
        printf("%d ", mtx[i][j]);
    printf("\n");
}
//output
//1 2
//2 3
//3 4
Run Code Online (Sandbox Code Playgroud)

现在让我们看看指针:

括号是一个非常好的结构,可以帮助简化事情,但是当我们需要在动态环境中工作时它没有帮助,所以我们需要从指针的角度考虑这一点.如果我们想存储整数的"行",我们需要一个数组:

int row[2] = {1,2};
Run Code Online (Sandbox Code Playgroud)

你知道吗?我们可以像指针一样访问它.

printf("%d, %d\n",*row,*(row+1));   //prints 1, 2
printf("%d, %d\n",row[0],row[1]);   //prints 1, 2
Run Code Online (Sandbox Code Playgroud)

现在,如果我们不知道行中的值的数量,如果我们有一个指向int的指针,我们可以使这个数组成为动态长度,并且我们给它一些内存:

int *row = malloc(X * sizeof(int));  //allow for X number of ints
*row = 1;        //row[0] = 1
*(row+1) = 2; //row[1] = 2
…
*(row+(X-1)) = Y; // row[x-1] = Some value y
Run Code Online (Sandbox Code Playgroud)

所以现在我们有一个动态的一维数组; 一排.但是我们想要很多行,而不仅仅是一行,我们不知道有多少行.这意味着我们需要另一个动态1维数组,该数组的每个元素都将是一个指向行的指针.

//we want enough memory to point to X number of rows
//each value stored there is a pointer to an integer
int ** matrix = malloc(X * sizeof(int *));

//conceptually:
(ptr to ptr to int)     (pointer to int)
   **matrix ------------> *row1 --------> [1][2]
                          *row2 --------> [2][3]
                          *row3 --------> [3][4]
Run Code Online (Sandbox Code Playgroud)

现在剩下要做的就是编写将执行这些动态分配的代码:

int i, j, value = 0;

//allocate memory for the pointers to rows
int ** matrix = malloc(Rows * sizeof(int*));

//each row needs a dynamic number of elements
for(i=0; i<Rows; i++){
    // so we need memory for the number of items in each row… 
    // we could call this number of columns as well
    *(matrix + i) = malloc(X * sizeof(int));

     //While we’re in here, if we have the items we can populate the matrix
    for(j=0; j<X; j++)
        *(*(matrix+i)+j) = value; // if you deference (matrix + i) you get the row
                                  // if you add the column and deference again, you
                                  // get the actual item to store (not a pointer!)
}
Run Code Online (Sandbox Code Playgroud)

现在最重要的事情之一就是确保我们在完成后释放内存.每个级别malloc()应具有相同的free()调用次数,并且调用应采用FILO顺序(与malloc调用相反):

for(i=0; i<Rows; i++) 
    free(*(matrix + i));
free(matrix);

//set to NULL to clean up, matrix points to allocated memory now so let’s not use it!
matrix = NULL; 
Run Code Online (Sandbox Code Playgroud)

  • @Lundin:就像说巴黎和热核炸弹的区别在于拼写,因为一个拼写为"巴黎",另一个拼写为"热核炸弹".事实上,不是语法是核心差异或差异与最大效果.语法只是一种交流方式; 传达的东西才是真正的问题.另一种看待这种情况的方法是将其翻译成另一种语言:假设语法被交换但基本行为保持不变.会更好吗?不,双指针问题仍然存在. (3认同)
  • 很好的答案,但是请不要使用指针到指针的语法,它会创建分段的多维。与静态分配的数组不兼容的数组,也与C标准库函数(例如memcpy,memset,bsearch,qsort等)不兼容的数组。有关分配动态多维度的首选方法,请参见Jens的答案。数组。 (2认同)