C99报价
这个答案旨在引用和解释C99 N1256标准草案的相关部分。
声明者的定义
声明者这个词会出现很多,所以让我们理解它。
从语言语法中,我们发现以下下划线字符是声明符:
int f(int x, int y);
^^^^^^^^^^^^^^^
int f(int x, int y) { return x + y; }
^^^^^^^^^^^^^^^
int f();
^^^
int f(x, y) int x; int y; { return x + y; }
^^^^^^^
声明符是函数声明和定义的一部分。
有两种类型的声明符:
参数类型列表
标识符列表
参数类型列表
声明如下:
int f(int x, int y);
定义如下:
int f(int x, int y) { return x + y; }
它被称为参数类型列表,因为我们必须给出每个参数的类型。
标识符列表
定义如下:
int f(x, y)
int x;
int y;
{ return x + y; }
声明如下:
int g();
我们不能声明具有非空标识符列表的函数:
int g(x, y);
因为6.7.5.3“函数声明符(包括原型)”说:
3函数声明符中不属于该函数定义的标识符列表应为空。
它被称为标识符列表,因为我们只在f(x, y)上给出标识符f(void)和(...),类型后来。
这是一种较旧的方法,不应再使用了。 6.11.6函数声明符说:
1使用带有空括号的函数声明符(不是prototype-format参数类型声明符)是一个过时的功能。
和简介解释了什么是过时的功能:
某些功能已过时,这意味着它们可能会被考虑使用 退出本国际标准的未来修订版。 他们被保留是因为 它们的广泛使用,但它们在新的实现中的使用(用于实现 不建议使用或新程序(用于语言[6.11]或库功能[7.26])
f()vs f(void)用于声明
当你写的时候:
void f();
它必然是一个标识符列表声明,因为6.7.5“声明者”说将语法定义为:
direct-declarator:
[...]
direct-declarator ( parameter-type-list )
direct-declarator ( identifier-list_opt )
所以只有标识符列表版本可以为空,因为它是可选的(f(void))。
f(void)是唯一的语法节点,它定义了声明符的(...)部分。
那么我们如何消除歧义并使用没有参数的更好的参数类型列表呢? 6.7.5.3函数声明符(包括原型)说:
10 void类型的未命名参数作为列表中唯一项的特殊情况指定该函数没有参数。
所以:
void f(void);
就是这样。
这是一个明确允许的神奇语法,因为我们不能以任何其他方式使用f(void)类型参数:
void f(void v);
void f(int i, void);
void f(void, int);
如果我使用f()声明会发生什么?
也许代码编译得很好:6.7.5.3函数声明符(包括原型):
14函数声明符中不属于a的空列表 该函数的定义指定没有关于数量或类型的信息 提供参数。
所以你可以逃脱:
void f();
void f(int x) {}
其他时候,UB可以爬起来(如果你很幸运,编译器会告诉你),你将很难搞清楚原因:
void f();
void f(float x) {}
请参阅:为什么空声明适用于带有int参数的定义但不适用于浮点参数?
定义的f()和f(void)
f() {}
VS
f(void) {}
是相似的,但不完全相同。
6.7.5.3函数声明符(包括原型)说:
14函数声明符中的空列表是该函数定义的一部分,指定该函数没有参数。
看起来类似于f(void)的描述。
但仍然......似乎:
int f() { return 0; }
int main(void) { f(1); }
符合未定义的行为,同时:
int f(void) { return 0; }
int main(void) { f(1); }
是不符合的,如下所述:为什么gcc允许将参数传递给定义为不带参数的函数?
TODO明白为什么。 与原型有关还是与原型有关。 定义原型。