如何将多维数组传递给 C 和 C++ 中的函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2828648/
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-08-28 11:12:05  来源:igfitidea点击:

How to pass a multidimensional array to a function in C and C++

c++carraysmultidimensional-array

提问by Moeb

#include<stdio.h>
void print(int *arr[], int s1, int s2) {
    int i, j;
    for(i = 0; i<s1; i++)
        for(j = 0; j<s2; j++)
            printf("%d, ", *((arr+i)+j));
}

int main() {
    int a[4][4] = {{0}};
    print(a,4,4);
}

This works in C, but not in C++.

这适用于 C,但不适用于 C++。

error:

错误:

cannot convert `int (*)[4]' to `int**' for argument `1' to 
`void print(int**, int, int)'

Why does it not work in C++? What change is needed to be made?

为什么它在 C++ 中不起作用?需要做哪些改变?

回答by AnT

This code will notwork in either C or C++. An array of type int[4][4]is not convertible to a pointer of type int **(which is what int *arr[]stands for in parameter declaration). If you managed to compileit in C, it is simply because you probably ignored a C compiler warning of basically the same format as the error message you got from C++ compiler. (Sometimes C compilers issue warningsfor what is essentially an error.)

此代码不适用于 C 或 C++。类型数组int[4][4]不能转换为类型指针int **(这是int *arr[]参数声明中的意思)。如果您设法用 C编译它,那仅仅是因为您可能忽略了与从 C++ 编译器获得的错误消息格式基本相同的 C 编译器警告。(有时 C 编译器会针对本质上是错误的内容发出警告。)

So, again, don't make assertions that are not true. This code does not work in C. In order to convert a built-in 2D array into a int **pointer you can use a technique like this one

所以,再一次,不要做出不正确的断言。这段代码在 C 中不起作用。为了将内置的 2D 数组转换为int **指针,您可以使用这样的技术

Converting multidimensional arrays to pointers in c++

将多维数组转换为 C++ 中的指针

(See the accepted answer. The problem is exactly the same.)

(请参阅已接受的答案。问题完全相同。)

EDIT:The code appearsto work in C because another bug in the printing code is masquerading the effects of the bug in array passing. In order to properly access an element of an int **pseudo-array, you have to use expression *(*(arr + i) + j), or better a plain arr[i][j](which is the same thing). You missed the extra *which made it print something that has absolutely nothing to do with the content of your array. Again, initialize your array in mainto something else to see that the results you are printing in C have absolutely nothing to do with the your intended content of the array.

编辑:代码似乎在 C 中工作,因为打印代码中的另一个错误是伪装数组传递中错误的影响。为了正确访问int **伪数组的元素,您必须使用 expression *(*(arr + i) + j),或者更好的是普通arr[i][j](这是同一件事)。你错过了额外的*东西,这使它打印出与数组内容完全无关的东西。同样,将您的数组初始化main为其他内容,以查看您在 C 中打印的结果与您想要的数组内容完全无关。

If you change the printfstatement as shown above, your code will most likely crash because of the array-passing bug I described initially.

如果你改变printf上面的语句,你的代码很可能会因为我最初描述的数组传递错误而崩溃。

One more time: you cannot pass a int[4][4]array as an int **pseudo-array. This is what the C++ is telling you in the error message. And, I'm sure, this is what your C compiler told you, but you probably ignored it, since it was "just a warning".

再一次:您不能将int[4][4]数组作为int **伪数组传递。这就是 C++ 在错误消息中告诉您的内容。而且,我敢肯定,这就是你的 C 编译器告诉你的,但你可能忽略了它,因为它“只是一个警告”。

回答by Lucas

The problem is, that

问题是,那

int a[4][4];

will actually be stored in a physically continuous memory. So, to access an arbitrary part of your 4x4 array, the function "print" needs to know the dimensions of the array. For example the following little piece of code, will access the same part of the memory in two different ways.

实际上会存储在物理上连续的内存中。因此,要访问 4x4 数组的任意部分,函数“print”需要知道数组的维度。例如下面的一小段代码,将以两种不同的方式访问内存的同一部分。

#include <iostream>

void print(int a[][4]) {
    for (int i = 0; i <4; i++) {
        for (int j = 0; j < 4; j++) {
            //accessing as 4x4 array
            std::cout << a[i][j] <<std::endl;        

            //accessing corresponding to the physical layout in memory
            std::cout <<  *(*(a)+ i*4 + j) << std::endl;  

        }
    }
}

int main() {
    int a[4][4];

    //populating the array with the corresponding indices from 0 to 15
    int m = 0;
    for (int i = 0; i<4; i++) {
        for (int j= 0; j < 4; j++) {
            a[i][j] =  m;
            m++;
        }
    }
    print(a);
}

So the memory layout doesn't change but the way of accessing does. It can be visualized like a checkerboard.

所以内存布局不会改变,但访问方式会改变。它可以像棋盘一样可视化。

   0  1  2  3
  ----------
0| 1  2  3  4
1| 5  6  7  8
2| 9 10 11 12
3|13 14 15 16

But the real physical memory looks like this.

但真正的物理内存看起来是这样的。

0*4+0 0*4+1 0*4+2 0*4+3 1*4+0 1*4+1 1*4+2 1*4+3 2*4+1   etc.
-----------------------------------------------------
1      2       3    4     5     6      7     8     9    etc.

In c++ the data of an array is stored row-by-row and the length of a row (in this case 4) is always necessary to get to the proper memory offset for the next row. The first subscript therefore only indicates the amount of storage that is needed when the array is declared, but is no longer necessary to calculate the offset afterwards.

在 C++ 中,数组的数据是逐行存储的,并且行的长度(在本例中为 4)总是需要获得下一行的正确内存偏移量。因此,第一个下标仅指示声明数组时所需的存储量,而不再需要之后计算偏移量。

回答by IVlad

#include<stdio.h>
void print(int arr[][4], int s1, int s2) {
    int i, j;
    printf("\n");
    for(i = 0; i<s1; i++) {
        for(j = 0; j<s2; j++) {
            printf("%d, ", *((arr+i)+j));
        }
    }
    printf("\n");
}

int main() {
    int a[4][4] = {{0}};
    print(a,4,4);
}

This will work, where by work I mean compile. @AndreyT explained why your version doesn't work already.

这会起作用,我所说的工作是指编译。@AndreyT 解释了为什么您的版本已经不起作用。

This is how you should pass a 2d array.

这就是你应该如何传递一个二维数组。

For clarity, you can also specify both sizes in the function declaration:

为清楚起见,您还可以在函数声明中指定两种大小:

#include<stdio.h>
void print(int arr[4][4], int s1, int s2) {
    int i, j;
    printf("\n");
    for(i = 0; i<s1; i++) {
        for(j = 0; j<s2; j++) {
            printf("%d, ", *((arr+i)+j));
        }
    }
    printf("\n");
}

int main() {
    int a[4][4] = {{0}};
    print(a,4,4);
}

Both will work.

两者都会起作用。

You should also change *((arr+i)+j)to either a[i][j](preferably) or *(*(arr+i)+j)if your intention is to access the jth element of row i.

您还应该更改*((arr+i)+j)a[i][j](最好)或者*(*(arr+i)+j)您的意图是访问jrow的第 th 个元素i

回答by Christoph

Here's a version which is both working, but theoretically invalid (see below) C90 and C++98:

这是一个既有效又理论上无效的版本(见下文)C90 和 C++98:

#include <stdio.h>

static void print(int *arr, size_t s1, size_t s2)
{
    size_t i, j;
    printf("\n");
    for(i = 0; i < s1; i++) {
        for(j = 0; j < s2; j++) {
            printf("%d, ", arr[i * s2 + j]);
        }
    }
    printf("\n");
}

int main(void) {
    int a[4][4] = {{0}};
    print(a[0], 4, 4);
    return 0;
}

A C++ version using templates (adapted from Notinlist's answer) could look like this:

使用模板的 C++ 版本(改编自Notinlist 的回答)可能如下所示:

#include <iostream>
#include <cstring>

using namespace std;

template <size_t N, size_t M>
struct IntMatrix
{
    int data[N][M];
    IntMatrix() { memset(data, 0, sizeof data); }
};

template <size_t N, size_t M>
ostream& operator<<(ostream& out, const IntMatrix<N,M>& m)
{
    out << "\n";
    for(size_t i = 0; i < N; i++) {
        for(size_t j = 0; j < M; j++) {
            out << m.data[i][j] << ", ";
        }
    }
    out << "\n";
    return out;
}

int main()
{
    IntMatrix<4,4> a;
    cout << a;
    return 0;
}

Alternatively, you could use nested STL containers - ie vector< vector<int> >- instead of a plain array.

或者,您可以使用嵌套的 STL 容器 - 即vector< vector<int> >- 而不是普通数组。

With C99, you could do

使用 C99,你可以做到

static void print(size_t s1, size_t s2, int arr[s1][s2]) {
    printf("\n");
    for(size_t i = 0; i < s1; i++) {
        for(size_t j = 0; j < s2; j++) {
            printf("%d, ", arr[i][j]);
        }
    }
    printf("\n");
}

and call it as

并将其称为

print(4, 4, a);


As Robert pointed out in the comments, the first snippet actually involves undefined behaviour. However, assuming that pointer arithmetics will always result in a pointer even when undefined behaviour is involved (and not blow up your computer), there is only a single possible result because of other restrictions within the standard, ie this is an instance of where the standard leaves something unnecessarily undefined.

正如罗伯特在评论中指出的那样,第一个片段实际上涉及未定义的行为。但是,假设即使涉及未定义的行为(并且不会炸毁您的计算机),指针算术也始终会产生指针,由于标准中的其他限制,只有一个可能的结果,即这是一个实例标准会留下一些不必要的未定义内容。

As far as I can tell, substituting

据我所知,替换

print(a[0], 4, 4);

with

union m2f { int multi[4][4]; int flat[16]; } *foo = (union m2f *)&a;
print(foo->flat, 4, 4);

will make it legal C.

将使其合法 C.

回答by bobobobo

You can use int**instead. Its much more flexible:

你可以int**改用。它更灵活:

#include <stdio.h>
#include <stdlib.h>
void print(int **a, int numRows, int numCols )
{
  int row, col ;
  for( int row = 0; row < numRows; row++ )
  {
    for( int col = 0; col < numCols ; col++ )
    {
      printf("%5d, ", a[row][col]);
    }
    puts("");
  }
}

int main()
{
  int numRows = 16 ;
  int numCols = 5 ;
  int **a ;

  // a will be a 2d array with numRows rows and numCols cols

  // allocate an "array of arrays" of int
  a = (int**)malloc( numRows* sizeof(int*) ) ;

  // each entry in the array of arrays of int
  // isn't allocated yet, so allocate it
  for( int row = 0 ; row < numRows ; row++ )
  {
    // Allocate an array of int's, at each
    // entry in the "array of arrays"
    a[row] = (int*)malloc( numCols*sizeof(int) ) ;
  }

  int count = 1 ;
  for( int row = 0 ; row < numRows ; row++ )
  {
    for( int col = 0 ; col < numCols ; col++ )
    {
      a[row][col] = count++ ;
    }
  }

  print( a, numRows, numCols );
}

Another thingwhich you may be interested in is a structure like D3DMATRIX:

您可能感兴趣的另一件事是像D3DMATRIX这样的结构:

typedef struct _D3DMATRIX {
    union {
        struct {
            float        _11, _12, _13, _14;
            float        _21, _22, _23, _24;
            float        _31, _32, _33, _34;
            float        _41, _42, _43, _44;

        };
        float m[4][4];
    };
} D3DMATRIX;

D3DMATRIX myMatrix ;

The sweet thing about this little tidbit is you can use both myMatrix.m[0][0](to access the first element), or you can use myMatrix._11to access that same element as well. The unionis the secret.

这个小花絮的好处是你可以同时使用两者myMatrix.m[0][0](访问第一个元素),或者你也可以使用myMatrix._11访问同一个元素。该联盟是秘密。

回答by Notinlist

#include<cstdio>
template <size_t N, size_t M>
struct DataHolder
{
    int data[N][M];
    DataHolder()
    {
       for(int i=0; i<N; ++i)
           for(int j=0; j<M; ++j)
               data[i][j] = 0;
    }
};

template <size_t N, size_t M>
void print(const DataHolder<N,M>& dataHolder) {
    printf("\n");
    for(int i = 0; i<N; i++) {
        for(int j = 0; j<M; j++) {
            printf("%d, ", dataHolder.data[i][j]);
        }
    }
    printf("\n");
}

int main() {
    DataHolder<4,4> a;
    print(a);
}

回答by Robert Gamble

Aside from using variable-length arrays in C99, you can't really portablywrite a function to accept a multidimensional array if the sizes of the arrays are not known at compile-time, See Question 6.19of the C-FAQ. The best way to handle this is to simulate multidimensional arrays using dynamically allocated memory. Question 6.16does a very good job of explaining the details of doing this.

除了在C99采用变长数组,你不能真正便携写一个函数接受多维数组如果数组的大小并不在编译时已知的,见问题6.19中的C-FAQ。处理此问题的最佳方法是使用动态分配的内存来模拟多维数组。 问题 6.16很好地解释了这样做的细节。

回答by it-west.net

Multidimensional arrays are continuous blocks of memory. So you can do it this way:

多维数组是连续的内存块。所以你可以这样做:

#include <stdio.h>

void pa(const int *a, int y, int x)
{
    int i, j;
    for (i=0;i<y;i++)
    {
        for (j=0;j<x;j++)
            printf("%i", *(a+j+i*x));
        printf("\n");
    }
}

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

    pa(a[0], 4, 3);

    return 0;
}

It also works in C++;

它也适用于 C++;

回答by devmabbott

#include<stdio.h>
void print(int (*arr)[4], int s1, int s2) {
    int i, j;
    for(i = 0; i<s1; i++)
        for(j = 0; j<s2; j++)
            printf("%d, ", arr[i][j]);
}

int main() {
    int a[4][4] = {{6}};
    print(a,4,4);
}

this will compile edit: someone already posted this solution my bad

这将编译编辑:有人已经发布了这个解决方案我不好

回答by kriss

Short answer, you may change the program as following

简短的回答,你可以改变程序如下

void print(int arr[], int s1, int s2) {
...
printf("%d,", *(a+i + s2*j));
...
print((int*)a,4,4);

This would need a better answer explaining the differences between pointer and pointer arithmetic and arrays in C and C++. I won't launch into that now. Maybe someone else ?

这需要一个更好的答案来解释 C 和 C++ 中指针和指针算术与数组之间的区别。我现在不会启动。也许是别人?

I obviously am not shocked by the same point as other posters in your code. What bother me most in the print function header is that you use a double indirection for an array where you do not intend to change initial pointer back (in fact it can't be done as it is a constant). @|V|lad answer fix this by setting one or two dimensions to a fixed constant, but then passing s1 and s2 become useless.

显然,我对代码中其他海报的同一点并不感到震惊。在打印函数头中最让我烦恼的是,您对数组使用了双重间接寻址,而您不打算将初始指针改回(实际上,它是一个常量,因此无法完成)。@|V|lad 回答通过将一维或二维设置为固定常量来解决此问题,但随后传递 s1 和 s2 变得无用。

All depends of what you really want to do. Is print a general purpose array printing function or a specialized one for some array types ?

一切都取决于你真正想做什么。print 是通用数组打印功能还是某些数组类型的专用功能?