C++ inline内联函数详解

2023-10-29

函数是一个可以重复使用的代码块,CPU 会一条一条地挨着执行其中的代码。CPU 在执行主调函数代码时如果遇到了被调函数,主调函数就会暂停,CPU 转而执行被调函数的代码;被调函数执行完毕后再返回到主调函数,主调函数根据刚才的状态继续往下执行。

一个 C/C++ 程序的执行过程可以认为是多个函数之间的相互调用过程,它们形成了一个或简单或复杂的调用链条,这个链条的起点是 main(),终点也是 main()。当 main() 调用完了所有的函数,它会返回一个值(例如return 0;)来结束自己的生命,从而结束整个程序。

函数调用是有时间和空间开销的。程序在执行一个函数之前需要做一些准备工作,要将实参、局部变量、返回地址以及若干寄存器都压入栈中,然后才能执行函数体中的代码;函数体中的代码执行完毕后还要清理现场,将之前压入栈中的数据都出栈,才能接着执行函数调用位置以后的代码。关于函数调用的细节,我们已经在《C语言内存精讲》一章中的《一个函数在栈上到底是怎样的》《用一个实例来深入剖析函数进栈出栈的过程》两节中讲到。

如果函数体代码比较多,需要较长的执行时间,那么函数调用机制占用的时间可以忽略;如果函数只有一两条语句,那么大部分的时间都会花费在函数调用机制上,这种时间开销就就不容忽视。

为了消除函数调用的时空开销,C++ 提供一种提高效率的方法,即在编译时将函数调用处用函数体替换,类似于C语言中的宏展开。这种在函数调用处直接嵌入函数体的函数称为内联函数(Inline Function),又称内嵌函数或者内置函数。

指定内联函数的方法很简单,只需要在函数定义处增加 inline 关键字。请看下面的例子:

 
  1. #include <iostream>
  2. using namespace std;
  3. //内联函数,交换两个数的值
  4. inline void swap(int *a, int *b){
  5.     int temp;
  6.     temp = *a;
  7.     *a = *b;
  8.     *b = temp;
  9. }
  10. int main(){
  11. int m, n;
  12. cin>>m>>n;
  13. cout<<m<<", "<<n<<endl;
  14. swap(&m, &n);
  15. cout<<m<<", "<<n<<endl;
  16. return 0;
  17. }

运行结果:
45 99↙
45, 99
99, 45

注意,要在函数定义处添加 inline 关键字,在函数声明处添加 inline 关键字虽然没有错,但这种做法是无效的,编译器会忽略函数声明处的 inline 关键字。

当编译器遇到函数调用swap(&m, &n)时,会用 swap() 函数的代码替换swap(&m, &n),同时用实参代替形参。这样,程序第 16 行就被置换成:

 
  1. int temp;
  2. temp = *(&m);
  3. *(&m) = *(&n);
  4. *(&n) = temp;

编译器可能会将 *(&m)、*(&n) 分别优化为 m、n。

当函数比较复杂时,函数调用的时空开销可以忽略,大部分的 CPU 时间都会花费在执行函数体代码上,所以我们一般是将非常短小的函数声明为内联函数。

由于内联函数比较短小,我们通常的做法是省略函数原型,将整个函数定义(包括函数头和函数体)放在本应该提供函数原型的地方。下面的例子是一个反面教材,这样的写法是不被推荐的:

 
  1. #include <iostream>
  2. using namespace std;
  3. //声明内联函数
  4. void swap1(int *a, int *b); //也可以添加inline,但编译器会忽略
  5. int main(){
  6. int m, n;
  7. cin>>m>>n;
  8. cout<<m<<", "<<n<<endl;
  9. swap1(&m, &n);
  10. cout<<m<<", "<<n<<endl;
  11. return 0;
  12. }
  13. //定义内联函数
  14. inline void swap1(int *a, int *b){
  15. int temp;
  16. temp = *a;
  17. *a = *b;
  18. *b = temp;
  19. }


使用内联函数的缺点也是非常明显的,编译后的程序会存在多份相同的函数拷贝,如果被声明为内联函数的函数体非常大,那么编译后的程序体积也将会变得很大,所以再次强调,一般只将那些短小的、频繁调用的函数声明为内联函数。

内联函数看起来简单,但是有很多细节要引起重视,我们将在后续两节《C++内联函数也可以用来代替宏》《如何规范地使用C++内联函数》对内联函数的本质以及使用规范进行深入的讲解。

最后需要说明的是,对函数作 inline 声明只是程序员对编译器提出的一个建议,而不是强制性的,并非一经指定为 inline 编译器就必须这样做。编译器有自己的判断能力,它会根据具体情况决定是否这样做。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ inline内联函数详解 的相关文章

  • 进程何时获得 SIGABRT(信号 6)?

    C 中进程获得 SIGABRT 的场景有哪些 该信号是否始终来自进程内部 或者该信号可以从一个进程发送到另一个进程吗 有没有办法识别哪个进程正在发送该信号 abort 向调用进程发送SIGABRT信号 就是这样abort 基本上有效 abo
  • asp.net 文本框文本模式数字,仅允许数字

    我只是想知道 ASP NET 中是否有一种方法只允许文本框中的数字textmode number 当我使用这个时
  • 我的线程图像生成应用程序如何将其数据传输到 GUI?

    Mandelbrot 生成器的缓慢多精度实现 线程化 使用 POSIX 线程 Gtk 图形用户界面 我有点失落了 这是我第一次尝试编写线程程序 我实际上并没有尝试转换它的单线程版本 只是尝试实现基本框架 到目前为止它是如何工作的简要描述 M
  • 以编程方式检查页面是否需要基于 web.config 设置进行身份验证

    我想知道是否有一种方法可以检查页面是否需要基于 web config 设置进行身份验证 基本上如果有这样的节点
  • 构造函数中显式关键字的使用

    我试图了解 C 中显式关键字的用法 并查看了这个问题C 中的explicit关键字是什么意思 https stackoverflow com questions 121162 但是 那里列出的示例 实际上是前两个答案 对于用法并不是很清楚
  • 暂停下载线程

    我正在用 C 编写一个非常简单的批量下载程序 该程序读取要下载的 URL 的 txt 文件 我已经设置了一个全局线程和委托来更新 GUI 按下 开始 按钮即可创建并启动该线程 我想要做的是有一个 暂停 按钮 使我能够暂停下载 直到点击 恢复
  • IronPython:没有名为 json 的模块

    我安装了 IronPython 我的 python 文件如下所示 import sys print sys version import json 运行它的代码 var p Python CreateEngine var scope p C
  • 在非活动联合成员上使用“std::addressof”是否定义明确[重复]

    这个问题在这里已经有答案了 下面的代码是尝试实现constexpr的版本offsetof在 C 11 中 它可以在 gcc 7 2 0 和 clang 5 0 0 中编译 这取决于申请std addressof工会非活跃成员的成员 这是明确
  • 无法将类型“System.IO.Stream”隐式转换为“Java.IO.InputStream”

    我提到了一些类似的问题 但没有一个涉及IO 当我使用时 我在java中使用了相同的代码Eclipse 那次就成功了 但现在我尝试在中使用这段代码Mono for Android C 它不起作用 我正在尝试运行此代码来创建一个InputStr
  • 生产代码中的 LRU 实现

    我有一些 C 代码 需要使用 LRU 技术实现缓存替换 目前我知道两种实现LRU缓存替换的方法 每次访问缓存数据时使用时间戳 最后比较替换时的时间戳 使用缓存项的堆栈 如果最近访问过它们 则将它们移动到顶部 因此最后底部将包含 LRU 候选
  • 将构建日期放入“关于”框中

    我有一个带有 关于 框的 C WinForms 应用程序 我使用以下方法将版本号放入 关于 框中 FileVersionInfo GetVersionInfo Assembly GetExecutingAssembly Location F
  • 获取 2 个数据集 c# 中的差异

    我正在编写一个简短的算法 它必须比较两个数据集 以便可以进一步处理两者之间的差异 我尝试通过合并这两个数据集并将结果更改放入新的数据集来实现此目标 我的方法如下所示 private DataSet ComputateDiff DataSet
  • 是否可以有一个 out ParameterExpression?

    我想定义一个 Lambda 表达式out范围 有可能做到吗 下面是我尝试过的 C Net 4 0 控制台应用程序的代码片段 正如您在 procedure25 中看到的 我可以使用 lambda 表达式来定义具有输出参数的委托 但是 当我想使
  • 当前的 x86 架构是否支持非临时加载(来自“正常”内存)?

    我知道有关此主题的多个问题 但是 我没有看到任何明确的答案或任何基准测量 因此 我创建了一个处理两个整数数组的简单程序 第一个数组a非常大 64 MB 第二个数组b很小 无法放入 L1 缓存 程序迭代a并将其元素添加到相应的元素中b在模块化
  • 转到定义:“无法导航到插入符号下的符号。”

    这个问题的答案是社区努力 help privileges edit community wiki 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 我今天突然开始在我的项目中遇到一个问题 单击 转到定义 会出现一个奇怪的错误 无法导航到
  • 运算符“==”不能应用于“int”和“string”类型的操作数

    我正在编写一个程序 我想到了一个数字 然后计算机猜测了它 我一边尝试一边测试它 但我不断收到不应该出现的错误 错误是主题标题 我使用 Int Parse 来转换我的字符串 但我不知道为什么会收到错误 我知道它说 不能与整数一起使用 但我在网
  • 我在在线程序挑战编译器中遇到演示错误

    include
  • 使用 C# 从 DateTime 获取日期

    愚蠢的问题 给定日期时间中的日期 我知道它是星期二 例如我如何知道它的 tue 2 和 mon 1 等 Thanks 您正在寻找星期几 http msdn microsoft com en us library system datetim
  • 匿名结构体作为返回类型

    下面的代码编译得很好VC 19 00 23506 http rextester com GMUP11493 标志 Wall WX Za 与VC 19 10 25109 0 标志 Wall WX Za permissive 这可以在以下位置检
  • 错误:无效使用不完整类型“类 Move”/未定义对 Move::NONE 的引用

    拜托 我不知道为什么这个简单的代码被拒绝 它给了我 2 个编译错误 请帮帮我 I use 代码 块 20 03 我的编译器是GNU GCC 移动 hpp class Move public Move Move int int public

随机推荐

  • hyper-v、VT-x、WSL2冲突问题

    hyper v功能与vt x冲突 开启hyper v后vt x显示为未开启 其中 如果需要使用虚拟机 需要开启vt x 安卓虚拟机VMware等 WSL2网上查资料需要开启hyper v 但是我没装成 一直报错 请启用虚拟机平台 Windo
  • STL 比较器

    一 什么是 tmd 比较器 在C 的STL 标准模板库 中 比较器通常用于对容器中的元素进行排序或查找 STL提供了多种比较器的方式 主要通过函数对象或者lambda表达式来实现 以下是一些常见的比较器示例 1 函数对象 Function
  • 开源协议介绍

    Apache Licene 2 0 Apache Licence是著名的非盈利开源组织Apache采用的协议 该协议和BSD类似 同样鼓励代码共享和尊重原作者的著作权 同样允许代码修改 再发布 作为开源或商业软件 需要满足的条件也和BSD类
  • pycharm使用多版本python----疑难之setuptools

    系列文章目录 第一章 pycharm使用多版本python 疑难之setuptools 提示 写完文章后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 系列文章目录 文章目录 前言 一 setuptools是什么 二 解决办法
  • 封装springboot基础框架(含demo)

    写此篇博文目的有2个 一 我想做一个最小化框架 包括配置 拦截器 工具包等等都一应具备 使开发可以直接开始编写业务代码 所以我的demo的名字叫做springboot base 二 这也是为了以后介绍springcloud做的一个铺垫 该框
  • vscode Java开发环境搭建

    java系列文章目录 文章目录 java系列文章目录 前言 本文的目的 一 安装Java Extension Pack插件 二 配置全局的Java与Maven 三 开始创建Maven项目 四 配置该项目的settings json 五 补全
  • 自制无盘更新服务器,无盘云更新安装服务器流程

    无盘云更新安装服务器流程 内容精选 换一换 在 云服务器列表 页 单击 操作 列下的 查看应用 查看应用的状态为 安装失败 将鼠标移动至 安装失败 处 查看具体的失败原因 并根据表1进行故障排除 CVR服务端支持使用的应用需要满足以下条件
  • 不简单的单例模式Singleton

    版权声明 本文为博主原创文章 未经博主允许不得转载 单例模式 即Ensure a class only has one instance and provide a global point of access to it 只有一个实例 是
  • 猎头推荐成功一个人竟然收年薪一半,也太赚了吧

    猎头推荐成功一个人竟然收年薪一半 也太赚了吧 这个问题似乎有点危言耸听了 有网友说女朋友是猎头 推荐成功并没有提成 要一个季度内推多少个之后 之后再成的才有30 提成 当然每个公司情况都不一样 很多猎头是不挣钱的 公司有成本线 超出成本线你
  • 【QT】Mac电脑(苹果电脑)Qt 菜单栏不显示的问题解决办法

    QT Mac电脑 苹果电脑 Qt 菜单栏不显示的问题解决办法 我所遇到的问题 并不是qt for mac 不显示菜单栏 而是菜单栏显示到Mac 菜单栏上去了 我想让Qt 菜单栏显示位置和win 系统下一样 不管是在纯代码环境和UI 设计环境
  • 水仙花数JAVA代码实现

    java代码实现求取水仙花数 水仙花数 Narcissistic number 也被称为超完全数字不变数 pluperfect digital invariant PPDI 自恋数 自幂数 阿姆斯壮数或阿姆斯特朗数 Armstrong nu
  • js 刷新页面但是不闪烁_Js历史

    1 Js是为了赶上java的潮流 把名字从LiveScript改为JavaScript 2 Js出现原因 最初的输入验证必须把表单数据发到服务端上才能验证 而最初的js是为了解决这个问题 3 Web浏览器只是js实现的可能的宿主环境之一 其
  • C - Candy Machine 二分

    传送门 题意 JB非常喜欢糖果 有一天 他发现了一台糖果机 里面有 N里面有糖果 看完机器的说明书后 他知道他可以选择一个子集 N糖果 每颗糖果都有一个甜味价值 JB 选择子集后 假设所选糖果的平均甜度值为 X 所有甜度值严格大于的糖果 X
  • 可以写进简历的软件测试电商项目(超详细版),不进来get一下?

    前言 说实话 在找项目的过程中 我下载过 甚至付费下载过 N多个项目 联系过很多项目的作者 但是绝大部分项目 在我看来 并不适合你拿来练习 它们或多或少都存在着 问题 比如 1 大部分项目是web项目 很难找到app项目 特别是有app安装
  • svn 服务器忽略文件夹,SVN忽略不提交文件夹

    层次分析模型 AHP 及其MATLAB实现 今天用将近一天的时间学习了层次分析模型 AHP 主要参考了一份pdf 这个网站 和暨南大学章老师的课件 现写出一些自己总结的要点 一 层次分析法的基本步骤 角度一 实际问题 分解 gt SQL S
  • spring 总结

    1 spring是什么 是一个开源的控制反转和面向切面的容器框架 2 控制反转就是应用本身不负责依赖对象的创建和维护 依赖对象的创建和维护是由外部容器负责的 这样控制权就由应用转移到了外部容器 控制权的转移就是所谓反转 3 依赖注入 在运行
  • 有头链表实现(C++描述)

    有头链表实现 include
  • PAT(Advanced Level)刷题指南 —— 第八弹

    一 1015 Reversible Primes 进制转换 质数判定 1 问题描述 不断给出两个数 N 和 D 直到输入负数终止 判断 N 是否为素数 并以 D 为基数倒转该数后 判断转化为 10进制 后还是不是素数 如果都满足 就输出Ye
  • [数值计算-19]:万能的任意函数的数值求导数方法

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 120378620 目录 第1章 前言
  • C++ inline内联函数详解

    函数是一个可以重复使用的代码块 CPU 会一条一条地挨着执行其中的代码 CPU 在执行主调函数代码时如果遇到了被调函数 主调函数就会暂停 CPU 转而执行被调函数的代码 被调函数执行完毕后再返回到主调函数 主调函数根据刚才的状态继续往下执行