C 和 C++ 中的内联函数
介绍
C++ 添加了 inline 关键字,可以为函数定义添加前缀,例如:
inline int max_int( int a, int b ) { return a > b ? a : b; }
给编译器一个“提示”,程序整体上可能会从函数内联中受益于性能。
已内联的函数在调用它的每个点都会扩展其代码,而不是执行以下正常的函数调用机制:
- 保存寄存器。
- 将参数值压入堆栈。
- 执行调用指令。
- 函数最终执行ret指令。
- 恢复寄存器。
对于非常小的函数,内联可以提高性能。 但就像大多数其他事情一样,也需要权衡。
内联关键字已向后移植到 C99,但要求略有不同 - 稍后会详细介绍。
与宏的区别
内联函数就像(并且旨在取代)类似函数的宏。 一般来说,这是一件好事,因为内联函数 是 函数,并且具有完整的函数语义,而不是仅由不理解 C 或 C++ 的预处理器完成的文本替换。
与 max_int() 函数等效的宏:
#define MAX_INT(A,B) A > B ? A : B /* bad implementation */
存在以下问题:
- 扩展参数,例如 MAX(n & 0xFF, 8),可能会导致错误的运算符优先级。
- 有副作用的参数,例如 MAX(n++, 8),可能有多个副作用。
- 定义时不对参数进行类型检查。
- 错误通常很冗长且难以阅读。
另外,一个宏:
- 可以修改它的参数(这通常不是你想要的)。
内联函数不存在这些问题,但可以产生相同的性能优势。 因此,请使用内联函数而不是类似函数的宏。
只是一个提示
如前所述,指定内联只是对编译器的一个“提示”,即程序总体上可能会从内联函数中受益于性能。 编译器可以随意忽略提示。
为什么? 因为在某些情况下,这要么不是一个好主意,要么是不可能的。 当满足以下任一条件时,函数要么不内联,要么通常不内联:
- 功能“太大”。
- 您通过函数指针调用该函数。
- 该函数是递归的。
- 该函数有一个循环。
可能还有其他原因。这一切都高度依赖于函数、其参数、编译器以及为其提供的任何选项。
如果编译器不能或选择不内联函数,它会不警告您它尚未这样做(默认情况下)。 一些编译器,例如 gcc,有一个 -Winline 选项,它会警告您并给出函数未内联的原因。
指定内联类似于指定寄存器的旧代码 - 它们都只是提示。
何时(以及何时不)内联
对于大多数函数来说,执行函数的大部分成本都在函数体中,而不是在函数调用机制中。 因此,为了使函数成为内联的良好候选者,它通常必须是:
- 足够小,使得函数调用机制的成本占主导地位。
- 用于性能真正重要的地方,例如紧密循环。
如有疑问,请分析您的代码。 使用内联不是神奇的“让我更快”关键字。 此外,过度使用内联可能会导致代码膨胀,从而进一步使程序的性能整体更差。
有关更多信息,请参阅内联疾病。
通常适合内联的函数包括:
- “单行代码”,例如“getters”和“setters”。
- 对其他函数调用的简单包装,为参数提供特定值或进行强制转换。
理想的内联函数既提高性能又减少代码大小。
但是,任何内联函数的一个警告是,如果其定义发生更改,则需要重新编译所有使用它的代码。
内联优化
如果内联函数实际上是由编译器内联的,那么,除了省略正常函数调用机制的代码之外,编译器还可以:
- Eliminate one or more function arguments completely whose values are constants via immediate addressing.
- Perform better optimizations spanning the code the function is inlined into that it normally couldn’t perform across function boundaries.
Inline Function Definition
In order for the compiler to be able to inline a function, it has to be able to “see” its definition (not just its declaration) in every .c or .cpp file it’s used in just like a macro. Hence, an inline function must be defined in a header file.
Normally, a function, like everything else, must have exactly one definition by adhering to the one definition rule (ODR). However, since the definition of an inline function is “seen” in multiple .c or .cpp files, the ODR is suspended for that function.
It is possible to have different definitions for inline functions having the same name, but this results in undefined behavior since the compiler has no way to check that every definition is the same.
To inline a function in C++, all you need do is prefix the function definition with inline — that’s it. The compiler and/or linker will automatically discard all but one definition from the final executable file for you.
However, to inline a function in C, you additionally must explicitly tell the compiler into what .o file to put the one definition in the event the compiler is either unable or unwilling to inline a function via extern inline.
For example, in exactly one .c file, you would declare a function like:
// util.c extern inline int max_int( int, int );
That tells the compiler to “put the one definition for max_int() into util.o.”
Alternatively in C, you can instead declare an inline function static also:
static inline int max_int( int a, int b ) { return a > b ? a : b; }
If you do this, then:
- You do not have to declare a function extern inline anywhere.
- However, if the compiler doesn’t inline a function, it will generate a definition in every .c file it’s included into again leading to code bloat.
- If the function has any static local variables, every definition will have distinct copies (that may or may not be what you want).
Conclusion
Inline functions, if used judiciously, can yield performance gains. Generally, only very small functions are good candidates for inlining.
Starting in C++11, inline functions can alternatively be declared constexpr, but that’s a story for another time.
References
- Linux kernel coding style, §15 The inline disease.
- Myth and reality about inline in C99.
- The Annotated C++ Reference Manual, Margaret A. Ellis & Bjarne Stroustrup, Addison-Wesley, 1990, ISBN 0-201-51459-1, §7.1.2 Function Specifiers, pp. 99–105.
以上是C 和 C++ 中的内联函数的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

C#和C 的历史与演变各有特色,未来前景也不同。1.C 由BjarneStroustrup在1983年发明,旨在将面向对象编程引入C语言,其演变历程包括多次标准化,如C 11引入auto关键字和lambda表达式,C 20引入概念和协程,未来将专注于性能和系统级编程。2.C#由微软在2000年发布,结合C 和Java的优点,其演变注重简洁性和生产力,如C#2.0引入泛型,C#5.0引入异步编程,未来将专注于开发者的生产力和云计算。

C#和C 的学习曲线和开发者体验有显着差异。 1)C#的学习曲线较平缓,适合快速开发和企业级应用。 2)C 的学习曲线较陡峭,适用于高性能和低级控制的场景。

C 学习者和开发者可以从StackOverflow、Reddit的r/cpp社区、Coursera和edX的课程、GitHub上的开源项目、专业咨询服务以及CppCon等会议中获得资源和支持。1.StackOverflow提供技术问题的解答;2.Reddit的r/cpp社区分享最新资讯;3.Coursera和edX提供正式的C 课程;4.GitHub上的开源项目如LLVM和Boost提升技能;5.专业咨询服务如JetBrains和Perforce提供技术支持;6.CppCon等会议有助于职业

C 通过第三方库(如TinyXML、Pugixml、Xerces-C )与XML交互。1)使用库解析XML文件,将其转换为C 可处理的数据结构。2)生成XML时,将C 数据结构转换为XML格式。3)在实际应用中,XML常用于配置文件和数据交换,提升开发效率。

静态分析在C 中的应用主要包括发现内存管理问题、检查代码逻辑错误和提高代码安全性。1)静态分析可以识别内存泄漏、双重释放和未初始化指针等问题。2)它能检测未使用变量、死代码和逻辑矛盾。3)静态分析工具如Coverity能发现缓冲区溢出、整数溢出和不安全API调用,提升代码安全性。

C 在现代编程中仍然具有重要相关性。1)高性能和硬件直接操作能力使其在游戏开发、嵌入式系统和高性能计算等领域占据首选地位。2)丰富的编程范式和现代特性如智能指针和模板编程增强了其灵活性和效率,尽管学习曲线陡峭,但其强大功能使其在今天的编程生态中依然重要。

使用C 中的chrono库可以让你更加精确地控制时间和时间间隔,让我们来探讨一下这个库的魅力所在吧。C 的chrono库是标准库的一部分,它提供了一种现代化的方式来处理时间和时间间隔。对于那些曾经饱受time.h和ctime折磨的程序员来说,chrono无疑是一个福音。它不仅提高了代码的可读性和可维护性,还提供了更高的精度和灵活性。让我们从基础开始,chrono库主要包括以下几个关键组件:std::chrono::system_clock:表示系统时钟,用于获取当前时间。std::chron

C 的未来将专注于并行计算、安全性、模块化和AI/机器学习领域:1)并行计算将通过协程等特性得到增强;2)安全性将通过更严格的类型检查和内存管理机制提升;3)模块化将简化代码组织和编译;4)AI和机器学习将促使C 适应新需求,如数值计算和GPU编程支持。
