- 👑专栏内容:C++学习笔记
- ⛪个人主页:子夜的星的主页
- 💕座右铭:日拱一卒,功不唐捐
关键字inline
是C++相对于C语言的又一个扩充,在函数的声明或定义、函数的返回类型前加上关键字inline
,即可把函数指定为内联函数从而提升程序运行的效率。但使用inline
是要付出代价的,正如茨威格在《断头王后》中那样写道:“ 那时候她还太年轻,不知道所有命运馈赠的礼物,早已在暗中标好了价格。” 那么inline
的优势和它为此要付出的代价是什么呢?让我们来慢慢揭晓!
当一个函数被调用执行时,首先要在栈中为形参和局部变量分配存储空间,然后还要将实参的值复制给形参,接下来还要将函数的返回地址放入栈中,最后才跳转到函数内部执行。这个过程是要消耗时间和栈空间(放置函数内数据的内存空间)的。当一个函数非常短小,但由于被放入循环体中大量的循环,就会消耗大量的时间。同时由于栈空间是有限,所以频繁大量的使用也会造成因栈空间不足所造成的出错。
内联函数是一种编程语言结构,用来建议编译器对一些特殊函数进行内联扩展;也就是说建议编译器将指定的函数体插入并取代每一处调用该函数的地方(上下文),从而节省了每次调用函数带来的额外时间开支。在C++中在一个函数的前面加上inline
进行修饰,就会将这个函数变为内联函数。
小C挑衅大哥也不是一天两天了,本着 能打就打 以理服人的做法。接下来我们就举个例子现场回应他的挑衅。
int Add(int left,int right)
{return left + right;
}
int main ()
{int ret = 0;ret = Add(1,2);return 0;
}
接着,我们点住ret
查看其汇编代码。
我们可以看到,ret
的汇编代码中,有一句的前面有一个call
,在汇编语言中call
就是函数调用指令。它的作用就是将程序当前执行的位置IP压入堆栈中,转移到调用的子程序。
接下来。我们在刚刚的函数前面加入inline
进行修饰,再看其汇编语言。
inline int Add(int left,int right)
{return left + right;
}
int main ()
{int ret = 0;ret = Add(1,2);return 0;
}
哇,汇编代码中的call
果然消失不见了呢~
那接下来该干嘛了?当然是以理服人!
C语言中确实也有不建立栈帧的方法,那就是使用宏来定义函数。
#define Add(int x,int y) return x+y;
呀…写错了,我重写!
#define Add(x,y) x+y;
额…又错了!再给一次机会!
#define Add(x,y) (x)+(y)
呜…失误!再再给我一次机会!
#define Add(x,y)((x)+(y))
哇…终于写对了!!!!
宏其实是一种非常暴力的替换,正因为如此,它的缺点有很多:
inline
是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会
用函数体替换函数调用,这样就会导致编译出来的可执行程序变大。
inline
可能被忽略inline
对于编译器而言只是一个建议,不同编译器关于inline
实现机制可能不同。
一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline
修饰,否则编译器会忽略inline
特性。
《C++prime》第五版关于inline
的建议:
定义和声明分离的后果是什么?以下面的代码为例:
F.h |
#include
using namespace std;
inline void f(int i);
F.cpp |
#include "F.h"
void f(int i)
{cout << i << endl;
}
main.cpp |
#include "F.h"
int main()
{f(10);return 0;
}
运行结果:
为什么普通函数可以将定义和声明分离,而内联函数不行呢?
看汇编代码,普通函数会产生跳转时对应的地址。而内联函数默认在用的地方已经展开了,不需要产生。所以只有声明的话,在声明的地方已经展开了,这会导致在调用的时候没法展开。
优点:
缺点:
建议:
上一篇:怎么把网页变成灰色?怎么让头像或某一部分不变灰色?filter/backdrop-filter/mix-blend-mode/svg/grayscale(1)
下一篇:hadoop入门两道面试题