關於頭文件中的 static inline函數


轉載:https://blog.csdn.net/huanghui167/article/details/41346663

關於頭文件中的 static inline函數
 
    頭文件中常見static inline函數,於是思考有可能遇到的問題,如頭文件經常會被包含會不會產生很多副本?網上說法不一。於是自己驗證。 經過arm-none-eabi-gcc下測試后得出結論。
 
    inline 關鍵字實際上僅是 建議內聯並不強制內聯,gcc中O0優化時是不內聯的,即使是O2以上,如果該函數被作為函數指針賦值,那么他也不會內聯,也必須產生函數實體,以獲得該函數地址。經測試c文件中的僅inline函數即使Os優化也不內聯,因為沒有static,編譯認他是全局的,因此像普通函數一樣編譯了,本c文件也一樣通過 bl inline_func 這樣的方式調用,不像網上別人說的,本c會內聯,其他c文件則通過bl inline_func 方式。加入static 后則內聯了。(Os優化等級測試)
    所以在頭文件中用inline時務必加入static,否則當inline不內聯時就和普通函數在頭文件中定義一樣,當多個c文件包含時就會重定義。所以加入static代碼健壯性高,如果都內聯了實際效果上是一樣的。(gcc下驗證過O0級別includes.h中僅定義inline的函數,編譯失敗,Os編譯成功)
 
為什么要在頭文件中定義函數呢?
雖然知道了頭文件中用inline函數時要加入static,但是為什么要在頭文件中定義函數呢?
一些簡單的封裝接口函數,如 open() { vfs_open() } 僅僅是為了封裝一個接口,我們不希望耗費一次函數調用的時間,解決方法一是宏,但是作為接口,宏不夠清晰。那選擇inline,但是如果在c文件中寫
main.c
inline void open(void)
{
    vfs_open();
頭文件加聲明,外部要使用則不會內聯的,因為編譯器有個原則,以c文件為單位進行逐個編譯obj,每個c文件的編譯是獨立的,該c文件用到的外部函數都在編譯時預留一個符號,只有等到所有obj生成后鏈接時才給這些符號地址(鏈接腳本決定地址),所以其他c文件編譯時只會看到這個函數的聲明而無法知道她的實體,就會像普通函數一樣通過bl 一個函數地址,等鏈接的時候再填入該地址了,他做不到內聯展開。
所以要內聯則必須在每個用到它的c文件體現實體,那就只有在頭文件了, 所以會把這類希望全局使用又希望增加效率的函數實現在頭文件中static inline
 
static inline 的壞處
    因為inline 是C99才有的關鍵字,C89沒有,有部分編譯器不支持,或者部分支持,如支持__inline 或 __inline__等,所以我們一般會用一個宏定義inline 如:
#define INLINE    static inline
不支持inline時:
#define INLINE    static
    但是這樣如果編譯器不支持inline 即意味着之前 static inline的函數全部被修改為 static,在頭文件中寫static會有什么后果呢?
經過測試果然和我們想的一樣,每個c文件包含了該頭文件后全部都有了該函數副本。這無疑增大了很多代碼量。比如在include.h
這樣的大頭文件,幾乎每個c文件我們都會包含他,相當於每一C文件都會加入一個 static void func(void){...}  實體。如果是函數宏則不會有這種問題,函數宏是沒有實際代碼的,沒調用他時代碼不存在。這就是頭文件中用static inline 函數的壞處。但是可以通過優化解決,經過測試,O0優化下在頭文件中定義static 函數包含該頭文件的三個c文件的確都有了該函數,但是在Os優化下則只有調用了該函數的C文件才有實體。這是由編譯器對static函數的特性決定的。總之他的法則和我們想的一致,就是頭文件僅僅是單純的展開,而每個C獨立編譯,不會因為知道其他個C文件定義了該函數,這個c文件就把他當外部bl了。
 
 
 
關於c文件中的static函數
static函數除了文件內使用這個功能外,在優化上也有作用,static定義后如果該文件沒有函數調用他,那么他意味着沒有用,其他文件不可能調用,所以開優化就被優化掉了(已驗證),無優化時則還在。這點一定要注意中斷函數最好不要寫static,中斷函數如果沒有中斷服務函數的,即沒有被調用,static可能被優化,當然也不一定,因為沒有中間服務函數的獨立中斷服務函數必須在鏈接腳本體現,可能需要再鏈接腳本上加入KEEP參數應該就沒問題。
 
這里排除非gcc編譯器,如code worrior編譯器則不同,code worrior是定制型的,通過識別main函數,因此他有主函數枝干可以判斷哪些函數被調用,哪些是無用的,但是gcc不同,沒有所謂的main,所以三個c文件如果沒有static的函數,即使開優化也都會編譯出來,他並不知道哪些函數有用,哪些函數無用。而static后他就一定知道他沒被調用即無用,所以可以優化掉。
 
以上均指arm-none-eabi-gcc環境下測試的結論,其他編譯器有些不同。但是c語言的編譯規則還是以gcc為標准,即使其他編譯器有些不同我們平時編程時還是以這個為標准。
在codeworrior 編譯器上測試,即使0優化依然會把inline內聯,有main函數,沒有被主干調用的都是無用函數全部被標記UNUSED,不編譯,因此上面討論的當不支持inline時static函數實體過多的問題它也不存在。


免責聲明!

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



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