为什么我的参数不能是
void example(int Array[][]){ /*statements*/}
为什么需要指定数组的列大小?例如,3
void example(int Array[][3]){/*statements*/}
我的教授说这是强制性的,但我在开学前就开始编码了,我记得当我把这个作为参数时,没有语法或语义错误?或者我错过了什么?
1hdlvixo1#
当涉及到描述参数时,数组总是退化为指向其第一个元素的指针。当你将一个声明为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个字节?
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
size_of_item_being_pointed_by_a
Array[][]
Array + 2
3* sizeof(int) * 2
kulphzqa2#
在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个元素之后,然后再前进一个索引。如果您宣告函式,例如:
a[2][1]
*(head + 2*3 + 1)
0
1
void some_function(int array[][]) {...}
从语法上讲,这不应该是一个错误。但是,当你现在试图访问array[2][3]时,你不能分辨应该访问哪个元素。另一方面,当你有:
array[2][3]
void some_function(int array[][5]) {...}
您知道,使用array[2][3],可以确定您实际访问的是内存地址为*(&array[0][0] + 2*5 + 3)的元素**,因为**该函数知道第二维的大小。如前所述,还有一个选项,您可以声明如下函数:
*(&array[0][0] + 2*5 + 3)
void some_function(int *array, int cols) { ... }
因为这样的话,调用函数的“信息”和以前一样--列数。访问数组元素的方式会有一些不同:你必须在通常写array[i][j]的地方写*(array + i*cols + j),因为array现在是一个指向整数的指针(而不是指针)。当你这样声明一个函数时,你必须小心地使用数组中实际 * 声明 * 的列数来调用它,而不仅仅是使用。例如:
array[i][j]
*(array + i*cols + 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; }
lymnna713#
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]将是不可能的,因为下标运算符要求其指针操作数是指向完整类型的指针,因此,在数组声明符上保留约束是有意义的,因为它与预期的表达式一致,即参数将是一个二维数组,而不仅仅是指向一个一维数组的指针。
void example(int Array[][])
Array[]
int []
int (*Array)[]
(*Array)[j]
int Array[][]
Array[i][j]
u5rb5r594#
实际上,无论是二维数组还是一维数组,它都是以一行的形式存储在内存中的。因此,编译器应该在哪一行中断开指示下一行中的下一个数字的行,我们应该提供列的大小。适当地断开行将给予行的大小。让我们看一个例子:
int a[][3]={ 1,2,3,4,5,6,7,8,9,0 };
该数组a在存储器中存储为:但是,由于我们已经将列大小指定为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"); } }
输出:在另一种情况下,
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; ^
m528fe3b5#
正如我们所知,我们可以在函数中将变量作为参数传递。类似地,我们可以在C中传递二维数组。C不允许我们将整个数组作为参数传递给函数。但是,我们可以通过指定不带索引的数组名来传递指向数组的指针。
我们可以通过指定二维数组的列的大小来将二维数组传递给函数。这里需要记住的重要一点是,行的大小是可选的,但列的大小不应留空,否则编译器将显示错误。二维数组以单行形式存储在内存中。因此,为了说明编译器应该在哪里将行断开,指示下面的数字将出现在接下来的行中,我们应该提供列的大小。适当地将行断开将自动给予行的大小。
来源:https://www.scaler.com/topics/two-dimensional-array-in-cpp/
b4wnujal6#
有一个类似的帖子关于这个。你可以参考下面的链接。Creating Array in C and passing pointer to said array to function希望它有帮助。另一方面,由于整个内存是以线性方式排列的,因此编译器需要第二维度,以便将“Array”从一个指针移动到下一个指针
tsm1rwdh7#
在内存中创建二维数组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]下一个问题是,为什么会这样呢?因为,保持语言的规范和结构,anytype a[3][4]实际上扩展成anytype (*a)[4],因为数组退化成指针。事实上,它也扩展成anytype (*(*a)),然而,你现在已经完全失去了二维数组的大小。所以,你必须帮助编译器。如果你向程序请求a[2],程序可以按照处理一维数组的步骤执行,它只返回the 3rd element of sizeof(object pointed to),这里指向的对象是大小为4的任意类型对象。
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]
anytype (*a)[4]
anytype (*(*a))
a[2]
the 3rd element of sizeof(object pointed to)
7条答案
按热度按时间1hdlvixo1#
当涉及到描述参数时,数组总是退化为指向其第一个元素的指针。
当你将一个声明为
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
个字节?kulphzqa2#
在C/C++中,即使是2-D数组也是按顺序存储的,一行接一行地存储在内存中。因此,当你(在一个函数中)有:
您实际使用
a[2][1]
访问的元素是*(head + 2*3 + 1)
,因为按顺序,该元素在0
行的3个元素之后,在1
行的3个元素之后,然后再前进一个索引。如果您宣告函式,例如:
从语法上讲,这不应该是一个错误。但是,当你现在试图访问
array[2][3]
时,你不能分辨应该访问哪个元素。另一方面,当你有:您知道,使用
array[2][3]
,可以确定您实际访问的是内存地址为*(&array[0][0] + 2*5 + 3)
的元素**,因为**该函数知道第二维的大小。如前所述,还有一个选项,您可以声明如下函数:
因为这样的话,调用函数的“信息”和以前一样--列数。访问数组元素的方式会有一些不同:你必须在通常写
array[i][j]
的地方写*(array + i*cols + j)
,因为array
现在是一个指向整数的指针(而不是指针)。当你这样声明一个函数时,你必须小心地使用数组中实际 * 声明 * 的列数来调用它,而不仅仅是使用。例如:
lymnna713#
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]
将是不可能的,因为下标运算符要求其指针操作数是指向完整类型的指针,因此,在数组声明符上保留约束是有意义的,因为它与预期的表达式一致,即参数将是一个二维数组,而不仅仅是指向一个一维数组的指针。u5rb5r594#
实际上,无论是二维数组还是一维数组,它都是以一行的形式存储在内存中的。因此,编译器应该在哪一行中断开指示下一行中的下一个数字的行,我们应该提供列的大小。适当地断开行将给予行的大小。
让我们看一个例子:
该数组a在存储器中存储为:
但是,由于我们已经将列大小指定为3,因此内存在每3个数字之后拆分。
输出:
在另一种情况下,
编译器只知道有3行,但不知道每行中的元素数,因此它无法分配内存,并将显示错误。
输出:
m528fe3b5#
正如我们所知,我们可以在函数中将变量作为参数传递。类似地,我们可以在C中传递二维数组。
C不允许我们将整个数组作为参数传递给函数。但是,我们可以通过指定不带索引的数组名来传递指向数组的指针。
我们可以通过指定二维数组的列的大小来将二维数组传递给函数。这里需要记住的重要一点是,行的大小是可选的,但列的大小不应留空,否则编译器将显示错误。二维数组以单行形式存储在内存中。因此,为了说明编译器应该在哪里将行断开,指示下面的数字将出现在接下来的行中,我们应该提供列的大小。适当地将行断开将自动给予行的大小。
来源:https://www.scaler.com/topics/two-dimensional-array-in-cpp/
b4wnujal6#
有一个类似的帖子关于这个。你可以参考下面的链接。Creating Array in C and passing pointer to said array to function希望它有帮助。
另一方面,由于整个内存是以线性方式排列的,因此编译器需要第二维度,以便将“Array”从一个指针移动到下一个指针
tsm1rwdh7#
在内存中创建二维数组
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]
下一个问题是,为什么会这样呢?因为,保持语言的规范和结构,
anytype a[3][4]
实际上扩展成anytype (*a)[4]
,因为数组退化成指针。事实上,它也扩展成anytype (*(*a))
,然而,你现在已经完全失去了二维数组的大小。所以,你必须帮助编译器。如果你向程序请求
a[2]
,程序可以按照处理一维数组的步骤执行,它只返回the 3rd element of sizeof(object pointed to)
,这里指向的对象是大小为4的任意类型对象。