函数定义
函数定义将函数体(声明与语句的序列)与函数名及参数列表关联。不同于函数声明,函数定义只允许在文件作用域(不存在嵌套函数)。
C 支持二种函数定义的形式:
说明符与限定符 形参列表声明符 函数体 | (1) | ||||||||
说明符与限定符 标识符列表声明符 声明列表 函数体 | (2) | (C2x 前) | |||||||
其中
说明符与限定符 | - | 下列的组合 |
形参列表声明符 | - | 用参数列表指代函数参数的函数类型的声明器 |
标识符列表声明符 | - | 用标识符列表指代函数参数的函数类型的声明器 |
声明列表 | - | 在 标识符列表声明符 中声明每个参数的声明序列。这些声明不能使用初始化器,而且仅允许 register 作为存储类说明符。 |
函数体 | - | 复合语句,是花括号所包括的声明及语句序列,只要调用此函数就会被执行 |
1) 新式 (C89) 函数定义。此定义引入函数自身,并为任何将来的函数调用表达式提供函数原型,强迫从实参表达式转换到声明形参类型。
int max(int a, int b) { return a>b?a:b; } double g(void) { return 0.1; }
2) (C2x 前) 旧式 (K&R) 函数定义。此定义不表现为原型,而任何将来的函数调用表达式将进行默认参数提升。
int max(a, b) int a, b; { return a>b?a:b; } double g() { return 0.1; }
解释
同函数声明,函数的返回类型由 说明符与限定符 中的类型说明符确定,并像在声明中一样可以由 声明符 修改。返回类型必须是完整的非数组对象类型或 void 类型。
同函数声明,若返回类型会有 cvr 限定,则为构造函数类型的目的,将它调整到其无限定版本。 |
(C17 起) |
同函数声明,为构造函数类型的目的,将参数类型从函数调整到指针,从数组调整到指针,并且为确定兼容函数类型的目的,忽略所有参数类型的顶层 cvr 限定符。
不同于函数声明,不允许无名形参,即使不在函数中使用也必须命名它们。仅有的例外是参数列表 (void) 。
int f(int, int); // 声明 // int f(int, int) { return 7; } // 错误 int f(int a, int b) { return 7; } // 定义 int g(void) { return 8; } // OK : void 不声明参数
在函数体内,每个参数都是左值表达式,它们拥有自动存储期和块作用域。参数在内存中的布局(或者它们究竟是否存储于内存中)是未指定的:这是调用约定的一部分。
int main(int ac, char **av) { ac = 2; // 参数是左值 av = (char *[]){"abc", "def", NULL}; f(ac, av); }
函数调用机制上的其他细节见函数调用运算符,关于从函数返回,见 return 。
__func__在每个 函数体 内,拥有块作用域和静态存储期的预定义变量 __func__ 可用,它如同立即通过以下方式在开花括号后定义: static const char __func__[] = "function name"; |
(C99 起) |
注解
参数列表必须显式存在于声明器中,不能从 typedef 继承它
typedef int p(int q, int r); // p 是类型为 int(int, int) 的函数 p f { return q + r; } // 错误
C89 中, 说明符与限定符 是可选的,若省略它,则函数返回类型默认为 int (可由 声明符 修改)。 另外,旧式定义不要求在 声明列表 中声明每个参数。任何缺少声明的参数拥有 int 类型 max(a, b) // a 和 b 拥有 int 类型,返回类型为 int { return a>b?a:b; } |
(C99 前) |
引用
- C11 standard (ISO/IEC 9899:2011):
- 6.9.1 Function definitions (p: 156-158)
- C99 standard (ISO/IEC 9899:1999):
- 6.9.1 Function definitions (p: 141-143)
- C89/C90 standard (ISO/IEC 9899:1990):
- 3.7.1 Function definitions