C++ 改进 C 内容之——inline(内联函数)

目录

一、内联函数是什么

二、内联函数怎么写

三、内联函数有啥用

四、注意事项

1、内联没展开

2、 内联函数不一定比一般函数好用

3、内联函数的声明与定义

五、内联函数在什么阶段展开 

六、什么时候会用到内联函数


一、内联函数是什么

                                

在学习内联函数之前,我们先引入C的一个概念:

用宏实现a+b的操作

#define Add(a,b) a + b               X
#define Add(a,b) ( a + b )           X
#define Add(a,b) (a) + (b)           X
#define Add(a,b) ( (a) + (b) ) ;    X
#define Add(a,b) ( (a) + (b) )      √

        

这里只解释为什么( a + b )是错的。   

例:Add ( 1 | 3 , 2 & 5)

这里会改变运算顺序,本来先对两个位分别运算,结果却变成了先算 | ,然后算 + ,最后算 &

这就是C语言宏的缺陷之一

1、容易出错,语法细节多

2、不能调试

3、没有类型检查(如上式的 a 和 b 甚至可以是 char 类型)

        

综上,C++祖师爷决定做一个“违背祖宗”的决定:抛弃C的宏(但你依旧可以用,反正我是不想再用了),采用内联函数(linine)来代替宏 

                                


二、内联函数怎么写

                

我们完全可以像写函数一样写内联函数,只需要在前面加上 inline 就行

inline int Add(int a, int b)
{
	int c = a + b;
	return c;
}

int main()
{
	int a = 1;
	int b = 2;
	int c=Add(a,b);
}

上图就是一个内联函数,或者你就可以叫他宏(不过还是不建议这么叫,毕竟还是很不一样的)

看样貌无非就是常规函数的定义前面加上一个inline罢了

如果光看,其实平平无奇。他不就和普通的函数差不多嘛?那为啥还要设计一个内联函数徒增烦恼呢?

                 


三、内联函数有啥用

                        

光看肯定是看不来个123的,我们直接调试来观察

!在调试之前,先调一个设置,不然是看不出来的!

调试状态下右键代码区,选择反汇编

调试结果如下:

 你会发现:明明函数内容一模一样,但是汇编的指令却不一样,why?

这就是内联函数的独到之处!

        

我们先理解 call指令 是什么意思:

说简单点,call指令就是调用一个函数。先call他的地址,然后找过去,执行函数内容

        

那么就意味着,inline并没有调用函数,所以这就有了内联函数的特点:

内联函数是一种以空间换时间的做法,在编译阶段,会用函数体替换函数的调用,他减少了函数创建栈帧的时间开销(因为没有了函数调用,就不用开辟栈帧)

         


四、注意事项

                

1、内联没展开

有时候你会发现他并没有按你想要的方式内联展开

不同的编译器对inline的实现机制可能不一样

你提出的inline对于编译器来说只是一个请求,而编译器可以拒绝你的请求!

因为程序员错误的使用内联函数,会浪费存储空间资源,所以编译器认为不如自己管理这个内联函数。如果你想要让他成为内联,编译器认可后就可以通过你的建议

        


2、 内联函数不一定比一般函数好用

注意:内联函数是一种以空间换时间的做法,他虽然节省了开辟栈帧的时间,但是他的本质是用函数体进行替换(与宏一模一样,只是开始替换的阶段不一样。宏是在预处理阶段替换的,而内联是在编译阶段替换的)

如图,对于常规函数来说,每次执行函数时,调用的函数都在同一块空间

代表:每次调用的时候就创建栈帧,返回的时候就销毁栈帧。所以不管调用add函数多少次,实际占用的空间也就是一个add函数的栈帧

                        

但是,如果换做内联函数

如图,内联函数中不存在函数调用(没有call),他是把函数体替换进去(不是直接copy内容,而是会有一定的调整)

        

设想一下,如果这个add函数里面有100行代码,main函数里面调用1000次

那么main函数会变成什么样呢?

1、用非内联函数

        每次调用占一行,add函数占100行,共记100+1000==1100行

2、用内联函数

        每个调用都是一次替换,一次替换要放入100行内容,总共替换1000次,共计100*1000=100000行

综上:简直是指数级提升!!内联函数掉大分

        


3、内联函数的声明与定义

 1、声明与定义不分文件写(要么在本文件下,要么在包含的头文件下)

2、声明与定义分文件写

可以把内联函数就理解成一个宏,碰到add就替换掉他

因为内联函数是直接替换函数的调用、链接的时候不可能生成地址,所以如果定义在第3个文件中,那么这个函数是没有地址的,链接的目的就是找函数地址,这样就发生了:只有add函数的声明,而没有定义,所以就会报错

        


五、内联函数在什么阶段展开 

                

结论:在编译阶段展开

这里用Linux下的gcc编译器演示(由于作者目前还没详细了解汇编语言,所以暂时用排除法跟大家演示)

1、先创建.cpp文件

2、对.cpp进行预处理,生成.i文件

3、再对其进行编译,生成.s文件

在这个文件中内联就展开了(作者通过排除法证明),因为:

再往后是汇编阶段(将汇编指令翻译成二进制代码,所以这一阶段只是存粹的翻译)

再然后是链接阶段(这一阶段可以理解为只是将不同函数的地址信息公开,让函数调用时能找到函数的定义。而前面我们以已经证明,内联函数没有调用,也就不会生成地址,所以他没有参与这个链接环节)

        


六、什么时候会用到内联函数

由于他的特性:

1、是由宏改进而来

2、可以减少栈帧开辟的时间

3、便于调试

4、展开会消耗空间

所以我们一般在优化规模较小、流程直接、频繁调用的时候可以想想内联函数。还有,尽量别递归使用内联函数,很多编译器不支持,而且行数过多的也不好内联展开

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值