惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

MongoDB | Blog
MongoDB | Blog
博客园 - 聂微东
Attack and Defense Labs
Attack and Defense Labs
WordPress大学
WordPress大学
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Spread Privacy
Spread Privacy
AI
AI
宝玉的分享
宝玉的分享
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
C
Cyber Attacks, Cyber Crime and Cyber Security
爱范儿
爱范儿
Help Net Security
Help Net Security
V
Visual Studio Blog
大猫的无限游戏
大猫的无限游戏
Forbes - Security
Forbes - Security
P
Privacy & Cybersecurity Law Blog
Project Zero
Project Zero
IT之家
IT之家
Hugging Face - Blog
Hugging Face - Blog
博客园 - 三生石上(FineUI控件)
S
SegmentFault 最新的问题
有赞技术团队
有赞技术团队
T
Troy Hunt's Blog
美团技术团队
T
Threatpost
K
Kaspersky official blog
V
V2EX
Scott Helme
Scott Helme
Vercel News
Vercel News
T
The Blog of Author Tim Ferriss
T
Tailwind CSS Blog
V
Vulnerabilities – Threatpost
Last Week in AI
Last Week in AI
PCI Perspectives
PCI Perspectives
Google Online Security Blog
Google Online Security Blog
Apple Machine Learning Research
Apple Machine Learning Research
Engineering at Meta
Engineering at Meta
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
I
InfoQ
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
酷 壳 – CoolShell
酷 壳 – CoolShell
GbyAI
GbyAI
L
LINUX DO - 最新话题
T
The Exploit Database - CXSecurity.com
L
LangChain Blog
S
Security @ Cisco Blogs
The Last Watchdog
The Last Watchdog
H
Hacker News: Front Page
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
TaoSecurity Blog
TaoSecurity Blog

方寸之間

修复 Arch Linux 的内核缺失问题 - 方寸之間 安装定制化 Vim - 方寸之間 树莓派配置旁路由过程记录 - 方寸之間 Cloudflare Tunnel 不完全上手指南 - 方寸之間 解除 New Bing 地区和浏览器限制的方法 - 方寸之間 FRP 上手教程 - 方寸之間 深入理解 Linux nohup 命令 - 方寸之間 C++智能指针是什么 - 方寸之間 PGP 工作原理详解 - 方寸之間 如何恢复 Windows EFI 分区 - 方寸之間 Scaleway IPV6 server 申请及使用攻略 - 方寸之間 Arch Linux 升级系统提示签名无效 - 方寸之間 Arch Linux 音响有杂音的解决办法 - 方寸之間 Arch Linux 如何切换内核 - 方寸之間 Django 项目时区更改错误的解决方案 - 方寸之間 超简单的 Arch Linux + Windows 双启动教程 - 方寸之間 使用 UFW 配置 Linux 防火墙 - 方寸之間 Git 中的一个特殊 hash - 方寸之間 深入理解数据库事务 - 方寸之間 CRLF 和 LF 之间的区别与联系 - 方寸之間 DNS 的更新是如何工作的? - 方寸之間 浅析 Linux 的 cron 命令 - 方寸之間 如何快速查看 github 代码库中早期 commits - 方寸之間 黑科技:使用 GitHub 搭建自己的短链接服务 - 方寸之間 Build site with Franklin.jl - 方寸之間 iwd 的使用教程 - 方寸之間 超好用的 UML 工具推荐 - 方寸之間 ArchLinux 安装配置笔记 (Updated) - 方寸之間 如何将 Julia 添加到 Jupyter Notebook - 方寸之間 费曼技巧:最好的学习方式 - 方寸之間 How to Create Linux Desktop Entry - 方寸之間 Make a TODO robot with Github Actions - 方寸之間 C++继承 多态 虚函数 - 方寸之間 C++ Static 关键字详解 - 方寸之間 C++ type conversion notes - 方寸之間 Solution for _CRT_SECURE_NO_WARNINGS error - 方寸之間 IR Homework - 方寸之間 Customize Ubuntu themes, icons and Shell - 方寸之間 恢复右键菜单的新建命令 - 方寸之間 Hugo+Github 搭建个人博客 - 方寸之間
C++ inline 关键字详解 - 方寸之間
Mercas · 2019-12-22 · via 方寸之間

C++ inline 关键字的学习笔记记录

当程序执行函数调用指令时,CPU 将存储该函数调用后指令的内存地址,将函数的参数复制到堆栈上,最后将控制权转移到指定的函数。然后,CPU 执行函数代码,将函数返回值存储在预定义的内存位置/寄存器中,并将控制权返回给调用函数。如果函数的执行时间少于从调用者函数到被调用函数(被调用者)的切换时间,则这可能会成为开销。对于大型函数和/或执行复杂任务的函数,与函数运行所花费的时间相比,函数调用的开销通常微不足道。但是,对于小型的常用功能,进行函数调用所需的时间通常比实际执行函数代码所需的时间多得多。对于小功能,由于小功能的执行时间少于切换时间,因此会产生开销。

C++提供了 inline 函数,以减少函数调用的开销。内联函数是在调用时在行中扩展的函数。调用内联函数时,将在内联函数调用时插入或替换内联函数的整个代码。替换由 C ++编译器在编译时执行。如果内联函数很小,则可以提高效率。

inline 函数看起来像函数,动作像函数,但是又比宏方便很多,调用 inline 函数省去了参数压栈、生成汇编语言的 CALL 调用、返回参数、执行 return 等过程所花费的额外开销。不过 inline 函数也有缺点:增加目标码的大小。因为 inline 函数的设计思想是对该函数的所有调用都用函数本身替换掉,和宏定义一样,但是比宏定义用起来要简单很多。而且 inline 的调试同样很难。

所以,inline 函数对于函数体积很小,却又频繁调用这样的函数来说,可能比函数调用产生的代码更少,但是效率却不可同日而语。有一点需要注意的是:**inline 只是对编译器提出一个申请,并不是强制命令。编译器可以忽略内联请求。**在以下情况下,编译器可能不会执行内联:

  • 如果函数包含循环。(对于 while,do-while)
  • 如果函数包含静态变量。
  • 如果函数是递归的。
  • 如果函数的返回类型不是 void,并且函数主体中不存在 return 语句。
  • 如果函数包含 switch 或 goto 语句。

inline 可以显示声明,也可以隐式声明。隐式声明方式是将函数定义在类定义之中(函数定义时没有 inline 关键字),编译器也会当作 inline 函数。另外,定义在类中的 friend 函数也会被隐式声明为 inline 函数。

显式声明的时候必须与函数定义体放在一起才能使函数成为内联,仅将 inline 放在函数声明前面不起任何作用。定义函数内联的语法为:

inline return-type function-name(parameters)

{

// function code

}

此外,inline 函数通常放在头文件中,因为大多数 build environments 在编译期进行 inlining,编译器必须知道函数什么样子才能把函数调用替换成函数本体。

大多数 virtual 函数不能 inlining:因为 virtual 知道运行时才知道调用哪个函数,而 inline 是在执行前进行替换。此外,编译器通常不会 inlining通过函数指针进行的调用,下面是一个实例:

inline void f() {...} // assume compilers are willing to inline calls to f

void (*pf)() = f; // pf points to f

...

f(); // this call will be inlined, because it's a "normal" call

pf(); // this call probably won't be, because it's through // a function pointer

内联函数的优点:

  1. 不会发生函数调用开销。
  2. 调用函数时,还节省了 push / pop 变量在栈上的开销。
  3. 它还节省了从函数返回调用的开销。
  4. 内联函数时,可以使编译器对函数主体执行特定于上下文的优化。对于正常的函数调用,这种优化是不可能的。通过考虑调用上下文和被调用上下文的流程可以获得其他优化。
  5. 内联函数可能对于嵌入式系统有用(如果很小),因为内联函数所产生的代码少于函数调用的前导和返回。

内联函数的缺点:

  1. 内联函数中添加的变量消耗了额外的寄存器,在内联函数之后,如果要使用寄存器的变量编号增加,则它们可能会增加寄存器变量资源利用的开销。这意味着当在函数调用点替换内联函数主体时,该函数使用的变量总数也会被插入。因此,将用于变量的寄存器数量也将增加。因此,如果函数内联后的变量数急剧增加,则肯定会导致寄存器利用率增加。
  2. 如果使用太多的内联函数,则由于重复执行相同的代码,二进制可执行文件的大小将很大。
  3. 过多的内联也会降低指令 Cache 命中率,从而降低了从高速缓存到主存储器的指令获取速度。
  4. 如果有人更改了内联函数中的代码,则内联函数可能会增加编译时间开销,然后必须重新编译所有调用位置,这是因为编译器将需要再次替换所有代码以反映更改,否则它将继续使用旧功能。
  5. 内联函数对于许多嵌入式系统可能没有用。因为在嵌入式系统中,代码大小比速度更重要。
  6. 内联函数可能会导致崩溃,因为内联可能会增加二进制可执行文件的大小。内存溢出会导致计算机性能下降。

Note:

  • 将大多数内联限制在小的,经常调用的函数中。这有助于调试和二进制可升级性,最大程度地减少潜在的代码膨胀,并最大程度地提高程序速度。
  • 不要仅仅因为函数模板出现在头文件中就声明 inline。

Reference

Effective C++

https://www.geeksforgeeks.org/inline-functions-cpp/