我們都知道inline是用來聲明內聯函數的關鍵字。不過在嵌入式應用中,對於ARM片子,采用GCC編譯器,往往有2種聲明內聯函數關鍵字:inline和__attribute __((always_inline)),它們有什么區別呢?
對編譯器
1. inline : 建議編譯器內聯,實際是否內聯由編譯器決定(根據優化等級);
2. __attribute __((always_inline)):強制編譯器將函數當做內聯函數;
意義
優勢:
1.內聯函數擁有更快的調用速度(減少了函數調用);
2.可以實現把函數代碼集成到調用者處;
3.有利於編譯器優化代碼,減少代碼量(取決於具體情況);
缺點:可能導致代碼量膨脹(取決於具體情況);
用法
1.inline
__inline__是ISO C90的寫法
寫法1
inline int foo(int *a)
{
return (*a) ++;
}
寫法2
inline foo(int *a); int foo(int *a) { return (*a) ++; }
2. __attribute __((always_inline)) 或者_attribute__ ((__gnu_inline__))
適用於gcc編譯器(參考gcc 參考手冊)
inline void foo(int a) __attribute __((always_inline));
或者
__attribute __ ((always_inline)) void foo(int a);
static , extern與inline
static inline 表示只會是當前模塊才會調用這個內聯函數;
non-static inline 表示可能有其他模塊會調用這個內聯函數,別的模塊也不能定義這個函數。
如果函數定義中,同時指定inline和extern,該定義僅用於內聯。函數也不會獨自編譯(因為已經被認為可能會被其他模塊調用,會在每個調用處內聯、編譯)
與內聯相關的所有函數屬性
何時內聯?
關於函數屬性 always_inline,一般inline只有特定優化等級才會內聯。而對於聲明always_inline的函數,會一直保持內聯,而且不論有沒有指定優化等級。
注意declare和define/definition的區別: declare表示聲明,definition表示定義(分配存儲空間);
gnu_inline在gcc 4.1.3以后版本才可用, __GNUC_GNU_INLINE__ or __GNUC_STDC_INLINE__任一已定義才可用。
該屬性應該用於同時聲明為inline的函數。如果一個函數聲明為extern,該定義僅用於inline。用gnu_inline修飾的函數,不會編譯成獨立函數,意思是一定會編譯成內聯函數。
可以用在這種情況,頭文件聲明加上該函數屬性,而庫文件(或.c文件)包含一個副本,但無需extern,頭文件的gnu_inline也會引起內聯。
如果一個函數既不是extern,也不是static,函數被編譯成一個獨立函數,也會盡可能被內聯。
不會內聯的情況
關於inline(內聯) 正如gcc手冊描述的那樣(別的編譯器需要查看具體的手冊),gcc 不會真正內聯任何函數,如果使用了 "-fno-inline" 選項,或者如果使用了 "-O0"(優化等級)。當然除此之外,gcc還有其他多種情況不會真正內聯函數。
"-Winline" 選項
阻止內聯
阻止inline,可以使用函數屬性 __noinline__ 。即使使用了建議inline關鍵字修飾,__noinline__ 也會阻止真正內聯。
__attribute__((__noinline__)) inline static int f(int a, int b) { return (a + b); } main() { // .. f(1,2) ; // 該處函數不會真正內聯 }
如何判斷是否內聯?
可以通過查看生成的匯編代碼,比如.lst文件,assembly文件。函數調用往往包含入棧、出棧的操作(匯編指令),而內聯函數沒有這些。
參考
1. gcc參考手冊(Using the GNU Compiler Collection For gcc version 4.9.3 );