C++中哪些函數不能聲明為inline?


inline關鍵字僅僅是對編譯器的建議,編譯器有權力決定一個函數是否在調用處嵌入。因為內聯函數要在調用處展開,編譯器必須能在每一個調用處能看到該函數的定義,因此最好將函數實現放在頭文件中(而且實現在類定義中的成員函數即便不加inline關鍵字也會自動成為內聯函數)。在實現文件中該函數之前要加上inline關鍵字的方式是有問題的:如果調用的obj文件在函數定義之前生成,那么該處就無法嵌入內聯函數了。如果普通函數需要成為內聯函數,在定義時加上inline關鍵字。

  1. 包含了遞歸、循環等結構的函數一般不會被內聯。
  2. 虛擬函數一般不會內聯,但是如果編譯器能在編譯時確定具體的調用函數,那么仍然會就地展開該函數。
  3. 如果通過函數指針調用內聯函數,那么該函數將不會內聯而是通過call進行調用。
  4. 構造和析構函數一般會生成大量代碼,因此一般也不適合內聯。
  5. 如果內聯函數調用了其他函數也不會被內聯。

如果想要阻止某函數被內聯,可以在函數體前加上 __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

//立即數21eax寄存器中的值(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函數的參數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM