?作者簡(jiǎn)介:嵌入式入坑者,與大家一起加油,希望文章能夠幫助各位?。。。?br> ??個(gè)人主頁(yè):@rivencode的個(gè)人主頁(yè)
??系列專欄:玩轉(zhuǎn)C語(yǔ)言
??保持學(xué)習(xí)、保持熱愛、認(rèn)真分享、一起進(jìn)步?。?/p>
一.最快的關(guān)鍵詞-register
1.可執(zhí)行的文件執(zhí)行的本質(zhì)是什么
我們都知道一個(gè)源文件要生成我們計(jì)算機(jī)課執(zhí)行的文件要經(jīng)過(guò):
源文件(test.c)—>預(yù)編譯—>編譯—>鏈接—>可執(zhí)行文件(test.exe)
對(duì)這個(gè)過(guò)程的詳細(xì)描述請(qǐng)看 《預(yù)處理指令》
不管是源文件還是生成的可執(zhí)行文件它們都是文件,而且都放在硬盤中!!
可執(zhí)行的文件其實(shí)存放的是計(jì)算機(jī)可執(zhí)行的指令(二進(jìn)制代碼),當(dāng)我們?nèi)?zhí)行這個(gè)文件的時(shí)候屏幕會(huì)打印出執(zhí)行后的結(jié)果,但大家有沒(méi)有想過(guò)我們執(zhí)行的這個(gè)文件的過(guò)程經(jīng)歷了什么。
運(yùn)行可執(zhí)行文件的本質(zhì)是將數(shù)據(jù)(二進(jìn)制指令)加載到內(nèi)存,然后讓CPU再去內(nèi)存中讀取數(shù)據(jù)(暫存在cpu中的寄存器或高速緩存中),經(jīng)過(guò)CPU的計(jì)算好的數(shù)據(jù)又重新加回內(nèi)存,然后從內(nèi)存把結(jié)果打印到屏幕上
。
但這里有個(gè)問(wèn)題,為啥CPU要經(jīng)過(guò)一個(gè)內(nèi)存讀取數(shù)據(jù),直接向硬盤讀取數(shù)據(jù)不更好嘛,根本原因就是內(nèi)存讀寫數(shù)據(jù)的速度比硬盤快的多.
這里就能理解定義變量為什么是在內(nèi)存開辟空間:當(dāng)程序執(zhí)行起來(lái)的時(shí)候數(shù)據(jù)(二進(jìn)制代碼)已經(jīng)加載到內(nèi)存,包括定義變量的二進(jìn)制指令,此時(shí)存儲(chǔ)變量的空間只能是在內(nèi)存中開辟。
2.最快的關(guān)鍵詞register
CPU主要是負(fù)責(zé)進(jìn)行計(jì)算的硬件單元,但是為了方便運(yùn)算,一般第一步需要先把數(shù)據(jù)從硬盤寫入內(nèi)存,再提前將數(shù)據(jù)從內(nèi)存讀取到CPU內(nèi),那么也就需要CPU具有一定的數(shù)據(jù)臨時(shí)存儲(chǔ)能力,所以集成了一組叫做寄存器的硬件,用來(lái)做臨時(shí)數(shù)據(jù)的保存,然后CPU一條一條指令執(zhí)行運(yùn)算,執(zhí)行完后結(jié)果會(huì)寫回內(nèi)存。
既然寄存器的速度很快那我們能不能將變量直接存儲(chǔ)在寄存器中呢,答案是可以的??!
register修飾變量
盡量
將所修飾變量,會(huì)將變量一步到位放入CPU寄存器中,從而達(dá)到提高CPU效率的目的。
這里為什么是盡量呢,雖然我們用register修飾了變量,但編譯器會(huì)判斷這個(gè)變量值不值得放入寄存器,和能不能放入寄存器,所以編譯器不一定會(huì)將該變量放入寄存器。
這樣就引申出什么樣的變量適合放入寄存器
-
局部變量,全局變量最好不要,(全局變量生命周期是整個(gè)程序運(yùn)行期間,所以會(huì)導(dǎo)致CPU寄存器被長(zhǎng)時(shí)間占用)
-
不會(huì)被寫入(改變變量的值)的變量(因?yàn)橐坏懭?,運(yùn)行結(jié)果就需要寫回內(nèi)存,后續(xù)還要讀取檢測(cè)的話,register修飾就沒(méi)有意義了)
-
變量會(huì)被高頻被讀取
-
register不能大量使用,因?yàn)榧拇嫫鲾?shù)量有限
被register修飾的變量不能被取地址,原因就是地址是內(nèi)存的中的概
念
二.聲明關(guān)鍵字-extern
1.變量的生命周期與作用域
1.變量的定義與聲明
變量的定義:需要在內(nèi)存中開辟空間要存儲(chǔ)數(shù)據(jù),同時(shí)也可以初始化變量的值,但一個(gè)變量的定義只能有一次
。
聲明:不管是函數(shù)的聲明還是變量的聲明,聲明只是一個(gè)告知并不會(huì)開辟內(nèi)存
,告知編譯器我們已經(jīng)定義了可以使用,而且一個(gè)函數(shù)或變量的聲明可以多次。
2.變量的生命周期與作用域
生命周期:指的是該變量從定義(開辟內(nèi)存保存數(shù)據(jù))到被釋放存儲(chǔ)空間的時(shí)間段記住它是一個(gè)時(shí)間概念。
作用域:指的是該變量能夠被使用的范圍,只有在作用域范圍之內(nèi)變量名才可以被使用。
3.全局變量與局部變量
- 全局變量
定義:在函數(shù)外定義的變量叫做全局變量,存儲(chǔ)在全局?jǐn)?shù)據(jù)區(qū),具有全局性
生命周期:定義完成后程序運(yùn)行的整個(gè)生命周期內(nèi)一直都有效。
作用域:整個(gè)工程。
- 局部變量
定義:包含在代碼塊( { } )中的變量叫做局部變量,存儲(chǔ)棧區(qū),局部變量具有臨時(shí)性。
生命周期:從進(jìn)入代碼塊形成局部變量開辟空間,退出代碼塊釋放空間而經(jīng)歷這個(gè)過(guò)程的時(shí)間叫做局部變量的生命周期
作用域:代碼塊內(nèi),代碼塊:用{ }括起來(lái)的區(qū)域,就叫作代碼塊。
舉例:
局部變量的出了作用域,變量將不能正常被訪問(wèn)
全局變量的作用域是整個(gè)工程當(dāng)全局變量與局部變量命名沖突時(shí)優(yōu)先使用局部變量
2.STM32工程中頭文件的作用
在一個(gè)工程中應(yīng)該有多個(gè)外設(shè)源文件,以及一個(gè)包含一個(gè)main函數(shù)的源文件,各位外設(shè)源文件實(shí)現(xiàn)的功能不一樣(功能函數(shù)),肯定避免不了各個(gè)外設(shè)源文件相互調(diào)用函數(shù)情況,此時(shí)我們只需要每個(gè)源文件對(duì)應(yīng)一個(gè)外設(shè)頭文件。
而頭文件包含:
1.其他外設(shè)頭文件和系統(tǒng)頭文件
2.所有全局變量的聲明
3.所有的函數(shù)聲明
4.#define,結(jié)構(gòu)體聲明,枚舉聲明,typedef類型等等當(dāng)其他源文件想調(diào)用該外設(shè)源文件的內(nèi)容時(shí)只要包含這個(gè)外設(shè)源文件對(duì)應(yīng)頭文件就OK了
頭文件的作用:組織項(xiàng)目結(jié)構(gòu)的時(shí)候,減少大型項(xiàng)目的維護(hù)成本
外設(shè)相關(guān)文件
stm32f10x.h這個(gè)文件是非常牛逼的啦
stm32f10x.h:實(shí)現(xiàn)了內(nèi)核(CPU)之外的所有外設(shè)的寄存器映射,以及一些外設(shè)庫(kù)函數(shù)的參數(shù)
stm32f10x_xx.c: 外設(shè)的驅(qū)動(dòng)函數(shù)庫(kù)文件
xx: GPIO、USRAT、I2C、SPI、FSMC…
這里我截取的一了部分
stm32f10x_xx.h: 存放外設(shè)的初始化結(jié)構(gòu)體,外設(shè)初始化結(jié)構(gòu)體成員的參數(shù)列表,外設(shè)固件庫(kù)函數(shù)的聲明
3.聲明關(guān)鍵字extern
1.extern修飾全局變量
為了體現(xiàn)頭文件的作用我把聲明統(tǒng)統(tǒng)放到test.h這個(gè)頭文件中
將聲明放入頭文件中,只要包含這個(gè)源文件的頭文件就可以使用這些聲明,根本原因是在預(yù)編譯過(guò)程會(huì)將源文件包含的頭文件的內(nèi)容全部包含到該源文件中,詳情請(qǐng)參考—》預(yù)編譯指令
注意:聲明并不會(huì)開辟內(nèi)存,只是一個(gè)告知作用,不能用聲明來(lái)初始化全局變量的值。
2.extern聲明函數(shù)
注意:聲明函數(shù)前面的extern可以省略(不會(huì)報(bào)錯(cuò)),聲明全局變量的前面extern不能省略。
雖然聲明函數(shù)可以省略extern關(guān)鍵字,但建議不要省略,使代碼更加嚴(yán)謹(jǐn)。
前面test.h頭文件中忘記加條件編譯防止頭文件被重復(fù)包涵這里補(bǔ)上:具體原理請(qǐng)看–>預(yù)編譯指令
**
三.static關(guān)鍵字三個(gè)作用(重點(diǎn))
1.static修飾全局變量
static修飾全局變量:該變量的只在本文件內(nèi)被訪問(wèn),不能被外部其他直接
訪問(wèn)。
原理:static修飾全局變量改變的是全局變量的作用域,作用域從整個(gè)工程變?yōu)楸疚募?nèi),全局變量的生命周期并沒(méi)有變。
雖然不能直接訪問(wèn)但是可以通過(guò)函數(shù)調(diào)用的方式進(jìn)行間接訪問(wèn)
2.static修飾函數(shù)
static修飾函數(shù),該函數(shù)只能在本文內(nèi)被訪問(wèn),不能在外部其他文件直接
訪問(wèn)。
原理:static修飾函數(shù)也是改變的函數(shù)的作用域,作用域從整個(gè)工程變?yōu)楸疚募?nèi)。
static修飾函數(shù),當(dāng)然函數(shù)也可以間接被訪問(wèn),不就在嵌套一個(gè)函數(shù)嘛。
3.static修飾局部變量
static修飾局部變量,則該局部變量存儲(chǔ)在靜態(tài)數(shù)據(jù)區(qū),更改局部變量的生命周期從局部變量生命周期改成全局生命周期也就是說(shuō)程序運(yùn)行的整個(gè)生命周期內(nèi)一直都有效,但作用域仍然不變還是一個(gè)代碼塊內(nèi)
。
先看一段代碼,局部變量不加static關(guān)鍵字修飾:
局部變量加static關(guān)鍵字修飾:
static修飾局部變量,局部變量的作用域仍然不變還是在一個(gè)代碼塊內(nèi)
出了代碼塊,變量并不能被正常訪問(wèn),static修飾局部變量你只能說(shuō)局部變量活的更久了,就是沒(méi)有被釋放該變量的內(nèi)存,一定要細(xì)細(xì)品味?。?/strong>
為什么static修飾局部變量生命周期具有全局性:
根本原因是存儲(chǔ)的位置發(fā)生了變化,被static修飾局部變量被存放在全局?jǐn)?shù)據(jù)區(qū)。
還有一個(gè)問(wèn)題局部變量為啥具有臨時(shí)性,這里簡(jiǎn)單提一下叭
這里簡(jiǎn)單提一下關(guān)于函數(shù)棧幀還有很多細(xì)節(jié),這里不是我們的重點(diǎn),到后面會(huì)專門出一篇關(guān)于函數(shù)棧幀的詳解,系統(tǒng)闡述具體原理。
四.只讀關(guān)鍵字-const(重點(diǎn))
1.const修飾變量
1.const修飾變量:變量不可以直接
被修改
注意:
這里const并不會(huì)修飾關(guān)鍵字int,所以int的位置并不影響結(jié)果。
所以const int a 與int const a 是等價(jià)的 const修飾的都是變量a但習(xí)慣是第一種。
雖然不能直接修改,但可以通過(guò)指針操作間接修改變量的值:
對(duì)與用const修飾的變量,如果真正想改還是可以做到的,那問(wèn)題來(lái)了const的意義是什么呢
如果我們用const去修飾一個(gè)變量肯定希望變量不應(yīng)該被修改
1.讓編譯器進(jìn)行直接修改式檢查 ,當(dāng)直接修改const修飾的變量時(shí)編譯器報(bào)錯(cuò)提醒。
2. 告訴其他程序員(正在改你代碼或者閱讀你代碼的)這個(gè)變量后面盡量不要改,改了可能會(huì)出問(wèn)題。
const定義的常量不是標(biāo)準(zhǔn)意義上的常量,作為數(shù)組的元素個(gè)數(shù)
字符串真正意義不能被修改
2.const修飾數(shù)組
2.const修飾數(shù)組:數(shù)組的每個(gè)元素不能被直接
修改
利用指針間接修改數(shù)組元素
3.const修飾指針
const關(guān)鍵字不能修飾關(guān)鍵字(int ),所以直接忽略int的位置,然后往const的右邊看離誰(shuí)近就修飾誰(shuí),這里只能修飾 * 操作符 或 指針 p。
-
1.const
int*p; //const修飾*
操作符,p指針可變,p指向的對(duì)象(*p)不可變。 -
2.
intconst *p; //const修飾*
操作符,p指針可變,p指向的對(duì)象(*p)不可變。與上式一樣
-
3.
int* const p; //const修飾 p 指針,p指針不可變,p指向的對(duì)象(*p)可變。 -
4.const
int* const p; //前一個(gè)const修飾*
操作符,后一個(gè)const修飾 p 指針,指針p與指針指向的對(duì)象*p都不能變。
4.const修飾函數(shù)參數(shù)
這里只是為了代碼更加嚴(yán)謹(jǐn),因?yàn)檫@個(gè)函數(shù)只是一個(gè)打印的函數(shù),所以我們加一個(gè)const關(guān)鍵字修飾 參數(shù)的 *操作符使函數(shù)內(nèi)并不能修飾我們的a變量的值。
5.const修飾函數(shù)的返回值
一般用來(lái)修飾返回的指針。
正確的方式應(yīng)該接收的指針前面也加const修飾
五.最易變的關(guān)鍵字-volatile(重點(diǎn))
1.理解volatile關(guān)鍵字
volatile是易變的,不穩(wěn)定的意思。
要用volatile關(guān)鍵字修飾的變量,表示該變量可能被操作系統(tǒng),硬件(例如單片機(jī)的外設(shè)),或者其他線程等更改,如果我們用volatile關(guān)鍵字去修飾這個(gè)變量,則編譯器對(duì)訪問(wèn)該變量的的代碼就不在進(jìn)行優(yōu)化(我自身對(duì)優(yōu)化的理解:編譯器可能會(huì)認(rèn)為我們自己寫的代碼不會(huì)修改這個(gè)變量,則把該變量直接拿到CPU中,而之后CPU不會(huì)再去內(nèi)存讀取該變量,但殊不知雖然我們寫的代碼不會(huì)修改該變量,但硬件可能會(huì)對(duì)該變量進(jìn)行修改,而修改之后的值是存放在內(nèi)存之中,而CPU此時(shí)并不會(huì)去內(nèi)存讀取該變量的值,最后導(dǎo)致變量的值不能及時(shí)更新),可以提定對(duì)特殊地址的穩(wěn)定訪問(wèn)。
接下來(lái)在linux環(huán)境下做個(gè)實(shí)驗(yàn):
不加volatile:
總結(jié):不加volatile關(guān)鍵字修飾pass這個(gè)變量則編譯器(會(huì)認(rèn)為pass這個(gè)變量不會(huì)被修改)可能會(huì)進(jìn)行優(yōu)化,之后CPU不會(huì)再?gòu)膬?nèi)存中讀取pass變量的值,那么問(wèn)題來(lái)了如果pass的值此時(shí)被操作系統(tǒng),或者硬件將pass的值改成0,按理說(shuō)CPU應(yīng)該退出循環(huán),但此時(shí)CPU并不會(huì)去內(nèi)存讀取pass的值(0),則CPU并不會(huì)退出循環(huán)那就出現(xiàn)了問(wèn)題?。?/strong>
加加volatile:
總結(jié):加上volatile修飾變量,此時(shí)會(huì)忽略編譯器的優(yōu)化,也就是說(shuō)CPU會(huì)一直向內(nèi)存讀取的pass的值再進(jìn)行判定是否執(zhí)行循環(huán),此時(shí)就算pass的值被硬件改成0,則CPU也可以讀取到然后終止循環(huán)
2.volatile關(guān)鍵字在STM32中的運(yùn)用
你仔細(xì)觀察你會(huì)發(fā)現(xiàn)基本上SMT32中定義的所有寄存器都前面都加上了
volatile關(guān)鍵字來(lái)進(jìn)行修飾。
每個(gè)結(jié)構(gòu)體成員前增加了一個(gè)“__IO”前綴,它的原型是volatile關(guān)鍵字,這些結(jié)構(gòu)體內(nèi)的成員,都代表著寄存器,而寄存器(在內(nèi)存當(dāng)中)很多時(shí)候是由硬件(外設(shè)或 STM32 芯片狀態(tài))修改的,也就是說(shuō)即使 CPU 不執(zhí)行代碼修改這些變量,變量的值也有可能被外設(shè)修改、更新
,所以每次使用這些變量的時(shí)候,我們都要求CPU 去該變量的地址重新讀取這些寄存器。 若沒(méi)有這個(gè)關(guān)鍵字修飾,在某些情況下,編譯器認(rèn)為沒(méi)有代碼修改該變量,就直接從 CPU 的某個(gè)緩存(CPU內(nèi)部的寄存器或高速緩存)獲取該變量值
,這時(shí)雖然可以加快執(zhí)行速度,但該緩存中的是陳舊數(shù)據(jù),與我們要求的寄存器最新狀態(tài)可能會(huì)有出入。
總結(jié):volatile關(guān)鍵字叫編譯器不要進(jìn)行優(yōu)化,也就是說(shuō)讓CPU每次都老老實(shí)實(shí)去內(nèi)存中讀取的寄存器中的值,慢點(diǎn)就慢點(diǎn)但是可以保證CPU讀取的寄存器的值都是最新的?。?!文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-404719.html
結(jié)尾
上面加了重點(diǎn)二字面試極有可能考到,基本上是C語(yǔ)言中最重要的幾個(gè)關(guān)鍵字,運(yùn)用比較廣泛,我們必須理解并掌握它,如果覺得本文對(duì)有有小小的幫助就快快點(diǎn)贊收藏叭!?。?!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-404719.html
到了這里,關(guān)于static,const,volatile,extern,register關(guān)鍵字深入解析的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!