外部定义(二)
三、函数定义
函数定义(function definition)的语法格式如下所示:
attribute-specifier-sequenceopt declaration-specifiers declarator function-body
其中attribute-specifier-sequenceopt是可选的属性说明符序列;declaration-specifiers是声明说明符;declarator是声明符;function-body是函数体,函数体是一个复合语句(compound statement)。
ISO/IEC 9899:2024标准对函数定义作了一些限制,具体如下:
-- 函数定义中声明的标识符(即函数名)应具有函数类型,该类型由函数定义的声明符部分指定。
int func(int i) //标识符func的类型是int (int)类型。 { return i; }
-- 函数的返回类型应为void类型或者非数组的完整对象类型。
typedef int Arr[3]; int f1(void); //合法。 void f2(void); //合法。 Arr f3(int); //非法,Arr的类型是int [3]类型。
-- 如果声明说明符中存在存储类说明符,只能是extern或者static存储类说明符。
extern int f1(int); //合法,具有外部链接。 static int f2(int); //合法,具有内部链接。
-- 如果形参类型列表只包含一个void类型的形参,则该形参声明符不得包含标识符。
int f1(void); //合法。 int f2(void v); //非法。
-- 未指定大小的变长数组类型不得用于函数定义中的形参声明。
int f1(int, int [*]); //合法。 int f2(int, int [*]); //合法。 int f1(int i, int arr[*]) //非法。 { ... } int f2(int i, int *ptr) //合法。 { ... }
函数定义中可选的属性说明符序列属于该函数。
[[nodiscard]] int func(int i) { ... } ... int temp; temp = func(3); //编译正常。 func(5); //编译时会被警告。
函数定义中的声明符指定了所定义函数的函数名以及所有形参类型(可选择性地包括形参名);该声明符同时作为同一编译单元内后续调用该函数时的函数原型。
//func(int i, double d)是函数声明符。 int func(int i, double d) { ... }
如果需要接收可变数量的实参,而函数定义未使用以省略号结尾的形参类型列表,则其行为是未定义的。
//maxValue函数可接收可变数量的实参。 int maxValue(int N, ...) { ... }
形参类型列表、紧随其后的声明符的属性说明符序列以及函数体复合语句共同构成一个单独的块。每个形参都具有自动存储期;其标识符(如果存在)应为左值。函数定义中形参的可见范围从其声明完成时开始,直至函数定义结束。形参的每个实例的生命周期自函数调用开始执行声明时起,直至函数调用终止。
未声明名称的形参在函数体内不能访问。
//函数定义。 void func(int) { puts("It's very important!"); } ... //函数调用。 func(5);
这种语法通常用于满足接口约定或者故意忽略某些参数的特殊场景。
形参标识符不能在函数体内重新声明,但封闭块除外。
int func(int a, int b) { int a; //非法。 ... { int b; //合法。 ... } ... }
进入函数时,每个可变修改类型形参的大小表达式以及在形参中使用的typeof类运算符会被评估,每个实参表达式的值会转换成对应形参类型,转换如同赋值操作。如果实参是数组表达式或者函数指示符,在函数调用前转换成对应的指针类型。所有形参完成赋值后,开始执行函数体的复合语句。
除非另有明确说明,如果执行到终止函数体的}符号,并且函数的返回值被调用者使用,那么这种行为是未定义的。
(注:对于返回类型是void类型的函数,无返回值被调用者使用;对于返回类型是非void类型的函数,不会执行到终止函数体的}符号。)
函数定义中函数的返回类型及其原型不能从typedef定义的类型别名继承(如果typedef名是函数类型名。)。
typedef int Func(void); Func f1; //函数原型合法。 //函数定义非法。 Func f1 { ... } //函数定义非法。 Func f2(void) { ... } Func f3; //函数原型合法。 //函数定义合法。 int f3(void) { ... } //函数定义合法。 Func *f4(void) { ... }
如果将一个函数传递给另一个函数,以下两种形式的函数定义是等价的。
int func(void); //第一种函数定义形式。 int f(int func(void)) { ... } //第二种函数定义形式。 int f(int (*ptr)(void)) { ... }
因为函数指示符作为形参时会转换成对应的指针类型。
主要参考资料: