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

所以…我在我的主要上有一个动态分配的数组:

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

我还想构建一个函数,用这个动态分配的数组做一些事情。 到目前为止我的function是:

 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, ...); 

…?

如果我将其声明为:

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

我还能做到:

 array[position] = value; 

是的 – 这是合法的语法。

另外,如果我正在使用动态分配的矩阵,那么声明函数原型是正确的:

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

要么

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

…?

如果您正在使用多个维度,则必须声明函数声明中除第一个维度之外的所有维度的大小,如下所示:

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

此语法不会像您认为的那样执行:

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

这声明了一个名为matrix的参数,它是指向int的指针; 尝试使用matrix[i][j]取消引用可能会导致分段错误。

这是在C中使用多维数组的许多困难之一。

以下是解决此主题的有用SO问题: 定义矩阵并将其传递给C中的函数

我还能做到:

 array[position] = value; 

是的,因为索引运算符p[i]*(ptr + i) 100%相同。 实际上你可以写5[array]而不是array[5] ,它仍然可以工作。 在C数组中实际上只是指针。 使数组定义与指针不同的唯一因素是,如果你取一个“true”数组标识符的sizeof ,它会给你实际的存储大小分配,而获取指针的sizeof只会给你一个大小指针,通常是系统的整数大小(虽然可以不同)。

此外,如果我正在使用动态分配的矩阵,哪一个是声明函数原型的正确方法:( …)

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

 void foo(int matrix[]) 

并在内部计算正确的偏移量,如

 matrix[width*j + i] 

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

不,你只是继续使用array[position] = value

最后,无论你是将一个参数声明为int *something还是int something[] ,都没有什么区别。 两者都有效,因为数组定义只是一些隐藏的指针数学。

但是,关于如何理解代码存在一个区别:

  • int array[]总是表示一个数组(虽然它可能只是一个元素)。
  • 然而, int *pointer可以是指向单个整数或整个整数数组的指针。

就寻址/表示而言: pointer == array == &array[0]

如果您正在使用多个维度,那么事情就会有所不同,因为如果您明确定义多维数组,C会强制您声明最后一个维度:

 int **myStuff1; // valid int *myStuff2[]; // valid int myStuff3[][]; // invalid int myStuff4[][5]; // valid 

是的,请使用array[position] ,即使参数类型是int *array 。 您给出的替代方法( *array[position] )在这种情况下实际上是无效的,因为[]运算符优先于*运算符,使其等效于*(array[position]) ,它试图取消引用a的值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声明的方式是matrixInHeap指向的’thing’是一个n int值的数组,所以sizeof(*matrixInHeap) == n * sizeof(int) ,或者整个行的大小矩阵。 matrixInHeap[2][4]工作原理是因为matrixInHeap[2]将地址matrixInHeap推进2 * sizeof(*matrixInHeap) ,跳过两行n整数,得到第3行的地址,然后是最后的[4]从第三行中选择第五个元素。 (记住数组索引从0开始而不是1)

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

 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))