”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > C 和 C++ 中的内联函数

C 和 C++ 中的内联函数

发布于2024-11-08
浏览:412

Inline Functions in C and C

介绍

C添加了inline关键字,可以为函数定义添加前缀,如:

inline int max_int( int a, int b ) {
    return a > b ? a : b;
}

给编译器一个“提示”,程序总体上可能会从内联函数.

中受益

内联的函数在调用它的每个点都扩展了其代码,而不是执行正常的函数调用机制:

  • 保存寄存器。
  • 将参数值压入堆栈。
  • 执行调用指令。
  • 该函数最终执行ret指令。
  • 恢复寄存器。

对于非常小的函数,内联可以带来性能提升。 但就像大多数其他事情一样,也需要权衡。

inline 关键字被向后移植到 C99,但要求略有不同 - 稍后会详细介绍。

与宏的区别

内联函数类似于(并且旨在替换)类似函数的宏。 一般来说,这是一件好事,因为内联函数 函数,并且具有完整的函数语义,而不是仅由不理解 C 或 C 的预处理器完成的文本替换。

与 max_int() 函数等效的宏:

#define MAX_INT(A,B)  A > B ? A : B  /* bad implementation */

存在以下问题:

  • 扩展参数,例如 MAX(n & 0xFF, 8),可能会导致错误的运算符优先级。
  • 具有副作用的参数,例如 MAX(n , 8),可以有多个副作用。
  • 定义时没有对参数进行类型检查。
  • 错误通常很冗长且难以阅读。

另外还有一个宏:

  • 可以修改其参数(这通常是不是您想要的)。

内联函数不存在这些问题,但可以产生相同的性能优势。 因此,请使用内联函数而不是类似函数的宏。

只是一个提示

如上所述,指定内联只是对编译器的一个“提示”,即程序总体上可能会从内联函数中受益于性能。 编译器可以随意忽略提示。

为什么? 因为在某些情况下,这要么不是一个好主意,要么是不可能的。 当满足以下任一条件时,函数要么不内联,要么通常不内联:

  • 功能“太大”。
  • 您通过函数指针调用该函数。
  • 该函数是递归的。
  • 该函数有一个循环。

可能还有其他原因。这一切都高度依赖于函数、其参数、编译器以及为其提供的任何选项。

如果编译器不能或选择不内联函数,它会 not 警告您它尚未这样做(默认情况下)。 一些编译器,例如 gcc,有一个 -Winline 选项,它会警告您并给出函数未内联的原因。

指定内联类似于指定寄存器的旧代码 - 它们都只是提示。

何时(以及何时不)内联

对于大多数函数,执行函数的大部分成本都在函数体中,而不是在函数调用机制中。 因此,为了使函数成为内联的良好候选者,它通常必须是:

  • 足够小,以便函数调用机制的成本占主导地位。
  • 用于性能真正重要的地方,例如紧密循环中。

如有疑问,请分析您的代码。 使用内联不是一个神奇的“让我更快”关键字。 此外,过度使用内联可能会导致代码膨胀,从而进一步使程序的性能整体更差

更多信息,请参阅内联疾病。

通常适合内联的函数包括:

  • “单行词”,例如“getters”和“setters”。
  • 对其他函数调用的简单包装,这些函数为参数提供特定值或进行强制转换。

理想的内联函数两者都提高了性能并且减少了代码大小。

但是,任何内联函数的一个警告是,如果其定义发生更改,则需要重新编译所有使用它的代码。

内联优化

如果内联函数实际上是由编译器内联的,那么,除了省略正常函数调用机制的代码之外,编译器还可以:

  • 通过立即寻址完全消除一个或多个值为常量的函数参数。
  • 跨函数内联的代码执行更好的优化,而它通常无法跨函数边界执行。

内联函数定义

为了使编译器能够内联函数,它必须能够在使用它的每个 .c 或 .cpp 文件中“看到”它的 定义(不仅仅是它的声明)就像宏一样。 因此,内联函数必须在头文件中定义

通常,函数与其他所有函数一样,必须遵循单一定义规则 (ODR),具有 精确的 定义。 但是,由于内联函数的定义在多个 .c 或 .cpp 文件中“可见”,因此该函数的 ODR 被暂停。

具有相同名称的内联函数可以有不同的定义,但这会导致未定义的行为,因为编译器无法检查每个定义是否相同。

要在 C 中内联函数,您所需要做的就是在函数定义前面加上 inline 前缀 - 就是这样。 编译器和/或链接器将自动为您丢弃最终可执行文件中除一个定义之外的所有内容。

但是,要在 C 中内联函数,您还必须 显式告诉编译器在哪个 .o 文件中放置一个定义,以防编译器无法或不愿意通过以下方式内联函数:外部内联。

例如,在

恰好一个.c文件中,您可以声明一个如下函数:

//util.c 外部内联 int max_int( int, int );
// util.c
extern inline int max_int( int, int );
这告诉编译器“将 max_int() 的一个定义放入 util.o。”

或者在 C 中,您也可以将内联函数声明为静态:


静态内联 int max_int( int a, int b ) { 返回 a > b ?甲:乙; }
// util.c
extern inline int max_int( int, int );
如果您这样做,那么:

    你确实
  • 必须在任何地方声明一个外部内联函数。
  • 但是,如果编译器不内联函数,它将在
  • 每个 .c 文件中生成一个定义,并将其再次包含到其中,从而导致代码膨胀。
  • 如果函数有任何静态局部变量,则每个定义都将具有
  • 不同的副本(这可能是也可能不是您想要的)。
结论

内联函数如果使用得当,可以带来性能提升。 一般来说,只有

非常小的函数适合内联。

从 C 11 开始,内联函数也可以声明为 constexpr,但这是另一个故事了。

参考

    Linux 内核编码风格,§15 内联疾病。
  • 关于 C99 中内联的神话与现实。
  • 带注释的 C 参考手册,Margaret A. Ellis 和 Bjarne Stroustrup,Addison-Wesley,1990,ISBN 0-201-51459-1,§7.1.2 99–105.
版本声明 本文转载于:https://dev.to/pauljlucas/inline-functions-in-c-and-c-2040如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 初级后端开发人员寻求无偿工作以获得经验
    初级后端开发人员寻求无偿工作以获得经验
    大家好, 我叫 Harith,是一名初级后端开发人员。我对使用 Python 和 Django 框架进行 Web 开发非常感兴趣。我希望通过为开源项目做出贡献来获得更多实践经验。 我提供什么: 愿意在没有任何报酬的情况下参与项目,因为我的主要目标是提高我的技能和拓宽我的知识。 了解 Django 和...
    编程 发布于2024-11-08
  • 如何在 Python 中检查列表是否共享任何项目?
    如何在 Python 中检查列表是否共享任何项目?
    在 Python 中测试列表是否共享任何项目简介在 Python 中处理多个列表时,通常需要确定是否有元素重叠在这些列表之间。这是各种数据分析和操作任务的基本操作。简答在 Python 中测试列表重叠的推荐方法是利用 not set(a).isdisjoint(b ) 表达。它为此任务提供了一种普遍...
    编程 发布于2024-11-08
  • Node.js 中与 WebSockets 和 Socket.IO 的实时通信
    Node.js 中与 WebSockets 和 Socket.IO 的实时通信
    现代 Web 应用程序通常需要实时通信,无论是聊天系统、实时更新、协作编辑还是通知。传统的 HTTP 通信不足以满足实时应用程序的需要,因为它依赖于请求-响应模式。这就是 WebSockets 发挥作用的地方,它允许服务器和客户端之间进行全双工通信。 在本文中,我们将探讨: WebSocket 是什...
    编程 发布于2024-11-08
  • 如何解决使用 JavaScript 更改 iframe src 的问题
    如何解决使用 JavaScript 更改 iframe src 的问题
    使用 JavaScript 更改 iframe src:疑难解答单击单选按钮时更改 iframe 的 src 属性时遇到问题。要纠正此问题,必须检查代码以确定确切的原因。一个可能的问题是括号的错误使用。在您的代码中,该行:document.getElementById['calendar'].src...
    编程 发布于2024-11-08
  • 为什么 `window.onscroll` 在 iPhone/iPad 上不起作用?
    为什么 `window.onscroll` 在 iPhone/iPad 上不起作用?
    在 iPhone/iPad 上使用滚动事件捕获事件尝试在 iPad 上捕获滚动事件时,故障排除工作揭示了常见的方法例如 window.onscroll 和 document.onscroll 无法触发所需的响应。理解 iOS 上的事件处理设备iPhoneOS 事件处理机制与传统桌面浏览器不同。在连续...
    编程 发布于2024-11-08
  • 从开发人员到审阅者:初级开发人员审阅数据库查询的清单
    从开发人员到审阅者:初级开发人员审阅数据库查询的清单
    作为开发人员,提供高质量的代码至关重要,这些代码不仅具有功能性,而且还针对性能进行了优化。在开发人员领域的三年里,我从一名实践开发人员转变为审阅者角色。我在审核过程中关注的关键领域之一是数据库查询优化。 为什么关注数据库查询? 数据库查询可以显着影响应用程序的性能。编写得好的查询可以有效地获取数据,...
    编程 发布于2024-11-08
  • Mockito 是最好的 Java 模拟框架吗?  对其优缺点的综合评价。
    Mockito 是最好的 Java 模拟框架吗? 对其优缺点的综合评价。
    最佳 Java 模拟框架:Mockito在 Java 中,制作模拟对象对于有效的单元测试至关重要。鉴于选择过多,为此目的确定最佳框架可能会令人畏惧。本文评估了最突出的选择之一 Mockito,重点介绍了它的优点和缺点。Mockito 因其用户友好的语法而脱颖而出,使其易于开发人员使用。其简化方法针对...
    编程 发布于2024-11-08
  • 如何可靠地获取当前运行的Python文件的路径?
    如何可靠地获取当前运行的Python文件的路径?
    如何获取当前执行的Python文件的路径问题:确定当前运行的Python文件的路径可能很麻烦,特别是当遇到在特定场景下证明不可靠的方法时。其中包括从另一个脚本或在 IDLE 或 Mac OS X v10.6 等特定环境中启动执行的实例。解决方案:通用获取当前执行的 Python 的文件路径文件,采用...
    编程 发布于2024-11-08
  • Stack Overflow 如何创建这些信息丰富的弹出消息?
    Stack Overflow 如何创建这些信息丰富的弹出消息?
    复制 Stack Overflow 的弹出消息功能您可能已经注意到 Stack Overflow 上出现的时尚且内容丰富的弹出消息。这些消息为用户提供了有价值的通知和指导,您可能想知道如何在自己的网站上实现类似的功能。Stack Overflow 利用 HTML、CSS 和 JavaScript 的...
    编程 发布于2024-11-08
  • 为什么 Python 中没有元组理解?
    为什么 Python 中没有元组理解?
    理解 Python 中元组推导式的缺失在 Python 编程语言中,列表推导式和字典推导式提供了生成结构化数据的有效方法。然而,缺乏元组理解是一个异常现象。本文深入探讨了这一遗漏背后的原因。元组不变性是原因的假设并不成立。元组确实是不可变的,但这个属性并不妨碍它们在推导式中构建。问题的关键在于 Py...
    编程 发布于2024-11-08
  • 如何使用 VLC 模块在 Python 中播放 MP3 歌曲?
    如何使用 VLC 模块在 Python 中播放 MP3 歌曲?
    使用 Python 播放 MP3 歌曲使用正确的工具,在 Python 中播放 MP3 歌曲可以非常简单。错误的做法:尝试使用wave模块打开MP3文件,如下图所示不推荐:import wave w = wave.open("e:/LOCAL/Betrayer/Metalik Klinik...
    编程 发布于2024-11-08
  • 如何为Apache PHP应用程序配置环境变量?
    如何为Apache PHP应用程序配置环境变量?
    Apache PHP 应用程序的环境变量配置开发依赖环境变量的 PHP 应用程序时,必须清楚地了解如何配置环境变量使用 Apache 时设置这些变量。本文旨在提供有关配置可在 PHP 中访问的环境变量的指导,确保 Web 应用程序的正确运行。具体来说,为同一服务器中的各个域配置单独的环境变量是一种常...
    编程 发布于2024-11-08
  • 如何从 Activity 访问 ViewPager 片段方法?
    如何从 Activity 访问 ViewPager 片段方法?
    从 Activity 访问 ViewPager Fragment 方法许多移动应用程序使用片段,即代表模块化屏幕部分的独立组件。使用视图分页器管理多个片段可实现流畅的导航和页面动画。有时,开发人员需要在片段中执行特定操作以响应外部事件,例如用户在视图寻呼机上滑动。然而,实现此功能可能会遇到某些挑战。...
    编程 发布于2024-11-08
  • 如何在 Python 中按列值对散点图着色?
    如何在 Python 中按列值对散点图着色?
    按列值对散点图着色在 Python 中,Matplotlib 库提供了多种自定义散点图美观的方法。一项常见任务是根据特定列中的值分配颜色。Seaborn 集成一种解决方案是利用基于 Matplotlib 构建的 Seaborn 库。 Seaborn 提供 sns.relplot 和 sns.Face...
    编程 发布于2024-11-08
  • 为什么 fmt.Printf 显示负整数的二进制表示与 Go 中预期的不同?
    为什么 fmt.Printf 显示负整数的二进制表示与 Go 中预期的不同?
    二进制补码和 fmt.Printf:解开二进制表示之谜处理有符号整数时,计算机使用二进制补码来表示负值。这与典型的二进制表示不同,其中符号由单独的位指示。例如,在二进制补码中,整数 -5 表示为 1111 1011。但是,使用 fmt.Printf 打印二进制表示形式可能会产生意外结果。例如,以下代...
    编程 发布于2024-11-08

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3