Function declarations
函数声明引入一个 标识符 ,该标识符用于指代函数,并可选择性地指定函数参数的类型(即 原型 )。函数声明(与 定义 不同)可以出现在块作用域和文件作用域中。
目录 |
语法
在函数声明的 声明语法 中,可能被声明符修改的 类型说明符 序列指定了 返回类型 (可以是除数组或函数类型之外的任何类型),而 声明符 具有 三种 (C23前) 两种 (C23起) 形式之一:
noptr-declarator
(
parameter-list
)
attr-spec-seq
(optional)
|
(1) | ||||||||
noptr-declarator
(
identifier-list
)
attr-spec-seq
(optional)
|
(2) | (C23前) | |||||||
noptr-declarator
(
)
attr-spec-seq
(optional)
|
(3) | ||||||||
其中
| noptr-declarator | - | 除未加括号的指针声明符之外的任何 声明符 。该声明符中包含的标识符即为成为函数指示符的标识符。 |
| parameter-list | - | 可以是单独的关键字 void ,也可以是逗号分隔的 形参 列表(可能以 可变参数 结尾) |
| identifier-list | - | 逗号分隔的标识符列表,仅当该声明符作为旧式 函数定义 的一部分时可用 |
| attr-spec-seq | - | (C23) 可选的 属性 列表,应用于函数类型 |
int max(int a, int b); // declaration int n = max(12.01, 3.14); // OK, conversion from double to int
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)
说明
函数的返回类型由 说明符与限定符 中的类型说明符决定,并可能按照 声明 中的常规方式被 声明符 修改,必须是非数组对象类型或 void 类型。若函数声明非定义式,则返回类型可以是 不完整类型 。返回类型不能带有cv限定符:任何带有限定符的返回类型在构建函数类型时,都会被调整为无限定版本。
void f(char *s); // 返回类型为 void int sum(int a, int b); // sum 的返回类型为 int int (*foo(const void *p))[3]; // 返回类型为指向 3 个 int 数组的指针 double const bar(void); // 声明函数类型为 double(void) double (*barp)(void) = bar; // 正确:barp 是指向 double(void) 的指针 double const (*barpc)(void) = barp; // 正确:barpc 也是指向 double(void) 的指针
函数声明符可以与其他声明符组合,只要它们能够共享其类型说明符和限定符
int f(void), *fip(), (*pfi)(), *ap[3]; // 声明两个函数和两个对象 inline int g(int), n; // 错误:inline限定符仅适用于函数 typedef int array_t[3]; array_t a, h(); // 错误:数组类型不能作为函数的返回类型
若函数声明出现在任何函数外部,其引入的标识符具有 文件作用域 和 外部链接 ,除非使用了 static 关键字或存在可见的早期静态声明。若声明出现在其他函数内部,该标识符具有块作用域(同时具有内部或外部链接)。
int main(void) { int f(int); // 外部链接,块作用域 f(1); // 函数定义需要在程序中的某处可用 }
在 不属于 函数定义 部分的 (C23前) 声明中,参数无需命名:
int f(int, int); // 声明 // int f(int, int) { return 7; } // 错误:定义中的参数必须命名 // 此定义自 C23 起被允许
函数 参数列表 中的每个参数都是一个 声明 ,用于引入单个变量,并具有以下附加属性:
- 声明符中的标识符是可选的 (除非此函数声明是函数定义的一部分) (C23 前)
int f(int, double); // 正确 int g(int a, double b); // 同样正确 // int f(int, double) { return 1; } // 错误:定义必须命名参数 // 此定义自 C23 起被允许
- 函数参数唯一允许的 存储类说明符 是 register ,并且在非定义的函数声明中该说明符会被忽略
int f(static int x); // 错误 int f(int [static 10]); // 正确(数组索引的 static 不是存储类说明符)
- 任何数组类型的参数都会被调整为对应的指针类型 ,如果数组声明符的方括号之间存在限定符,则该指针类型可能被限定 (C99 起)
int f(int[]); // 声明 int f(int*) int g(const int[10]); // 声明 int g(const int*) int h(int[const volatile]); // 声明 int h(int * const volatile) int x(int[*]); // 声明 int x(int*)
- 函数类型的任何形参都会被调整为对应的指针类型
int f(char g(double)); // 声明 int f(char (*g)(double)) int h(int(void)); // 声明 int h(int (*)(void))
-
参数列表可以以
, ...或仅以...(C23 起) 结尾,详见 可变参数函数 。
int f(int, ...);
- 参数不能具有 void 类型(但可以具有指向 void 的指针类型)。完全由关键字 void 组成的特殊参数列表用于声明不接收任何参数的函数。
int f(void); // 正确 int g(void x); // 错误
- 任何出现在参数列表中且可被视作类型定义名或参数名的标识符,均被视为类型定义名: int f ( size_t , uintptr_t ) 会被解析为接收两个未命名参数(类型分别为 size_t 和 uintptr_t )的新式函数声明符,而非开启函数定义、接收两个名为 " size_t " 和 " uintptr_t " 参数的旧式声明符。
- 参数可以具有不完整类型 并可使用 VLA 表示法 [ * ] (C99 起) (但在 函数定义 中,经过数组到指针和函数到指针调整后的参数类型必须是完整类型)。
|
属性说明符序列 同样可应用于函数参数。 |
(since 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 |
(until C23) |
与 函数定义 不同,参数列表可以从 typedef 继承
typedef int p(int q, int r); // p 是函数类型 int(int, int) p f; // 声明 int f(int, int)
|
在C89中, 说明符与限定符 是可选的,若省略则函数返回类型默认为 int (可能被 声明符 修正)。 *f() { // function returning int* return NULL; } |
(C99前) |
缺陷报告
下列行为变更缺陷报告被追溯应用于先前发布的C语言标准。
| 缺陷报告 | 适用范围 | 发布时行为 | 正确行为 |
|---|---|---|---|
| DR 423 | C89 | 返回类型可能带有限定符 | 返回类型被隐式解除限定 |
参考文献
- C23 标准 (ISO/IEC 9899:2024):
-
- 6.7.7.4 函数声明符(包括原型)(p: 130-132)
- C17 标准 (ISO/IEC 9899:2018):
-
- 6.7.6.3 函数声明符(含原型)(页码: 96-98)
- C11 标准 (ISO/IEC 9899:2011):
-
- 6.7.6.3 函数声明符(包含原型)(p: 133-136)
- C99标准(ISO/IEC 9899:1999):
-
- 6.7.5.3 函数声明符(含原型)(第118-121页)
- C89/C90 标准 (ISO/IEC 9899:1990):
-
- 3.5.4.3 函数声明符(包含原型)
参见
|
C++ 文档
关于
函数声明
|