函数声明
来自 cppreference.cn
函数声明引入一个标识符,用于指定一个函数,并可选地指定函数参数的类型(即原型)。函数声明(与函数定义不同)可以出现在块作用域以及文件作用域中。
目录 |
[编辑] 语法
在函数声明的声明语法中,类型说明符序列(可能由声明符修改)指定返回类型(可以是除数组或函数类型以外的任何类型),并且声明符有以下三种形式之一:
noptr-declarator ( parameter-list ) attr-spec-seq(可选) |
(1) | ||||||||
noptr-declarator ( identifier-list ) attr-spec-seq(可选) |
(2) | (直至 C23) | |||||||
noptr-declarator ( ) attr-spec-seq(可选) |
(3) | ||||||||
其中
noptr-declarator | - | 任何声明符,除了未加括号的指针声明符。此声明符中包含的标识符成为函数指定符。 |
parameter-list | - | 单个关键字void或逗号分隔的参数列表,列表末尾可以是一个省略号参数 |
identifier-list | - | 逗号分隔的标识符列表,仅当此声明符用作旧式函数定义的一部分时才可能出现 |
属性说明序列 | - | (C23)可选的属性列表,应用于函数类型 |
1) 新式 (C89) 函数声明。此声明既引入函数指定符本身,又作为任何未来函数调用表达式的函数原型,强制参数表达式转换为声明的参数类型,并进行编译时参数数量检查。
int max(int a, int b); // declaration int n = max(12.01, 3.14); // OK, conversion from double to int
2) (C23 前) 旧式 (K&R) 函数定义。此声明不引入原型,任何未来函数调用表达式将执行默认参数提升,如果参数数量与形参数量不匹配,将导致未定义行为。
int max(a, b) int a, b; // definition expects ints; the second call is undefined { return a > b ? a : b; } int n = max(true, (char)'a'); // calls max with two int args (after promotions) int n = max(12.01f, 3.14); // calls max with two double args (after promotions)
3) 非原型函数声明。此声明不引入原型(C23 前)。一种新式函数声明,等同于参数列表void(C23 起)。
[编辑] 解释
函数的返回类型,由说明符和限定符中的类型说明符确定,并可能像声明中通常一样由声明符修改,必须是非数组对象类型或void类型。如果函数声明不是定义,则返回类型可以是不完整类型。返回类型不能是 cvr 限定的:任何限定的返回类型在构建函数类型时都会调整为其非限定版本。
void f(char *s); // return type is void int sum(int a, int b); // return type of sum is int. int (*foo(const void *p))[3]; // return type is pointer to array of 3 int double const bar(void); // declares function of type double(void) double (*barp)(void) = bar; // OK: barp is a pointer to double(void) double const (*barpc)(void) = barp; // OK: barpc is also a pointer to double(void)
函数声明符可以与其他声明符结合使用,只要它们可以共享其类型说明符和限定符
int f(void), *fip(), (*pfi)(), *ap[3]; // declares two functions and two objects inline int g(int), n; // Error: inline qualifier is for functions only typedef int array_t[3]; array_t a, h(); // Error: array type cannot be a return type for a function
如果函数声明出现在任何函数之外,它引入的标识符具有文件作用域和外部链接,除非使用了static
或者可见更早的静态声明。如果声明出现在另一个函数内部,该标识符具有块作用域(并且具有内部或外部链接)。
int main(void) { int f(int); // external linkage, block scope f(1); // definition needs to be available somewhere in the program }
声明中的参数如果不是函数定义的一部分(C23 前)不需要命名
int f(int, int); // declaration // int f(int, int) { return 7; } // Error: parameters must be named in definitions // This definition is allowed since C23
参数列表中的每个参数都是一个引入单个变量的声明,具有以下附加属性:
- 声明符中的标识符是可选的(除非此函数声明是函数定义的一部分)(C23 前)
int f(int, double); // OK int g(int a, double b); // also OK // int f(int, double) { return 1; } // Error: definition must name parameters // This definition is allowed since C23
- 参数允许的唯一存储类说明符是
register
,并且在非定义的函数声明中它被忽略
int f(static int x); // Error int f(int [static 10]); // OK (array index static is not a storage class specifier)
- 任何数组类型的参数都调整为相应的指针类型,如果数组声明符的方括号之间有限定符,则指针类型也可以是限定的(C99 起)
int f(int[]); // declares int f(int*) int g(const int[10]); // declares int g(const int*) int h(int[const volatile]); // declares int h(int * const volatile) int x(int[*]); // declares int x(int*)
- 任何函数类型的参数都调整为相应的指针类型
int f(char g(double)); // declares int f(char (*g)(double)) int h(int(void)); // declares int h(int (*)(void))
- 参数列表可以以
, ...
结尾或者就是...
(C23 起),详见变长参数函数。
int f(int, ...);
- 参数不能是void类型(但可以是void的指针类型)。完全由关键字void组成的特殊参数列表用于声明不带参数的函数。
int f(void); // OK int g(void x); // Error
- 参数列表中出现的任何可以被视为 typedef 名称或参数名称的标识符都被视为 typedef 名称:int f(size_t, uintptr_t) 被解析为声明一个接受两个无名参数(类型分别为 size_t 和 uintptr_t)的函数的新式声明符,而不是一个以“size_t”和“uintptr_t”为参数名开始函数定义的旧式声明符。
- 参数可以具有不完整类型,并且可以使用 VLA 记号 [*](C99 起)(但在函数定义中,经过数组到指针和函数到指针调整后的参数类型必须是完整类型)
属性说明符序列也可以应用于函数参数。 |
(自 C23 起) |
关于函数调用机制的其他细节,请参见函数调用运算符;关于从函数返回,请参见return。
[编辑] 注意
与 C++ 不同,声明符 f() 和 f(void) 具有不同的含义:声明符 f(void) 是一个新式(原型)声明符,声明一个不带参数的函数。声明符 f() 是一个声明带未指定数量参数的函数(除非用于函数定义) int f(void); // declaration: takes no parameters int g(); // declaration: takes unknown parameters int main(void) { f(1); // compile-time error g(2); // undefined behavior } int f(void) { return 1; } // actual definition int g(a,b,c,d) int a,b,c,d; { return 2; } // actual definition |
(直至 C23) |
与函数定义不同,参数列表可以从 typedef 继承
typedef int p(int q, int r); // p is a function type int(int, int) p f; // declares int f(int, int)
在 C89 中,说明符和限定符是可选的,如果省略,函数的返回类型默认为int(可能由声明符修改)。 *f() { // function returning int* return NULL; } |
(直到 C99) |
[编辑] 缺陷报告
以下行为改变的缺陷报告被追溯地应用于以前发布的 C 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
DR 423 | C89 | 返回类型可能被限定 | 返回类型被隐式取消限定 |