C++中的inline函数(内联函数)

前言:我们在C中学习宏的时候,就体会到了宏在编写的时候需要注意非常多的细 节,否则会导致出错,而C++在设计的时候便为解决C语言在一些情况下不方便而提出了inline(内联函数)这一新特性

1 ADD宏函数

ADD函数宏的写法有以下四种:

  • 1 #define ADD(int a, int b) return a + b;
  • 2 #define ADD(a, b) a + b;
  • 3 #define ADD(a, b) (a + b)
  • 4 #define ADD(a, b) ((a) + (b))
    你觉得上面哪种是对的?我们一个一个的进行分析
1.1 #define ADD(int a, int b) return a + b;

我们首先要说明的是宏函数本质就是一种替换而不是函数,这样写在语法的层面 上就是错误的

1.2 #define ADD(a, b) a + b;

所以我们一般定义宏函数的时候不在后面加;

并且注意在这个例子里面就算不加;也是一个错误的写法

按理来说我们希望得到的是(1+2)*(2+3)=15,那为什么结果是8呢?

还是要抓住宏函数就是一个替换的规则ADD(1,2)*ADD(2,3)被替换为1+2\*2+3=8,我们可以看出我们没有保证每一个ADD宏替换后的独立。

1.3 #define ADD(a, b) (a + b)

所以这个写法也是错误的,假如ab其中是一个表达式的话,这个宏函数的替换就会违背我们预想的逻辑。

1.4 #define ADD(a, b) ((a) + (b))

这样的写不仅保证了ADD宏函数的独立性而且保证了ADD宏函数参数ab的独立性,是一个正确的宏函数。

小结:我们可以看出写对一个宏函数要注意的细节还是很多的,一不留神就容易出现问题。但是宏函数能够在预处理阶段替换,不用建立函数栈帧,可以提效(提高效率),而我们C++inline就可以继承这些优点并且屏蔽实现细节
在回顾宏后,接下来我们正式进入inline函数的学习

2 inline(内联函数)

2.1 liline的基本概念和特征
  • inline修饰的函数就是内联函数
  • 编译阶段,编译器会将内联函数展开(类似于宏替换),这样函数就不会创建栈帧,从而提高效率
  • inline修饰的内联函数是否展开取决于编译器,inline修饰函数这是给编译器一个建议,并且不同编译器对内联函数展开情况各不相同,因为这是C++标准未定义的。

关于内联函数展开的说明:内联函数的展开就是在编译阶段,编译器会在这个位置直接插入函数代码(并不是原封不动的插入 ,要根据上下文逻辑进行必要的转换和适配 ,以保证代码语义的正确性)

宏的实现需要我们自己去实现,而liline内联函数编译器帮助我们处理 ,但编译器又不是特别智能,它只能处理一些短小函数 ,对于代码较多的,递归函数即使加上inline也会被编译器忽略。
vs编译器下debug版本默认不展开inline,我们想要实现liline需要设置

2.2 vs设置inline选项


设置好后我们就可以通过汇编看内联函数非内联函数 的区别了

普通add函数:

内联add函数:

我们可以明显看出在内联函数中inline没有创建函数栈帧,并且都被展开在main函数的cout << add(x, y) << endl;的这里,我们可以看到内联函数的这一块汇编代码 明显比普通函数

2.3 inline声明和定义不能分离

inline声明和定义分离会产生链接错误,inline被展开其就没有函数地址,会在链接的时候出错

我们要理解这个,必须先简单的回顾下编译和链接的过程

F.h中有f1f2函数的声明,而在F.cpp中有这两个函数的实现,那我们要在Test.cpp文件中使用这两个函数涉及到什么过程呢?

  • 预处理:首先预处理将头文件F.h内容添加到两个cpp文件中。(预处理阶段还做了其它事)
  • 编译:检查语法生成汇编代码,这时候编译器已经将F.cpp中的f1f2函数编译并有其函数地址并记录在符号表中,而由于Test.cpp函数中只有两个函数的声明,所以使用这两个函数的位置编译的时候两个函数的地址并未填入(等待编译的时候补充)
  • 汇编:将汇编代码转化为二进制机器指令
  • 链接:将两个cpp的目标文件合并为.exe的可执行文件,这个时候Test.o(linux下)目标文件便去F.o符号表中找f1f2函数地址,以填补在汇编指令中call 指定函数地址空缺。

而被inline修饰的内联函数具有内部链接属性,它不会出现在符号表中,如果f1或者f2是内联函数那么Test.o就无法从符号表中获取函数地址从而找到函数完成调用,从而导致链接错误。

inline的内联函数一样,static修饰的函数也具有内部链接属性(函数只能在该文件使用,其它文件找不到)

extern可以声明函数的存在,从而让编译器不报错,并在后面的链接阶段从其它文件的符号表中去寻找函数地址,我们可以看到函数被正确使用。

static修饰后:

发生了链接错误,static将函数有外部链接属性变成内部链接属性

总结:符号表就是各个文件调用其它文件的说明书,而inlinestatic会将函数从这个说明书(符号表)去除,从而让文件无法使用其它文件的函数(就是不知道这个函数的地址)。

那内联函数这么好,我们是不是凡是个函数都给前面加inline变成内联函数呢?

答案当然是不可以,我们可以通过一个场景来看

inlinefunc函数被频繁的调用的话,每个位置都被展开,那么整个程序的代码量便会大大增加,会导致程序运行变慢。

所以我们一般将调用频繁且短小函数设置为内联函数。

相关推荐
神仙别闹5 小时前
基于 C++ 实现两个有序链表序列的交集
java·c++·链表
hansang_IR7 小时前
【题解】[APIO2023] 赛博乐园 / cyberland
c++·算法·图论
落羽的落羽8 小时前
【AI】快速理解AI应用的相关名词概念
linux·c++·人工智能·python·计算机网络·算法
刚入门的大一新生8 小时前
Linux-进程控制
linux·运维·服务器·c++
刃神太酷啦8 小时前
Redis 进阶核心:持久化 (RDB/AOF)、事务与主从复制全解析----《Hello Redis!》(5)
linux·c语言·数据库·c++·redis·缓存·bootstrap
喜欢吃燃面11 小时前
深入 C++ STL:从 unordered_set与unordered_map到底层哈希表的原理与实现
数据结构·c++·散列表
FlightYe12 小时前
音视频修炼之基础理论(五):AAC格式与解析
android·linux·c++·音视频·aac
ShineWinsu12 小时前
对于MySQL:数据库的操作的解析
linux·数据库·c++·mysql·面试·笔试·库的操作
云小逸13 小时前
C++ 第一阶段:对象、内存与生命周期
开发语言·c++
夜航海图13 小时前
S-57 数据解剖:把一个 ENC 文件拆给你看
c++