C++内联函数详解

举报
YIN_尹 发表于 2023/08/16 16:03:34 2023/08/16
724 0 0
【摘要】 内联函数除了上面的内容,我们C++的祖师爷呢还觉得C语言中的宏也不是很好:我们在C语言的预处理那一章也有比较详细的学习过宏,也分析了宏的缺点:当然宏也是有一些优点的:比如我们定义一个宏函数(当然它不是一个真正的函数),它也可以完成一个函数的功能,但是呢它不用像真正的函数那样建立函数栈帧。补充:C++中建议用const和枚举enum代替宏定义的常量从宏的这些优缺点出发,C++又引入了一个新的概...

内联函数

除了上面的内容,我们C++的祖师爷呢还觉得C语言中的宏也不是很好:

我们在C语言的预处理那一章也有比较详细的学习过宏,也分析了宏的缺点:

dbbcb3be4e1b404f9e648fa17a67cbc6.png

当然宏也是有一些优点的:

比如我们定义一个宏函数(当然它不是一个真正的函数),它也可以完成一个函数的功能,但是呢它不用像真正的函数那样建立函数栈帧。

补充:C++中建议用const和枚举enum代替宏定义的常量

从宏的这些优缺点出发,C++又引入了一个新的概念——内联函数。

7.1 概念

那什么是内联函数呢?

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。

如果是定义一个普通的函数:

88c6091c5a594a1dbc0e32b7900a217c.png

我们调试看它的汇编:

d9ba8b4bb05b481399e87d4d71b7dfee.png

大家可能对汇编不太懂,但是看到这个call 函数名(地址)其实就是去调用这个函数建立栈帧了。

那我们现在把它改成内联函数:

d46329f7066f477ea7fe0e745554c322.png

如果在上述函数前增加inline关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的调用。

我们再来看一下反汇编:db6934bbd288465fa5b8c79d16af1d3c.png

怎么没起作用啊。

怎么样让它起作用呢?

🆗,它在release版本下面会起作用,或者在debug版本下需要我们进行一下设置:

35dc897d7446473285ad12cd27d93a1b.png

2a63e7180aa44f15ae68ab9d458c1302.png

c1e77fd83e0343798b7fe682e28e927c.png

bbda6af5b46b434c84c9b5fbefd446ab.png

846befb58c944f2d9da8ae9d27a8d249.png

然后我们再来看:

893b7131e3c649cabb8761f1eb3988fe.png

这次是不是就没有call Add那个指令了,说明我们的内联函数就起作用了,就没有函数调用建立栈帧的开销了。

7.2 内联函数的特性

inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用。

缺陷:可能会使目标文件变大,

优势:少了调用开销,提高程序运行效率。

简单解释一下:

这里说的这个空间换时间,不是说程序运行时占用的空间会变大,而是最终产生的可执行程序可能会变大。

因为C++编译器会在调用内联函数的地方将函数直接展开,这样的话与普通的调用相比,产生的指令就可能变多,所以最终生成的可执行程序可能会变大。

inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。

下图为《C++prime》第五版关于inline的建议:

af71f0a0d83d4e0fb6674de3efddc835.png

也就是说:

不是所有的内联函数最终都会按照内联函数进行处理的,一般规模较小并且频繁使用的的我们才实现成内联函数,否则即使我们实现成内联函数,编译器也不一定会按照内联函数进行处理。

举个例子:

刚才的Add函数我们用inline修饰后调试发现确实按照内联函数处理了,那如果我们现在在Add函数中再多增加一些语句,有可能就不再按内联函数执行了:

df076ca8b9144c11bda174f2bbb658e3.png

我们再来调试观察一下:

5bcf8cbba2e24edba3380db826f01723.png

是不是又有call Add了。

内联函数不能声明和定义分离,分离会导致链接错误

因为内联函数编译期间就会在函数调用的地方被展开,不会像普通的函数调用那样call一个地址然后跳转调用,就没有函数地址了(可以认为内联函数不会产生地址进符号表),这样链接的时候就会找不到。

所以内联函数如果有声明的话,把声明定义放一起。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。