【C++】内联函数

前言

在C语言中,我们学习过宏的用法。宏通常被用于进行简单的文本替换来执行一系列的操作,比如一些简单的运算。使用宏可以避免函数调用时建立栈帧的开销,提高程序的性能。我们首先来写一个实现加法功能的宏:

cpp 复制代码
#define ADD(x, y) ((x) + (y))
int main()
{
	int a = 10;
	int b = 20;
	cout << ADD(10, 20) << endl;

	return 0;
}

这个宏完美实现了我们的加法需求,但在定义宏时需要格外小心,因为宏可能存在潜在的问题,如副作用和作用域。为确保宏替换后的正确性,可能需要加上多个括号,这样就比较繁琐。而且宏无法进行类型检查,因为它只是完成替换功能。为了解决这些问题,C++引入了内联函数,提供了一种更安全、清晰且性能保持良好的替代方案。


概念

用 inline 修饰的函数叫做内联函数,编译时C++编译器会视情况在调用内联函数的地方展开,此时同样没有函数调用建立栈帧的开销,因此可以提升程序运行的效率。现在我们将上面定义的宏改写成内联函数:

cpp 复制代码
inline int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 10;
	int b = 20;
	int ret1 = Add(10, 20);

	return 0;
}

写成内联函数后,我们就不需要像定义宏一样注意括号的细节了。我们转到反汇编来看一下:

我们发现它并没有展开,而是和普通的函数调用一样,也建立了栈帧,因为这里依旧有 call 指令,原因是在 debug 模式下需要手动对编译器进行设置才能展开,而 release 模式下则不需要,但是 release 模式下无法调试,我们也无法看到展开的效果,因此接下来我们先设置下编译器。

右键单击箭头指向的这个位置。

如图所示,选择程序数据库这一选项。

内联函数扩展这里选择只适用于 _inline 这一选项,然后点击确定,这样就设置好了。这时候我们在调试一次,转到反汇编来看:

可以看到 call 指令已经没有了,函数确实是直接展开了。并且内联函数下我们依旧可以对程序进行调试。这里有一个小细节,mov 指令是先针对左操作数也就是10的,而对于普通的函数调用,在建立栈帧时先压入栈中的是右操作数,如果不清楚的话可以往上回顾一下没展开的那段汇编代码。我们再来看看宏的汇编代码:

显而易见宏是不一样的,因为它是直接进行替换的。宏替换后只有这样这样一句语句,显然无法进行调试。


特性

  1. 内联是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段就会用函数体替换函数调用。这样可以省去调用函数的开销,提高程序的运行效率。但是事物总是具有两面性的,内联函数也会存在缺陷,如可能会使目标文件变大,就算内联函数本身很短,但是在调用了很多次的情况下会有很多次的展开,总代码行数就会变长了。

  2. 内联对于编译器而言只是一个建议,是否采纳这个建议的决定权在于编译器。不同编译器关于内联的实现机制可能不同。一般建议将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现,一般10行左右是一个界限)、不是递归、且频繁调用的函数采用 inline 修饰,否则编译器会忽略内联建议。不可能把一个100行代码的函数当成内联对吧,否则多次调用的情况下目标文件岂不是非常的大。

  3. 建议不要将内联函数的声明和定义分离,这样可以避免其他源文件在使用该函数时发生链接错误。由于内联函数在调用时会直接被展开,其函数地址不会进入符号表,导致在链接阶段无法找到该函数的地址。因此,如果一定要将内联函数的声明和定义分开,那么该内联函数只能在包含其定义的源文件中使用。


潜在面试问题

宏的优缺点:

优点:

  1. 提高代码的复用性,通过简单的文本替换实现通用功能。
  2. 在一些情况下,宏能够提高程序的性能。

缺点:

  1. 宏不便于调试,因为宏在预编译阶段就进行了替换,因此难以在调试中观察。
  2. 可能导致代码可读性和可维护性下降,并且容易被误用。
  3. 缺乏类型安全的检查,可能导致错误的使用。

C++中用什么替代宏:

  1. 常量的定义可以使用 const 或 enum 来替代宏,提高类型安全和可读性。
  2. 短小函数的定义可以使用内联函数来替代宏,这样不仅保障了性能,还避免了宏可能引入的潜在问题,并提高了代码的可维护性。
相关推荐
Highcharts.js2 小时前
倒置百分比堆叠面积图表示列详解|Highcharts大气成分图表代码
开发语言·信息可视化·highcharts·图表开发·面积图·图表示例·推叠图
csdn_aspnet2 小时前
C语言 Lomuto分区算法(Lomuto Partition Algorithm)
c语言·开发语言·算法
晨曦中的暮雨2 小时前
4.15腾讯 CSIG云服务产线 一面
java·开发语言
存在morning2 小时前
【GO语言开发实践】二 GO 并发快速上手
大数据·开发语言·golang
xiaoerbuyu12334 小时前
开源Java 邮箱 基于SpringBoot+Vue前后端分离的电子邮件
java·开发语言
C+++Python4 小时前
C++ 进阶学习完整指南
java·c++·学习
sparEE5 小时前
c++值类别、右值引用和移动语义
开发语言·c++
zhangjw345 小时前
第11篇:Java Map集合详解,HashMap底层原理、哈希冲突、JDK1.8优化、遍历方式彻底吃透
java·开发语言·哈希算法
jrrz08285 小时前
Apollo MPC Controller
c++·自动驾驶·apollo·mpc·横向控制·lateral control
benpaodeDD6 小时前
视频10,11,12,13——java程序的加载与执行,安装jdk
java·开发语言