inline关键字仅仅是对编译器的建议,编译器有权力决定一个函数是否在调用处嵌入。由于内联函数要在调用处展开,编译器必须能在每个调用处能看到该函数的定义,所以最好将函数实现放在头文件中(并且实如今类定义中的成员函数即使不加inline关键字也会自动成为内联函数)。在实现文件中该函数以前要加上inline关键字的方式是有问题的:若是调用的obj文件在函数定义以前生成,那么该处就没法嵌入内联函数了。若是普通函数须要成为内联函数,在定义时加上inline关键字。 c++
若是想要阻止某函数被内联,能够在函数体前加上 __attribute__((noinline)) 。 函数
有时函数是否原地展开与编译时指定的优先级有关,下面就是一个例子: 优化
inline int foo(int x) { spa
return x+42; 命令行
} 指针
int main(int argc, char* argv[]) c++11
{ 递归
printf("%d\n", foo(42)); 编译器
return 0; string
}
使用以下命令行获得汇编代码,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函数的参数。