在C语言中将数组和矩阵作为指针或指向指针的指针传递给函数

17

考虑以下代码:

void
foo( int* array ) 
{
    // ...
}

void
bar( int** matrix ) 
{
    // ...
}

int
main( void ) {
    int array[ 10 ];
    int matrix[ 10 ][ 10 ];

    foo( array );
    bar( matrix );

    return 0;
}

我不明白为什么会收到这个警告:

warning: passing argument 1 of ‘bar’ from incompatible pointer type

尽管 'foo' 的调用似乎没有问题。

谢谢 :)

6个回答

54

很明显,C语言社区对此并不十分了解,这可以从浏览SO得到证明。神奇的是,以下所有内容都完全等价,100%相同:

void foo(int (*array)[10]);
void foo(int array[][10]);
void foo(int array[10][10]);
void foo(int array[42][10]);

区分指针和数组非常重要。 数组不是指针。可以将数组转换为指向其第一个元素的指针。如果你有一个指针,你有以下内容:

--------
| ptr  |  -------> data
--------

然而,如果你有一个数组,你会得到这个:

---------------------------
| c1 | c2 | c3 | ... | cn |
---------------------------

使用指针,数据存储在另一个星球上,但通过指针链接。数组本身就包含了数据。现在,多维数组只是一个数组的数组。 这些数组被嵌套到父数组中。因此,你的数组的大小为:

(sizeof(int) * 10) * 10

那是因为你有10个数组,它们都是由10个整数组成的数组。现在,如果你想传递这个数组,它会被转换。但是转换成什么呢?它的第一个元素的指针。元素类型不是指针,而是一个数组。因此,你传递的是一个指向包含10个整数的数组的指针:

int (*)[10] // a pointer to an int[10]
它既不是一个int*的数组,也不是一个int**。你可能会问为什么这个数组不被传递为int**。这是因为编译器必须知道行长度。如果你做一个array[1][0],编译器将从二维数组的开始处距离sizeof(int) * 10字节寻址。它在指向数组类型中解码这些信息。
因此,你必须在以上完全等效的函数原型中选择一个。自然而然的,最后一个版本只是令人困惑的。如果一个参数被声明为数组,编译器会默默地忽略外层维度中写入的任何数字。所以我也不会使用倒数第二个版本。最好使用第一个或第二个版本。重要的是要记住,在C中没有(真正的)数组参数!最终参数将成为指针(在这种情况下为指向数组的指针)。
请注意,上面的多维情况与以下一维情况类似。以下4个版本都是完全等效的:
void foo(int *array);
void foo(int array[]);
void foo(int array[10]);
void foo(int array[42]);

虽然Mark Pim的回答完全围绕我的问题的主要问题,但是我想以此回答作为推荐来补偿您尝试解释在C中将数组作为参数传递的细节的所有努力。谢谢! - Auron

15

在C语言中传递多维数组是一个棘手的问题。请参阅这个FAQ

需要考虑的问题是如何使用bar。如果您总是知道它将被传递为一个10x10的数组,则可以将其重写为:

bar(int matrix[10][10]);

如果你想处理维度不同的数组,那么你可能需要传入长度参数:

bar(int *matrix, int width, int height);

你的回答很好。谢谢! - Auron

6
问题在于数据结构matrix[10][10]实际上不是指向10个数组[10]的十个指针的表格,而是一个由100个整数组成的连续数组。函数bar的正确签名如下:
bar (int matrix[10][10])

如果你想使用间接表示法来表示矩阵,并且将int **matrix作为bar函数的参数类型,那么你需要以不同的方式进行分配:

int *matrix[10];
int my_data[100];
int i;
for (i = 0; i < 10; i++) { matrix[i] = &(my_data[i * 10]); }
bar(matrix);

现在的'matrix' 匹配类型 int **. 'matrix' 是由十个指针数组组成的,您可以通过指针传递它,因此可以获取第二个'*'。

问题在于数据结构matrix[10][10]实际上不是指向十个数组[10]的十个指针的表格,而是一个由100个整数组成的顺序数组。这就是我感到困惑的原因! - Auron

3
这里有一些用于练习的代码——它包含了所有可能的二维数组传递类型以及访问元素值的代码。
#include <stdio.h>

#define NUMROWS 2
#define NUMCOLUMNS 5

#define FILL_ARRAY() \
    *array[0] = '1'; \
    (*array)[7] = '2'; \
    *(array[1]) = '3'; \
    *(*(array+1)+1) = '4'; \
    *(array[0]+3) = '5'; \
    *(*array+2) = '7'; \
    array[0][1] = '6'; 

void multi_01( char (*array)[NUMCOLUMNS] )       { FILL_ARRAY(); }
void multi_02( char array[][NUMCOLUMNS] )        { FILL_ARRAY(); }
void multi_03( char array[NUMROWS][NUMCOLUMNS] ) { FILL_ARRAY(); }
void multi_04( char **array )                    { FILL_ARRAY(); }
void multi_05( char *array[] )                   { FILL_ARRAY(); }
void multi_06( char *array[NUMCOLUMNS] )         { FILL_ARRAY(); }

int main(int argc, char **argv)
{
    int i;
    char mystr[NUMROWS][NUMCOLUMNS] = { { 'X', 'X', 'X', 'X'}, {'X','X','X'} };
    char *pmystr[sizeof(mystr)/sizeof(*mystr)];
    int numcolumns = sizeof(*mystr);
    int numrows = sizeof(mystr)/sizeof(*mystr);
    for( i=0; i<numrows; i++ ) pmystr[i] = *(mystr+i);

    multi_01( mystr );  multi_02( mystr );  multi_03( mystr );
    multi_04( pmystr ); multi_05( pmystr ); multi_06( pmystr );

    printf("array '%s', '%s'\n", mystr[0], mystr[1]);

    getc(stdin);
    return 0;
}

2

您应该将bar定义为:

bar( int* matrix )

在C语言中,所有数组都应该作为int*(或其他类型的type_of_element*)传递。
如果您的数据确实是指针数组,则可以使用int **。例如:int[*data[]。这就是在main(int argc, char *argv[])中得到的内容。

-1
int **matrix

这意味着您拥有一个指向指针的指针。这通常用于表示指向指针数组(也称为向量)的指针。但这绝对不是这种情况。

int matrix[10][10]

这更像是指向大小为10x10个整数的单个内存部分的指针。试着改成:

void bar(int *matrix[])

bar的值为int **matrix,这不是传递矩阵的方式。 - Ulterior

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