将多维数组的数组指针传递给函数,并指定边界

3

假设我有多个(数量不确定)2D数组(甚至可能是可变长度的):

int a[2][2] = {{1,2},{3,4}};
int b[2][2] = {{5,6},{7,8}};
...

我希望把一个2D数组传递给函数。但我不想将这些2D数组复制到3D数组中。然而,我想指定边界,以便函数知道2D数组的维度,这样我就可以方便地使用[i][j]索引它们。

如何格式化函数的签名,使其接受一个指向包含2D数组的数组(长度未知),并且该函数知道它们的维度呢?

例如,类似于:

void myfunc(int[][3] *test, int len)

当然,这在语法上是无效的。在指针传递的数组中指定数组边界是否不可能在C中实现?我是否被迫将a和b移入指针中,或者被迫将它们复制到三维数组中?
2个回答

3
如果您的编译器支持可变长度数组,您可以编写以下代码:
void myfunc( int rows, int cols, int a[rows][cols] );

请注意,第三个参数会被隐式转换为类型为int ( * )[cols]的指针,这意味着在函数内部您正在处理一个一维数组的指针。尽管如此,您仍然可以使用以下表达式:

for ( int i = 0; i < rows; i++ )
{
    for ( int j = 0; j < cols; j++ ) a[i][j] = value;
}

否则,如果编译器不支持可变长度数组且所有数组的第二维度相同,则可以像这样声明函数:
void myfunc(int (*a)[2], int rows);
请注意,这个声明
int[][3] *test

无论如何都是不正确的。

如果您想传递多个二维数组,则可以在main函数中声明一个一维数组,例如:

int a[2][2] = {{1,2},{3,4}};
int b[2][2] = {{5,6},{7,8}};
//...

int ( *p[] )[2] = { a, b, /*...*/ };

然后将其传递给一个函数。

在这种情况下,该函数看起来应该像这样:

void myfunc( int ( **p )[2], size_t n );

这里有一个演示程序。
#include <stdio.h>

void myfunc( int ( **p )[2], size_t n )
{
    for ( size_t i = 0; i < n; i++ )
    {
        for ( size_t j = 0; j < 2; j++ )
        {
            for ( size_t k = 0; k < 2; k++ ) printf( "%d ", p[i][j][k] );
            putchar( '\n' );
        }
        putchar( '\n' );
    }
}

int main(void) 
{
    int a[2][2] = {{1,2},{3,4}};
    int b[2][2] = {{5,6},{7,8}};

    int ( *p[] )[2] = { a, b };

    myfunc( p, sizeof( p ) / sizeof( *p ) );

    return 0;
}

它的输出是

1 2 
3 4 

5 6 
7 8 

如果数组的第一维不是固定的而是变化的,那么您可以将一个包含数组第一维度的数组也传递给函数。

很遗憾,这并没有回答我的问题。我们采用你的答案,但修改一下,使我可以传递多个 a。也许每个 a 存在于一个数组中,或者它在堆中。你如何更新 myfunc 的签名,以便编译器知道每个 a 的维度? - Anti Earth
非常感谢,看起来很有前途!但是,我有点担心它是否有效,因为二维数组的数量与维数相等。也就是说,我怀疑 (**p)[2] 表示符号上表示 外部 数组的长度为 2 (ab),而不是表示最内层的维度为 2。因此,我对它的工作原理感到困惑。 - Anti Earth
@AntiEarth 我[2] 表示数组 a 和 b 的第二个(最右边)维度,例如 int a[2][2]。 - Vlad from Moscow
啊,我明白了,因为在 *p[] 周围有 括号!所以 int **p[2] 读作“最外层有维度2”,但是 int (**p)[2] 读作“最内层有维度2”?这真的很有帮助,非常感谢! - Anti Earth

0
如果你需要传递两个参数到一个函数中,你可以选择以下两种方式:
1. 传递两个单独的参数; 2. 创建一些包含指向这些参数的指针的数据结构,并传递该数据结构的指针。
无论你的参数是数组还是其他任何类型,都不会影响这个规则。
当你有可变数量的参数时,同样适用上述规则。你可以将可变数量的参数传递给一个函数,但这是另一个话题,所以我们将集中讨论第二种选项。在这种情况下,你的数据结构应该是一个指向参数的指针数组。
那么,当你的参数具有复杂类型(例如数组、指向函数返回数组指针的指针数组等)时,如何创建一个数据结构呢?答案很简单:使用typedef。
typedef int MyThing[2][2]; // could be anything
MyThing one = {{1,2},{3,4}};
MyThing two = ...; // etc

MyThing* manyPointersToThings[] = {&one, &two};

void myFunc(int nThings, MyThing things[nThings]) {

   // in here, each things[i] is a *pointer* to MyThing
   (*things[0])[1][2] = 42;
    // whatever
}

这适用于任何类型的thing。如果您的thing实际上是一个数组,还有另一种选择:您的数据结构可以存储指向数组第一个元素的指针,而不是指向数组本身的指针。
typedef int MyThing[2]; // this type names an *element* of your array
MyThing one[2] = {{1,2},{3,4}};
MyThing two[2] = ...; // etc

MyThing* manyPointersToThings[] = {one, two}; // note no & here

void myFunc(int nThings, MyThing things[nThings]) {

   // in here, each things[i] is a pointer to the first element of an array
   things[0][1][2] = 42;
    // whatever
}

使用此选项,您可以获得一些灵活性,因为您的数组不必全部具有相同的大小。您还可以避免使用括号中的丑陋解引用。

为了完整起见,这里是相同函数的原型,不包括typedef:

void myFunc(int nThings, int (*things[nThings])[2][2]) // version 1
void myFunc(int nThings, int (*things[nThings])[2]) // version 2

这些比typedef版本更灵活,因为现在您可以使用变量(另一个参数)而不是硬编码的数字2。

如果您在写上述内容时遇到麻烦,请尝试this


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