如何在C语言函数中传递二维数组(矩阵)?

90

我需要这样做以便对矩阵进行持久化操作,那是否意味着它需要通过引用传递?

这样做是否足够?

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

4个回答

151

C语言没有真正的多维数组,但有几种模拟它们的方法。将这样的数组传递给函数的方式取决于用于模拟多个维度的方法:

1)使用数组的数组。只有在编译时完全确定数组边界或编译器支持VLA's时才能使用此方法:

#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() - 为什么只调用一次?。)


在上面提到的第一种方式中,代码将无法编译。分别需要将第17行和第35行中的“rows”和“cols”更改为“ROWS”和“COLS”。 - KZcoding
7
函数void func_vla(int array[rows][cols], int rows, int cols) 应修改为 void func_vla(int rows, int cols, int array[rows][cols]) - David Ranieri
@KZcoding:如果编译器支持C99,或者支持C11并且未定义__STDC_NO_VLA__,则第17行和第35行中使用的VLA符号是正确的。如果编译器不支持VLAs,则当然无法编译。 - Jonathan Leffler
malloc返回一个void指针,你确定在需要时不需要将其转换为int *或int **吗?解决方案2 - CocoCrisp
强制类型转换更多地是C++的惯例,明白了!谢谢@BartvanIngenSchenau - CocoCrisp
显示剩余2条评论

21

如何传递一个变长的二维数组

对于C和C++最简洁的技巧是:像传递一维数组一样传递二维数组,然后在函数内部使用作为二维数组。

#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 ++始终维护1D数组。因此,我们可以像这样传递任何多维数组。


1
@David Ranieri 如果您知道数组的维度,我看不出为什么您不能引用特定的元素。例如int matrix[2][2],占用了4个内存“单元”,因此从*(p)*(p+3)(其中p是第一个元素的地址)都在分配的内存维度内。这个答案也是一样的。您发布的帖子是关于在数组中最后一个元素之后引用一个元素,而这里并非如此。@Minhas Kamal提供的答案是绝对安全的,只要您在rawcol中传递合法的边界值。 - jwpol
对于i/j等于0/1的*(matrix + i*col + j)的评估会引发未定义行为。matrix指针所指向的对象类型是int[2]。因此,*(matrix + 2)是不正确的。 - tstanisl
@tstanisl 抱歉,我无法理解您的示例。您能否稍微解释一下? - Minhas Kamal
我的意思是,matrix[0]被转换为指向第一行第一个元素的指针。这个三个元素长的矩阵的行。因此,如果索引大于2,则无法访问它而不会引发UB。 - tstanisl
我现在明白你的意思了。理论上,你是对的 :) 这个解决方案的重点是- 在 C/C++ 中,多维数组的支持就像是一种语法糖。在内存层面上,它始终是一个一维数组。因此,在实际情况下,对于所有标准的 C/C++ 编译器,这段代码应该可以工作。如果您能想到一个实际的情况,其中这段代码会失败,请分享给我们。 - Minhas Kamal
显示剩余5条评论

15

我不知道你所说的"数据不会丢失"是什么意思。以下是如何将普通的二维数组传递给函数:

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

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

29
随机小知识:需要使用N的原因是因为计算机需要知道每个“行”要增加指针的距离。除了第一个维度外,所有维度都是必要的。C将数组存储为内存块,没有分隔符。 - Christian Mann
数据不会丢失意味着不使用malloc。感谢您的帮助。 - Shweta
1
@Christian Mann:这是一个好的知识点。今天我碰巧写了一个详细的解释 :-) http://stackoverflow.com/questions/3906777/error-defining-and-initializing-multidimensional-array/3910533#3910533 - Arun
今天似乎每个人都在遇到多维数组的问题。 :) 我在另一个问题中也写了类似的解释:https://dev59.com/4lHTa4cB1Zd3GeqPORLd - casablanca
2
@ChristianMann 相反,数组语法之所以起作用,是因为编译器将参数中的数组声明调整为指向第一个元素的指针,即 int (*)[N]。这也是为什么除了最外层维度之外的所有维度都必须提供 - 数组只会衰减一次。这与“计算机需要知道什么”完全无关 - 纯属胡说八道。将其与 1D 情况进行比较:void func (int [n]),它被调整为 void func (int*),所有大小信息都丢失了 - “计算机”一无所知,编译器也不关心。 - Lundin

4

二维数组:

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

}

三维数组:

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

}

4D数组:

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

}

多维数组:

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

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接