简体   繁体   English

将多维数组作为函数参数传递

[英]passing multi-dimensional arrays as function arguments

As we know, when passing arrays as function arguments, only the first dimension's size can be empty, the others must be specified. 众所周知,当将数组作为函数参数传递时,只有第一维的大小可以为空,其他维必须指定。

void my_function(int arr[5][10][15]); // OKAY!
void my_function(int arr[][10][15]); // OKAY!
void my_function(int arr[][][15]); // WRONG!!!
void my_function(int arr[][][]); // WRONG!!!

What is logic behind this? 这背后的逻辑是什么? Could someone explain the main reason? 有人可以解释主要原因吗?

Passing arrays to a function is an illusion: arrays instantly decay to a pointer when you use them. 将数组传递给函数是一种错觉:使用数组时,数组会立即衰减为指针。 That is, with this example: 也就是说,在此示例中:

int foo[10];
foo[1];

the way this is interpreted by the compiler, in foo[1] , foo is first transformed to a pointer to element 0 of foo , and then subscripting is the same as *(pointer_to_foo + 1) . 按照编译器解释的方式,在foo[1]foo首先转换为指向foo元素0的指针,然后下标与*(pointer_to_foo + 1)

However, this is only possible with the first dimension of an array. 但是,这仅在数组的第一维上才有可能。 Suppose this: 假设这:

int foo[5][5];

This puts 25 integer elements contiguously in memory. 这将25个整数元素连续放置在内存中。 It is not the same as int** foo , and not convertible to int** foo , because int** foo represents some number of pointers to some number of pointers, and they don't have to be contiguous in memory. 它与int** foo ,并且不能转换为int** foo ,因为int** foo表示一些指针到一定数量的指针,并且它们不必在内存中是连续的。

While it's legal to use an array type as a function parameter, the compiler builds it identically to if you had specified a pointer to the array's element type: 使用数组类型作为函数参数是合法的,但是编译器将其与您指定了指向数组元素类型的指针相同地构建:

int foo(int bar[10]); // identical to `int foo(int* bar)`

But what happens if you pass a multi-dimensional array? 但是,如果传递多维数组会发生什么呢?

int foo(int bar[5][5]); // identical to what?

Only the first dimension of the array can undergo decay. 仅阵列的第一维可以经历衰减。 The equivalent signature for int foo here would be: 这里的int foo的等效签名为:

int foo(int (*bar)[5]); // identical to `int foo(int bar[5][5])`

where int (*bar)[5] is a pointer to an array of 5 integers. 其中int (*bar)[5]是指向5个整数的数组的指针。 Stacking up more dimensions doesn't change the idea, only the first dimension will decay, and the other dimensions need to have a known size. 堆叠更多的尺寸并不会改变想法,只有第一个尺寸会衰减,而其他尺寸则需要具有已知的尺寸。

In other words, you can skip the first dimension because the compiler doesn't care about its size, as it instantly decays. 换句话说,您可以跳过第一维,因为编译器并不关心它的大小,因为它会立即衰减。 However, subsequent dimensions do not decay at the call site, so you need to know their size. 但是,后续尺寸不会在调用站点衰减,因此您需要知道其尺寸。

Arrays in C and C++ are stored contiguously in memory. C和C ++中的数组连续存储在内存中。 In the case of a multidimensional array, this means you have an array of arrays. 对于多维数组,这意味着您有一个数组数组。

When you traverse an array, you don't necessarily need to know how many elements are in the array. 遍历数组时,不必一定要知道数组中有多少个元素。 You do however need to know the size of each element of the array so you can jump to the correct element. 但是,您确实需要知道数组每个元素的大小,才能跳转到正确的元素。

That's why int arr[][][15] is incorrect. 这就是为什么int arr[][][15]不正确的原因。 It says that arr is an array of unknown size elements are of type int [][15] . 它说arr是大小为int [][15]的未知大小的数组。 But this means you don't know how big each array element is, so you don't know where each array element it in memory. 但这意味着您不知道每个数组元素的大小,因此您也不知道每个数组元素在内存中的位置。

Starting with two bits of standardese: 从标准的两位开始:

6.3.2.1 Lvalues, arrays, and function designators 6.3.2.1左值,数组和函数指示符
... ...
3 Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ''array of type '' is converted to an expression with type ''pointer to type '' that points to the initial element of the array object and is not an lvalue. 3除非它是sizeof运算符, _Alignof运算符或一元&运算符的操作数,或者是用于初始化数组的字符串文字,否则将类型为“ array of type ”的表达式转换为表达式用类型“指向阵列对象的初始元素,是不是左值”指针为类型 “”。 If the array object has register storage class, the behavior is undefined. 如果数组对象具有寄存器存储类,则该行为是不确定的。
... ...
6.7.6.3 Function declarators (including prototypes) 6.7.6.3函数声明符(包括原型)
... ...
7 A declaration of a parameter as ''array of type '' shall be adjusted to ''qualified pointer to type '', where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. 7参数声明为“ 类型数组”应调整为“ 类型指针”,其中类型限定符(如果有)是在数组类型派生的[]中指定的那些。 If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression. 如果关键字static也出现在数组类型派生的[]中,则对于函数的每次调用,对应的实际参数的值应提供对数组第一个元素的访问,该元素至少具有指定数量的元素通过大小表达式。

C 2011 online draft C 2011在线草案

Lovely. 可爱。 What does all that mean? 这是什么意思?

Let's start with the declaration of an array of some arbitrary type T : 让我们开始声明一些任意类型T的数组:

T arr[N];

The type of the expression arr is "N-element array of T " ( T [N] ). 表达式 arr的类型为“ T N个元素数组”( T [N] )。 Unless that expression is the operand of the unary & , sizeof , or _Alignof operators as specified in 6.3.2.1/3 above, then the type of the expression is converted ("decays") to "pointer to T " ( T * ) and the value of the expression is the address of the first element - ie, &arr[0] . 除非该表达式是上面_Alignof中指定的一元&sizeof_Alignof运算符的操作数,否则表达式的类型将转换(“衰减”)为“ T指针”( T * ),并且表达式的值是第一个元素的地址,即&arr[0]

When we pass arr as a parameter to a function, as in 当我们将arr作为参数传递给函数时,如

foo( arr );

what foo actually receives is a pointer, not an array, and you would write the function prototype as foo实际接收的是一个指针,而不是一个数组,您可以将函数原型编写为

void foo( T *arr )

Or... 要么...

As a notational convenience, C allows you to declare the formal parameter using array notation: 为了方便使用符号,C允许您使用数组符号声明形式参数:

void foo( T arr[N] )

or 要么

void foo( T arr[] )

In this case, both T arr[N] and T arr[] are "adjusted" to T *arr as per 6.7.6.3/7 above, and all three forms declare arr as a pointer (this is only true for function parameter declarations). 在这种情况下,根据上面的6.7.6.3/7,T T arr[N]T arr[]都被“调整”为T *arr ,并且所有三种形式都将arr声明为指针(这适用于函数参数声明)。

That's easy enough to see for 1D arrays. 对于一维阵列,这很容易看到。 But what about multidimensional arrays? 但是多维数组呢?

Let's replace T with an array type A [M] . 让我们用数组类型A [M]替换T Our declaration now becomes 我们的声明现在变成

A arr[N][M]; // we're creating N M-element arrays.

The type of the expression arr is "N-element array of M-element arrays of A " ( A [M][N] ). 表达式arr的类型是“ A的M个元素数组的N个元素数组”( A [M][N] )。 By the rule above, this "decays" to type pointer to *M-element array* of A " ( A (*)[M]`). So when we call 根据上述规则,此“衰减”将pointer to *M-element array* of A的pointer to *M-element array* of " ( A(*)[M]`)的pointer to *M-element array* of 。因此,当我们调用

foo( arr );

the corresponding prototype is 相应的原型是

void foo( A (*arr)[M] )1

which can also be written as 也可以写成

void foo( A arr[N][M] )

or 要么

void foo( A arr[][M] );

Since T arr[N] and T arr[] are adjusted to T *arr , then A arr[N][M] and A arr[][M] are adjusted to A (*arr)[M] . 由于将T arr[N]T arr[]调整为T *arr ,因此A arr[N][M]A arr[][M]调整为A (*arr)[M]

Let's replace A with another array type, R [O] : 让我们用另一个数组类型R [O]替换A

R arr[N][M][O];

The type of the expression arr decays to R (*)[M][O] , so the prototype of foo can be written as 表达式arr的类型衰减为R (*)[M][O] ,因此foo的原型可以写成

void foo( R (*arr)[M][O] )

or 要么

void foo( R arr[N][M][O] )

or 要么

void foo( R arr[][M][O] )

Are you starting to see the pattern yet? 您是否开始看到图案了?

When a multi-dimensional array expression "decays" to a pointer expression, only the first (leftmost) dimension is "lost", so in a function prototype declaration, only the leftmost dimension my be left blank. 当多维数组表达式“衰减”到指针表达式时,只有第一个(最左边的)维是“丢失的”,因此在函数原型声明中,只有最左边的维可以留空。


  1. Since the subscript [] operator has a higher precedence than the unary * operator, A *arr[M] would be interpreted as "M-element array of pointers to A ", which is not what we want. 由于下标[]运算符的优先级高于一元*运算符,因此A *arr[M]将被解释为“指向A M元素数组”,这不是我们想要的。 We have to explicitly group the * operator with the identifier to properly declare it as a pointer to an array. 我们必须将*运算符与标识符明确组合在一起,以正确地将其声明为指向数组的指针。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM