七茉贡、存儲類&作用域&生命周期&鏈接屬性
7.1扁位、概念詞:存儲類(棧准潭、堆、數(shù)據(jù)區(qū)域仇、.bss段刑然、.text段)
作用域(代碼塊作用范圍,也就是變量作用的范圍)
生命周期(變量的誕生和死亡)
鏈接屬性(外鏈接屬性殉簸、內(nèi)鏈接屬性闰集、無連接屬性)
7.2、Linux下的內(nèi)存映射(分配情況般卑、組織情況):見圖內(nèi)存映射。其中有關(guān)進(jìn)程的空間爽雄,如進(jìn)程控制塊蝠检、頁表等都是在內(nèi)核里面的。文件區(qū)是映射外部文件的挚瘟,如打開記事本叹谁,那么這個文件臨時存放在文件區(qū)域。(見引用資料)
問題:虛擬地址技術(shù)乘盖? 解決:后期在Linux應(yīng)用/網(wǎng)絡(luò)編程會講焰檩。
OS下和裸機(jī)下C程序加載執(zhí)行的差異? 解決:在arm裸機(jī)第十六部分有介紹订框。
7.3析苫、存儲類關(guān)鍵字:
<1> auto 自動的(一個用法:修飾局部變量,在定義變量時可以省略) 【外鏈接:與第二個c文件鏈接】【內(nèi)鏈接:只與本c文件鏈接】【無連接:就是無鏈接】
<2> static 靜態(tài)的(有兩個用法穿扳,第一個是修飾局部變量衩侥,意思是當(dāng)作全局變量,存放在數(shù)據(jù)區(qū)矛物,作用域只是定義的那個函數(shù)范圍茫死,生命周期和整個程序一樣,屬于無連接
第二個是修改全局變量/函數(shù)履羞,意思是這個全局變量/函數(shù)只在當(dāng)前c文件有效峦萎,其他c文件是不能使用它的屡久,屬于內(nèi)鏈接,普通全局變量屬于外連接)
<3>register 寄存器(一個用法爱榔,修飾變量涂身,作用是讓編譯器把這個變量放在寄存器中,當(dāng)這個變量頻繁的被使用時搓蚪,用這個方法可以提高效率蛤售,但有時候不一定就放在寄存器,因為寄存器是有限的妒潭,沒有剩余的寄存器了)
<4>extern (一個用法悴能,修飾全局變量,表示該文件要使用的這個變量雳灾,在另外一個c文件中已經(jīng)定義了漠酿,其一個聲明的作用,不能初始化)
<5>volatile (一個用法谎亩,修飾變量炒嘲,表示對這個變量的語句不要去優(yōu)化)
(1) volatile的字面意思:可變的、易變的匈庭。C語言中volatile用來修飾一個變量夫凸,表示這個變量可以被編譯器之外的東西改變。編譯器之內(nèi)的意思是變量的值的改變是代碼的作用阱持,編譯器之外的改變就是這個改變不是代碼造成的夭拌,或者不是當(dāng)前代碼造成的,編譯器在編譯當(dāng)前代碼時無法預(yù)知衷咽。譬如在中斷處理程序isr中更改了這個變量的值鸽扁,譬如多線程中在別的線程更改了這個變量的值,譬如硬件自動更改了這個變量的值(一般這個變量是存在寄存器镶骗,或許當(dāng)其他進(jìn)程要用到這個寄存器時桶现,就把這個寄存器的變量給改變了,同時也就改變了這個變量)
(2) 以上說的三種情況(中斷isr中引用的變量鼎姊,多線程中共用的變量骡和,硬件會更改的變量)都是編譯器在編譯時無法預(yù)知的更改,此時應(yīng)用使用volatile告訴編譯器這個變量屬于這種(可變的此蜈、易變的)情況即横。編譯器在遇到volatile修飾的變量時就不會對改變量的訪問進(jìn)行優(yōu)化,就不會出現(xiàn)錯誤裆赵。
(3) 編譯器的優(yōu)化在一般情況下非常好东囚,可以幫助提升程序效率。但是在特殊情況(volatile)下战授,變量會被編譯器想象之外的力量所改變页藻,此時如果編譯器沒有意識到而去優(yōu)化則就會造成優(yōu)化錯誤桨嫁,優(yōu)化錯誤就會帶來執(zhí)行時錯誤。
而且這種錯誤很難被發(fā)現(xiàn)份帐。
(4) volatile是程序員意識到需要volatile然后在定義變量時加上volatile璃吧,如果你遇到了應(yīng)該加volatile的情況而沒有加程序可能會被錯誤的優(yōu)化。如果在不應(yīng)該加volatile而加了的情況程序不會出錯只是會降低效率废境。
所以我們對于volatile的態(tài)度應(yīng)該是:正確區(qū)分畜挨,該加的時候加不該加的時候不加,如果不能確定該不該加為了保險起見就加上噩凹。
舉例子1: int a=3 巴元,b,c;
b=a;
c=b;
那么編譯器會優(yōu)化成 c=b=a=3; 如果此時遇到上述三種情況驮宴,突然改變了a的值逮刨,那么,對于沒有優(yōu)化前是對的堵泽,但是對于優(yōu)化后修己,那么c仍然是3,就會出錯迎罗。
所以當(dāng)我們程序員知道這個變量會發(fā)生改變時睬愤,就應(yīng)該加 volatile,提示編譯器不要幫我們做優(yōu)化佳谦。
舉列子2:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
這段代碼的有個惡作劇戴涝。這段代碼的目的是用來返指針ptr指向值的平方,但是钻蔑,由于ptr指向一個volatile型參數(shù),編譯器將產(chǎn)生類似下面的代碼:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地該變奸鸯,因此a和b可能是不同的咪笑。結(jié)果,這段代碼可能返不是你所期望的平方值娄涩!正確的代碼如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
<6> restrict (1)c99中才支持的窗怒,所以很多延續(xù)c89的編譯器是不支持restrict關(guān)鍵字,gcc支持的蓄拣。
(2)restrict 作用是讓編譯器對這個變量做一些優(yōu)化扬虚,讓它提高效率。下面的網(wǎng)站有列子球恤。
(3)restrict只用來修飾指針辜昵,不能修飾普通變量,它表示只能該指針才能修改它的內(nèi)容。如用memcpy時咽斧,兩個內(nèi)存存在重疊的現(xiàn)象堪置。
(4)https://blog.chinaunix.net/uid-22197900-id-359209.html (這個網(wǎng)站里面有詳細(xì)的例子)
(5)memcpy和memmove的區(qū)別 void *memcpy( void * restrict dest ,const void * restrict src,sizi_t n)躬存;這樣它可以優(yōu)化成memmove原理的方式(當(dāng)存在重疊時,先復(fù)制到一段內(nèi)存空間舀锨,然后再把它復(fù)制到目的空間)
7.4岭洲、作用域:
(1)全局變量起名字一般是 g_a;
(2)名字加前綴表示
7.5、總結(jié):<1>局部變量地址由運(yùn)行時在棧上分配得到坎匿,多次執(zhí)行時地址不一定相同盾剩,函數(shù)不能返回該類變量的地址(指針)作為返回值。
<2>為什么要分為數(shù)據(jù)段和.bbs段替蔬?因為當(dāng)加載到內(nèi)存重定位時告私,如果這些數(shù)據(jù)(包括0)一個一個的復(fù)制,會降低效率进栽,為0的變量德挣,直接清內(nèi)存就可以了,這樣提高效率快毛。
<3>在頭文件聲明全局變量時格嗅, extern int a; 聲明函數(shù)就是 void int max(int a, int b);
<4>寫程序盡量避免使用全局變量,尤其是非static類型的全局變量唠帝。能確定不會被其他文件引用的全局變量一定要static修飾屯掖。(因為全局變量占內(nèi)存的時間是最長的,要看你的變量是不是需要這么長的時間襟衰,這樣可以節(jié)約內(nèi)存空)
往期文章列表:****往期熱文:
基礎(chǔ)C語言知識串串香(1)
===========我是華麗的分割線===========
更多知識:
點(diǎn)擊關(guān)注專題:嵌入式Linux&ARM
或瀏覽器打開:http://www.reibang.com/c/42d33cadb1c1
或掃描二維碼: