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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14548753/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 05:10:55  来源:igfitidea点击:

Passing a multidimensional variable length array to a function

carraysc99

提问by alveko

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 arrayto 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) 说:
error: declaration of ‘arr' as multidimensional array must have bounds for all dimensions except the first

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[],编译器仍然会抱怨:
error: cannot convert ‘int (*)[(((sizetype)(((ssizetype)n) + -1)) + 1)]' to ‘int**' for argument ‘2' to ‘void foo(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)或其他方式可能更好。但是,从理论的角度来看,我仍然有点好奇这里的答案是什么。

回答by Kos

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++ 中将数组传递给函数有点有趣。没有数组类型的右值,所以你实际上是在传递一个指针。

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 或不带或诸如此类。

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

VLA,标准 C99

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

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

VLA 带反向参数,前向参数声明(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.

二维数组的 [x][y] 表示法有效,因为数组的类型包含宽度。没有 VLA = 数组类型必须在编译时固定。

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

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

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

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

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

  • you're in the green in C,
  • you still need a mess in C++, use classes instead.
  • 你在C的绿色中,
  • 您仍然需要在 C++ 中一团糟,请改用类。

For C++, boost::multi_arrayis a solid choice.

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

A workaround

解决方法

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

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

  • a 1D array of pointers to T(A)
  • a 2D array of T(B)
  • 指向T(A) 的一维指针数组
  • 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.

您也可能遇到类似的解决方案,为 eachB的行单独分配。在 C 中,这看起来像 malloc-in-a-loop,类似于 C++ 的 vector-of-vectors。然而,这剥夺了将整个数组放在一个块中的好处。

回答by Pankaj Rai

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