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

时间:2021-08-11 23:20:22

There are tons of similar questions, but still I could not find any answer relevant for the feature of variable length arrays in C99/C11.

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

How to pass multidimensional variable length array to a function in C99/C11?

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

For example:

例如:

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

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

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

编译器(g++-4.7 -std=gnu++11)说:错误:“arr”的声明是多维数组,除了第一个外,所有维度都必须有边界。

If I change it to int *arr[], compiler still complains:
error: cannot convert ‘int (*)[(((sizetype)(((ssizetype)n) + -1)) + 1)]’ to ‘int**’ for argument ‘2’ to ‘void foo(int, int**)’

如果我将它更改为int* arr[],编译器仍然会报错:不能将' int (*)[((((sizetype)((((ssizetype)n) + -1)] ' ' ' . ' int* ' '转换为' int* '

Next question, how to pass it by value and how to pass it by reference? Apparently, usually you don't want the entire array to be copied when you pass it to a function.

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

With constant length arrays it's simple, since, as the "constant" implies, you should know the length when you declare the function:

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

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

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

I know, passing arrays to functions like this is not a best practice, and I don't like it at all. It is probably better to do with flat pointers, or objects (like std:vector) or somehow else. But still, I'm a bit curios what is the answer here from a theoretical standpoint.

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

2 个解决方案

#1


39  

Passing arrays to functions is a bit funny in C and C++. There are no rvalues of array types, so you're actually passing a pointer.

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

To address a 2D array (a real one, not array of arrays), you'll need to pass 2 chunks of data:

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

  • the pointer to where it starts
  • 指针指向它开始的地方
  • how wide one row is
  • 一排多宽啊

And these are two separate values, be it C or C++ or with VLA or without or whatnot.

这是两个独立的值,C或c++或VLA,或非VLA,等等。

Some ways to write that:

Simplest, works everywhere but needs more manual work

最简单,适用于任何地方,但需要更多的手工工作

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

VLA, standard C99

,C99标准

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

VLA w/ reversed arguments, forward parameter declaration (GNU C extension)

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

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

C++ w/ VLA (GNU C++ extension, terribly ugly)

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;
}

Big remark:

The [x][y] notation with a 2D array works because the array's type contains the width. No VLA = array types must be fixed at compile-time.

使用2D数组的[x][y]符号可以工作,因为数组的类型包含宽度。必须在编译时固定所有VLA =数组类型。

Hence: If you can't use VLA, then...

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

  • there's no way to handle it in C,
  • 没有办法用C表示,
  • there's no way to handle it without a proxy class w/ overloaded operator overloading in C++.
  • 在c++中,如果没有代理类w/重载的操作符重载,就无法处理它。

If you can use VLA (C99 or GNU C++ extensions), then...

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

  • you're in the green in C,
  • 绿色的是C,
  • you still need a mess in C++, use classes instead.
  • 您仍然需要在c++中搞得一团糟,使用类代替。

For C++, boost::multi_array is a solid choice.

对于c++, boost:::multi_array是一个可靠的选择。

A workaround

For 2D arrays, you can make two separate allocations:

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

  • a 1D array of pointers to T (A)
  • 指向T (a)的一维指针数组
  • a 2D array of T (B)
  • 一个T (B)的二维数组

Then set the pointers in (A) to point into respective rows of (B).

然后设置(A)中的指针指向(B)的相应行。

With this setup, you can just pass (A) around as a simple T** and it will behave well with [x][y] indexing.

通过这种设置,您可以将(A)作为一个简单的T**传递出去,它将在[x][y]索引中表现良好。

This solution is nice for 2D, but needs more and more boilerplate for higher dimensions. It's also slower than the VLA solution because of the extra layer of indirection.

这个解决方案对于2D来说很好,但是对于更高的维度需要越来越多的样板。它也比VLA溶液慢,因为有额外的间接层。

You may also run into a similar solution with a separate allocation for every B's row. In C this looks like a malloc-in-a-loop, and is analogous of C++'s vector-of-vectors. However this takes away the benefit of having the whole array in one block.

您可能还会遇到类似的解决方案,每个B行都有一个单独的分配。在C中,这看起来像一个mallocin -a-loop,并且类似于c++的向量向量。然而,这就剥夺了将整个数组放在一个块中的好处。

#2


0  

There is no clear cut way for doing this but you can use a workaround to treat a 2 dimensional array as a one dimensional array and then reconvert it to a two dimensional array inside the function.

这样做没有明确的捷径,但是你可以使用一个工作区来把二维数组当作一维数组来处理,然后将其重新转换为函数内的二维数组。

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

#1


39  

Passing arrays to functions is a bit funny in C and C++. There are no rvalues of array types, so you're actually passing a pointer.

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

To address a 2D array (a real one, not array of arrays), you'll need to pass 2 chunks of data:

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

  • the pointer to where it starts
  • 指针指向它开始的地方
  • how wide one row is
  • 一排多宽啊

And these are two separate values, be it C or C++ or with VLA or without or whatnot.

这是两个独立的值,C或c++或VLA,或非VLA,等等。

Some ways to write that:

Simplest, works everywhere but needs more manual work

最简单,适用于任何地方,但需要更多的手工工作

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

VLA, standard C99

,C99标准

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

VLA w/ reversed arguments, forward parameter declaration (GNU C extension)

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

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

C++ w/ VLA (GNU C++ extension, terribly ugly)

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;
}

Big remark:

The [x][y] notation with a 2D array works because the array's type contains the width. No VLA = array types must be fixed at compile-time.

使用2D数组的[x][y]符号可以工作,因为数组的类型包含宽度。必须在编译时固定所有VLA =数组类型。

Hence: If you can't use VLA, then...

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

  • there's no way to handle it in C,
  • 没有办法用C表示,
  • there's no way to handle it without a proxy class w/ overloaded operator overloading in C++.
  • 在c++中,如果没有代理类w/重载的操作符重载,就无法处理它。

If you can use VLA (C99 or GNU C++ extensions), then...

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

  • you're in the green in C,
  • 绿色的是C,
  • you still need a mess in C++, use classes instead.
  • 您仍然需要在c++中搞得一团糟,使用类代替。

For C++, boost::multi_array is a solid choice.

对于c++, boost:::multi_array是一个可靠的选择。

A workaround

For 2D arrays, you can make two separate allocations:

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

  • a 1D array of pointers to T (A)
  • 指向T (a)的一维指针数组
  • a 2D array of T (B)
  • 一个T (B)的二维数组

Then set the pointers in (A) to point into respective rows of (B).

然后设置(A)中的指针指向(B)的相应行。

With this setup, you can just pass (A) around as a simple T** and it will behave well with [x][y] indexing.

通过这种设置,您可以将(A)作为一个简单的T**传递出去,它将在[x][y]索引中表现良好。

This solution is nice for 2D, but needs more and more boilerplate for higher dimensions. It's also slower than the VLA solution because of the extra layer of indirection.

这个解决方案对于2D来说很好,但是对于更高的维度需要越来越多的样板。它也比VLA溶液慢,因为有额外的间接层。

You may also run into a similar solution with a separate allocation for every B's row. In C this looks like a malloc-in-a-loop, and is analogous of C++'s vector-of-vectors. However this takes away the benefit of having the whole array in one block.

您可能还会遇到类似的解决方案,每个B行都有一个单独的分配。在C中,这看起来像一个mallocin -a-loop,并且类似于c++的向量向量。然而,这就剥夺了将整个数组放在一个块中的好处。

#2


0  

There is no clear cut way for doing this but you can use a workaround to treat a 2 dimensional array as a one dimensional array and then reconvert it to a two dimensional array inside the function.

这样做没有明确的捷径,但是你可以使用一个工作区来把二维数组当作一维数组来处理,然后将其重新转换为函数内的二维数组。

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