将多维可变长度数组传递给函数

时间:2013-01-27 15:11:48

标签: c arrays c99

有很多类似的问题,但我仍然找不到任何与C99 / C11中可变长度数组功能相关的答案。

如何将多维可变长度数组传递给C99 / C11中的函数?

例如:

void foo(int n, int arr[][]) // <-- error here, how to fix?
{
}

void bar(int n)
{
    int arr[n][n];
    foo(n, arr);
}

编译器(g++-4.7 -std=gnu++11)说:
error: declaration of ‘arr’ as multidimensional array must have bounds for all dimensions except the first

如果我将其更改为int *arr[],编译器仍抱怨:
error: cannot convert ‘int (*)[(((sizetype)(((ssizetype)n) + -1)) + 1)]’ to ‘int**’ for argument ‘2’ to ‘void foo(int, int**)’

下一个问题,如何通过值传递它以及如何通过引用传递它?显然,通常你不希望在将它传递给函数时复制整个数组。

对于常量长度数组,它很简单,因为正如“常量”所暗示的那样,在声明函数时应该知道长度:

void foo2(int n, int arr[][10]) // <-- ok
{
}

void bar2()
{
    int arr[10][10];
    foo2(10, arr);
}

我知道,将数组传递给这样的函数并不是最佳实践,我根本不喜欢它。使用平面指针或对象(如std:vector)或其他方式可能更好。但是,从理论的角度来看,我有点好奇这里的答案是什么。

2 个答案:

答案 0 :(得分:43)

将数组传递给函数在C和C ++中有点滑稽。没有数组类型的右值,所以你实际上是在传递一个指针。

要处理2D数组(实数,不是数组数组),您需要传递2个数据块:

  • 指向它开始的指针
  • 一行有多宽

这些是两个独立的值,无论是C或C ++还是VLA或没有或什么都不是。

写一些方法:

最简单,无处不在,但需要更多的手工工作

void foo(int width, int* arr) {
    arr[x + y*width] = 5;
}

VLA,标准C99

void foo(int width, int arr[][width]) {
    arr[x][y] = 5;
}

VLA w /反向参数,前向参数声明(GNU C扩展名)

void foo(int width; int arr[][width], int width) {
    arr[x][y]=5;
}

C ++ w / VLA(GNU C ++扩展,非常难看)

void foo(int width, int* ptr) {
    typedef int arrtype[][width];
    arrtype& arr = *reinterpret_cast<arrtype*>(ptr);
    arr[x][y]=5;
}

大评论:

带有2D数组的[x] [y]表示法有效,因为数组的类型包含宽度。没有VLA =数组类型必须在编译时修复。

因此:如果你不能使用VLA,那么......

  • 没有办法在C中处理它,
  • 如果没有代理类w /在C ++中重载运算符重载,就无法处理它。

如果您可以使用VLA(C99或GNU C ++扩展),那么......

  • 你是C中的绿色,
  • 你仍然需要在C ++中乱七八糟,而是使用类。

对于C ++,boost::multi_array是一个不错的选择。

解决方法

对于2D数组,您可以进行两次单独的分配:

  • 指向T(A)
  • 的一维指针数组
  • T(B)
  • 的二维数组

然后将(A)中的指针设置为指向(B)的各行。

使用此设置,您只需将(A)作为简单T**传递,并且在[x][y]索引时效果很好。

此解决方案适用于2D,但需要越来越多的样板以获得更高的尺寸。由于额外的间接层,它也比VLA解决方案慢。

您也可能遇到类似的解决方案,每个B行都有单独的分配。在C语言中,它看起来像一个malloc-in-a-loop,类似于C ++的向量向量。然而,这消除了将整个阵列放在一个块中的好处。

答案 1 :(得分:1)

没有明确的方法可以做到这一点,但你可以使用一种解决方法将二维数组视为一维数组,然后将其重新转换为函数内的二维数组。

void foo2(int n, int *arr) 
{
    int *ptr; // use this as a marker to go to next block
    int i;
    int j;

    for(i = 0; i < n; i++)
    {
        ptr = arr + i*n; // this is the starting for arr[i] ...
        for (j = 0; j < n ;j++)
        {
            printf(" %d ", ptr[j]); // This is same as arr[i][j]
        }
    }
}

void bar2()
{
    int arr[10][10];
    foo2(10, (int *)arr);
}