C++中哪些函数不能声明为inline?
inline关键字仅仅是对编译器的建议,编译器有权力决定一个函数是否在调用处嵌入。因为内联函数要在调用处展开,编译器必须能在每一个调用处能看到该函数的定义,因此最好将函数实现放在头文件中(而且实现在类定义中的成员函数即便不加inline关键字也会自动成为内联函数)。在实现文件中该函数之前要加上inline关键字的方式是有问题的:如果调用的obj文件在函数定义之前生成,那么该处就无法嵌入内联函数了。如果普通函数需要成为内联函数,在定义时加上inline关键字。
- 包含了递归、循环等结构的函数一般不会被内联。
- 虚拟函数一般不会内联,但是如果编译器能在编译时确定具体的调用函数,那么仍然会就地展开该函数。
- 如果通过函数指针调用内联函数,那么该函数将不会内联而是通过call进行调用。
- 构造和析构函数一般会生成大量代码,因此一般也不适合内联。
- 如果内联函数调用了其他函数也不会被内联。
如果想要阻止某函数被内联,可以在函数体前加上 __attribute__((noinline)) 。
有时函数是否原地展开与编译时指定的优先级有关,下面就是一个例子:
inline int foo(int x) {
return x+42;
}
int main(int argc, char* argv[])
{
printf("%d\n", foo(42));
return 0;
}
使用如下命令行得到汇编代码,g++ -S -Wall -DDEBUG -D_GNU_SOURCE -std=c++11 -I/usr/include test.cpp:
.section .text._Z3fooi,"axG",@progbits,_Z3fooi,comdat
.weak _Z3fooi
.type _Z3fooi, @function
_Z3fooi:
//rbp寄存器的值入栈,保存main函数的栈基地址
pushq %rbp
//将rsp寄存器的值写入到rbp寄存器,将main函数的栈顶指针赋予foo函数的栈底指针
movq %rsp, %rbp
//此时edi寄存器中的值是42,放入foo栈底向上4byte处
movl %edi, -4(%rbp)
//将42放入eax寄存器
movl -4(%rbp), %eax
//立即数21与eax寄存器中的值(42)相加
addl $21, %eax
//恢复main函数的栈底
popq %rbp
//返回
ret
.size _Z3fooi, .-_Z3fooi
.section .rodata
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
//rbp寄存器的值入栈,保存_start函数的栈基地址
pushq %rbp
//将rsp寄存器的值写入到rbp寄存器,将_start函数的栈顶指针赋予main函数的栈底指针
movq %rsp, %rbp
//rsp寄存器的值减去立即数16,即main函数的栈大小为16
subq $16, %rsp
//将edi寄存器中的值放入rbp寄存器中的值减4的位置(栈底向上4byte)
movl %edi, -4(%rbp)
//将rsi寄存器中的值放入rbp寄存器中的值减16的位置(栈底向上16byte)
movq %rsi, -16(%rbp)
//立即数42放入edi寄存器
movl $42, %edi
//调用函数foo
call _Z3fooi
//eax寄存器的值(63)放入esi寄存器
movl %eax, %esi
//LC0段地址放入edi寄存器
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
leave
ret
.size main, .-main
使用如下命令行生成汇编代码:g++ -S -O -Wall -DDEBUG -D_GNU_SOURCE -std=c++11 -I/usr/include test.cpp(增加了一个优化选项)
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%d\n"
.globl main
.type main, @function
main:
subq $8, %rsp
movl $63, %esi
movl $.LC0, %edi
call printf
movl $0, %eax
addq $8, %rsp
ret
.size main, .-main
可见foo函数并未被调用,而是直接将立即数63放入esi寄存器作为printf函数的参数。
C++中哪些函数不能声明为inline?的更多相关文章
- C++中哪些函数不能声明为virtual?
首先要明确,virtual是用于支持类多态的关键字,所以出现在类声明之外的地方都是错误的.由此可以断定下文的1. 普通函数(即非类成员函数)不能是virtual的,否则不能通过编译,virtual只能 ...
- C++ DLL中导出函数的声明的方法
定义: TESTDLLEXPORT_API int fnTestDllExport(void); TESTDLLEXPORT_API int fnTestCall(void); TESTDLLEXPO ...
- 在C++中调用DLL中的函数 (3)
1.dll的优点 代码复用是提高软件开发效率的重要途径.一般而言,只要某部分代码具有通用性,就可将它构造成相对独立的功能模块并在之后的项目中重复使用.比较常见的例子是各种应用程序框架,ATL.MFC等 ...
- 《APUE》中的函数整理
第1章 unix基础知识 1. char *strerror(int errnum) 该函数将errnum(就是errno值)映射为一个出错信息字符串,返回该字符串指针.声明在string.h文件中. ...
- DLL中导出函数的两种方式(dllexport与.def文件)
DLL中导出函数的声明有两种方式: 一种方式是:在函数声明中加上__declspec(dllexport): 另外一种方式是:采用模块定义(.def)文件声明,(.def)文件为链接器提供了有关被链接 ...
- 在C++中调用DLL中的函数(3)
1.dll的优点 代码复用是提高软件开发效率的重要途径.一般而言,只要某部分代码具有通用性,就可将它构造成相对独立的功能模块并在之后的项目中重复使用.比较常见的例子是各种应用程序框架,ATL.MFC等 ...
- (转)DLL中导出函数的两种方式(dllexport与.def文件)
DLL中导出函数的两种方式(dllexport与.def文件)http://www.cnblogs.com/enterBeijingThreetimes/archive/2010/08/04/1792 ...
- 【转】DLL中导出函数的两种方式(dllexport与.def文件)
DLL中导出函数的两种方式(dllexport与.def文件) DLL中导出函数的声明有两种方式: 一种方式是:在函数声明中加上__declspec(dllexport):另外一种方式是:采用模块定义 ...
- 为什么模板函数的声明和实现都放在.h文件中
当你不使用这个模板函数或模板类,编译器并不实例化它,当你使用时,编译器需要实例化它,因为编译器是一次只能处理一个编译单元,也就是一次处理一个cpp文件,所以实例化时需要看到该模板的完整定义.所以都放在 ...
随机推荐
- 第1次作业:使用Packet Tracer分析HTTP数据包
个人信息: • 姓名:李微微 • 班级:计算1811 • 学号:201821121001 一.摘要 本文将会描述使用Packet Tracer工具用到的网络结构 ...
- asp.net配置全局应用程序类 巧妙达到定时生成静态页面
//在项目里添加一个"全局应用程序类(Global Application Class)",在里面写这样的代码: public class Global : System.Web. ...
- Android四大组件之服务的两种启动方式详解
Service简单概述 Service(服务):是一个没有用户界面.可以在后台长期运行且可以执行操作的应用组件.服务可由其他应用组件启动(如:Activity.另一个service).此外,组件可以绑 ...
- 【linux】【jenkins】自动化运维三 整合gitlab、docker发布vue项目
由于工作需要,这里我先创建一个vue的工程. 1.首先安装好gitlab相关插件:GitLab.GitLab Hook.NodeJS 插件安装参考:https://www.cnblogs.com/jx ...
- vue报错:[Vue warn]: Do not use built-in or reserved HTML elements as component id: header
报错的信息大致是不要将内置或保留的HTML元素用作组件ID 解决的办法是修改name符合规范或者直接删除组件内的name属性.
- 夯实Java基础系列4:一文了解final关键字的特性、使用方法,以及实现原理
目录 final使用 final变量 final修饰基本数据类型变量和引用 final类 final关键字的知识点 final关键字的最佳实践 final的用法 关于空白final final内存分配 ...
- Java中Synchronized的优化原理
我们知道,从 JDK1.6 开始,Java 对 Synchronized 同步锁做了充分的优化,甚至在某些场景下,它的性能已经超越了 Lock 同步锁.那么就让我们来看看,它究竟是如何优化的. 原本的 ...
- Terminal MultipleXer---终端复用器tmux基本使用
Terminal MultipleXer---终端复用器tmux 使用场景:1.scp大文件 2:编译大文件 3:多窗口对比文件 1.安装tmux [root@localhost ~]# yum in ...
- FTP协议的主动模式和被动模式的区别
最近准备做一个<FtpCopy系列教程>,主要讲解Ftp协议主动模式和被动模式的区别.以及FTP服务器的安装部署,然后通过几个常用实例演示,详细讲解如何使用FtpCopy进行数据自动备份. ...
- git checkout 提示 “error: The following untracked working tree files would be overwritten by checkout” 解决
问题描述 Windows 或者 macOS 操作系统中,文件名是不区分大小写的.对于已经提交到仓库中的文件修改文件名的大小写,然后又用 git rm 命令将老文件从 Git 仓库删除掉,并保存下新的文 ...