第6课

第6课 - 内联函数分析

0. 回顾C中的带参函数、宏和内联函数

  带参函数 内联函数
优点 编译器会做参数的静态类型检查

原地展开,没有调用开销;

并且在预处理阶段完成,不占用编译时间。

函数代码被装入符号表中,在使用时进行替换;

没有调用开销,效率高,会进行参数类型检查

缺点

需要传参、栈变量的开辟和销毁

压栈、跳转、返回开销;

不进行类型检查,多次宏替换会导致代码体积变大;

一些参数的副作用会导致得出错误的结果。

函数代码较长,使用内联将消耗过多内存;

函数体内有循环,执行代码的时间比较长。

1. 常量与宏回顾

  (1)C++中的const常量可以替代宏常数定义,如:  const int A = 3; ←→ #define A  3

  (2)C++中是否有解决方案,可以用来替代宏代码片段呢?

2. 内联函数

2.1 内联函数的定义

  (1)C++编译器可以将一个函数进行内联编译,被C++编译器内联编译的函数叫内联函数

  (2)C++中使用 inline 关键字声明内联函数。如:

    

  (3)内联函数声明时inline关键字必须和函数定义结合在一起,否则编译器会直接忽略内联请求。

  (4)C++中推荐使用内联函数替代宏代码片段

2.2 内联函数的特点

  (1)内联函数具有普通函数的特征(参数检查返回类型等)

  (2)C++编译器直接将内联函数的函数体插入到函数调用的地方

  (3)内联函数没有普通函数调用时的额外开销(压栈、跳转、返回)

  (4)C++编译器也不一定满足函数的内联请求,函数的内联请求可能被编译器拒绝

             

          图1:编译器拒绝inline请求(仍为函数调用)            图2:函数体被嵌入到调用的地方

 1 #include <stdio.h>
 2 
 3 #define FUNC(a, b) ((a) < (b) ? (a) : (b))
 4 
 5 //MSVC下:要让inline、__forceinline生效必须得做如下的设置:
 6 //①在"项目" → "配置属性" → "C/C++" → "优化" → "内联函数扩展"中选择"只适用于__inline(/ Ob1)"
 7 //②在"配置属性" → "C/C++" → "常规" → "调试信息格式"中选择"程序数据库( / Zi)"
 8 
 9 inline int func(int a, int b)
10 {
11     return a < b ? a : b;
12 }
13 
14 int main(int argc, char *argv[])
15 {
16     int a = 1;
17     int b = 3;
18 
19     /*
20     int c = FUNC(++a, b);  //相当于(++a)<(b)?:(++a):(b);
21 
22     printf("a = %d
", a); //3
23     printf("b = %d
", b); //3
24     printf("c = %d
", c); //3
25     */
26 
27     int c = func(++a, b);
28 
29     printf("a = %d
", a);     //2
30     printf("b = %d
", b);        //3
31     printf("c = %d
", c);     //2
32     
33     return 0;
34 }
内联函数初探

2.3 内联函数与宏的不同

  内联函数
处理方式 预处理器处理,只是进行简单的文本替换

编译器处理,会将函数体嵌入到调用的地方。

但内联请求也可能被编译器拒绝

类型检查 不做类型检查 具有普通函数的特征,会进行参数和返回类型的检查
副作用

2.4 现代C++编译器对内联函数的优化

  (1)现代C++编译器能够进行编译优化,一些函数即没有inline声明,也可能被内联编译

  (2)一些现代的C++编译器提供了扩展语法,可用下列列关键字替代inline来对函数进行强制内联,如:

      ① g++:__atrribute__((always_inline))属性

      ② MSVC:__forceinline

 1 #include <stdio.h>
 2 
 3 //MSVC2013下:在函数声明或定义前加inline或__forceinline都可以
 4 //同时,这两个的表现行为几乎一模一样。只不过__forceinline是MS
 5 //下的,而inline是标准C++的,可移植性更高。
 6 
 7 //__forceinline
 8 //__attribute__((always_inline))
 9 //inline
10 int add_inline(int n); 
11 
12 int main()
13 {
14     int r = add_inline(10);
15 
16     printf("r = %d
", r);
17 
18     return 0;
19 }
20 
21 __forceinline int add_inline(int n)
22 {
23     int ret = 0;
24 
25     for (int i = 0; i < n; i++)
26     {
27         ret += i;
28     }
29 
30     return ret;
31 }
内联函数深度示例

3. C++中inline内联编译的限制

  最新的C++编译器只要函数体不是太夸张都可以强制内联成功。列出下面这些是因为工作中使用的C++编译器版本可能较低,无法满足。

  (1)不能存在任何形式的循环语句

  (2)不能存在过多的条件判断语句

  (3)函数体不能过于庞大

  (4)不能对函数进行取址操作

  (5)函数内联声明必须在调用语句之前

4. 小结

  (1)C++中可以通过 inline 声明内联函数

  (2)编译器直接将内联函数体扩展到函数调用的地方

  (3)inline只是一种请求,编译器不一定允许这种请求

  (4)内联函数省去了函数调用时压栈跳转返回的开销

原文地址:https://www.cnblogs.com/shiwenjie/p/7121504.html