为什么在将2D数组作为参数传递时需要指定列大小?

时间:2012-10-10 06:46:23

标签: c arrays parameters multidimensional-array parameter-passing

为什么我的参数不能

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

为什么我需要指定数组的列大小?比如说3

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

我的教授说它是强制性的,但我在学校开始之前编码,我记得当我把它作为参数时没有语法或语义错误?还是我错过了什么?

7 个答案:

答案 0 :(得分:31)

在描述参数时,数组总是会衰减成指向第一个元素的指针。

当您将声明为int Array[3]的数组传递给函数void foo(int array[])时,它会衰减为指向数组开头的指针,即int *Array;。顺便说一句,您可以将参数描述为int array[3]int array[6]或甚至int *array - 所有这些都是等效的,您可以毫无问题地传递任何整数数组。

对于数组(2D数组)的数组,它也会衰减到指向其第一个元素的指针,这恰好是一个单维数组,即我们得到int (*Array)[3]

在此指定大小非常重要。如果它不是强制性的,那么编译器就没有办法知道如何处理表达式Array[2][1]

要取消引用编译器需要在连续的内存块中计算所需项的偏移量(int Array[2][3]是一个连续的整数块),这应该很容易用于指针。如果a是指针,则a[N]将展开为start_address_in_a + N * size_of_item_being_pointed_by_a。如果在函数内部使用表达式Array[2][1](我们想要访问此元素),Array是指向单维数组的指针,并且适用相同的公式。查找size_of_item_being_pointed_by_a需要最后一个方括号中的字节数。如果我们只有Array[][],就不可能找到它,因此无法取消引用我们需要的数组元素。

如果没有大小,指针算术将无法用于数组数组。 Array + 2会产生什么地址:提前Array前2个字节中的地址(错误)或提前指向3* sizeof(int) * 2个字节?

答案 1 :(得分:11)

在C / C ++中,即使是2-D数组也是按顺序存储的,在内存中依次存储。所以,当你(在一个函数中):

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

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

您实际使用a[2][1]访问的元素是*(head + 2*3 + 1),因此顺序排列,该元素位于0行的3个元素之后,1的3个元素行,然后再一个索引。

如果您声明如下函数:

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

从语法上讲,它不应该是一个错误。但是,当您现在尝试访问array[2][3]时,您无法分辨应该访问哪个元素。另一方面,当你有:

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

您知道使用array[2][3],可以确定您实际访问内存地址*(&array[0][0] + 2*5 + 3) 的元素,因为该函数知道第二维的大小。

还有一个选项,如前所述,您可以声明一个类似的函数:

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

因为这样,你用相同的&#34;信息&#34;来调用该函数。和以前一样 - 列数。您可以稍微不同地访问数组元素:您必须在*(array + i*cols + j)处编写array[i][j],因为array现在是指向整数的指针(而不是指针)。 / p>

当您声明这样的函数时,您必须小心地使用实际声明的列数来调用它,而不仅仅是使用它。所以,例如:

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

答案 2 :(得分:2)

C 2018 6.7.6.2指定了数组声明符的语义,第1段对此进行了约束,包括:

元素类型不得为不完整或函数类型。

在诸如void example(int Array[][])之类的函数声明中,Array[]是一个数组声明符。因此,它必须满足其元素类型不能不完整的约束。该声明中其元素类型为int [],由于未指定大小,该元素类型不完整。

对于要调整为指针的参数,C标准不能消除此约束没有根本原因。生成的类型int (*Array)[]是合法声明,被编译器接受,并且可以以(*Array)[j]的形式使用。

但是,声明int Array[][]建议Array至少与二维数组相关联,因此应以Array[i][j]的形式使用。即使声明int Array[][]被接受并调整为int (*Array)[],也无法将其用作Array[i][j],因为下标运算符要求其指针操作数是指向完整类型的指针,并且这一要求是不可避免的,因为它是计算元素地址所必需的。因此,保持对数组声明符的约束是有意义的,因为它与预期的表达式一致,即参数将是二维数组,而不仅仅是指向一个一维数组的指针。

答案 3 :(得分:0)

有关于此的类似帖子。您可以参考以下链接。 Creating Array in C and passing pointer to said array to function 希望它有所帮助。

另一方面,编译器需要第二维,以便它可以将“数组”从一个指针移动到下一个指针,因为整个存储器以线性方式排列

答案 4 :(得分:-1)

当你在内存中创建一个二维数组anytype a[3][4]时,你实际创建的是34任何类型对象的连续块。

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]

现在接下来的问题是,为什么会这样?因为,保持语言的规范和结构,anytype a[3][4]实际上扩展为anytype (*a)[4],因为数组会衰减为指针。事实上,它也扩展到anytype (*(*a)),但是,你现在已经完全失去了2D数组的大小。所以,你必须帮助编译器。

如果您向程序询问a[2],程序可以按照与1D阵列完全相同的步骤进行操作。它只能返回the 3rd element of sizeof(object pointed to),此处指向的对象大小为4 anytype对象。

答案 5 :(得分:-1)

这一切都与内部数组表示有关。所以像int arr_2 [2][2] = {{1,2},{3,4}};这样的数组在内存中看起来像1,2,3,4,每个都是整数类型。传递给函数时,arr_2大致相当于&arr[0][0](指向第一个元素的指针)。

在内存int arr[2] = {2,5};中以类似2,5的方式存储和访问常用数组。编译器通过其基数加偏移时间类型base_ptr + index * type来获取它们。要获取存储在int类型的索引1处的值,我们写&arr[0] + 1 * sizeof(int) = 5

因此可以像base_ptr + type * 2nd_dem_len + index那样访问二维数组。要获得存储在[1] [1]的值,我们写&arr_2[0][0] + sizeof(int) * 1 + 1 = 4

因此,如果编译器不知道2nd_dem_len或第二维的长度,则无法访问该数组。它不知道它,因为数组在传递给函数时被淡化为该表示,因为编译器将其转换为没有关于大小信息的指针。

答案 6 :(得分:-1)

实际上无论是2d数组还是1d数组,它都存储在单行内存中。所以说编译器应该在哪里打破行,指示下一行中的下一个数字我们应该是提供列大小。并且适当地分解行将给出行的大小。

让我们看一个例子:

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

此数组a存储在内存中:

  1  2  3  4  5  6  7  8  9  0

但是由于我们已经将列大小指定为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");
   }

}
  

输出:

 1  2  3  
 4  5  6  

在另一种情况下,

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

编译器只知道有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");
   }

}
  

输出:

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