简体   繁体   English

为什么我们在传递二维数组作为参数时需要指定列大小?

[英]Why do we need to specify the column size when passing a 2D array as a parameter?

Why can't my parameter be为什么我的参数不能是

void example(int Array[][]){ /*statements*/}

Why do I need to specify the column size of the array?为什么我需要指定数组的列大小? Say for example, 3比如说,3

void example(int Array[][3]){/*statements*/}

My professor said its mandatory, but I was coding before school started and I remembered that there was no syntactical or semantic error when I made this my parameter?我的教授说这是强制性的,但我在开学前就开始编码了,我记得当我把它作为我的参数时没有语法或语义错误? Or did I miss something?还是我错过了什么?

When it comes to describing parameters, arrays always decay into pointers to their first element.在描述参数时,数组总是衰减为指向它们的第一个元素的指针。

When you pass an array declared as int Array[3] to the function void foo(int array[]) , it decays into a pointer to the beginning of the array ie int *Array;当您将声明为int Array[3]的数组传递给函数void foo(int array[]) ,它会衰减为指向数组开头的指针,即int *Array; . . Btw, you can describe a parameter as int array[3] or int array[6] or even int *array - all these will be equivalent and you can pass any integer array without problems.顺便说一句,您可以将参数描述为int array[3]int array[6]甚至int *array - 所有这些都是等效的,您可以毫无问题地传递任何整数数组。

In case of arrays of arrays (2D arrays), it decays to a pointer to its first element as well, which happens to be a single dimensional array ie we get int (*Array)[3] .在数组数组(二维数组)的情况下,它也会衰减到指向其第一个元素的指针,这恰好是一个一维数组,即我们得到int (*Array)[3]

Specifying the size here is important.在这里指定大小很重要。 If it were not mandatory, there won't be any way for compiler to know how to deal with expression Array[2][1] , for example.例如,如果它不是强制性的,编译器将无法知道如何处理表达式Array[2][1]

To dereference that a compiler needs to compute the offset of the item we need in a contiguous block of memory ( int Array[2][3] is a contiguous block of integers), which should be easy for pointers.要取消引用编译器需要计算我们在连续内存块中需要的项目的偏移量( int Array[2][3]是一个连续的整数块),这对于指针来说应该很容易。 If a is a pointer, then a[N] is expanded as start_address_in_a + N * size_of_item_being_pointed_by_a .如果a是指针,则a[N]扩展为start_address_in_a + N * size_of_item_being_pointed_by_a In case of expression Array[2][1] inside a function (we want to access this element) the Array is a pointer to a single dimensional array and the same formula applies.在函数中表达式Array[2][1]情况下(我们想要访问这个元素), Array是一个指向一维数组的指针,同样的公式也适用。 The number of bytes in the last square bracket is required to find size_of_item_being_pointed_by_a .最后一个方括号中的字节数需要找到size_of_item_being_pointed_by_a If we had just Array[][] it would be impossible to find it out and hence impossible to dereference an array element we need.如果我们只有Array[][]就不可能找到它,因此不可能解引用我们需要的数组元素。

Without the size, pointers arithmetics wouldn't work for arrays of arrays.如果没有大小,指针算术将不适用于数组数组。 What address would Array + 2 produce: advance the address in Array 2 bytes ahead (wrong) or advance the pointer 3* sizeof(int) * 2 bytes ahead?什么地址将Array + 2产品:提前地址Array 2个字节进取(错误)或推进指针3* sizeof(int) * 2个字节未来?

In C/C++, even 2-D arrays are stored sequentially, one row after another in memory.在 C/C++ 中,即使是二维数组也是按顺序存储在内存中的,一行接一行。 So, when you have (in a single function):因此,当您拥有(在单个函数中)时:

int a[5][3];
int *head;

head = &a[0][0];
a[2][1] = 2; // <--

The element you are actually accessing with a[2][1] is *(head + 2*3 + 1) , cause sequentially, that element is after 3 elements of the 0 row, and 3 elements of the 1 row, and then one more index further.您实际使用a[2][1]访问的元素是*(head + 2*3 + 1) ,依次导致该元素在第0行的 3 个元素和第1行的 3 个元素之后,然后又一个指数。

If you declare a function like:如果您声明一个函数,如:

void some_function(int array[][]) {...}

syntactically, it should not be an error.从语法上讲,它不应该是错误的。 But, when you try to access array[2][3] now, you can't tell which element is supposed to be accessed.但是,当您现在尝试访问array[2][3] ,您无法确定应该访问哪个元素。 On the other hand, when you have:另一方面,当你有:

void some_function(int array[][5]) {...}

you know that with array[2][3] , it can be determined that you are actually accessing element at the memory address *(&array[0][0] + 2*5 + 3) because the function knows the size of the second dimension.您知道使用array[2][3] ,可以确定您实际上正在访问内存地址*(&array[0][0] + 2*5 + 3)处的元素,因为该函数知道第二维度。

There is one other option, as previously suggested, you can declare a function like:如前所述,还有另一种选择,您可以声明一个函数,如:

void some_function(int *array, int cols) { ... }

because this way, you are calling the function with the same "information" as before -- the number of columns.因为这样,您将使用与以前相同的“信息”(列数)调用该函数。 You access the array elements a bit differently then: you have to write *(array + i*cols + j) where you would usually write array[i][j] , cause array is now a pointer to integer (not to a pointer).您访问数组元素的方式略有不同:您必须在通常编写array[i][j]地方编写*(array + i*cols + j) ,因为array现在是指向整数的指针(而不是指向指针的指针) )。

When you declare a function like this, you have to be careful to call it with the number of columns that are actually declared for the array, not only used.当你声明一个这样的函数时,你必须小心地使用实际为数组声明的列数来调用它,而不仅仅是使用。 So, for example:因此,例如:

int main(){
   int a[5][5];
   int i, j;

   for (i = 0; i < 3; ++i){
       for (int j=0; j < 3; ++j){
           scanf("%d", &a[i][j]);
       }
   }

   some_function(&a[i][j], 5); // <- correct
   some_function(&a[i][j], 3); // <- wrong

   return 0;
}

C 2018 6.7.6.2 specifies the semantics of array declarators, and paragraph 1 gives constraints for them, including: C 2018 6.7.6.2 规定了数组声明符的语义,第 1 段给出了对它们的约束,包括:

The element type shall not be an incomplete or function type.元素类型不应是不完整或函数类型。

In a function declaration such as void example(int Array[][]) , Array[] is an array declarator.在诸如void example(int Array[][])类的函数声明中, Array[]是一个数组声明符。 So it must satisfy the constraint that its element type must not be incomplete.所以它必须满足其元素类型不能不完整的约束。 Its element type in that declaration is int [] , which is incomplete since the size is not specified.它在该声明中的元素类型是int [] ,这是不完整的,因为未指定大小。

There is no fundamental reason the C standard could not remove that constraint for parameters that are about to be adjusted to pointers.对于即将调整为指针的参数,C 标准没有任何根本原因不能消除该约束。 The resulting type int (*Array)[] is a legal declaration, is accepted by compilers, and can be used in the form (*Array)[j] .结果类型int (*Array)[]是合法声明,被编译器接受,并且可以以(*Array)[j]形式使用。

However, the declaration int Array[][] suggests that Array is at least associated with a two-dimensional array, and hence is to be used in the form Array[i][j] .但是,声明int Array[][]表明Array至少与二维数组相关联,因此以Array[i][j]的形式使用。 Even if the declaration int Array[][] were accepted and were adjusted to int (*Array)[] , using it as Array[i][j] would not be possible because the subscript operator requires that its pointer operand be a pointer to a complete type, and this requirement is not avoidable as it is needed to calculate the address of the element.即使声明int Array[][]被接受并调整为int (*Array)[] ,将其用作Array[i][j]也是不可能的,因为下标运算符要求其指针操作数是一个指针到一个完整的类型,这个要求是不可避免的,因为它需要计算元素的地址。 Thus, keeping the constraint on the array declarator makes sense, as it is consistent with the intended expression that the argument will be a two-dimensional array, not just a pointer to one one-dimensional array.因此,保持对数组声明符的约束是有道理的,因为它与预期的表达式一致,即参数将是一个二维数组,而不仅仅是一个指向一个一维数组的指针。

As we know, we can pass a variable as an argument(s) in a function. Similarly, we can pass two-dimensional arrays in C++.正如我们所知,我们可以在 function 中将变量作为参数传递。类似地,我们可以在 C++ 中传递二维 arrays。

C++ does not allow us to pass an entire array as an argument to a function. However, we can pass a pointer to an array by specifying the array's name without an index. C++ 不允许我们将整个数组作为参数传递给 function。但是,我们可以通过指定不带索引的数组名称来传递指向数组的指针。

We can pass a 2D array to a function by specifying the size of the columns of a 2D array.我们可以通过指定二维数组列的大小将二维数组传递给 function。 One of the important things to remember here is that the size of rows is optional but the size of the column should not be left empty else the compiler will show an error.这里要记住的重要事情之一是行的大小是可选的,但列的大小不应留空,否则编译器将显示错误。 A 2D array is stored in the memory in a single line.二维数组存储在 memory 中的一行中。 So, to say the compiler where should it break the row indicating the following numbers to be in the next rows we are supposed to provide the column size.因此,要说编译器应该在哪里打破指示以下数字的行,我们应该提供列大小。 And breaking the rows appropriately will automatically give the size of the rows.适当地打破行将自动给出行的大小。

source: https://www.scaler.com/topics/two-dimensional-array-in-cpp/来源: https://www.scaler.com/topics/two-dimensional-array-in-cpp/

There is a similar post regarding this.有一个类似的帖子关于这个。 You can refer below link.您可以参考以下链接。 Creating Array in C and passing pointer to said array to function Hope it helps. 在 C 中创建数组并将指向该数组的指针传递给函数希望它有所帮助。

On the other hand, compiler needs to the second dimension so that it can move "Array" from one pointer to next since the whole memory is arranged in a linear fashion另一方面,编译器需要第二维,以便它可以将“数组”从一个指针移动到下一个指针,因为整个内存以线性方式排列

Actually whether it is a 2d array or a 1d array, it is stored in the memory in a single line.So to say the compiler where should it break the row indicating the next numbers to be in the next rows we are supposed to provide the column size.实际上无论是二维数组还是一维数组,它都存储在内存中的一行中。所以说编译器应该在哪里打破行指示下一行中的下一个数字,我们应该提供列大小。 And breaking the rows appropriately will give the size of the rows.并适当地打破行将给出行的大小。

Let's see an example:让我们看一个例子:

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

This array a is stored in the memory as:这个数组 a 在内存中存储为:

  1  2  3  4  5  6  7  8  9  0

But since we have specified the column size as 3 the memory splits after every 3 numbers.但是由于我们已将列大小指定为 3,因此每 3 个数字后内存就会拆分。

#include<stdio.h>

int main() {
   int a[][3]={1,2,3,4,5,6},i,j;
   for(i=0;i<2;i++)
   {
       for(j=0;j<3;j++)
       {
           printf("%d  ",a[i][j]);
       }
       printf("\n");
   }

}

OUTPUT:输出:

 1  2  3  
 4  5  6  

In the other case,在另一种情况下,

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

The compiler only knows that there are 3 rows but it doesn't know the number of elements in each row so it cannot allocate memory and will show an error.编译器只知道有 3 行,但不知道每行中的元素数,因此无法分配内存并显示错误。

#include<stdio.h>

int main() {
   int a[3][]={1,2,3,4,5,6},i,j;
   for(i=0;i<3;i++)
   {
       for(j=0;j<2;j++)
       {
           printf("%d  ",a[i][j]);
       }
       printf("\n");
   }

}

OUTPUT:输出:

 c: In function 'main':
    c:4:8: error: array type has incomplete element type 'int[]'
    int a[3][]={1,2,3,4,5,6},i,j;
        ^

I thought this was a cool approach.我认为这是一个很酷的方法。 If you take this as the formula to calculate the address of an element in the array:如果将此作为计算数组中元素地址的公式:

a[i][j] = baseArrayAddress + (i + (colSize + elementSize)) + (j * (elementSize)) a[i][j] = baseArrayAddress + (i + (colSize + elementSize)) + (j * (elementSize))

Then you can see that the only thing the compiler needs to know (which it can't otherwise infer) is the size of the column, thus you need to provide it as the programmer so the algorithm can run to calculate the offset.然后你可以看到编译器唯一需要知道的(它不能以其他方式推断)是列的大小,因此你需要以程序员的身份提供它,以便算法可以运行以计算偏移量。

The row number only acts as a multiplier and is provided by the programmer when trying to dereference an array location.行号仅用作乘数,由程序员在尝试取消引用数组位置时提供。

When you create a 2D array, anytype a[3][4] , in memory what you actually create is 3 contiguous blocks of 4 anytype objects.当您创建一个二维数组时,任何类型anytype a[3][4] ,在内存中您实际创建的是4任意类型对象的3个连续块。

a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3]

Now the next question is, why is that so?现在下一个问题是,为什么会这样? Because, keeping with the spec and structure of the language, anytype a[3][4] actually expands out into anytype (*a)[4] , because arrays decay into pointers.因为,按照语言的规范和结构, anytype a[3][4]实际上扩展为anytype (*a)[4] ,因为数组衰减为指针。 And in fact that also expands out into anytype (*(*a)) , however, you've now completely lost the size of the 2D array.事实上,这也扩展为anytype (*(*a)) ,但是,您现在已经完全失去了二维数组的大小。 So, you must help the compiler out a bit.所以,你必须帮助编译器。

If you ask the program for a[2] , the program can follow the exact same steps that it does for 1D arrays.如果您向程序询问a[2] ,则程序可以执行与一维数组完全相同的步骤。 It simply can return the 3rd element of sizeof(object pointed to) , the object pointed to here is of size 4 anytype objects.它可以简单地返回the 3rd element of sizeof(object pointed to)这里指向的对象是大小为 4 的 anytype 对象。

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

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