在C语言中将动态分配的数组作为参数传递

5

那么...我在主程序中有一个动态分配的数组:

int main()
{
    int *array;
    int len;

    array = (int *) malloc(len * sizeof(int));
    ...
    return EXIT_SUCCESS;
}

我还想编写一个函数来对这个动态分配的数组进行操作。 到目前为止,我的函数是:
void myFunction(int array[], ...)
{
   array[position] = value;
}

如果我将其声明为:
void myFunction(int *array, ...);

我还能够做以下事情吗:

array[position] = value;

否则我将不得不做:

*array[position] = value;

如果我正在使用动态分配矩阵,哪种是正确的函数原型声明方式:

void myFunction(int matrix[][], ...);

或者

void myFunction(int **matrix, ...);

...?

4个回答

8

If I declare it as:

void myFunction(int *array, ...);

Will I still be able to do:

array[position] = value;
是的-这是合法语法。

Also, if I am working with a dynamically allocated matrix, which one is correct to declare the function prototype:

void myFunction(int matrix[][], ...);

Or

void myFunction(int **matrix, ...);

...?

如果您正在使用多个维度,您将需要在函数声明中声明除第一个维度以外的所有维度的大小,如下所示:
void myFunction(int matrix[][100], ...);

这个语法不会做你想要的事情:

void myFunction(int **matrix, ...);
matrix[i][j] = ...

这里声明了一个名为“matrix”的参数,它是一个指向指向整数的指针;尝试使用“matrix[i][j]”进行间接引用可能会导致分段错误。
这是在C中使用多维数组的许多困难之一。
以下是一个有用的SO问题,涉及到这个主题: 在C中定义矩阵并将其传递给函数

好的,但是是否可以声明为void myFunction(int **matrix, ...)并使用像matrix[i][j] = value这样的东西? - Thi G.
@ThiG。我更新了我的答案以回答你的问题。希望这可以帮到你! - Tom

3
是的,请使用array[position],即使参数类型是int *array。你提供的另一种方式(*array[position])在这种情况下实际上是无效的,因为[]运算符比*运算符具有更高的优先级,它等同于*(array[position]),试图取消引用a[position]的值,而不是其地址。
对于多维数组会更复杂一些,但你可以这样做:
int m = 10, n = 5;

int matrixOnStack[m][n];
matrixOnStack[0][0] = 0;      // OK
matrixOnStack[m-1][n-1] = 0;  // OK
// matrixOnStack[10][5] = 0;  // Not OK. Compiler may not complain
                              // but nearby data structures might.

int (*matrixInHeap)[n] = malloc(sizeof(int[m][n]));
matrixInHeap[0][0] = 0;       // OK
matrixInHeap[m-1][n-1] = 0;   // OK
// matrixInHeap[10][5] = 0;   // Not OK. coloring outside the lines again.
matrixInHeap的声明应该这样解释:指向nint值数组的“thing”,因此sizeof(*matrixInHeap) == n * sizeof(int),即矩阵中一整行的大小。 matrixInHeap[2][4]之所以有效,是因为matrixInHeap[2]将地址matrixInHeap向前移动了2 * sizeof(*matrixInHeap),跳过了两个完整的n个整数的行,从而得到第三行的地址,然后最后的[4]选择第三行的第五个元素。(请记住,数组索引从0开始,而不是1)

当指向普通多维c数组时(假设您已经知道大小),可以使用相同的类型:

int (*matrixPointer)[n] = matrixOnStack || matrixInHeap;

现在假设您想要一个函数,该函数将可变大小的矩阵之一作为参数。当早期声明变量时,类型中包含了一些关于大小的信息(在堆栈示例中是两个维度,在堆示例中是最后一个维度n)。因此,函数定义中的参数类型将需要那个n值,只要我们将其作为单独的参数包括进来即可,像这样定义函数:

void fillWithZeros(int m, int n, int (*matrix)[n]) {
    for (int i = 0; i < m; ++i)
        for (int j = 0; j < n; ++j)
            matrix[i][j] = 0;
}

如果在函数内不需要使用变量m,我们完全可以将其省略,只要保留变量n即可。
bool isZeroAtLocation(int n, int (*matrix)[n], int i, int j) {
    return matrix[i][j] == 0;
}

然后我们只需要在调用函数时包含大小即可:

fillWithZeros(m, n, matrixPointer);
assert(isZeroAtLocation(n, matrixPointer, 0, 0));

在某些情况下,我们似乎正在为编译器做工作,尤其是在函数体中根本不使用 n(或仅将其用作类似函数的参数)的情况下,但至少它有效。

关于可读性的最后一点:使用 malloc(sizeof(int[len])) 等同于 malloc(len * sizeof(int))(任何告诉你不同的人都不理解 C 语言结构填充),但第一种写法让读者明显地了解我们正在谈论一个数组。对于 malloc(sizeof(int[m][n]))malloc(m * n * sizeof(int)) 同样适用。


你可以把 *(array + position) 理解为 array[position] 的同义词,对吗? - Petr L.

1

Will I still be able to do:

array[position] = value;
是的,因为索引运算符 p[i]*(ptr + i) 是完全相同的。实际上,你可以写成 5[array] 而不是 array[5],它仍然有效。在C中,数组实际上只是指针。唯一使数组定义与指针不同的是,如果你对一个“真正”的数组标识符执行 sizeof,它会给你分配的实际存储大小,而对指针执行 sizeof 将只给你指针的大小,通常是系统的整数大小(尽管可能不同)。

另外,如果我正在使用动态分配的矩阵,哪种方式才是正确的函数原型声明:(…)

两者都不是,因为它们都是指向数组的指针数组,这些数组可以是非连续的。出于性能原因,你希望矩阵是连续的。所以你只需要写

void foo(int matrix[])

并在内部计算正确的偏移量,例如:
matrix[width*j + i]

请注意,使用括号语法编写此内容看起来很奇怪。还要注意,如果您对指针或“未指定长度的数组”函数参数使用sizeof,则会获得指针的大小。

1
不,你只需要继续使用array[position] = value。最终,将参数声明为int *somethingint something[]没有实际区别。两者都可以工作,因为数组定义只是一些隐藏的指针数学。然而,在代码理解方面有一个区别: int array[]总是表示一个数组(尽管可能只有一个元素)。int *pointer可以是单个整数的指针,也可以是整数数组的指针。就寻址/表示而言:pointer == array == &array[0]。如果您正在使用多个维度,则情况略有不同,因为C强制您声明最后一个维度,如果您明确定义多维数组。
int **myStuff1;    // valid
int *myStuff2[];   // valid
int myStuff3[][];  // invalid
int myStuff4[][5]; // valid

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