函数原型和块代码中的变量声明:差异?

问题描述 投票:1回答:3

为什么int array[]会在main中引发编译错误但不会在函数原型中引发编译错误?这是否意味着总是在函数原型中编写int * array更好?

void myfoo (int array[]) { // No compilation error
;}

void myfoo1 (int *array1) { // No compilation error
;}

int main() {
    int array[]; // Compilation error
    int* array1; // No compilation error
}
c arrays function-call
3个回答
2
投票

从根本上说,main块内的数组声明需要大小而函数参数中的数组声明不是main中的声明定义数组,而函数参数仅接收其他定义的数组。

所以main中的定义需要一个大小,因为它必须为数组保留存储空间。

函数参数只是接收一个数组,所以它只需要知道数组的起始位置。它不需要知道大小。 (也就是说,编译器不需要知道大小就可以编译代码。函数可能需要知道大小才能执行其预期目的,但这是程序员而不是编译器的问题。)

由于C规则,数组实际上永远不会作为函数参数传递。每当数组作为函数参数给出时,编译器会自动将其转换为指向其第一个元素的指针。类似地,函数参数可以是指针但实际上不能是数组。将函数参数声明为数组时,编译器会自动调整它以声明指针。所以函数声明void myfoo(int array[])会自动调整为void myfoo(int *array)

需要在main中声明大小的具体规则是C 2018 6.7 7:

如果声明对象的标识符没有链接,则对象的类型应在其声明符的末尾完成,或者如果它具有初始化器则由其init-declarator的末尾完成; ...


4
投票

在代码中,在函数内部,为数组定义编写int array[];是一个语义错误。

在这种情况下,array是一个没有链接的变量,(“没有存储类说明符extern声明的对象的块作用域标识符”)并且根据规范:

如果声明对象的标识符没有链接,则对象的类型应在其声明者的末尾完成,[....]

一个空的[](中间有或没有空格),不会为数组定义创建一个有效的构造,因为数组的大小仍然是未知的(注意缺少一个显式的大括号括起来的初始化器列表) 。因此,您的编译器会抱怨,因为类型不完整,并且不知道要为该数组变量保留的内存总量。

另一方面,对于函数参数,数组类型参数实际上衰减为指向该类型的指针,因此,指定数组大小不是必需的。引用第§6.7.6.3章

参数声明为''数组类型''应调整为''限定指向类型'',其中类型限定符(如果有)是在数组类型派生的[和]中指定的那些。 [...]

如果函数声明符不是该函数定义的一部分,则参数可能具有不完整的类型,并且可以在其声明符说明符序列中使用[*]表示法来指定可变长度数组类型。

所以,基本上,一个声明

 void myfoo (int array[])

相当于

void myfoo (int *array)

所以,这是编译器接受的。


1
投票
void myfoo (int array[]) { // No compilation error
;}

这意味着将使用整数数组(任意大小)调用myfoo()。编译器可以编译它。

void myfoo1 (int *array1) { // No compilation error
;}

这意味着将使用指向整数的指针调用myfoo1()。编译器可以编译它。

int main() {
    int array[]; // Compilation error
    int* array1; // No compilation error
}

int array[];

这是数组的定义。但是编译器无法确定数组的大小。所以它不知道要分配多少内存。所以你必须提供大小或用一些值初始化它,如下所示。

int array[3];

要么

int array[] = {1,2,1};

.

int* array1;

这只是整数指针的声明。它没有错,编译器可以编译它。

© www.soinside.com 2019 - 2024. All rights reserved.