当前位置: 首页 > news >正文

C++修炼之练气期第八层——内联函数

  

文章目录

一、宏的缺点

引例

改正一

改正二

改正三

宏的缺陷

二、内联函数的概念

三、内联与非内联的区别

四、内联函数的特性


专栏导读

🌸作者简介:花想云,在读本科生一枚,致力于 C/C++、Linux 学习。

🌸本文收录于 C++系列,本专栏主要内容为 C++ 初阶、C++ 进阶、STL 详解等,专为大学生打造全套 C++ 学习教程,持续更新!

🌸相关专栏推荐:C语言初阶系列 C语言进阶系列 数据结构与算法 

大家是否还记得C语言中的宏函数?内联函数与C语言中宏函数作用类似,但是由于宏的缺陷较多,使用体验较差且安全性不高,所以C++中不建议使用宏,而是使用内联函数替代宏。本章我们就一起学习内联函数吧~

一、宏的缺点

引例

在学习宏时,我们曾经实现过ADD的宏函数,作用是求两个数的和。例如:

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

如果你的宏学的还不错的话,会发现上面的代码就是个典型的错误示例,说是错误锦集也不为过。我们试着将它修改正确。

改正一

首先,末尾的分号是必须要去掉的,否则编译都不会通过;

//改正一
#define ADD(x , y)  x+y

好了,接下来进行测试;

//测试用例1
int a = 3, b = 5;
printf("%d\n", ADD(a, b));

//测试用例2
printf("%d\n", ADD(a | b, a & b);

执行结果为:测试用例1通过、测试用例2错误;

原因是 #define 意为替换,测试用例实际上执行的是:

printf("%d\n", 3 | 5 + 3 & 5);

又因为运算符 ' + ' 的优先级高于 ' & ' 和 ' | ' ,所以结果错误。

那么继续改正。

改正二

为了解决优先级问题,需要给每个值都添加括号;

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

继续测试;

//测试用例3
int a = 3, b = 5;
printf("%d\n", ADD(a, b)*2);

OK,测试未通过,原因很简单:乘优先于加;编译后的代码其实是这样的:

printf("%d\n", (3)+(5)*2);

继续改正;

改正三

依然是优先级的问题,这次需要为整体添加括号;

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

终于,我们的ADD宏函数最终被修改正确。

宏的缺陷

显而易见,一个功能如此简单的ADD宏,都有这么多错误的版本,要是面对复杂的工程项目那么宏的安全性就让它的使用变得谨慎万分。

此外,宏的缺点还有:

1. 宏不能调试

由于宏在预处理阶段就会被替换,所以不能调试。

2. 宏没有类型检查

宏的参数不需要定义类型,导致宏容易出现类型相关的错误。

3. 有些场景下非常复杂,容易出错,不容易掌握

二、内联函数的概念

inline 修饰的函数叫做 内联函数,类似于宏,编译阶段内联函数在调用的地方进行展开,不会建立函数栈帧。没有了建立函数栈帧的开销,意味着程序的效率会因此提高。

//定义一个内联函数
inline int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 3, b = 5;

	int ret = Add(a, b);
	cout << ret << endl;
	return 0;
}

三、内联与非内联的区别

非内联函数在调用时,会建立函数栈帧,内联函数则不会;下面我们就在调用两种不同的函数时,查看各自的汇编代码。

//非内联函数
int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 3, b = 5;

	int ret = Add(a, b);
	cout << ret << endl;
	return 0;
}


如上图所示,该指令就是调用函数的指令,调用函数必会建立函数栈帧。再来看看内联函数;

//内联函数
inline int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 3, b = 5;

	int ret = Add(a, b);
	cout << ret << endl;
	return 0;
}

如图,此处并没有调用函数的过程,而是直接展开。

四、内联函数的特性

内联函数并不总是最好的选择,它也是有利有弊。

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

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

例如,我们将上述内联函数Add稍作修改,使它看起来规模较大较为繁琐,此时内联函数特性被忽略。

inline int Add(int x, int y)
{
	int z = x + y;
	z = x + y;
	z += x + y;
	z = x + y;
	z = x + y;
	z = x * y;
	z = x + y;
	z += x + y;
	z -= x + y;
	z += x + y;
	z += x * y;
	z -= x / y;
	z += x + y;
	z += x + y;

	return z;
}

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

相关文章:

  • SpringMVC-0315
  • 【数论】最大公约数、约数的个数与约数之和定理
  • 解析带小数的字节流
  • Linux基础命令大全(下)
  • MySql分页查询性能优化
  • 学会这12个Python装饰器,让你的代码更上一层楼
  • AutoSAR NM【一文读懂网络管理接口】
  • 2023年网络安全比赛--CMS网站渗透中职组(超详细)
  • 求最大公约数和最小公倍数---辗转相除法(欧几里得算法)
  • SpringBoot和Spring AOP默认动态代理方式
  • 华为OD机试 - 插队(Java JS Python)
  • OpenAI 发布GPT-4——全网抢先体验
  • 开源超级终端工具——WindTerm
  • 低代码开发平台是什么意思?低代码开发平台优势!
  • JS中sort()方法返回值?
  • 【162天】黑马程序员27天视频学习笔记【Day02-上】
  • ECMAScript入门(七)--Module语法
  • Object.assign方法不能实现深复制
  • 翻译 | 老司机带你秒懂内存管理 - 第一部(共三部)
  • 关于字符编码你应该知道的事情
  • 和 || 运算
  • 将 Measurements 和 Units 应用到物理学
  • 那些被忽略的 JavaScript 数组方法细节
  • 容器服务kubernetes弹性伸缩高级用法
  • 入门到放弃node系列之Hello Word篇
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 用jQuery怎么做到前后端分离
  • 源码安装memcached和php memcache扩展
  • Nginx实现动静分离
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • ​secrets --- 生成管理密码的安全随机数​
  • #stm32整理(一)flash读写
  • $refs 、$nextTic、动态组件、name的使用
  • (2015)JS ES6 必知的十个 特性
  • (3)STL算法之搜索
  • (vue)页面文件上传获取:action地址
  • (力扣)循环队列的实现与详解(C语言)
  • (五)Python 垃圾回收机制
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • (转)setTimeout 和 setInterval 的区别
  • (转载)Linux 多线程条件变量同步
  • .apk文件,IIS不支持下载解决
  • .chm格式文件如何阅读
  • .MyFile@waifu.club.wis.mkp勒索病毒数据怎么处理|数据解密恢复
  • .net CHARTING图表控件下载地址
  • .net 生成二级域名
  • .NET 使用 JustAssembly 比较两个不同版本程序集的 API 变化
  • .NET导入Excel数据
  • .Net开发笔记(二十)创建一个需要授权的第三方组件
  • @modelattribute注解用postman测试怎么传参_接口测试之问题挖掘
  • [2024最新教程]地表最强AGI:Claude 3注册账号/登录账号/访问方法,小白教程包教包会
  • [AUTOSAR][诊断管理][ECU][$37] 请求退出传输。终止数据传输的(上传/下载)
  • [BJDCTF2020]The mystery of ip
  • [C#]使用C#部署yolov8-seg的实例分割的tensorrt模型