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 被展开,就没有函数地址了,链接就会找不到。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/472.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【linux】:进程地址空间

文章目录 前言一、进程地址空间总结前言 本篇文章接着上一篇文章继续讲解进程&#xff0c;主要讲述了进程在运行过程中是如何在内存中被读取的以及为什么要有虚拟地址的存在&#xff0c;CPU在运行过程中是拿到程序的虚拟地址还是真实的物理内存。 一、进程地址空间 下面我们先…

【Spring从入门到实战】第 5 讲:SpringBoot实现拦截器及其原理

本文已收录于专栏&#x1f332;《Spring从入门到实战》&#x1f332;专栏前言 大家好&#xff0c;我是执梗。本专栏将从Spring入门开始讲起&#xff0c;详细讲解各类配置的使用以及原因&#xff0c;到使用SpringBoot进行开发实战&#xff0c;旨在记录学习生活的同时也希望能帮到…

【Maven】Maven的安装与下载

目录 一、Maven 软件的下载 二、Maven 软件的安装 三、JDK 的准备与统一 1. JDK 环境: 2. Maven 及 JDK 配置 四、Maven 软件版本测试 &#x1f49f; 创作不易&#xff0c;不妨点赞&#x1f49a;评论❤️收藏&#x1f499;一下 一、Maven 软件的下载 为了使用 Maven 管理…

6万字144道耗时72小时吐血整理【金三银四(金九银十)面试小抄之Java经典面试题基础篇总结】(附答案)

目录一.前言二.Java基础面试篇1. 什么是Java&#xff1f;2.Java 和 C的区别&#xff1f;3.Java中常用的注释以及作用&#xff1f;4.标识符的命名规则5.JVM、JRE和JDK的关系6.Oracle JDK 和 OpenJDK 的对比7.Java中基本数据类型8.int 和 Integer 有什么区别9.switch 是否能作用在…

【Effective C++详细总结】第二章 构造/析构/赋值运算

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;C/C知识点 &#x1f4e3;专栏定位&#xff1a;整理一下 C 相关的知识点&#xff0c;供大家学习参考~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;…

理清gcc、g++、libc、glibc、libstdc++的关系

0 理清gcc、g++、libc、glibc、libstdc++的关系 0.1 $ dpkg -L libc6 $ dpkg -L libc6 /lib/x86_64-linux-gnu /lib/x86_64-linux-gnu/ld-2.31.so /lib/x86_64-linux-gnu/libBrokenLocale-2.31.so /lib/x86_64-linux-gnu/libSegFault.so /lib/x86_64-linux-gnu/libanl-2.31.s…

Java NIO Buffer

Buffer是一块内存&#xff0c;主要用在NIO Channel&#xff0c;比如FileChannel,SocketChannel。 对Channel的读写都是直接操作Buffer对象。 Buffer是一个工具类&#xff0c;提供了操作这个内存块的方法。 Buffer的实现主要有以下几种&#xff1a; Buffer的类型&#xff1a; …

我一个普通程序员,光靠GitHub打赏就年入70万,

一个国外程序员名叫 Caleb Porzio在网上公开了自己用GitHub打赏年入70万的消息和具体做法。 Caleb Porzio 发推庆祝自己靠 GitHub 打赏&#xff08;GitHub Sponsors&#xff09;赚到了 10 万美元。 GitHub Sponsors是 GitHub 2019 年 5 月份推出的一个功能&#xff0c;允许开发…

ConvMixer:Patches Are All You Need

Patches Are All You Need 发表时间&#xff1a;[Submitted on 24 Jan 2022]&#xff1b; 发表期刊/会议&#xff1a;Computer Vision and Pattern Recognition&#xff1b; 论文地址&#xff1a;https://arxiv.org/abs/2201.09792&#xff1b; 代码地址&#xff1a;https:…

Redis 主从库如何实现数据一致?

目录 1、主从库间如何进行第一次同步&#xff1f; 2、主从级联模式分担全量复制时的主库压力 3、主从库间网络断了怎么办&#xff1f; 总结 // 好的文章&#xff0c;值得反复去读 Redis 具有高可靠性&#xff0c;这里有两层含义&#xff1a;一是数据尽量少丢失&#xff0c;…

【Copula】基于二元Frank-Copula函数的风光出力场景生成方法【考虑风光出力的不确定性和相关性】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

SpringBoot:SpringBoot 的底层运行原理解析

声明原文出处&#xff1a;狂神说 文章目录1. pom.xml1 . 父依赖2 . 启动器 spring-boot-starter2. 主启动类的注解1. 默认的主启动类2. SpringBootApplication3. ComponentScan4. SpringBootConfiguration5. SpringBootApplication 注解6. spring.factories7. 结论8. 简单图解3…

【Python】如何使用Pandas进行数据可视化?

如何使用Pandas进行数据可视化&#xff1f;1. 如何创建简单图&#xff1f;1.1 创建线型图1.2 绘制直方图1.3 绘制条形图1.4 绘制饼图1.5 绘制散点图2. Plot方法有哪些&#xff1f;3. 如何定制图表的样式和颜色&#xff1f;4. 如何同时对多个DataFrame绘图&#xff1f;5. 总结参…

K8s运维-高级网络策略介绍

1什么是NetworkPolicy&#xff1f;如果你希望在 IP 地址或端口层面&#xff08;OSI 第 3 层或第 4 层&#xff09;控制网络流量&#xff0c; 则你可以考虑为集群中特定应用使用 Kubernetes 网络策略&#xff08;NetworkPolicy&#xff09;。NetworkPolicy 是一种以应用为中心的…

【1615. 最大网络秩】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; n 座城市和一些连接这些城市的道路 roads 共同组成一个基础设施网络。每个 roads[i] [ai, bi] 都表示在城市 ai 和 bi 之间有一条双向道路。 两座不同城市构成的 城市对 的 网络秩 定义为&#xff…

从0到1构建springboot web应用镜像并使用容器部署

文章目录一、生成镜像的两种方法1.1、使用commit生成镜像1.1.1、拉取Centos基础镜像1.1.2、启动Centos容器并安装Go1.1.3、commit生成新镜像1.1.4、使用新镜像验证Golang环境1.2、使用Dockerfile生成镜像二、基于Dockerfile生成一个springboot镜像2.1、准备springboot应用jar包…

python自动化办公(一)

本文代码参考其他教程书籍实现。 文章目录文件读写open函数读取文本文件写入文本文件文件和目录操作使用os库使用shutil库文件读写 open函数 open函数有8个参数&#xff0c;常用前4个&#xff0c;除了file参数外&#xff0c;其他参数都有默认值。file指定了要打开的文件名称&a…

FreeRTOS系列第1篇---为什么选择FreeRTOS?

1.为什么学习RTOS&#xff1f; 作为基于ARM7、Cortex-M3硬件开发的嵌入式工程师&#xff0c;我一直反对使用RTOS。不仅因为不恰当的使用RTOS会给项目带来额外的稳定性风险&#xff0c;更重要的是我认为绝大多数基于ARM7、Cortex-M3硬件的项目&#xff0c;还没复杂到使用RTOS的地…

【华为机试真题详解 Python实现】最差产品奖【2023 Q1 | 100分】

文章目录 前言题目描述输入描述输出描述示例 1题目解析参考代码前言 《华为机试真题详解》专栏含牛客网华为专栏、华为面经试题、华为OD机试真题。 如果您在准备华为的面试,期间有想了解的可以私信我,我会尽可能帮您解答,也可以给您一些建议! 本文解法非最优解(即非性能…

SpringBoot和Spring AOP默认动态代理方式

SpringBoot和Spring AOP默认动态代理方式 目录SpringBoot和Spring AOP默认动态代理方式1. springboot 2.x 及以上版本2. Springboot 1.x3.SpringBoot 2.x 为何默认使用 CglibSpring 5.x中AOP默认依旧使用JDK动态代理SpringBoot 2.x开始&#xff0c;AOP为了解决使用JDK动态代理可…