将多维数组传递给函数C

3

我在C语言中遇到了一个问题,就是如何将多维数组传递给函数。无论是在typedef中还是在参数中都存在问题。

目前,我的函数定义看起来像这样:

int foo(char *a, char b[][7], int first_dimension_of_array);

我声明一个数组如下:

char multi_D_array[20][7];

当我尝试像这样调用函数时:

foo(d, multi_D_array, 20);

我收到一个警告,指出函数的第二个参数来自不兼容的指针类型。我尝试了许多在线上看到的变化,但仍然无法正确获得它。此外,当使用GDB时,我发现只有2D数组的第一个数组被传递进来了。非常感谢您对我的错误进行反馈。


1
尝试传递一个指向数组的指针。 - Rafe Kettler
6个回答

5

重要警告:我通常不会以这种方式处理多维数组。

我只是为了检查而尝试了这个:

#include <stdio.h>

int foo(char b[][7])
{
    printf("%d\n", b[3][4]);
    return 0;
}

int main(int argc, char** argv)
{
    char multi_d_arr[20][7];
    multi_d_arr[3][4] = 42;
    foo(multi_d_arr);
    return 0;
}

使用gcc -Wall编译和运行时没有任何问题。老实说,我不确定第二个参数怎么可能被认为是不兼容的指针类型。工作得很好。

然而,既然你问了,我该如何处理二维数组呢?更好的方法是像这样使用指针:

char** array2d = malloc((MAX_i+1)*sizeof(char*));
for ( i = 0; i < (MAX_i+1); i++ )
{
    array2d[i] = malloc((MAX_j+1)*sizeof(char));
}

现在,为了更清楚明白,您可以访问的最大元素是array2d[MAX_i][MAX_j]

完成后不要忘记反转过程:

for ( i = 0; i < (MAX_i+1); i++ )
{
    free(array2d[i]);
}
free(array2d);

现在,使用这种方法,下标表示法仍然有效,因此array2d[x][y]仍然可以访问正确的值。从字面上讲,array2d是指针数组,每个array2d[i]也是如此。
为什么这是一种更好的方法?因为你可以拥有大小可变的子数组(sub-arrays),只要改变for循环即可。这种技巧经常用于字符串数组中,特别是在应用程序的int main(int argc, char** argv)签名中。 argv是一个包含长度可变的字符串的数组的数组。使用strlen()来确定它们的长度。
你能传递这个吗?当然可以。你刚刚在main函数中接收到它,但你也可以将你的函数签名写成这样:
int foo(char** ar2d, const unsigned int size);

并将其命名为:

char** somearray;
/* initialisation of the above */

foo(ar2d, thesizeofar2d);

/* when done, free somearray appropriately */

需要明确的是,您包含大小参数的方法(例如const unsigned int size)非常好,您应该坚持使用它。


你可以使用C99变长数组并根据其运行时大小声明数组类型,这样做会更好。你建议的方法真的是一个非常糟糕的想法。如果子数组的大小发生变化,你必须为每一行保留大小(否则可能访问未分配的子数组之外的位置)。这意味着需要分配一个带有长度和指向数据的指针的结构体,并且语法更加复杂。如果每个子数组的大小都相同,则仅使用一个malloc分配一块内存更加高效、易于阅读且错误更少。 - kriss
澄清一下,当子数组是字符串时,问题就完全不同了,因为零终止符隐式地给出了子数组大小(字符串大小),避免了沿着结构体保持大小的需要。此外,您经常希望存储指向静态分配字符串的指针。这种用例是唯一好的用例,您不应该从中推广。 - kriss

1
你在问题中给出的声明和函数调用是完全正确的 C 代码,使用 gcc 编译不会产生警告。
你确定你完全复制了有问题的代码吗?

0

从C99开始,你可以使用变长数组来完成像下面这样的操作。基本上为维度数组提供的参数用于声明数组大小。这很可能是现代编译器最好的方法。

#include <stdio.h>

int foo(char *a, int fdim, char b[fdim][7]){

    printf("b[19][6] = %d\n", b[19][6]);
    return 0;
}

int main(){
    char d[10];
    char multi_D_array[20][7];
    int x, y;

    for (x = 0 ; x < 7 ; ++x){
        for (y=0 ; y < 20 ; ++y){
            multi_D_array[y][x] = y + x;
        }
    }
    foo(d, 20, multi_D_array);
}

即使它也适用于 C++,但您也可能对阅读我的答案 那里那里 (以及其他人的答案)以更好地理解 C 数组感兴趣。


正确,只是对于第一维度而言,这一点都没有改变。你的声明与OP给出的声明完全等价(但位置不同)。只有从第二个维度开始,VLA作为函数参数才会带来一些新的东西。 - Jens Gustedt

0

0

嗯,从定义上来说,它是不兼容的。

char[20][7]char[][7] 不同。

只需摆脱警告即可。

它并没有传递“仅传递2D数组的第一个数组”,而是仅传递一个指向数组的指针(指向第一个元素或数组开头的指针)。 您在GDB中只能看到前7个元素,因为根据函数内部数组的类型,GDB只知道7个元素。


1
它们是兼容的。数组multi_D_array在函数调用上下文中使用时,会被转换为指向其第一个元素的指针,该元素具有类型char (*)[7]。参数char [][7]正好是这种类型。 - caf

-2
总之,C/C++ 对多维数组的处理不是很好。大多数情况下,将其作为单维数组并自行进行行列寻址会更容易些。

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