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函數的參數。