内联函数(inline)
频繁的调用函数,需要不停创造栈帧,销毁栈帧。
C语言中为了避免频繁调用小函数,使用宏
但是宏有缺点:1、不能调试,因为编译过程宏所在的位置直接被替换了,进不去宏函数。2、没有类型检查,比如需要传double你传了个int它也检查不出来。3、容易写错
#define Add(x,y) ((x)+(y))
为什么要写成 ((x) + (y)),外面的括号很好理解,里面加括号的原因是因为如果碰到 Add(a|b, a&b); 这种情况,加法的优先级大于按位与或,所以会先算b+a再与或就出错了
C++采用用内联的方式,下面分析一下不同代码对应的反汇编:
普通Add函数:
int Add(int left, int right)
{
return left + right;
}
int main()
{
int ret = 0;
ret = Add(1, 2);
return 0;
}
内联Add函数:
inline int Add(int left, int right)
{
return left + right;
}
int main()
{
int ret = 0;
ret = Add(1, 2);
return 0;
}
可以发现没有了压栈以及调用这个过程,而是直接展开了
注:编译器需要进行一下配置(debug下编译器默认不会对代码进行优化)
两种方式进入调试属性
注意:1.内联是一种以空间换时间的做法。2.内联说明只是向编译器发出的一个请求,编译器可以选择忽略这个请求。内联一般用于规模较小,流程直接,频繁调用的函数。3.inline不建议声明和定义分离(就是直接定义在头文件中,别像之前声明一个Stack.h,定义函数又来一个Stack.c),分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。
编译后代码太长,可执行程序的大小就太大了,也就是安装包太大,这样不好
为什么不能将声明和定义分离?因为编译器会分别对文件进行预处理(宏在预处理时展开)(头文件中的定义也在预处理阶段统统都加入到它所产生的输出文件中)和编译(内联在编译时展开),最后再链接到一起。在每个文件分别的编译过程中,内联函数不会进行建立栈帧,生成函数地址这样的过程,对应的符号表中没有函数的地址,链接时候就找不到(总之就是大家是分开编译的,不是一起编译的,本来在编译过程如果发现你要用我这个inline函数我直接给你替换的,可惜你跟我不在一个文件我没发现你,链接时候你想要调用这个函数抱歉我压根就没打算保存我的函数地址,我直接不进符号表,你在编译过程不找我替换那你后面再想找我没门了,过了这村没这店了)
写一下文件的变化过程:test.c/cpp 到 test.i(预处理文件)到 test.s(汇编代码文件)到 test.o(二进制代码)然后链接库 test.a(静态库)/test.so(动态库) 最后形成可执行文件