【C++】内联函数(inline function)详解

🦄个人主页:小米里的大麦-CSDN博客

🎏所属专栏:C++_小米里的大麦的博客-CSDN博客

🎁代码托管:C++: 探索C++编程精髓,打造高效代码仓库 (gitee.com)

⚙️操作环境:Visual Studio 2022

目录

一、前言

语法: 在函数定义前加上关键字 inline。

二、内联函数的正确使用

三、容易犯的错误

错误1:内联函数体太大

错误2:递归函数内联

错误3:条件编译下的调试问题

四、内联函数的特性

实践建议

五、与宏定义的区别

六、代码示例总结

总结 

共勉


一、前言

内联函数是一种建议编译器在调用函数时,不使用普通的函数调用机制(如压栈、跳转等),而是将函数体直接嵌入到调用点。它的优点是可以减少函数调用的开销,特别是对于频繁调用的小函数。以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,以提升程序运行的效率。

语法: 在函数定义前加上关键字 inline

inline int Add(int x, int y) {return (x + y) * 10;
}
  • 优点

    • 避免了函数调用的开销(如压栈、跳转、返回等)。
    • 适用于短小、频繁调用的函数。
  • 缺点

    • 如果函数较大,频繁嵌入会增大代码体积,可能导致性能下降(因为内存缓存可能溢出)。
    • 递归函数不能内联,因为无法确定函数的调用次数。

注意:

  1. 适用于短小的频繁调用的函数
  2. inline对于编译器仅仅只是一个建议,最终是否成为inline,编译器自己决定
  3. 如果函数过长或者过于复杂,即使加了inline也会被编译器否决掉(主要代表过长函数、递归函数),多长算长:编译器决定,每个编译器不同,vs默认在10行左右。
  4. 默认debug模式下,inline不会起作用,否则不方便调试了

二、内联函数的正确使用

内联函数适用于短小且逻辑简单的函数,因为函数体直接嵌入到代码中能减少函数调用开销,但函数过大会增加可执行文件的体积。

  • 适用场景频繁调用的短小函数,例如数学运算或简单的判断逻辑:

inline int Multiply(int x, int y) {return x * y;
}

编译器的决策: 虽然可以将函数声明为 inline,但最终是否内联是由编译器决定的。比如在以下情况下编译器会忽略内联建议:

  • 函数太复杂或体积过大。
  • 递归函数。
  • 函数包含了switchfor循环等复杂逻辑。
inline int ComplexFunc(int x) {if (x == 0) return 0;int result = 1;for (int i = 1; i <= x; i++) {result *= i;}return result;
}
// 这个函数较复杂,可能不会被内联。

三、容易犯的错误

虽然内联函数看似简单,但在实际使用中,存在一些常见的错误。

错误1:内联函数体太大

如果内联函数的逻辑复杂或体积较大,编译器可能会拒绝内联,从而使其成为普通函数调用。大部分编译器对于内联函数的体积有隐式的限制。

inline void LargeFunction() {for (int i = 0; i < 100000; i++) {cout << i << endl;}
}
// 这个函数过于庞大,编译器可能不会内联。

错误2:递归函数内联

递归函数不适合内联,因为内联意味着将函数体直接替换到调用点,而递归意味着函数会调用自身,导致无限的展开。

inline int Recursive(int n) {if (n <= 1) return 1;return n * Recursive(n - 1);
}
// 递归函数无法内联,因为函数体会无限展开。

错误3:条件编译下的调试问题

在Debug模式下,编译器一般不会对函数进行内联,因为内联后函数调试变得复杂。为了方便调试,编译器通常在Release模式下才会进行内联优化。

但是优秀的编译器vs提供了debug下的查看方法:

如果在上述函数前增加inline关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的调用。

查看方式:

  1. 在release模式下,查看编译器生成的汇编代码中是否存在call Add
  2. 在debug模式下,需要对编译器进行设置,否则不会展开(因为debug模式下,编译器默认不会对代码进行优化,版本不同,对应的设置操作也不同)

四、内联函数的特性

  1. 空间换时间
    使用内联函数的主要目的是通过减少函数调用的开销,来提高程序的运行效率。编译器在编译阶段将函数体替换到函数调用处,避免了常规函数调用时的栈帧创建与销毁等操作。

    • 优点: 减少了函数调用的开销,程序的执行效率可能有所提高。
    • 缺点: 如果函数体积较大,会导致生成的目标文件变大,进而影响性能。
  2. 编译器只将inline当作建议
    关键的一点是,inline 并不是强制要求,编译器可以选择忽略这个建议。这意味着即使在代码中声明了某个函数为内联函数,编译器也可以根据实际情况决定是否将其内联。一般来说,对于较短小且频繁调用的函数,编译器才更倾向于内联处理。

    • 编译器通常不会对较长、包含递归调用或者复杂逻辑的函数进行内联优化。
  3. 内联函数和定义分离可能导致问题
    如果内联函数的声明和定义被分离到不同的文件中,可能会出现链接错误。这是因为内联函数没有函数地址,编译器无法找到相应的定义,从而导致链接器找不到函数实现。通常内联函数应该定义在头文件中,以便在不同的编译单元中直接替换。

实践建议

  • 控制函数规模: 尽量将内联函数的规模保持在合理的范围内,过大的函数会让编译器难以进行内联优化。
  • 不要滥用: 内联并不是适合所有函数,尤其是涉及复杂逻辑的函数,内联反而可能引起更多的问题。
  • 频繁调用的函数优先考虑: 如果某个函数被频繁调用且较为简单,考虑将其声明为内联函数,以获得性能提升。

五、与宏定义的区别

许多人在初学时会混淆内联函数和宏定义。两者有相似之处,但有几个重要的区别:

  • 宏定义:宏定义在预处理阶段展开,没有类型检查,容易出现隐患。例如:

#define Add(x, y) ((x) + (y))int main() {int a = 5, b = 10;cout << Add(a, b) << endl;  // 输出15cout << Add(a++, b++) << endl;  // 这里有副作用,结果可能不是预期的
}
  • 内联函数:内联函数与普通函数一样,具有类型安全、可以调试。使用时更加可靠和灵活。
inline int Add(int x, int y) {return x + y;
}int main() {int a = 5, b = 10;cout << Add(a, b) << endl;  // 输出15cout << Add(a++, b++) << endl;  // 正常处理,避免了宏的副作用
}

六、代码示例总结

下面是使用内联函数的正确示例,以及常见的错误对比:

  • 正确使用内联函数

inline int Add(int x, int y) {return x + y;
}int main() {for (int i = 0; i < 10000; i++) {cout << Add(i, i + 1) << endl;  // 频繁调用的短小函数,适合内联}return 0;
}
  • 递归函数错误示例
inline int Factorial(int n) {if (n <= 1) return 1;return n * Factorial(n - 1);  // 递归函数不适合内联
}
  • 宏定义与内联函数的对比
#define Multiply(x, y) (x * y)inline int MultiplyInline(int x, int y) {return x * y;
}int main() {int a = 5, b = 10;cout << Multiply(a++, b++) << endl;   // 宏定义有副作用cout << MultiplyInline(a++, b++) << endl;  // 内联函数避免了这种副作用return 0;
}

总结 

  • 内联函数适合短小且频繁调用的函数,避免了宏定义的副作用,具有类型检查和调试功能。
  • 编译器有最终决策权,不一定会根据 inline 关键字做内联优化,特别是在函数较大或较复杂时。
  • 避免对递归函数和大型函数使用 inline

共勉

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

学不会最短路问题?看这篇就够了

数据结构入门学习&#xff08;全是干货&#xff09;——图论问题之最短路径 1 最短路径问题概述 最短路径问题的定义 在一个网络&#xff08;图&#xff09;中&#xff0c;求解两个顶点之间所有路径中边的权值之和最小的路径。这条路径称为最短路径。 源点(Source)&#xff…

ClickHouse-Kafka Engine 正确的使用方式

Kafka 是大数据领域非常流行的一款分布式消息中间件&#xff0c;是实时计算中必不可少的一环&#xff0c;同时一款 OLAP 系统能否对接 Kafka 也算是考量是否具备流批一体的衡量指标之一。ClickHouse 的 Kafka 表引擎能够直接与 Kafka 系统对接&#xff0c;进而订阅 Kafka 中的 …

openEuler系统安装内网穿透工具实现其他设备公网环境远程ssh连接

目录 前言 1. 本地SSH连接测试 2. openEuler安装Cpolar 3. 配置 SSH公网地址 4. 公网远程SSH连接 5. 固定连接SSH公网地址 6. SSH固定地址连接测试 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊聊openEuler系统安装内网穿透工具实现其他…

深度学习之微积分预备知识点(2)

极限&#xff08;Limit&#xff09; 定义&#xff1a;表示某一点处函数趋近于某一特定值的过程&#xff0c;一般记为 极限是一种变化状态的描述&#xff0c;核心思想是无限靠近而永远不能到达 公式&#xff1a; 表示 x 趋向 a 时 f(x) 的极限。 知识点口诀解释极限的存在左…

语言RPA流程组件介绍--获取网页信息

&#x1f6a9;【组件功能】&#xff1a;获取浏览器中显示网页的网页标题、源代码、网址、编码等信息 配置预览 配置说明 获取 网页源代码/标题/网址/编码 iframe 支持T或# 若获取的信息是框架iframe中的信息&#xff0c;需要手动填写框架名称&#xff0c;框架使用方法:框架…

文档图像恢复

文档图像恢复是指通过技术手段对损坏或质量不佳的文档图像进行修复&#xff0c;以提高其可读性和可用性。这种修复可以包括去除图像的噪声、畸变、阴影、模糊等多种问题&#xff0c;使文档图像更清晰、易于阅读。 文档图像恢复通常使用各种图像处理技术&#xff0c;包括但不限…

一个基于Vue3 + Arco Design + Vite3 + Pinia开箱即用的高质量中后台管理系统(附源码)

前言 随着业务的发展与复杂性的增加&#xff0c;现有的中后台管理系统面临着越来越多的挑战&#xff0c;如开发效率低下、系统性能瓶颈、项目扩展性差等问题。这些问题不仅影响了开发者的日常工作&#xff0c;还可能成为项目长期发展的障碍。那么&#xff0c;是否有一款软件能…

LabVIEW提高开发效率技巧----利用第三方库和工具

LabVIEW开发不仅依赖于自身强大的图形化编程能力&#xff0c;还得益于其庞大的用户社区和丰富的第三方库。这些工具和库能够帮助开发者快速解决问题&#xff0c;提升开发效率&#xff0c;避免从头开始编写代码。 1. LabVIEW工具网络&#xff08;NI Tools Network&#xff09; …

一些硬件知识(二十二)

搅拌机的转子是裸露在外面的&#xff0c;因此有一个安全开关&#xff0c;当上杯放上去后会按压安全开关&#xff0c;这样可以启动转子&#xff0c;否则是无法启动转子的&#xff0c;所以有些设备不通电或者转子不动是因为安全开关损坏&#xff1a; 、如下图&#xff0c;装上杯子…

详细分析Spring的动态代理机制

文章目录 1. JDK动态代理和CGLIB动态代理的区别1.1 适用范围1.2 生成的代理类1.3 调用方式 2. 问题引入3. 创建工程验证 Spring 默认采用的动态代理机制3.1 引入 Maven 依赖3.2 UserController.java3.3 UserService.java3.4 UserServiceImpl.java&#xff08;save方法添加了Tra…

JAVA开源项目 房屋租赁系统 计算机毕业设计

本文项目编号 T 041 &#xff0c;文末自助获取源码 \color{red}{T041&#xff0c;文末自助获取源码} T041&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析5.4 用例设计 六、核…

Linux中使用cp命令的 -f 选项,但还是提醒覆盖的问题

问题&#xff1a; linux 在执行cp的命令的时候&#xff0c;就算是执行 cp -f 也还是会提醒是否要进行替换。 问题原因&#xff1a; 查看别名&#xff0c;alias命令&#xff0c;看到cp的别名为cp -i&#xff0c;那就是说cp本身就是自带覆盖提醒&#xff0c;就算我们加上-f 的…

CentOS中使用DockerCompose方式部署带postgis的postgresql(附kartoza/docker-postgis镜像下载)

场景 CentOS中使用Docker部署带postgis的postgresql&#xff1a; CentOS中使用Docker部署带postgis的postgresql_centos postgis插件在容器中如何安装-CSDN博客 上面使用Docker搜索和拉取kartoza/postgis时并没有任何限制。 当下如果不能科学上网时&#xff0c;大部分镜像源…

JavaEE: 创造无限连接——网络编程中的套接字

文章目录 Socket套接字TCP和UDP的区别有连接/无连接可靠传输/不可靠传输面向字节流/面向数据报全双工/半双工 UDP/TCP api的使用UDPDatagramSocketDatagramPacketInetSocketAddress练习 TCPServerSocketSocket练习 Socket套接字 Socket是计算机网络中的一种通信机制&#xff0…

《机器人SLAM导航核心技术与实战》第1季:第9章_视觉SLAM系统

视频讲解 【第1季】9.第9章_视觉SLAM系统-视频讲解 【第1季】9.1.第9章_视觉SLAM系统_ORB-SLAM2算法&#xff08;上&#xff09;-视频讲解 【第1季】9.1.第9章_视觉SLAM系统_ORB-SLAM2算法&#xff08;下&#xff09;-视频讲解 【第1季】9.2.第9章_视觉SLAM系统_LSD-SLAM算法…

项目集成 与封装

1.element-plus 硅谷甄选运营平台,UI组件库采用的element-plus&#xff0c;因此需要集成element-plus插件&#xff01;&#xff01;&#xff01; 官网地址:https://element-plus.gitee.io/zh-CN/ 由于是后台管理系统 所以我们全部引入 pnpm install element-plus import {…

Spring:项目中的统一异常处理和自定义异常

介绍异常的处理方式。在项目中&#xff0c;都会进行自定义异常&#xff0c;并且都是需要配合统一结果返回进行使用。 1.背景引入 &#xff08;1&#xff09;背景介绍 为什么要处理异常&#xff1f;如果不处理项目中的异常信息&#xff0c;前端访问我们后端就是显示访问失败的…

Trace纳米侦查无人机技术详解

纳米无人机&#xff0c;作为微型无人机的一种&#xff0c;通常指尺寸和重量都非常小的无人机&#xff0c;其重量一般不超过几百克&#xff0c;甚至更小。这类无人机由于体积小、重量轻&#xff0c;具备高度的隐蔽性和灵活性&#xff0c;在军事侦察、环境监测、搜救行动等领域具…

Linux文件IO(八)-文件共享

什么是文件共享&#xff1f;所谓文件共享指的是同一个文件&#xff08;譬如磁盘上的同一个文件&#xff0c;对应同一个 inode&#xff09;被多个独立的读写体同时进行 IO 操作。多个独立的读写体大家可以将其简单地理解为对应于同一个文件的多个不同的文件描述符&#xff0c;譬…

【吊打面试官系列-MySQL面试题】MySQL_fetch_array 和 MySQL_fetch_object 的区别是什么?

大家好&#xff0c;我是锋哥。今天分享关于【MySQL_fetch_array 和 MySQL_fetch_object 的区别是什么&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; MySQL_fetch_array 和 MySQL_fetch_object 的区别是什么&#xff1f; 以下是 MySQL_fetch_array 和 MySQL_fe…