如何在 C 函数中传递 2D 数组(矩阵)?

How to pass 2D array (matrix) in a function in C?

提问人:Shweta 提问时间:10/12/2010 最后编辑:PiperShweta 更新时间:3/21/2022 访问量:316119

问:

我还需要这样做来持久化矩阵上的操作。这是否意味着它需要通过引用传递?

这就足够了吗?

void operate_on_matrix(char matrix[][20]);

C 多维数组 参数传递

评论


答:

15赞 casablanca 10/12/2010 #1

我不知道你说的“数据不会丢失”是什么意思。下面介绍如何将普通的 2D 数组传递给函数:

void myfunc(int arr[M][N]) { // M is optional, but N is required
  ..
}

int main() {
  int somearr[M][N];
  ...
  myfunc(somearr);
  ...
}

评论

29赞 Christian Mann 10/12/2010
随机事实:需要 N 的原因是计算机需要知道将每个“行”的指针递增多远。实际上,除了第一个维度之外,所有维度都是必要的。C 将数组存储为内存块,没有分隔符。
0赞 Shweta 10/12/2010
数据不会丢失意味着不使用 MALLOC。感谢您的帮助。
1赞 Arun 10/12/2010
@Christian Mann:这是一个很好的事实。我今天碰巧写了一个详细的解释:-)stackoverflow.com/questions/3906777/......
0赞 casablanca 10/12/2010
今天似乎每个人都在多维数组方面遇到了问题。:)我在另一个问题中也写了类似的解释:stackoverflow.com/questions/3911244/......
2赞 Lundin 4/10/2017
@ChristianMann 相反,数组语法之所以有效,是因为编译器将参数中的数组声明调整为指向第一个元素的指针,在本例中为 。这也是为什么必须提供除最外层维度之外的所有维度 - 阵列仅衰减一次。这与“计算机需要知道”完全无关——胡说八道。将其与 1D 情况进行比较:,它被调整到并且所有大小信息都丢失了 - “计算机”不了解任何事情,编译器也不在乎。int (*)[N]void func (int [n])void func (int*)
151赞 Bart van Ingen Schenau 10/12/2010 #2

C 实际上没有多维数组,但有几种方法可以模拟它们。将此类数组传递给函数的方式取决于用于模拟多个维度的方式:

1) 使用数组数组。仅当数组边界在编译时完全确定,或者编译器支持 VLA 时,才能使用:

#define ROWS 4
#define COLS 5

void func(int array[ROWS][COLS])
{
  int i, j;

  for (i=0; i<ROWS; i++)
  {
    for (j=0; j<COLS; j++)
    {
      array[i][j] = i*j;
    }
  }
}

void func_vla(int rows, int cols, int array[rows][cols])
{
  int i, j;

  for (i=0; i<rows; i++)
  {
    for (j=0; j<cols; j++)
    {
      array[i][j] = i*j;
    }
  }
}

int main()
{
  int x[ROWS][COLS];

  func(x);
  func_vla(ROWS, COLS, x);
}

2) 使用(动态分配的)指针数组到(动态分配的)数组。这主要用于在运行时之前不知道数组边界的情况。

void func(int** array, int rows, int cols)
{
  int i, j;

  for (i=0; i<rows; i++)
  {
    for (j=0; j<cols; j++)
    {
      array[i][j] = i*j;
    }
  }
}

int main()
{
  int rows, cols, i;
  int **x;

  /* obtain values for rows & cols */

  /* allocate the array */
  x = malloc(rows * sizeof *x);
  for (i=0; i<rows; i++)
  {
    x[i] = malloc(cols * sizeof *x[i]);
  }

  /* use the array */
  func(x, rows, cols);

  /* deallocate the array */
  for (i=0; i<rows; i++)
  {
    free(x[i]);
  }
  free(x);
}

3) 使用一维数组并修复索引。这既可以用于静态分配(固定大小)和动态分配的数组:

void func(int* array, int rows, int cols)
{
  int i, j;

  for (i=0; i<rows; i++)
  {
    for (j=0; j<cols; j++)
    {
      array[i*cols+j]=i*j;
    }
  }
}

int main()
{
  int rows, cols;
  int *x;

  /* obtain values for rows & cols */

  /* allocate the array */
  x = malloc(rows * cols * sizeof *x);

  /* use the array */
  func(x, rows, cols);

  /* deallocate the array */
  free(x);
}

4) 使用动态分配的 VLA。与选项 2 相比,这样做的一个优点是内存分配单一;另一个原因是需要更少的内存,因为不需要指针数组。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

extern void func_vla(int rows, int cols, int array[rows][cols]);
extern void get_rows_cols(int *rows, int *cols);
extern void dump_array(const char *tag, int rows, int cols, int array[rows][cols]);

void func_vla(int rows, int cols, int array[rows][cols])
{
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            array[i][j] = (i + 1) * (j + 1);
        }
    }
}

int main(void)
{
    int rows, cols;

    get_rows_cols(&rows, &cols);

    int (*array)[cols] = malloc(rows * cols * sizeof(array[0][0]));
    /* error check omitted */

    func_vla(rows, cols, array);
    dump_array("After initialization", rows, cols, array);

    free(array);
    return 0;
}

void dump_array(const char *tag, int rows, int cols, int array[rows][cols])
{
    printf("%s (%dx%d):\n", tag, rows, cols);
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
            printf("%4d", array[i][j]);
        putchar('\n');
    }
}

void get_rows_cols(int *rows, int *cols)
{
    srand(time(0));           // Only acceptable because it is called once
    *rows = 5 + rand() % 10;
    *cols = 3 + rand() % 12;
}

(参见 srand() — 为什么只调用它一次?

评论

0赞 KZcoding 5/27/2015
在上面提到的第一种方式中,代码将无法编译。第 17 行和第 35 行中的“rows”和“cols”必须分别更改为“ROWS”和“COLS”。
7赞 David Ranieri 10/13/2015
void func_vla(int array[rows][cols], int rows, int cols)应该是void func_vla(int rows, int cols, int array[rows][cols])
0赞 Jonathan Leffler 12/27/2018
@KZcoding:如果编译器支持 C99,或者如果它支持 C11 但未定义 ,则第 17 行和第 35 行中使用的 VLA 表示法是正确的。如果编译器不支持 VLA,那么它当然不会编译。__STDC_NO_VLA__
0赞 CocoCrisp 3/6/2019
malloc 返回一个 void 指针,您确定不需要在需要时将其转换为 int* 或 int**?解决方案 2
0赞 CocoCrisp 3/6/2019
铸造更像是一种 C++ 惯例,明白了!谢谢@BartvanIngenSchenau
4赞 shinxg 9/12/2018 #3

2D阵列:

int sum(int array[][COLS], int rows)
{

}

3D阵列:

int sum(int array[][B][C], int A)
{

}

4D阵列:

int sum(int array[][B][C][D], int A)
{

}

和 nD 数组:

int sum(int ar[][B][C][D][E][F].....[N], int A)
{

}
21赞 Minhas Kamal 1/9/2019 #4

传递可变长度 2D 数组的最简单方法

对于C和C++来说,最干净的技术是:像一维数组一样传递2D数组,然后在函数中用作2D数组。

#include <stdio.h>

void func(int row, int col, int* matrix){
    int i, j;
    for(i=0; i<row; i++){
        for(j=0; j<col; j++){
            printf("%d ", *(matrix + i*col + j)); // or better: printf("%d ", *matrix++);
        }
        printf("\n");
    }
}

int main(){
    int matrix[2][3] = { {0, 1, 2}, {3, 4, 5} };
    func(2, 3, matrix[0]);

    return 0;
}

在内部,无论数组有多少维,C/C++ 始终保持一维数组。因此,我们可以像这样传递任何多维数组。

评论

0赞 Kartik Chhajed 9/7/2020
只是一个小小的查询:如果我们调用像 这样的函数,那么我们应该有?func(2, 3, matrix)void func(int row, int col, int** matrix)
0赞 Minhas Kamal 10/24/2020
@KartikChhajed 您的代码需要从 到 的隐式转换,大多数 C/C++ 编译器可能不允许这样做。int*[3]int**
0赞 David Ranieri 10/24/2020
@jwpol不幸的是,这不是一个好主意:stackoverflow.com/q/25303647/1606345
1赞 jwpol 10/24/2020
@David 拉涅利 如果你知道数组的维度,我看不出有什么理由不取消引用某个特定元素。例如,占用 4 个内存“单元”,因此 from to(其中 p 是第一个元素的地址)都在分配内存的维度内。这个答案也一样。您发布的线程是关于在数组中的最后一个元素之后取消引用一个元素,这里的情况并非如此。@Minhas Kamal 提供的答案是绝对安全的,前提是您通过了 和 的合法边界。int matrix[2][2]*(p)*(p+3)rawcol
0赞 tstanisl 12/10/2021
对 for / 等于 0/1 的计算调用 UB。对象指针的类型为 。因此是不正确的*(matrix + i*col + j)ijmatrixint[2]*(matrix + 2)