第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\n", a); //3 23 printf("b = %d\n", b); //3 24 printf("c = %d\n", c); //3 25 */ 26 27 int c = func(++a, b); 28 29 printf("a = %d\n", a); //2 30 printf("b = %d\n", b); //3 31 printf("c = %d\n", 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\n", 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)内联函数省去了函数调用时压栈、跳转和返回的开销