首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么我们需要在传递二维数组作为参数时指定列的大小?

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

Stack Overflow用户
提问于 2012-10-10 14:46:23
回答 6查看 37.7K关注 0票数 37

为什么我的参数不能是

代码语言:javascript
复制
void example(int Array[][]){ /*statements*/}

为什么需要指定数组的列大小?例如,3

代码语言:javascript
复制
void example(int Array[][3]){/*statements*/}

我的教授说这是必修课,但我在开学前就开始编码了,我记得当我把这个作为我的参数时,没有语法或语义错误?还是我错过了什么?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2012-10-10 14:59:17

当涉及到描述参数时,数组总是衰减为指向其第一个元素的指针。

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

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

在这里指定大小很重要。例如,如果它不是强制性的,编译器将无法知道如何处理expression 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个字节?

票数 44
EN

Stack Overflow用户

发布于 2012-10-10 15:19:09

在C/C++中,即使是二维数组也是按顺序存储在内存中的。因此,当您拥有(在单个函数中):

代码语言:javascript
复制
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个元素之后,然后是更多的索引。

如果你像这样声明一个函数:

代码语言:javascript
复制
void some_function(int array[][]) {...}

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

代码语言:javascript
复制
void some_function(int array[][5]) {...}

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

如前所述,还有另一个选项,您可以像这样声明一个函数:

代码语言:javascript
复制
void some_function(int *array, int cols) { ... }

因为通过这种方式,您将使用与前面相同的“信息”--列数来调用函数。然后,您访问数组元素的方式有点不同:您必须在通常编写array[i][j]的地方编写*(array + i*cols + j),因为array现在是指向整数的指针(而不是指向指针)。

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

代码语言:javascript
复制
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;
}
票数 12
EN

Stack Overflow用户

发布于 2020-02-12 01:38:56

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]也是不可能的,因为下标运算符要求其指针操作数是指向完整类型的指针,而这一要求是不可避免的,因为它是计算元素地址所必需的。因此,保持对数组声明符的约束是有意义的,因为它与预期的表达式一致,即参数将是一个二维数组,而不仅仅是指向一个一维数组的指针。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12813494

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档