关于C之内联函数

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

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

#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定义被视为可替换的外部定义,这时inline内联就自动失效了,因为不是在.h头文件中。

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

itzyjr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值