🌹作者:云小逸
📝个人主页:云小逸的主页
📝Github:云小逸的Github
🤟motto:要敢于一个人默默的面对自己,强大自己才是核心。不要等到什么都没有了,才下定决心去做。种一颗树,最好的时间是十年前,其次就是现在!学会自己和解,与过去和解,努力爱自己。==希望春天来之前,我们一起面朝大海,春暖花开!==🤟
👏专栏:C++👏 👏专栏:Java语言👏👏专栏:Linux学习👏
👏专栏:C语言初阶👏👏专栏:数据结构👏👏专栏:备战蓝桥杯👏


前言

今天我们来学习C++中的一个比较重要的关键字–inline函数(内联函数),码字不易,希望多多支持!!!
在这里插入图片描述

——————————————————————————————

概念:

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

出现动机:

减少函数栈帧的开辟和销毁,如当频繁调用一个函数的时候(比如快排算法中函数多次被调用),此时消耗栈帧的比较大(不断地调用栈帧销毁栈帧)。
因此要将函数栈帧的优化掉

C语言的解决方案:宏函数

对于C语言中,可以使用宏函数。
使用宏函数为什么没有函数栈帧的消耗???
因为在预处理阶段就被替换了----优化了,没有了函数的栈帧的消耗了

新方案:C++方案—内联函数

有些人可能会比较疑惑:C语言中已经有方案了,为什么C++为什么还要再设计一个关建字方案:
因为宏函数有很多不方便的地方:

  1. 不可以调试
  2. 没有类型安全检查
  3. 容易写错
    比如你可以写一个ADD宏函数来验证:
#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)
#define ADD(x, y) ((x) + (y));

这五种写法都是很有可能是你写出来的(最后一个是对的,其余全是错的)
这几个宏定义的写法都是错误的,具体原因如下:

  1. #define ADD(int x, int y) {return x + y;}

该写法定义了一个带有参数列表的宏,但是参数列表应该在括号内,而不是放在宏名后面即ADD(int x, int y),正确的定义应该是:

#define ADD(x, y) {return x + y;}
  1. #define ADD(x, y) x + y

该写法没有使用括号把宏展开式中的参数括起来,可能会导致运算顺序出现问题。比如:

int result = 2 * ADD(3, 4);

在展开后就变成了 int result = 2 * 3 + 4; 发生了预期外的错误。 正确写法应该是:

#define ADD(x, y) ((x) + (y))
  1. #define ADD(x, y) (x + y)

该写法同样没有使用括号将宏展开式中的参数括起来,同样可能导致运算次序出现问题。比如:

int result = 2 * ADD(3, 4);

等价于 int result = 2 * (3 + 4); 发生了预期外的错误。正确写法应该是:

#define ADD(x, y) ((x) + (y))
  1. #define ADD(x, y) (x) + (y)

该写法看似使用括号将宏展开式中的参数括起来,但是由于缺少返回值表达式,比如return关键字,可能导致编译错误,语法上存在问题。 正确写法应该是:

#define ADD(x, y) ((x) + (y))

总而言之,在定义宏时,需要注意参数列表和展开式的正确书写,以及是否需要

总结:

在这里插入图片描述
因此宏函数极易出现错误。
这个知识点容易出错,但是面试的时候易被问到。

内联函数替代宏函数

在函数返回值前加一个inline即可, 那么函数就会在调用位置直接展开:
在这里插入图片描述

可以通过vs2013编译器上通过反汇编进行查看这个:
查看方式:

  1. 在release模式下,查看编译器生成的汇编代码中是否存在call Add
  2. 在debug模式下,需要对编译器进行设置,否则不会展开(因为debug模式下,编译器默认不会对代码进行优化,以下给出vs2013的设置方式)debug模式下不展开是因为为了支持调试!!!
    在这里插入图片描述
    在这里插入图片描述

特性:

以空间换时间

inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。

内联函数只是一个请求:

在这里插入图片描述

inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。下图为
《C++prime》第五版关于inline的建议:
在这里插入图片描述

适用范围

经验条款:C++中用Inline,const,enum来替换宏
在这里插入图片描述
适用范围:频繁调用小函数的时候:
在这里插入图片描述
但是内联函数是一个请求,不一定展开,当你频繁调用大函数的时候(干一些坏事情)编译器不会展开。
编译器有自己的规则,不喜欢你多管,hh
在这里插入图片描述
在这里插入图片描述

再举个栗子:代码膨胀

在这里插入图片描述
指令影响的是什么-----安装包
在这里插入图片描述
安装包大了–必然不好

inline不建议声明和定义分离:

inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

举个栗子:

正常情况(预处理-编译-链接)

代码

预处理:

首先编译器会对这个代码进行预处理,变成这个样子:即将所应用的头文件的内容放到文件内----
在这里插入图片描述

编译:

接下来进行代码的编译:
在这里插入图片描述
编译的时候将代码转换为汇编指令:此时我们可以看到Test.cpp文件中call的func函数的函数地址是未知的,正常情况是会在链接的时候在符号表中查找并将函数地址填入进行调用。

链接:

在这里插入图片描述
上面是正常的情况,下面说一下,当Inline函数调用和声明分离的时候:

内联情况:
预处理:

在这里插入图片描述

编译:

在这里插入图片描述
stack.cpp文件内不会生成指令,因为没有调用

链接:

在这里插入图片描述

建议:

直接在.h文件内定义就行了

总结:

在这里插入图片描述
只要是内联函数,函数定义与声明分离的时候,内联函数的函数地址不会进符号表,要不然还要判断编译器是否应用内联函数,这样太复杂了!!!

在这里插入图片描述
在这里插入图片描述

注意事项:

宏的优缺点?

优点:

1.增强代码的复用性。
2.提高性能。

缺点:

1.不方便调试宏。(因为预编译阶段进行了替换)
2.导致代码可读性差,可维护性差,容易误用。
3.没有类型安全的检查 。
C++有哪些技术替代宏?

  1. 常量定义 换用const enum
  2. 短小函数定义 换用内联函数

长不用,短用内联函数。


最后

十分感谢你可以耐着性子把它读完和我可以坚持写到这里,送几句话,对你,也对我:

1.一个冷知识:
屏蔽力是一个人最顶级的能力,任何消耗你的人和事,多看一眼都是你的不对。

2.你不用变得很外向,内向挺好的,但需要你发言的时候,一定要勇敢。
正所谓:君子可内敛不可懦弱,面不公可起而论之。

3.成年人的世界,只筛选,不教育。

4.自律不是6点起床,7点准时学习,而是不管别人怎么说怎么看,你也会坚持去做,绝不打乱自己的节奏,是一种自我的恒心。

5.你开始炫耀自己,往往都是灾难的开始,就像老子在《道德经》里写到:光而不耀,静水流深。

最后如果觉得我写的还不错,请不要忘记点赞✌,收藏✌,加关注✌哦(。・ω・。)

愿我们一起加油,奔向更美好的未来,愿我们从懵懵懂懂的一枚菜鸟逐渐成为大佬。加油,为自己点赞!

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐