为什么我的参数不能是
void example(int Array[][]){ /*statements*/}
为什么我需要指定数组的列大小?比如说,3
void example(int Array[][3]){/*statements*/}
我的教授说它是强制性的,但我在开学前就在编码,我记得当我把它作为参数时没有语法或语义错误?还是我错过了什么?
为什么我的参数不能是
void example(int Array[][]){ /*statements*/}
为什么我需要指定数组的列大小?比如说,3
void example(int Array[][3]){/*statements*/}
我的教授说它是强制性的,但我在开学前就在编码,我记得当我把它作为参数时没有语法或语义错误?还是我错过了什么?
在描述参数时,数组总是衰减为指向其第一个元素的指针。
当您传递一个声明为int Array[3]
函数的数组时void foo(int array[])
,它会衰减为指向数组开头的指针,即int *Array;
。顺便说一句,您可以将参数描述为int array[3]
或int array[6]
什int *array
至 - 所有这些都是等价的,您可以毫无问题地传递任何整数数组。
在数组数组(二维数组)的情况下,它也衰减到指向其第一个元素的指针,这恰好是一维数组,即我们得到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
它是指向一维数组的指针,并且适用相同的公式。find 需要最后一个方括号中的字节数size_of_item_being_pointed_by_a
。如果我们有Array[][]
,就不可能找到它,因此不可能取消引用我们需要的数组元素。
如果没有大小,指针算术将不适用于数组数组。Array + 2
将产生什么地址:将地址提前Array
2 个字节(错误)或将指针3* sizeof(int) * 2
字节提前?
在 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 个元素和该行的 3 个元素之后1
,然后再进一步索引。
如果您声明如下函数:
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) { ... }
因为这样,您使用与以前相同的“信息”——列数来调用函数。你访问数组元素的方式有点不同:你必须写*(array + i*cols + j)
你通常写的地方array[i][j]
,因为array
现在是一个指向整数的指针(而不是指针)。
当您声明这样的函数时,您必须小心地使用实际为数组声明的列数调用它,而不仅仅是使用。因此,例如:
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 规定了数组声明符的语义,第 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]
元素。因此,保持对数组声明符的约束是有意义的,因为它与预期的表达式一致,即参数将是一个二维数组,而不仅仅是指向一个一维数组的指针。
实际上,无论是二维数组还是一维数组,它都以一行的形式存储在内存中。所以要说编译器应该在哪里打破指示下一行中的下一个数字的行,我们应该提供列大小。并适当地打破行将给出行的大小。
让我们看一个例子:
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;
^
关于这个有一个类似的帖子。你可以参考下面的链接。 在 C 中创建数组并将指向所述数组的指针传递给函数 希望它有所帮助。
另一方面,编译器需要第二维,以便它可以将“数组”从一个指针移动到下一个,因为整个内存以线性方式排列
当您创建 2D 数组时anytype a[3][4]
,您在内存中实际创建的是任意类型对象 的3
连续块。4
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]
,程序可以按照与一维数组完全相同的步骤进行操作。它可以简单地返回the 3rd element of sizeof(object pointed to)
,这里指向的对象是大小为 4 的 anytype 对象。