1.问题引入
下面有一个函数,它会被调用多次
int add(int a, int b) //add会被调用10000次 { return a + b; } int main() { for (int i = 0; i < 10000; i++) { cout << add(i, i + 1) << endl; } return 0; }
根据前面函数栈帧的知识,每调用一次函数,就会建立函数栈帧,再销毁函数栈帧
多次调用一个函数,它栈帧的建立和销毁是有很大的消耗
C语言解决这个问题就是用一个宏函数替代add这个普通函数
#define ADD (((x)+(y))*10)
1
使用宏函数就可以解决栈帧的消耗,在预处理过程中,编译器就会把宏的内容替换到程序中使用宏的地方
但是宏函数复杂易错,不能调试,可读性差,没有类型安全的检查,这都是不好的点
所以在C++中,引入了内联函数的概念
2.概念
在C++中,可以在普通函数前加一个关键字: inline ,可以使函数变为内联函数
编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。
下面就是一个内联函数
inline int add(int a, int b) { return a + b; }
默认debug模式下面,inline不会起作用,release下面起作用
可以编译器生成的汇编代码来看一下inline的作用,观察汇编中是否有call add
1.可以在release模式下观察,但是此模式下编译器会进行优化,不易观察
2.在debug模式下观察,要对编译器进行设置,否则不会展开
在常规下,调试信息格式改为 程序数据库
接下来内联函数扩展中选 只 适用于_inline
接下来调试程序,转到反汇编
我们此时先看普通函数的反汇编:
可以看到,反汇编中有call add,就说明程序的确调用了函数
下面看一下内联函数的反汇编:
可以看到,将函数改为内联函数后,反汇编中就没有了call指令,就说明内联函数起了作用
内联函数只适用于短小并且频繁调用的函数,如果将所有函数都写为内联函数,就会导致代码膨胀,继而导致可执行程序变大
3.特性
1.inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用
缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
2.内联对于编译器仅仅只是一个建议,最终是否成为内联,编译器自己决定,比较长的函数和递归,即使加了inline 编译器也会否决掉
下面是一个较长函数的汇编代码,可以看出虽然函数用inline修饰了,但是编译器还是没有把它当作内联函数,还是有call语句
3.inline不建议声明和定义分离,分离会导致链接错误。
因为inline被展开,并且内联函数是不会进符号表的,不会生成函数地址了,链接就会找不到
可以把内联函数直接定义到头函数中
4.总结
一些频繁调用的函数,直接调用有消耗,可以使用宏,但是宏也有局限,最好使用内联函数
内联函数也不是万能的,适用于频繁调用的短函数
inline对于编译器只是一种建议,最终还是取决于编译器
内联函数的声明和定义不要分离