内联函数
一、C语言函数说明符
根据ISO/IEC 9899:2018标准第6.7.4 Function specifiers节,C语言存在两个函数说明符(function specifiers):inline、_Noreturn。
ISO/IEC 9899:2018标准对函数说明符的使用作了一些限制:
- 函数说明符只能用于函数标识符的声明。
- 具有外部链接的函数的内联定义不应包含具有静态存储期限或者线程存储期限的可修改对象的定义,也不应包含对具有内部链接标识符的引用。
- 宿主环境中不能对main函数使用函数说明符。
二、内联函数
函数声明中如果相同的函数说明符出现多次,其行为与出现一次是相同的。使用inline函数说明符声明的函数称为内联函数(inline function)。
inline int sum(int, int); static inline int max(int, int);
这里声明了两个内联函数:sum和max;不同的是:sum函数是具有外部链接的内联函数;max函数是具有内部链接的内联函数。
将函数声明为内联函数的目的是提醒编译器,该函数调用时应尽可能快地执行,编译器可以进行某些优化。具体实现中是否优化取决于编译器。编译器优化的一个常用方法是内联替换(inline substitution),即在函数调用处,使用函数体的代码替换函数调用。内联替换不是文本替换(textual substitution),也不会创建新函数。函数体内的宏扩展使用的是宏在函数体内出现时的定义,而不是函数调用时的定义。
#include <stdio.h> #define NUMBER 5 static inline void func1(void) { printf("%d\n", NUMBER); } static inline void func2(void); int main(void) { #ifdef NUMBER #undef NUMBER #define NUMBER 20 #endif func1(); //输出5。 func2(); //输出20。 printf("%d\n", NUMBER); //输出20。 return 0; } static inline void func2(void) { printf("%d\n", NUMBER); }
将输出:
5
20
20
任何具有内部链接的函数都可以是内联函数;但具有外部链接的函数如果声明为内联函数,应受到以下限制:
- 如果一个函数使用inline函数说明符声明为内联函数,则该函数应在同一编译单元内定义。
inline int sum(int, int); static inline int max(int, int);
sum函数具有外部链接,根据上述限制,应在同一编译单元内定义;max函数具有内部链接,应在同一编译单元内定义。
- 如果一个编译单元中某个函数的所有文件作用域声明都包含inline函数说明符且没有存储类说明符extern,则该编译单元中的函数定义是内联定义(inline definition)。
前面例子中的func1、func2函数定义都是内联定义。
- 内联定义不会为函数提供外部定义(external definition),也不禁止该函数在其他编译单元中的外部定义。内联定义提供了外部定义的替代方案,在同一编译单元中编译器可以使用外部定义实现对函数的任何调用。ISO/IEC 9899:2018标准并未规定这种情况下对函数的调用是使用内联定义还是使用外部定义。实现可能永远不会执行内联替换,也可能对内联声明作用域内的函数调用只执行内联替换。
test.c源文件
#include <stdio.h> /*内联定义。*/ static inline int sum(int a, int b) { return (a + b); } extern int sum(int a, int b); //创建外部定义。 int main(void) { int number = sum(3,5); printf("%d\n", number); return 0; }
gch.h头文件
#ifndef GCH_H_INCLUDED #define GCH_H_INCLUDED int sum(int a, int b); #endif
gch.c源文件
#include "gch.h" int sum(int a, int b) { return (a + b); }
在这个例子中test.c和gch.c是两个不同的编译单元。在test.c源文件中使用存储类说明符extern声明了sum函数,这使sum函数在其他文件中可见,所以可以在gch.c源文件中对sum函数进行定义。
由于内联定义不同于对应的外部定义以及其他编译单元中对应的内联定义,因此具有静态存储期限的所有对应对象在每个定义中是不同的。
static inline const char *func(void) { static const char country[] = "China"; return country; } extern const char *func(void); ... if(func() == func()) ...
表达式func() == func()的值可能是1,也可能是0。如果函数调用使用的是相同的内联定义或者外部定义,则表达式值为1;如果函数调用使用的是不同的内联定义或者外部定义,则表达式值为0。
三、内联函数的优点和缺点
内联函数最大的优点是节省函数调用开支;最大的缺点是增加程序代码,如果程序代码增加过多,反过来又会降低程序效率,所以内联函数比较适合小函数。
主要参考资料:
2、en.cppreference.com : inline function specifier