关于头文件中的 static inline函数
生活随笔
收集整理的這篇文章主要介紹了
关于头文件中的 static inline函数
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
關(guān)于頭文件中的 static inline函數(shù)
頭文件中常見static inline函數(shù),于是思考有可能遇到的問題,如頭文件經(jīng)常會被包含會不會產(chǎn)生很多副本?網(wǎng)上說法不一。于是自己驗證。經(jīng)過arm-none-eabi-gcc下測試后得出結(jié)論。
inline 關(guān)鍵字實際上僅是建議內(nèi)聯(lián)并不強(qiáng)制內(nèi)聯(lián),gcc中O0優(yōu)化時是不內(nèi)聯(lián)的,即使是O2以上,如果該函數(shù)被作為函數(shù)指針賦值,那么他也不會內(nèi)聯(lián),也必須產(chǎn)生函數(shù)實體,以獲得該函數(shù)地址。經(jīng)測試c文件中的僅inline函數(shù)即使Os優(yōu)化也不內(nèi)聯(lián),因為沒有static,編譯認(rèn)他是全局的,因此像普通函數(shù)一樣編譯了,本c文件也一樣通過 bl inline_func 這樣的方式調(diào)用,不像網(wǎng)上別人說的,本c會內(nèi)聯(lián),其他c文件則通過bl inline_func 方式。加入static 后則內(nèi)聯(lián)了。(Os優(yōu)化等級測試) 所以在頭文件中用inline時務(wù)必加入static,否則當(dāng)inline不內(nèi)聯(lián)時就和普通函數(shù)在頭文件中定義一樣,當(dāng)多個c文件包含時就會重定義。所以加入static代碼健壯性高,如果都內(nèi)聯(lián)了實際效果上是一樣的。(gcc下驗證過O0級別includes.h中僅定義inline的函數(shù),編譯失敗,Os編譯成功)
為什么要在頭文件中定義函數(shù)呢? 雖然知道了頭文件中用inline函數(shù)時要加入static,但是為什么要在頭文件中定義函數(shù)呢? 一些簡單的封裝接口函數(shù),如 open() { vfs_open() } 僅僅是為了封裝一個接口,我們不希望耗費一次函數(shù)調(diào)用的時間,解決方法一是宏,但是作為接口,宏不夠清晰。那選擇inline,但是如果在c文件中寫 main.c inline void open(void) { vfs_open(); }? 頭文件加聲明,外部要使用則不會內(nèi)聯(lián)的,因為編譯器有個原則,以c文件為單位進(jìn)行逐個編譯obj,每個c文件的編譯是獨立的,該c文件用到的外部函數(shù)都在編譯時預(yù)留一個符號,只有等到所有obj生成后鏈接時才給這些符號地址(鏈接腳本決定地址),所以其他c文件編譯時只會看到這個函數(shù)的聲明而無法知道她的實體,就會像普通函數(shù)一樣通過bl 一個函數(shù)地址,等鏈接的時候再填入該地址了,他做不到內(nèi)聯(lián)展開。 所以要內(nèi)聯(lián)則必須在每個用到它的c文件體現(xiàn)實體,那就只有在頭文件了,所以會把這類希望全局使用又希望增加效率的函數(shù)實現(xiàn)在頭文件中static inline。
static inline 的壞處 因為inline 是C99才有的關(guān)鍵字,C89沒有,有部分編譯器不支持,或者部分支持,如支持__inline 或 __inline__等,所以我們一般會用一個宏定義inline 如: #define INLINE ? ?static inline 不支持inline時: #define INLINE ? ?static 但是這樣如果編譯器不支持inline 即意味著之前 static inline的函數(shù)全部被修改為 static,在頭文件中寫static會有什么后果呢? 經(jīng)過測試果然和我們想的一樣,每個c文件包含了該頭文件后全部都有了該函數(shù)副本。這無疑增大了很多代碼量。比如在include.h 這樣的大頭文件,幾乎每個c文件我們都會包含他,相當(dāng)于每一C文件都會加入一個 static void func(void){...} ?實體。如果是函數(shù)宏則不會有這種問題,函數(shù)宏是沒有實際代碼的,沒調(diào)用他時代碼不存在。這就是頭文件中用static inline 函數(shù)的壞處。但是可以通過優(yōu)化解決,經(jīng)過測試,O0優(yōu)化下在頭文件中定義static 函數(shù)包含該頭文件的三個c文件的確都有了該函數(shù),但是在Os優(yōu)化下則只有調(diào)用了該函數(shù)的C文件才有實體。這是由編譯器對static函數(shù)的特性決定的。總之他的法則和我們想的一致,就是頭文件僅僅是單純的展開,而每個C獨立編譯,不會因為知道其他個C文件定義了該函數(shù),這個c文件就把他當(dāng)外部bl了。
關(guān)于c文件中的static函數(shù) static函數(shù)除了文件內(nèi)使用這個功能外,在優(yōu)化上也有作用,static定義后如果該文件沒有函數(shù)調(diào)用他,那么他意味著沒有用,其他文件不可能調(diào)用,所以開優(yōu)化就被優(yōu)化掉了(已驗證),無優(yōu)化時則還在。這點一定要注意中斷函數(shù)最好不要寫static,中斷函數(shù)如果沒有中斷服務(wù)函數(shù)的,即沒有被調(diào)用,static可能被優(yōu)化,當(dāng)然也不一定,因為沒有中間服務(wù)函數(shù)的獨立中斷服務(wù)函數(shù)必須在鏈接腳本體現(xiàn),可能需要再鏈接腳本上加入KEEP參數(shù)應(yīng)該就沒問題。
這里排除非gcc編譯器,如code worrior編譯器則不同,code worrior是定制型的,通過識別main函數(shù),因此他有主函數(shù)枝干可以判斷哪些函數(shù)被調(diào)用,哪些是無用的,但是gcc不同,沒有所謂的main,所以三個c文件如果沒有static的函數(shù),即使開優(yōu)化也都會編譯出來,他并不知道哪些函數(shù)有用,哪些函數(shù)無用。而static后他就一定知道他沒被調(diào)用即無用,所以可以優(yōu)化掉。
以上均指arm-none-eabi-gcc環(huán)境下測試的結(jié)論,其他編譯器有些不同。但是c語言的編譯規(guī)則還是以gcc為標(biāo)準(zhǔn),即使其他編譯器有些不同我們平時編程時還是以這個為標(biāo)準(zhǔn)。 在codeworrior 編譯器上測試,即使0優(yōu)化依然會把inline內(nèi)聯(lián),有main函數(shù),沒有被主干調(diào)用的都是無用函數(shù)全部被標(biāo)記UNUSED,不編譯,因此上面討論的當(dāng)不支持inline時static函數(shù)實體過多的問題它也不存在。
inline 關(guān)鍵字實際上僅是建議內(nèi)聯(lián)并不強(qiáng)制內(nèi)聯(lián),gcc中O0優(yōu)化時是不內(nèi)聯(lián)的,即使是O2以上,如果該函數(shù)被作為函數(shù)指針賦值,那么他也不會內(nèi)聯(lián),也必須產(chǎn)生函數(shù)實體,以獲得該函數(shù)地址。經(jīng)測試c文件中的僅inline函數(shù)即使Os優(yōu)化也不內(nèi)聯(lián),因為沒有static,編譯認(rèn)他是全局的,因此像普通函數(shù)一樣編譯了,本c文件也一樣通過 bl inline_func 這樣的方式調(diào)用,不像網(wǎng)上別人說的,本c會內(nèi)聯(lián),其他c文件則通過bl inline_func 方式。加入static 后則內(nèi)聯(lián)了。(Os優(yōu)化等級測試) 所以在頭文件中用inline時務(wù)必加入static,否則當(dāng)inline不內(nèi)聯(lián)時就和普通函數(shù)在頭文件中定義一樣,當(dāng)多個c文件包含時就會重定義。所以加入static代碼健壯性高,如果都內(nèi)聯(lián)了實際效果上是一樣的。(gcc下驗證過O0級別includes.h中僅定義inline的函數(shù),編譯失敗,Os編譯成功)
為什么要在頭文件中定義函數(shù)呢? 雖然知道了頭文件中用inline函數(shù)時要加入static,但是為什么要在頭文件中定義函數(shù)呢? 一些簡單的封裝接口函數(shù),如 open() { vfs_open() } 僅僅是為了封裝一個接口,我們不希望耗費一次函數(shù)調(diào)用的時間,解決方法一是宏,但是作為接口,宏不夠清晰。那選擇inline,但是如果在c文件中寫 main.c inline void open(void) { vfs_open(); }? 頭文件加聲明,外部要使用則不會內(nèi)聯(lián)的,因為編譯器有個原則,以c文件為單位進(jìn)行逐個編譯obj,每個c文件的編譯是獨立的,該c文件用到的外部函數(shù)都在編譯時預(yù)留一個符號,只有等到所有obj生成后鏈接時才給這些符號地址(鏈接腳本決定地址),所以其他c文件編譯時只會看到這個函數(shù)的聲明而無法知道她的實體,就會像普通函數(shù)一樣通過bl 一個函數(shù)地址,等鏈接的時候再填入該地址了,他做不到內(nèi)聯(lián)展開。 所以要內(nèi)聯(lián)則必須在每個用到它的c文件體現(xiàn)實體,那就只有在頭文件了,所以會把這類希望全局使用又希望增加效率的函數(shù)實現(xiàn)在頭文件中static inline。
static inline 的壞處 因為inline 是C99才有的關(guān)鍵字,C89沒有,有部分編譯器不支持,或者部分支持,如支持__inline 或 __inline__等,所以我們一般會用一個宏定義inline 如: #define INLINE ? ?static inline 不支持inline時: #define INLINE ? ?static 但是這樣如果編譯器不支持inline 即意味著之前 static inline的函數(shù)全部被修改為 static,在頭文件中寫static會有什么后果呢? 經(jīng)過測試果然和我們想的一樣,每個c文件包含了該頭文件后全部都有了該函數(shù)副本。這無疑增大了很多代碼量。比如在include.h 這樣的大頭文件,幾乎每個c文件我們都會包含他,相當(dāng)于每一C文件都會加入一個 static void func(void){...} ?實體。如果是函數(shù)宏則不會有這種問題,函數(shù)宏是沒有實際代碼的,沒調(diào)用他時代碼不存在。這就是頭文件中用static inline 函數(shù)的壞處。但是可以通過優(yōu)化解決,經(jīng)過測試,O0優(yōu)化下在頭文件中定義static 函數(shù)包含該頭文件的三個c文件的確都有了該函數(shù),但是在Os優(yōu)化下則只有調(diào)用了該函數(shù)的C文件才有實體。這是由編譯器對static函數(shù)的特性決定的。總之他的法則和我們想的一致,就是頭文件僅僅是單純的展開,而每個C獨立編譯,不會因為知道其他個C文件定義了該函數(shù),這個c文件就把他當(dāng)外部bl了。
關(guān)于c文件中的static函數(shù) static函數(shù)除了文件內(nèi)使用這個功能外,在優(yōu)化上也有作用,static定義后如果該文件沒有函數(shù)調(diào)用他,那么他意味著沒有用,其他文件不可能調(diào)用,所以開優(yōu)化就被優(yōu)化掉了(已驗證),無優(yōu)化時則還在。這點一定要注意中斷函數(shù)最好不要寫static,中斷函數(shù)如果沒有中斷服務(wù)函數(shù)的,即沒有被調(diào)用,static可能被優(yōu)化,當(dāng)然也不一定,因為沒有中間服務(wù)函數(shù)的獨立中斷服務(wù)函數(shù)必須在鏈接腳本體現(xiàn),可能需要再鏈接腳本上加入KEEP參數(shù)應(yīng)該就沒問題。
這里排除非gcc編譯器,如code worrior編譯器則不同,code worrior是定制型的,通過識別main函數(shù),因此他有主函數(shù)枝干可以判斷哪些函數(shù)被調(diào)用,哪些是無用的,但是gcc不同,沒有所謂的main,所以三個c文件如果沒有static的函數(shù),即使開優(yōu)化也都會編譯出來,他并不知道哪些函數(shù)有用,哪些函數(shù)無用。而static后他就一定知道他沒被調(diào)用即無用,所以可以優(yōu)化掉。
以上均指arm-none-eabi-gcc環(huán)境下測試的結(jié)論,其他編譯器有些不同。但是c語言的編譯規(guī)則還是以gcc為標(biāo)準(zhǔn),即使其他編譯器有些不同我們平時編程時還是以這個為標(biāo)準(zhǔn)。 在codeworrior 編譯器上測試,即使0優(yōu)化依然會把inline內(nèi)聯(lián),有main函數(shù),沒有被主干調(diào)用的都是無用函數(shù)全部被標(biāo)記UNUSED,不編譯,因此上面討論的當(dāng)不支持inline時static函數(shù)實體過多的問題它也不存在。
總結(jié)
以上是生活随笔為你收集整理的关于头文件中的 static inline函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《数字图像处理第二版》第三章部分习题
- 下一篇: 家长必读:不想逼孩子,你应该这样做