【C++】内联函数

前言

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

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

	return 0;
}

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


概念

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

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. 短小函数的定义可以
使用内联函数来替代宏,这样不仅保障了性能,还避免了宏可能引入的潜在问题,并提高了代码的可维护性。

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

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

相关文章

5、C语言:结构

结构 结构的基本知识结构与函数传递结构 结构数组、指向结构的指针自引用结构&#xff08;二叉树&#xff09;表查找类型定义&#xff08;typedef&#xff09;联合位字段 结构也是一种数据类型。类似于int、char、double、float等。 结构是一个或多个变量的集合&#xff0c;这些…

Linux系统——远程访问及控制

目录 一、OpenSSH服务器 1.SSH&#xff08;Secure Shell&#xff09;协议 2.OpenSSH 2.SSH原理 2.1公钥传输原理 2.2加密原理 &#xff08;1&#xff09;对称加密 &#xff08;2&#xff09;非对称加密 2.3远程登录 2.3.1延伸 2.3.2登录用户 3.SSH格式及选项 3.1延…

node(express.js创建项目)+连接mysql数据库

1.node npm的安装 2.express的安装 全局安装:npm install express -gnpm install -g express-generator// ps: 4.0版本把generator分离出来了&#xff0c;需要单独安装3.创建express项目 express 项目名称 cd 项目名称 npm install npm start4.项目中安装数据库 npm install…

Python读取log文件报错“UnicodeDecodeError”

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 问题描述&#xff1a; 写了一个读取log文件的Python脚本&#xff1a; # -*- coding:utf-8 -*- import os import numpy as np …

第01章_Java语言概述拓展练习(为什么要设置path?)

文章目录 第01章_Java语言概述拓展练习1、System.out.println()和System.out.print()有什么区别&#xff1f;2、一个".java"源文件中是否可以包括多个类&#xff1f;有什么限制&#xff1f;3、Something类的文件名叫OtherThing.java是否可以&#xff1f;4、为什么要设…

【Maven】009-Maven 简单父子工程搭建

【Maven】009-Maven 简单父子工程搭建 文章目录 【Maven】009-Maven 简单父子工程搭建一、需求说明1、结构2、第三方库 二、工程搭建1、父工程第一步&#xff1a;创建父工程第二步&#xff1a;引入公共依赖 lombok 和管理 hutool 依赖版本 2、公共子模块第一步&#xff1a;创建…

服务器出现500、502、503错误的原因以及解决方法

服务器我们经常会遇到访问不了的情况有的时候是因为我们服务器被入侵了所以访问不了&#xff0c;有的时候是因为出现了服务器配置问题&#xff0c;或者软硬件出现问题导致的无法访问的问题&#xff0c;这时候会出现500、502、503等错误代码。基于以上问题我们第一步可以先重启服…

MySQL核心SQL

一.结构化查询语言 SQL是结构化查询语言&#xff08;Structure Query Language&#xff09;&#xff0c;它是关系型数据库的通用语言。 SQL 主要可以划分为以下 3 个类别&#xff1a; DDL&#xff08;Data Definition Languages&#xff09;语句 数据定义语言&#xff0c;这…

vue2配置教程

5.12.3 Vue Cli 文档地址: https://cli.vuejs.org/zh/ IDEA 打开项目&#xff0c;运行项目

【年终总结】回首2023的精彩,迈向2024的未来

文章目录 一、历历在目&#xff0c;回首成长之路&#x1f3c3;‍1、坚持输出&#xff0c;分享所学2、积土成山&#xff0c;突破万粉3、不断精进&#xff0c;向外涉足 二、雅俗共赏&#xff0c;阅历百般美好&#x1f3bb;1、音乐之声&#xff0c;声声入耳2、书海遨游&#xff0c…

select...for update锁详解

select…for update锁详解 select…for update的作用就是&#xff1a;如果A事务中执行了select…for update&#xff0c;那么在其提交或回滚事务之前&#xff0c;B&#xff0c;C&#xff0c;D…事务是无法操作&#xff08;写&#xff09;A事务select…for update所命中的数据的…

php时间函数date()、getdate()、time()

目录 1. 时区修改 2. date() 3. getdate() 4. time() 1. 时区修改 位于东八区&#xff0c;修改php.ini 。date.timezone Asia/Shanghai 2. date() 获取时间函数 <?php header("Content-Type: text/html; charsetutf-8");$d date(H:i:s);//小时H&#xf…

linux驱动(五):framebuffer

本文主要探讨210的framebuffer驱动知识。 frameBuffer 用户态进程直接调用显卡写屏,framebuffer接口是给用户态进程用于写屏 framebuffer设备文件为fbx 清屏:dd if/dev/zero of/dev/fbx 清屏&#xff1a;$ dd if/dev/zero of/dev/fb0 bs1024 …

2023 IoTDB Summit:天谋科技高级开发工程师苏宇荣《汇其流:如何用 IoTDB 流处理框架玩转端边云融合》...

12 月 3 日&#xff0c;2023 IoTDB 用户大会在北京成功举行&#xff0c;收获强烈反响。本次峰会汇集了超 20 位大咖嘉宾带来工业互联网行业、技术、应用方向的精彩议题&#xff0c;多位学术泰斗、企业代表、开发者&#xff0c;深度分享了工业物联网时序数据库 IoTDB 的技术创新…

一键调整播放倍速,调整播放倍速的软件

你是否曾因为长时间的视频而感到厌烦&#xff1f;或者因为视频播放得太快而错过了一些重要内容&#xff1f;现在&#xff0c;有了我们的【媒体梦工厂】&#xff0c;这些问题都将得到完美解决。不论你是想快速浏览长视频&#xff0c;还是想让视频慢下来以便更好地学习或欣赏&…

spring-mvc(1):Hello World

虽然目前大多数都是使用springboot来开发java程序&#xff0c;或者使用其来为其他端提供接口&#xff0c;而为其他端提供接口&#xff0c;这些功能都是依靠springmvc实现的&#xff0c;所以有必要学习一下spring-mvc&#xff0c;这样才能更好的学习springboot。 一&#xff0c…

C++力扣题目236--二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以是它自己的…

代码随想录 Leetcode349. 两个数组的交集

题目&#xff1a; 代码(首刷看解析 2024年1月14日&#xff09;&#xff1a; class Solution { public:vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {unordered_set<int> a;unordered_set<int> res;for(int i 0…

每日一题——LeetCode1189.气球的最大数量

方法一 个人方法&#xff1a; 统计text字符串中b、a、l、o、n 这几个字符出现的次数 l和n需要两个才能拼成一个balloon&#xff0c;所以碰到l和o加1&#xff0c;其他字符加2 最后求出出现次数最少的那个字符再除以2就是能拼凑成的单词数量&#xff0c;避免出现小数要使用向下…

近红外光谱分析技术与基于深度学习的化学计量学方法

郁磊【副教授】&#xff1a;主要从事AI人工智能与大数据分析等相关研究&#xff0c;长期致力于人工智能与近红外生物医学工程等领域融合&#xff0c;主持并完成多项科研课题。著有《神经网络43个案例分析》等书籍。 // 讲座内容 1、近红外光谱基本理论、近红外光谱仪基本原理…