c语言 inline函数的总结,C语言陷阱与技巧第2节,使用inline函数可以提升程序效率,但是让inline函数生效是有条件的...

3171d24a526c71ac6d82fb138b2359e2.png

翻开 Linux 内核源代码,会发现内核在界说C言语函数时,有许多都带有 “inline”关键字,请看下图,那么这个关键字有什么效果呢?

inline 关键字的效果

在C言语程序开发中,inline 一般用于界说函数,inline 函数也被称作“内联函数”,C99 和 GNU C 均支撑内联函数。那么在C言语中,内联函数和一般函数有什么不同呢?其实,从 inline 这个姓名就应该能看出一点它的性质了——内联函数会在它被调用的方位上打开,这一点体现的和 define 宏界说是十分类似的。

将被调用的函数代码打开,操作系统就无需再在为被调用函数做请求栈帧和收回栈帧的作业,并且,由于编译器会把被调用的函数代码和函数本身放在一同优化,所以也有进一步优化C言语代码,进步功率的或许。

每发作一次函数调用,操作系统就要在程序的栈空间请求一块内存区域(栈帧),供被调用函数运用,被调用函数履行结束后,操作系统还要收回这些内存。

不过,天下没有免费的午饭,C言语程序要完成内联函数的上述特性是要支付必定的价值的。一般函数只需求编译出一份,就能够被一切其他函数调用,而内联函数没有严厉意义上的“调用”,它仅仅将本身的代码打开到被调用途的,这么做无疑会使整个C言语代码变长,也就意味着占用更多的内存空间,以及更多的指令缓存。

明显,假如乱用内联函数,cpu 的指令缓存肯定是不够用的,这会导致 cpu 缓存命中率下降,反而或许会下降整个C言语程序的功率。因而,主张把那些对时刻要求比较高,且C言语代码长度比较短的函数界说为内联函数。假如在C言语程序开发中的某个函数比较大,又会被重复调用,并且没有特别的时刻约束,是不适合把它做成内联函数的。

在 Linux 内核中,内联函数常常运用 static 润饰,例如:

staticinlinevoid set_value(unsignedint val){ ...}需求留意的是,内联函数必须在运用之前就界说好,不然编译器无法把这个函数打开。Linux 内核中常常像下面这样,将内联函数放在调用它的函数前面,请看C言语代码:

staticinlinevoid set_value(unsignedint val){ ...}int test_inline(){ set_value(3); ...}

所以,Linux 内核常常把内联函数界说在头文件里,这样在其他C言语代码文件最初包括头文件时,能保证内联函数在文件的最开端,无需再写额定的声明句子。

这也解说了为什么 Linux 内核为何常常运用 static 润饰内联函数,由于能够防止函数的重复界说。

前文说到内联函数的体现有些像 define 宏界说,可是为了类型安全和易读性,应优先运用内联函数而不是杂乱的宏。下面经过实例进一步剖析 inline 内联函数的特性。

inline内联函数的“打开代码”是什么意思?

运用过 define 写 C言语代码的朋友应该都知道,编译器在编译 C言语代码时,会将 define 界说的宏打开,而不是像一般函数那样运用 call 指令调用,例如下面这段C言语代码:

#include #define d_add(a, b) ((a)+(b))int f_add(int a, int b){ return a+b;}int main(){ int a = d_add(1, 2); int b = f_add(1, 2); return0;}

运用 gcc -E 编译这段C言语代码,能够得到预处理后的代码如下,明显 define 界说的宏被打开了,请看:

运用 gcc -g 指令编译C言语代码,得到可履行文件,然后调用 objdump 指令检查汇编代码,得到如下成果:

# gcc -g t1.c # objdump -dS a.out

从 f_add() 函数的汇编代码也能够看出,程序首先将 2 个参数赋值给寄存器,然后运用 call 指令调用 f_add() 函数。而宏界说 d_add() 就简略了,只要一行汇编代码,这种情况下,运用 define 宏界说明显功率更高。不过,宏界说没有参数的类型检查,运用起来不太安全,好在C言语还有 inline 函数,下面再界说一个 inline 函数,请看C言语代码如下:

staticinlineint i_add(int a, int b){ return a+b;}

在 main() 函数中运用 gcc -E 指令检查增加 inline 函数后的C言语代码预处理成果,如下:

能够看出,在预处理阶段,inline 函数并没有像 define 宏那样打开。现在运用 gcc -g 指令编译得到可履行文件,然后运用 objdump 检查汇编代码,如下:

从汇编代码能够看出,inline 函数好像并没有起到效果,i_add() 函数和 f_add() 函数的体现并没有什么不同,持续往上检查,发现编译器也将 i_add() 函数的汇编代码生成了,这无疑是将 i_add() 函数当作一般函数运用了:

staticinlineint i_add(int a, int b){ 400501: 55 push %rbp 400502: 4889 e5 mov %rsp,%rbp 400505: 897d fc mov %edi,-0x4(%rbp) 400508: 8975 f8 mov %esi,-0x8(%rbp) return a+b; 40050b: 8b 45 f8 mov -0x8(%rbp),%eax 40050e: 8b 55 fc mov -0x4(%rbp),%edx 400511: 01 d0 add %edx,%eax} 400513: 5d pop %rbp 400514: c3 retq怎么回事?不是说 inline 函数的体现和 define 宏类似,会将函数代码打开吗?其实,inline 仅仅主张编译器这么做,编译器终究会不会这么做就不必定了。这与编译器的优化等级相关,请看下图:

gcc 的 -O 选项能够指定优化等级,咱们上面编译程序时没有运用 -O 选项,因而编译器履行的是默许的 -O0,也即无优化编译。那能否在 -O0 优化等级也运用 inline 函数的特性呢?当然是能够的,只需求在界说 inline 函数时,增加 __attribute__((always_inline)) 即可,例如:

static __attribute__((always_inline)) inlineint i_add(int a, int b){ return a+b;}

现在再来编译C言语程序并检查汇编代码,得到如下成果:

这种情况下,编译器并没有为 i_add() 函数生成呼应的汇编代码。尽管 inline 函数在预处理阶段没有像 define 宏界说那样打开,可是在生成汇编代码阶段打开了,并且参加了调用它的代码部分的优化,这明显会让整个C言语程序的功率进步。

inline 函数尽管体现上很像 define 宏界说,可是却并不能彻底替代 define 宏界说,这一点在我之后的文章里会评论,敬请重视。

小结

在 C言语程序开发中,主张把那些对时刻要求比较高,且C言语代码长度比较短的函数界说为 inline 函数,这么做常常能够进步程序的功率。在默许的 -O0 编译优化项不能保证 inline 必定起效果,可是能够增加增加 __attribute__((always_inline))强制编译器对 inline 函数做相应的处理。由于 inline 函数会将自己打开,所以编译器一般不会再为 inline 生成汇编代码,不过,假如是经过函数指针的方式调用 inline 函数,编译器为了取得 inline 函数的地址,依然会为其生成汇编代码的。相关查找内联函数的特色inline什么意思matlab中inline内联函数举例内联函数比如静态内联函数

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值