矩阵作为 C89 中的函数参数

Matrices as function parameters in C89

在我本科生的大部分 C 编程课程中,我们都在学习 C99,而我们的讲师从不费心教我们 C99 与以前版本之间的主要区别。

我们最近获悉,我们可能会被要求在下一次考试中使用 C89 来实现解决方案。

我的问题是关于在函数内部声明和使用可变长度多维数组的使用。

在C99中,我可以有这样的函数:

void func(int cols, int mat[][cols], int rows);

而在 C89 中,VLA 和类似装置是不合法的。有人告诉我,您需要使用指向指针的指针。所以像:

void func(int **mat, int cols, int rows);

但是,我在理解方面遇到了问题:

  1. 您将如何在函数内访问此矩阵中的元素。您还能使用符号 mat[i][j] 吗?
  2. 您将如何声明和填充这样的矩阵。您是否以 int **mat; 之类的开头?我认为您必须使用 malloc(),但我很难找出确切的声明语句。
  3. 矩阵甚至可以像指针一样使用吗?我读过它们的工作方式相似但不完全相同。 C89中的概述问题如何解决?

附带问题。仍然关于可变大小矩阵,有人告诉我有这样的设置:

int rows, cols;

// get rows and cols from stdinput

int mat[rows][cols];

由于程序堆栈上的分配,这不是创建具有给定维度的矩阵的最佳方法。有什么更好的方法?

谢谢!

在 C89 中,如果您有类似 void func(int **mat, int cols, int rows) 的函数,您可以按如下方式寻址元素,但是,这应该是单个指针:

int main(void)
{
    int arr1[10][10];                    // automatic array, allocated on the stack
    int *arr2= malloc(100*sizeof(int));  // dynamic array, allocated on heap
    func(arr1, 10, 10);                  // pass to func. 
    func(arr2, 10, 10);                  // pass to func. Same for both arrays
    return 0;
}
void func(int *mat, int cols, int rows)
{
    // address element x, y, with x the column number and y the row number:
    int k= mat[cols*y + x];

由于编译器对行和列一无所知,因此您需要自己进行计算。因此,您首先跳过 ycols 列,然后取第 x 个元素。

编辑:

这适用于作为连续内存块的数组,相当于现代 C 标准的 VLA。

另一种方式是 "array of pointers to rows of data",但这不等同于 VLA,这是您的问题。其他答案讨论了此类解决方案。

  1. How you would access elements in this matrix inside the function. Can you still use the notation mat[i][j]?

是的。

  1. How you would declare and populate such a matrix. Do you begin with something like int **mat;? I'm thinking you'd have to use malloc(), but I'm having a hard time figuring out the exact declaration statement.

如果矩阵的大小在编译时未知,或者通常它很大,那么 malloc() 是可行的方法。像这样:

// assume nrows and ncols are dynamic
size_t nrows = /* ... */;
size_t ncols = /* ... */;
size_t i;
int **matrix;

matrix = malloc(nrows * sizeof(int*));
if (matrix == NULL) {
    perror("malloc() failed");
    exit(1);
}

for (i = 0; i < nrows; i++) {
    matrix[i] = malloc(ncols * sizeof(int));
    if (matrix[i] == NULL) {
        perror("malloc() failed");
        exit(1);
    }
}

/* fill the matrix */

/* use the matrix however you want */
func(matrix, nrows, ncols);

/* free the allocated memory once you don't need it anymore */
for (i = 0; i < nrows; i++)
    free(matrix[i]);
free(matrix);
  1. Can matrices even be used like pointers to pointers like that? I have read that they work similarly but not exactly the same. How do you solve the outlined problem in C89?

是的,他们可以。当传递给这样的函数时,数组会衰减为指针。矩阵也是如此,它会衰减为指向指针的指针。参见 What is array decaying

Side question [...] isn't the best way to go about creating a matrix with given dimensions, due to allocation on the program stack. what's a better way?

是的,没错,这不是最好的方法。通常程序的堆栈大小有限,因此在堆栈上分配大数组不是一个好主意。在某些情况下,您可能会超出分配给堆栈使用的可用内存,然后您的程序就会崩溃。在这种情况下,最好的方法是通过 malloc().

使用动态分配

However, I'm having issues understanding:

  1. How you would access elements in this matrix inside the function. Can you still use the notation mat[i][j]?
  2. How you would declare and populate such a matrix. Do you begin with something like int **mat;? I'm thinking you'd have to use malloc(), but I'm having a hard time figuring out the exact declaration statement.
  3. Can matrices even be used like pointers to pointers like that? I have read that they work similarly but not exactly the same. How do you solve the outlined problem in C89?

1. mat[i][j]

在 C89 中,您是正确的,除非由非标准编译器扩展提供(gcc 支持),否则不支持 VLA。但是,您可以在两个不同的数组中完成相同的操作。

如果您知道编译时的数量并且可以为该值定义一个常量,那么您可以声明一个指向的指针-数组 [COLS]。例如,如果您知道您将有 32 列和未知行数,您可以这样做:

#define COLS 32
...
    int (*array)[COLS] = malloc (rows * sizeof *array);

这将在一次调用中分配一个内存块,为 rowsint[32] 数组提供存储空间,让您像以前一样访问 array[i][j]。使用 指向数组的指针 的美妙之处在于您拥有单一分配和单一释放。您可以根据需要 realloc 行数。

(注意: 正如 @PaulOgilvie 指出的那样,将指向数组的指针传递给函数的方式有所不同。您不能传递为 int array[][cols] 与 VLA 一样,您必须传递为 int (*array)[cols] -- 您也可以将其与 VLA 一起使用,但反之则不成立)

你的另一个选择是声明一个指针到指针 type(例如int **array;)。注意这里没有涉及数组,它只是一个指向类型指针的指针。这里的分配是一个两步过程。您首先为一些 指针 (指针的行数)分配内存。例如:

int **array = malloc (rows * sizeof *array);

上面你分配了一个内存块,能够容纳 rows 指针 然后你可以单独分配和分配内存块来容纳任意数量的整数值(没有必要每行指向一个具有相同数量整数值的块——使 "jagged array" 成为可能,因为缺少更好的词)然后为 整数值分配存储(或您使用的任何类型),您会这样做:

for (int i = 0; i < rows; i++)
    array[i] = malloc (cols * sizeof *array[i]);

(注意:您必须验证每个分配,为简洁起见已将其省略。另请注意,在上述两种情况下,取消引用的指针已用于设置分配的类型大小,例如malloc (rows * sizeof *array)可能是malloc (rows * sizeof(int*)))。如果你总是使用 取消引用的指针 来设置 typesize -- 你永远不会得到错误的类型大小)

此时你有一个指向存储 rows 个指针的内存块的指针,然后你分配了一个能够容纳 cols 个整数值的内存块可以作为 array[i][j] 访问。此外,您可以在此处 realloc 内存块提供 rows 指针以在需要时随时添加行,但您还必须为整数值分配存储空间并将这些分配的块分配给新的行指针在您尝试在那里存储值之前。

当您完成基于指针到指针 的模拟二维数组后,您也可以免费使用 2 步。您必须先释放存储整数的已分配块,然后才能释放包含行指针的块,例如

for (int i = 0; i < rows; i++)
    free (array[i]);                /* free storage for integers */
free (array);                       /* free pointers */

2。填充任一对象

在任何一种情况下,由于您可以使用 array[i][j] 符号访问模拟的二维数组,因此您现在可以填充和访问 array 中的值,就像您在 C99+ 下使用二维 VLA 所做的那样。

3。矩阵可以和指向指针的指针一起使用

是的,模拟的二维数组提供与上述完全相同的功能。

How would you access the elements of this matrix inside the function. Can you still use the notation mat[i][j]?

是的,您可以使用类似数组的表示法来访问指针指向的值。

Is an array name a pointer?

How you would declare and populate such a matrix. Do you begin with something like int **mat;? I'm thinking you'd have to use malloc(), but I'm having a hard time figuring out the exact declaration statement.

这是你的选择。如果您处于需要静态分配的场景中,那么您可以使用普通数组声明。如果您事先不知道矩阵维度,那么您应该使用动态分配。在后一种情况下,您应该在指针语法中声明您的矩阵,以便它可以存储 malloc 返回的地址。

How do I work with dynamic multi-dimensional arrays in C?

Can matrices even be used like pointers to pointers like that? I have read that they work similarly but not exactly the same. How do you solve the outlined problem in C89?

在这种情况下不用担心,因为数组在被函数接收时最终会退化为指针。

Isn't this the best way to go about creating a matrix with given dimensions, due to allocation on the program stack. What's a better way?

这个问题同义于Static array vs. dynamic array in C++