日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

、简述global关键字的作用_详解static inline关键字

發(fā)布時間:2025/3/19 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 、简述global关键字的作用_详解static inline关键字 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

詳解static inline關鍵字

本文章為知乎用戶 @徐yang喲 原創(chuàng),禁止抄襲!

靈感來源

在查stm32的LL庫部分函數(shù)的API時,有時會查到這種函數(shù):

__STATIC_INLINE void LL_GPIO_SetPinOutputType ( GPIO_TypeDef * GPIOx, uint32_t PinMask, uint32_t OutputType);

我不禁對__STATIC_INLINE產(chǎn)生了好奇。在查看源文件后,發(fā)現(xiàn)這個關鍵字的定義如下

#ifndef __INLINE#define __INLINE __inline #endif #ifndef __STATIC_INLINE#define __STATIC_INLINE static __inline #endif #ifndef __STATIC_FORCEINLINE #define __STATIC_FORCEINLINE static __forceinline #endif

很明顯這個關鍵字是由static 和 __inline 一起構成的。所以我們要分別知道這兩個關鍵字的作用是什么。

關鍵字定義

static

在C語言中,函數(shù)默認情況下是global的。函數(shù)名前的static關鍵字使它們變成靜態(tài)。不同于C語言其他的global的函數(shù),訪問static函數(shù)被限制到聲明它們的文件。因此,當我們要限制對函數(shù)的訪問時,我們讓它們static。此外,在不同的文件中可以允許存在擁有相同函數(shù)名的static函數(shù)。

inline

inline是c99的特性。在c99中,inline是向編譯器建議,將被inline修飾的函數(shù)以內(nèi)聯(lián)的方式嵌入到調(diào)用這個函數(shù)的地方。而編譯器會判斷這樣做是否合適,以此最終決定是否這么做。

static inline的優(yōu)劣

根據(jù)上面的定義可以知道,如果static inline關鍵字生效(因為只有編譯器有最終決定權,我們只有建議權,這點在后面會細講),static inline會以一種類似于宏定義的方式,將調(diào)用被static inline修飾的函數(shù)的語句替換為那個函數(shù)體對應的指令,但實際上只是inline的作用,static作用其實是維護代碼的健壯性,實驗中會加以證明。 所以:

好處:
減少調(diào)用函數(shù)時的開銷,如: 減少傳參時可能引起的壓棧出棧的開銷。
減少PC跳轉時對流水線的破壞。
壞處: * 代碼所占體積會更大。

實驗

目的:直觀證明上述優(yōu)點和缺點、探究static的作用

實驗一 :探究static inline對代碼起到的影響

實驗背景:
硬件平臺:stm32f401re
IDE:stm32cubeMX、Keil(關掉指令優(yōu)化)
庫:LL庫(僅僅初始化芯片)
編程語言:C語言、Arm指令

實驗設計

嘗試構造四個函數(shù),這四個函數(shù)都是實現(xiàn)對四個參數(shù)相加,然后將結果返回,不同之處在于它們被不同的關鍵字修飾。

分別為: int Normal_Add(int n1,int n2,int n3,int n4,int n5); static int Static_Add(int n1,int n2,int n3,int n4,int n5); __inline int Inline_Add(int n1,int n2,int n3,int n4,int n5); static __inline int StaticInline_Add(int n1,int n2,int n3,int n4,int n5);
注:在Armcc編譯器的實現(xiàn)中,inline被實現(xiàn)為__inline
在這些函數(shù)中: Normal_Add是最簡單的函數(shù);
Static_Add是用static關鍵字修飾的函數(shù); Inline_Add是用inline關鍵字修飾的函數(shù);
StaticInline_Add是用static inline關鍵字修飾的函數(shù);

main.c

只羅列了關鍵部分

#include "funcTest.h" int main(void) {int i;i = Normal_Add(1,1,1,1,1);i = Static_Add(2,2,2,2,2);i = Inline_Add(3,3,3,3,3);i = StaticInline_Add(4,4,4,4,4);/*使用i,為了去掉編譯警告*/while (i > 0); }

funcTest.h

#ifndef __FUNCTEST_H #define __FUNCTEST_H/*Normal_Add的聲明*/ int Normal_Add(int n1,int n2,int n3,int n4,int n5);/*其他三個函數(shù)的定義*/ static int Static_Add(int n1,int n2,int n3,int n4,int n5){return (n1+n2+n3+n4+n5);; }__inline int Inline_Add(int n1,int n2,int n3,int n4,int n5) {return (n1+n2+n3+n4+n5);; }static __inline int StaticInline_Add(int n1,int n2,int n3,int n4,int n5) {return (n1+n2+n3+n4+n5); }; #endif

funcTest.h文件其實涉及到一個問題,為什么Static_Add函數(shù)和Inline_Add函數(shù)還有StaticInline_Add函數(shù)要放在.h頭文件里呢?

理由: 考慮下這三個函數(shù)的作用,我們希望它們被其他文件訪問到嗎?顯然希望。那么,如果我們?nèi)绻裺tatic修飾的函數(shù)僅僅放在.c源文件中,其實其他的源文件就不能訪問到那幾個函數(shù)了。至于Inline_Add函數(shù),其實對于armcc編譯器,放不放在頭文件都可以。但是Inline關鍵字其實不建議單獨用,原因是不安全,最好配合static使用。這個在下一個實驗可以看到

funcTest.h

#include <funcTest.h> /*Normal_Add函數(shù)的聲明部分*/ int Normal_Add(int n1,int n2,int n3,int n4,int n5) {return (n1+n2+n3+n4+n5); }

實驗結果

通過armcc生成的反匯編結果,我們來看一看這幾個函數(shù)對應的匯編指令有什么不同。

main.s

;注釋是自己加的;函數(shù)調(diào)用時:;R0-R3為參數(shù);剩下的一個變量在棧中;R0為返回值;R4為main函數(shù)里的變量i;Normal_Add函數(shù),加上子函數(shù)總共15條指令MOVS r0,#1MOV r3,r0MOV r2,r0MOV r1,r0;最后一個參數(shù)壓棧了STR r0,[sp,#0]BL Normal_AddMOV r4,r0;Static_Add函數(shù)MOVS r0,#2MOV r3,r0MOV r2,r0MOV r1,r0;最后一個參數(shù)壓棧了STR r0,[sp,#0]BL Static_AddMOV r4,r0;Inline_Add內(nèi)聯(lián)函數(shù),10條指令MOVS r0,#3MOV r1,r0MOV r2,r0MOV r3,r0MOV r5,r0;開始相加ADDS r6,r0,r1ADD r6,r6,r2ADD r6,r6,r3ADD r6,r6,r5;傳回變量iMOV r4,r6;StaticInline_Add內(nèi)聯(lián)函數(shù),10條指令MOVS r1,#4MOV r3,r1MOV r0,r1MOV r5,r1MOV r2,r1;開始相加ADDS r6,r1,r3ADD r6,r6,r0ADD r6,r6,r5ADD r6,r6,r2;傳回變量iMOV r4,r6NOP

functest.s

Static_Add PROC;還有保存現(xiàn)場的操作,相當浪費時間PUSH {r4,r5,lr}MOV r4,r0LDR r5,[sp,#0xc]ADDS r0,r4,r1ADD r0,r0,r2ADD r0,r0,r3ADD r0,r0,r5POP {r4,r5,pc}ENDPNormal_Add PROC;還有保存現(xiàn)場的操作,相當浪費時間PUSH {r4,r5,lr}MOV r4,r0LDR r5,[sp,#0xc]ADDS r0,r4,r1ADD r0,r0,r2ADD r0,r0,r3ADD r0,r0,r5POP {r4,r5,pc}ENDP

綜合以上兩個文件的代碼,我們不難發(fā)現(xiàn),非內(nèi)聯(lián)函數(shù)所用開銷顯然比內(nèi)聯(lián)函數(shù)的開銷大。因為非內(nèi)聯(lián)函數(shù)不但用的指令多,還訪問內(nèi)存了,還用跳轉指令破壞了流水線。所以肯定會慢很多。使用keil的debug功能,定量算它們所用時間差異。結果如下:

  • Normal_Add函數(shù)與static_Add函數(shù):
    0.000037s = 37us
  • Inline_Add函數(shù)與StaticInline_Add函數(shù):
    0.000012s = 12us

雖然在這里,沒有體現(xiàn)內(nèi)聯(lián)函數(shù)內(nèi)存開銷大的特點。但請想一想,如果一個內(nèi)聯(lián)函數(shù)被調(diào)用多次,那么這個函數(shù)就將被內(nèi)聯(lián)多次,代碼就會被復制多次;但是一個非內(nèi)聯(lián)函數(shù)被調(diào)用多次,卻并不會被復制多次。所以內(nèi)聯(lián)函數(shù)往往內(nèi)存開銷會大一點。

實驗二 :探究static inline和inline的區(qū)別

在講這個之前,其實我們需要知道一點。就是static inline關鍵字和inline關鍵字無法決定被關鍵字所修飾的函數(shù)是否最后真正會被內(nèi)聯(lián)。我們其實只有建議權,只有armcc編譯器才可以決定函數(shù)最后是否真正會被內(nèi)聯(lián)。

參見Armcc User Guide原文:

inline functions in C99
The C99 keyword inline hints to the compiler that invocations of a function qualified with inline are to be expanded inline. For example: c inline int max(int a, int b) { return (a > b) ? a : b; } The compiler inlines a function qualified with inline only if it is reasonable to do so. It is free to ignore the hint if inlining the function adversely affects performance. Note :The __inline keyword is available in C90.Note: The semantics of inline in C99 are different to the semantics of inline in Standard C++.
Comiler decisions on function inliningWhen function inlining is enabled, the compiler uses a complex decision tree to decide if a function is to be inlined.The following simplified algorithm is used: 1. If the function is qualified with forceinline, the function is inlined if it is possible to do so. 2. If the function is qualified with inline and the option --forceinline is selected, the function is inlined if it is possible to do so. If the function is qualified with inline and the option --forceinline is not selected, the function is inlined if it is practical to do so. 3. If the optimization level is -O2 or higher, or --autoinline is specified, the compiler automatically inlines functions if it is practical to do so, even if you do not explicitly give a hint that function inlining is wanted. When deciding if it is practical to inline a function, the compiler takes into account several other criteria, such as: ? The size of the function, and how many times it is called. ? The current optimization level. ? Whether it is optimizing for speed (-Otime) or size (-Ospace). ? Whether the function has external or static linkage. ? How many parameters the function has. ? Whether the return value of the function is used. Ultimately, the compiler can decide not to inline a function, even if the function is qualified with forceinline. As a general rule:
? Smaller functions stand a better chance of being inlined.
? Compiling with -Otime increases the likelihood that a function is inlined.
? Large functions are not normally inlined because this can adversely affect code density and performance.
A recursive function is never inlined into itself, even if __forceinline is used.

濃縮成一句話:

開發(fā)者決定不了一個函數(shù)是否被內(nèi)聯(lián),開發(fā)者只有建議權,只有編譯器具有決定權。

這就造成了一個很disturbing的事情:除非你看到一個函數(shù)的反匯編代碼,否則你很難確定他是不是內(nèi)聯(lián)函數(shù)。

下面,我們來看看一個被static inline修飾的非內(nèi)聯(lián)函數(shù):

static __inline int Fake_StaticInline_Add(int n1,int n2,int n3,int n4,int n5) {/*只是為了多湊幾條指令*/n1++;n1++;n1++;n1++;n1++;n1++;n1++;n1++;n1++;n1++;n1++;return (n1+n2+n3+n4+n5);; }

這個函數(shù)我們把他放在main.c中,并在main函數(shù)這樣調(diào)用:i = Fake_StaticInline_Add(6,6,6,6,6);。現(xiàn)在我們來看看他的反匯編代碼:

MOVS r0,#5MOV r3,r0MOV r2,r0MOV r1,r0STR r0,[sp,#0]BL Fake_StaticInline_AddMOV r4,r0NOP

驚不驚喜,意不意外? 這個函數(shù)居然是被調(diào)用了的,而并沒有被內(nèi)聯(lián)到調(diào)用它的地方。那么,為什么這個函數(shù)沒變成內(nèi)聯(lián)函數(shù)呢?
我推斷是因為我在這個函數(shù)中寫入了太多指令,編譯器判斷如果它變成內(nèi)聯(lián)函數(shù),會極大占用空間,所以不將它編譯成內(nèi)聯(lián)函數(shù)。
但你還記得我們之前把Inline_Add函數(shù)放在哪里嗎?放在了一個頭文件。試想,如果這個Inline_Add在沒有被編譯成內(nèi)聯(lián)函數(shù)的情況下,被include到了多個源文件中,勢必會產(chǎn)生函數(shù)重復定義的問題。 因此,我們要再加一個關鍵字static,才能避免這個問題。

結論

至此,我們直觀地理解了static inline的特點,并知道了為什么static和inline要聯(lián)合使用。
總結一下static inline什么時候用比較好: 當所修飾的函數(shù)語句較少時,尤其是只有一兩條語句的函數(shù)。 當你所修飾的函數(shù)不是遞歸函數(shù),而是正常的函數(shù)時。因為遞歸式不支持內(nèi)聯(lián)的。

感想

終于粗略地寫完了。這個我原以為一個小時就能完成地實驗,實際上做了一下午......
一句話與君共勉:

紙上得來終覺淺,絕知此事要躬行 與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的、简述global关键字的作用_详解static inline关键字的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: www.色99| av中文字| 欧美一级网 | 青青草中文字幕 | 中午字幕在线观看 | 探花视频在线免费观看 | 久久久久久久久久一区二区三区 | 久久久一区二区三区 | 国产a黄 | 国产亚洲精品美女久久久久 | 日韩在线观看av | 国产一区二区三区91 | 91成人动漫 | 91超碰在线观看 | 国产精品欧美一区喷水 | 丰满人妻一区二区三区无码av | xxxxwww一片| 亚洲 激情 小说 另类 欧美 | 亚洲av熟女高潮一区二区 | 人人爽人人爽人人 | 国产白丝袜美女久久久久 | 国产超碰人人模人人爽人人添 | 天堂99| 国内成人精品视频 | 好吊妞这里有精品 | 碰在线视频 | 成人www| 男男免费视频 | 伊人网在线观看 | 九九热精彩视频 | 国产一级特黄a高潮片 | 亚洲一区二区色 | 亚洲天堂久 | 少妇媚药按摩中文字幕 | 女人av在线 | 91精品国产综合久久精品图片 | 久久大尺度| 国产一区二区三区欧美 | 日韩在线看片 | 日本色视| 性久久久久久久 | 一区二区三区四区五区在线视频 | 麻豆影视大全 | 99精品久久久| 美女扒开尿口来摸 | 国产精品21p | 天天搞夜夜 | av黄色大片 | 97看片网| 青青青草视频在线观看 | 97自拍网| 欧美影院久久 | 国产电影一区二区三区 | 欧美一级黄色录像 | 91嫩草影视 | 亚洲国产网 | 欧美高清一区 | 亚洲欧美一区二区视频 | 精品一区91 | 爱爱视频网址 | av免费毛片 | 日韩av一卡| jizz18欧美18| 69久久| 午夜激情久久久 | 国产人成在线 | 色欧洲| 一卡二卡三卡在线视频 | 无码国产精品高潮久久99 | 色哟哟网站在线观看 | 蜜臀少妇久久久久久久高潮 | 日本中出视频 | 蜜桃视频一区二区在线观看 | 国产欧美一区二区三区精华液好吗 | 中文免费av | 久久女| 国产精品视频久久久久久 | 色七七久久 | 奇米888一区二区三区 | 少妇高潮一区二区三区99欧美 | 一级片美女 | 精品在线视频一区二区 | 久久亚洲AV成人无码国产人妖 | 国产三级伦理片 | 国产一级片免费视频 | 国产成人传媒 | 啪免费| 校园春色亚洲激情 | 在线视频一二区 | 自拍偷拍免费 | 久久久五月 | 亚洲第一a| 国产网站免费观看 | 精品少妇一区二区三区免费观看 | 草莓视频在线观看入口w | 成人毛片基地 | 秋霞精品一区二区三区 | 欧美日韩国产综合在线 | 日韩av在线免费看 |