当前位置:嗨网首页>书籍在线阅读

22-内联函数(C99)

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

16.7 内联函数(C99)

通常,函数调用都有一定的开销,因为函数的调用过程包括建立调用、传递参数、跳转到函数代码并返回。使用宏使代码内联,可以避免这样的开销。C99还提供另一种方法:内联函数(inline function)。读者可能顾名思义地认为内联函数会用内联代码替换函数调用。其实C99和C11标准中叙述的是:“把函数变成内联函数意味着尽可能快地调用该函数,其具体效果由实现定义”。因此,把函数变成内联函数,编译器可能会用内联代码替换函数调用,并(或)执行一些其他的优化,但是也可能不起作用。

创建内联函数的定义有多种方法。标准规定具有内部链接的函数可以成为内联函数,还规定了内联函数的定义与调用该函数的代码必须在同一个文件中。因此,最简单的方法是使用函数说明符 inline 和存储类别说明符 static 。通常,内联函数应定义在首次使用它的文件中,所以内联函数也相当于函数原型。如下所示:

#include <stdio.h>
inline static void eatline() // 内联函数定义/原型
{
   while (getchar() != '\n')
     continue;
}
int main()
{
   ...
   eatline();   // 函数调用
   ...
}

编译器查看内联函数的定义(也是原型),可能会用函数体中的代码替换 eatline() 函数调用。也就是说,效果相当于在函数调用的位置输入函数体中的代码:

#include <stdio.h>
inline static void eatline() //内联函数定义/原型
{
   while (getchar() != '\n')
     continue;
}
int main()
{
   ...
   while (getchar() != '\n') //替换函数调用
     continue;
   ...
}

由于并未给内联函数预留单独的代码块,所以无法获得内联函数的地址(实际上可以获得地址,不过这样做之后,编译器会生成一个非内联函数)。另外,内联函数无法在调试器中显示。

内联函数应该比较短小。把较长的函数变成内联并未节约多少时间,因为执行函数体的时间比调用函数的时间长得多。

编译器优化内联函数必须知道该函数定义的内容。这意味着内联函数定义与函数调用必须在同一个文件中。鉴于此,一般情况下内联函数都具有内部链接。因此,如果程序有多个文件都要使用某个内联函数,那么这些文件中都必须包含该内联函数的定义。最简单的做法是,把内联函数定义放入头文件,并在使用该内联函数的文件中包含该头文件即可。

// eatline.h
#ifndef EATLINE_H_
#define EATLINE_H_
inline static void eatline()
{
   while (getchar() != '\n')
     continue;
}
#endif

一般都不在头文件中放置可执行代码,内联函数是个特例。因为内联函数具有内部链接,所以在多个文件中定义同一个内联函数不会产生什么问题。

与C++不同的是,C还允许混合使用内联函数定义和外部函数定义(具有外部链接的函数定义)。例如,一个程序中使用下面3个文件:

//file1.c
...
inline static double square(double);
double square(double x) { return x * x; }
int main()
{
   double q = square(1.3);
   ...
//file2.c
...
double square(double x) { return (int) (x*x); }
void spam(double v)
{
   double kv = square(v);
   ...
//file3.c
...
inline double square(double x) { return (int) (x * x + 0.5); }
void masp(double w)
{
   double kw = square(w);
   ...

如上述代码所示,3个文件中都定义了 square() 函数。 file1.c 文件中是 inline static 定义; file2.c 文件中是普通的函数定义(因此具有外部链接); file3.c 文件中是 inline 定义,省略了 static

3个文件中的函数都调用了 square() 函数,这会发生什么情况? file1.c 文件中的 main() 使用 square() 的局部 static 定义。由于该定义也是 inline 定义,所以编译器有可能优化代码,也许会内联该函数。 file2.c 文件中, spam() 函数使用该文件中 square() 函数的定义,该定义具有外部链接,其他文件也可见。 file3.c 文件中,编译器既可以使用该文件中 square() 函数的内联定义,也可以使用 file2.c 文件中的外部链接定义。如果像 file3.c 那样,省略 file1.c 文件 inline 定义中的 static ,那么该 inline 定义被视为可替换的外部定义。

注意GCC在C99之前就使用一些不同的规则实现了内联函数,所以GCC可以根据当前编译器的标记来解释 inline