• C的内联函数(C99)


    内联函数(C99)

    通常,函数调用都有一定的开销,因为函数的调用过程包括建立调用、
    传递参数、跳转到函数代码并返回。使用宏使代码内联,可以避免这样的开销。

    C99还提供另一种方法:内联函数(inline function)。读者可能顾名思义地认为内联函数会用内联代码替换函数调用。其实C99和C11标准中叙述的是:“把函数变成内联函数建议尽可能快地调用该函数,其具体效果由实现定义”。

    因此,把函数变成内联函数,编译器可能会用内联代码替换函数调用,并(或)执行一些其他的优化,但是也可能不起作用。
    创建内联函数的定义有多种方法。标准规定具有内部链接的函数可以成
    为内联函数,还规定了内联函数的定义与调用该函数的代码必须在同一个文件中。因此,最简单的方法是使用函数说明符 inline 和存储类别说明符static。通常,内联函数应定义在首次使用它的文件中,所以内联函数也相当于函数原型。如下所示:

    #include 
    inline static void eatline() // 内联函数定义/原型
    {
    while (getchar() != '\n')
    continue;
    }
    int main()
    {
    ...
    eatline(); // 函数调用
    ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

    #include 
    inline static void eatline() //内联函数定义/原型
    {
    	while (getchar() != '\n')
    	continue;
    }
    int main()
    {
    	...
    	while (getchar() != '\n') //替换函数调用
    	continue;
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    由于并未给内联函数预留单独的代码块,所以无法获得内联函数的地址
    (实际上可以获得地址,不过这样做之后,编译器会生成一个非内联函
    数)。另外,内联函数无法在调试器中显示。
    内联函数应该比较短小。把较长的函数变成内联并未节约多少时间,因
    为执行函数体的时间比调用函数的时间长得多。
    编译器优化内联函数必须知道该函数定义的内容。这意味着内联函数定
    义与函数调用必须在同一个文件中。鉴于此,一般情况下内联函数都具有内部链接。因此,如果程序有多个文件都要使用某个内联函数,那么这些文件
    中都必须包含该内联函数的定义。最简单的做法是,把内联函数定义放入头
    文件,并在使用该内联函数的文件中包含该头文件即可。

    // eatline.h
    #ifndef EATLINE_H_
    #define EATLINE_H_
    inline static void eatline(){
    	while (getchar() != '\n')
    		continue;
    }
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

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

    如下代码所示,3个文件中都定义了square()函数。
    3个文件中的函数都调用了square()函数,这会发生什么情况?

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

    file1.c文件中是inline static定义;
    file1.c文件中的main()使用square()的局部static定义。由于该定义也是inline定义,所以编译器有可能优化代码,也许会内联该函数。

    file2.c 文件中是普通的函数定义(因此具有外部链接);
    file2.c 文件中,spam()函数使用该文件中 square()函数的定义,该定义具有外部链接,其他文件也可见。

    file3.c 文件中是 inline 定义,省略了static。
    file3.c文件中,编译器既可以使用该文件中square()函数的内联定义,也可以使用file2.c文件中的外部链接定义。如果像file3.c那样,省略file1.c文件inline定义中的static,那么该inline定义被视为可替换的外部定义。注意GCC在C99之前就使用一些不同的规则实现了内联函数,所以GCC可以根据当前编译器的标记来解释inline。

    参考

    《C Primer Plus》

  • 相关阅读:
    yolo v5 坐标相关的判断与转换,评价指标,训练结果解析
    代码的巨大进步
    【ElasticSearch系列-07】ES的开发场景和索引分片的设置及优化
    博客园主题样式更改总结
    【Kali】简单记录
    第2节 volatile 关键字内存可见性
    (附源码)计算机毕业设计SSM基于的英语学习网站的设计与实现
    ubuntu20.04 nvidia显卡驱动掉了,变成开源驱动,在软件与更新里选择专有驱动,下载出错,调整ubuntu镜像源之后成功修复
    Centos7搭建SVN代码控制服务器
    面试官:Redis中字符串的内部实现方式是什么?
  • 原文地址:https://blog.csdn.net/Shujie_L/article/details/134449006