目录
- 前言
- 内联函数的概念
- 内联函数的特性
- 内联函数的总结
前言
假设有这样的一个场景,有一个代码量不足三行的函数被调用了一万次,现在让你做优化,你会怎么考虑优化?
我们都知道函数调用是有时间和空间开销的。程序在执行一个函数之前需要做一些准备工作,要将实参、局部变量、返回地址以及若干寄存器都压入栈中,然后才能执行函数体中的代码;函数体中的代码执行完毕后还要清理现场,将之前压入栈中的数据都出栈,才能接着执行函数调用位置以后的代码。
如果函数体代码比较多,需要较长的执行时间,那么函数调用机制占用的时间可以忽略;如果函数只有一两条语句,那么大部分的时间都会花费在函数调用机制上,这种时间开销就就不容忽视。
所以为了解决上面的场景C语言使用了宏函数来进行对一些特定的场景使用。但是宏函数在使用过程中特别容易出错。比如:将这个Add函数写成宏函数。
写个简单Add的宏函数需要加这么多括号,考虑这么多反例,更要命的是宏是不支持调试的。为了解决这个"坑",祖师爷就在C++中引入了内联函数。
内联函数的概念
直观的来讲,在函数的返回值前加上关键字inline的函数就是内联函数。编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。
函数调用的汇编:
在默认的debug模式下,inline不起作用,否则不方便调试。所以,为了更好的看到展开后的汇编需要重新设置一下。步骤如下:
函数展开的汇编:
内联函数的特性
- inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
- inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。下图为《C++prime》第五版关于inline的建议:
编译器:我的规矩就是规矩(哈哈,开个玩笑)。
- inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。比如:
简单说明就是func()函数由于声明时是内联函数,所以在编译阶段没有进入符号表。在链接阶段时编译器只找到func()的声明,展开不了func函数。于是去符号表里找函数的地址,但是没找到所以就发生了链接错误。 - 类里面的成员函数也是内联函数。
内联函数的总结
使用:
当对程序执行性能有要求时,那么就适当使用内联函数
写一些功能专一且性能关键的函数,这些函数的函数体不大,包含了很少的执行语句。通过inline声明,编译器不需要跳转到内存其他地址去执行函数调用,也不需要保留函数调用时的现场据。
不使用:
不使用:如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
不使用:如果函数体内出现循环或者开关语句;那么执行函数体内代码的时间要比函数调用的开销大。