C語言有幾個(gè)關(guān)鍵字,,在定義一個(gè)變量或者一個(gè)函數(shù)的時(shí)候,指定其存儲(chǔ)區(qū)域類型,,被稱為存儲(chǔ)類關(guān)鍵字,,它們是:
static,extern,,register 和 auto
下面逐一講解,。
1,,static
其實(shí)這個(gè)關(guān)鍵字有三個(gè)作用,而不僅僅是存儲(chǔ)類型,。請看下面代碼:
// 1.修飾函數(shù),,使其只能在本文件可見 static void func(void) { static int n = 0; // 2. 修飾局部變量,使其存儲(chǔ)在靜態(tài)區(qū)(存儲(chǔ)類型) printf("%d\n", n); }
static int global; // 3. 修飾全局變量,,使其只能在本文件可見
注意到,,static在C語言中的三個(gè)作用,其中第1和第3個(gè)作用其實(shí)都是一樣的,,改變的是函數(shù)或者變量的可見范圍,。只有當(dāng)用static來修飾局部變量的時(shí)候,它的作用才是代表一個(gè)存儲(chǔ)區(qū)域,。
2,,extern
extern關(guān)鍵字用來修飾一個(gè)標(biāo)識(shí)符(變量或者函數(shù))的聲明,表明該標(biāo)識(shí)符在“別的地方”有定義,,參考以下代碼:
extern int a; // 聲明一個(gè)“別處”定義的全局變量,,此處extern不可省略 extern int f(); // 聲明一個(gè)“別處”定義的函數(shù),此處extern可省略
“別處”的意思僅僅指不在當(dāng)前位置,,可能是別的文件,,也可能是該文件的其他地方。注意,,如果聲明的是一個(gè)變量,,那只能是全局變量,局部變量不能用extern來聲明,。
3,,register
顧名思義,register用來修飾一個(gè)希望存放在寄存器中的變量,,但是這僅僅是對系統(tǒng)的一個(gè)建議,寄存器是一種稀缺資源,,能不能滿足要求要看系統(tǒng)當(dāng)時(shí)的情況,。但是,不管系統(tǒng)是否真的將變量存入寄存器,,程序都不能對該變量進(jìn)行取址運(yùn)算。另外,,并不是什么變量都可以定義為register類型,,因?yàn)榧拇嫫鞯拇笮「鶦PU的字長相關(guān),一般在32位系統(tǒng)當(dāng)中,,寄存器也是32位的,因此可以將小于等于32位的變量聲明為register型,。
被聲明位register類型的變量一般是因?yàn)槌绦驅(qū)ζ渥x寫性能很敏感,比如在底層操作系統(tǒng)中,,要頻繁地對某個(gè)變量進(jìn)行讀寫,,而且這個(gè)變量的讀寫速度直接影響到了程序的性能,是整個(gè)程序的熱點(diǎn)也是瓶頸,,那么就很有必要將其聲明為register存儲(chǔ)型,。
4,auto
auto這個(gè)關(guān)鍵字就像signed一樣,,一般都會(huì)被省略,。因?yàn)閍uto的含義是將一個(gè)變量存儲(chǔ)在“自動(dòng)”存儲(chǔ)區(qū),所謂的自動(dòng)存儲(chǔ)區(qū)就是進(jìn)程的??臻g,,而普通的局部變量默認(rèn)就是存儲(chǔ)在 棧空間的,,所以沒有必要再用auto來修飾,。而auto又不能修飾全局變量,因?yàn)槿肿兞恳欢ㄊ谴鎯?chǔ)在靜態(tài)區(qū)的,。請看下面的分析:
// 所有的全局變量統(tǒng)統(tǒng)存儲(chǔ)在靜態(tài)區(qū) int g1; static int g2; // static 跟存儲(chǔ)類型無關(guān),,它改變的是g2的可見范圍。
int main(void) { int a; // 不需要auto修飾,,默認(rèn)就是自動(dòng)變量,,存儲(chǔ)在棧空間 static int b; // 被static修飾,,存儲(chǔ)在靜態(tài)區(qū) }
下面是每一種變量在內(nèi)存空間中的分布情況:
注意上圖中的用戶棧和運(yùn)行時(shí)堆,,它們是會(huì)在程序運(yùn)行時(shí)動(dòng)態(tài)變化的,里面的變量時(shí)而誕生時(shí)而消亡,,具體而言指的是:在棧中存在的變量都是局部變量(包括函數(shù)的形參),,其生命周期是從定義它的語句開始,到離開其作用域?yàn)橹?。在堆中存在的變量都是所謂的動(dòng)態(tài)內(nèi)存,,這些內(nèi)存的生命周期都是由程序員指定的,從malloc()分配某塊內(nèi)存開始,,到free()釋放這塊內(nèi)存結(jié)束,。堆和棧分別向上和向下的箭頭代表它們在內(nèi)存中的增長的趨勢。
相對地,,讀寫段(即靜態(tài)區(qū)),、代碼段,、常量區(qū)等內(nèi)存區(qū)域則在程序運(yùn)行期間是“穩(wěn)定”的,即不會(huì)被釋放,,這些區(qū)域的內(nèi)容將會(huì)被一直保留,,直到整個(gè)程序退出為止。
這里來著重強(qiáng)調(diào)一下堆和棧,。
對于棧而言,,要明確的一點(diǎn)是棧空間的大小是有限的,,通常來講,,對于一個(gè)32位的系統(tǒng)而言,一個(gè)進(jìn)程的默認(rèn)??臻g是8M,,如果是嵌入式LINUX系統(tǒng),其??臻g的大小可能經(jīng)過調(diào)整會(huì)更小,,比如1M。(如果是多線程,,多個(gè)線程的??臻g會(huì)一字排開,但是中間用零權(quán)限內(nèi)存來構(gòu)建一個(gè)警戒區(qū)加以保護(hù)),。而??臻g存放的是局部變量,因此,,以下情況不適合使用局部變量:
第一,,巨大的變量。比如一個(gè)2000個(gè)元素的數(shù)組,,那么大的數(shù)據(jù)量很有可能使得棧溢出,。
第二,動(dòng)態(tài)的數(shù)據(jù),。比如一個(gè)動(dòng)態(tài)增長的鏈表,,雖然程序運(yùn)行初時(shí)不大,但是程序無法預(yù)料以后該鏈表的節(jié)點(diǎn)數(shù)目,,像這種動(dòng)態(tài)的數(shù)據(jù)也不能用??臻g來存放。
由于??臻g的分配跟釋放是由系統(tǒng)自動(dòng)完成的,,其執(zhí)行效率高速度快,所以適合用來存放一些可以被臨時(shí)釋放的數(shù)據(jù),。
而堆空間,,則是程序運(yùn)行時(shí)大部分?jǐn)?shù)據(jù)的真正的演練場,,這部分空間的分配和釋放完全由程序員來控制,因此也叫做內(nèi)存的自由區(qū),,自由意味著謹(jǐn)慎,,如果只分配不釋放,內(nèi)存就會(huì)很快被用光,,因此,,堆空間是一個(gè)需要你更加負(fù)責(zé)任的地方,請看下面的代碼:
char * func(void) { char *p = malloc(100); // 在堆空間中申請一塊100bytes的內(nèi)存 return p; // 返回后,,堆內(nèi)存不會(huì)因?yàn)楹瘮?shù)的退出而釋放 }
int main(void) { char *k; k = func(); if(k == NULL) return -1; // 堆空間申請失敗,! ... // 堆空間申請成功,,并使用這塊內(nèi)存 ... free(k); // 釋放這塊內(nèi)存 }
動(dòng)態(tài)內(nèi)存的使用有幾點(diǎn)要注意:
1,使用完畢之后,,一定要釋放,。
2,如果不釋放,,那么該塊內(nèi)存只能等到程序全部退出之后才能被釋放,。
3,free() 只能釋放動(dòng)態(tài)內(nèi)存(即由malloc( ) 或者 calloc() 或者 realloc() 分配的內(nèi)存),。
4,,訪問動(dòng)態(tài)內(nèi)存的唯一方式是采用指針,因?yàn)閯?dòng)態(tài)內(nèi)存是匿名的,。
以上代碼中的第3行,,用一個(gè)char型指針來索引這塊動(dòng)態(tài)內(nèi)存,是因?yàn)閏har型指針可以很方便地遍歷內(nèi)存中的每一個(gè)字節(jié),。如果這塊內(nèi)存是用來存放特殊的數(shù)據(jù)類型,,就用需要的類型指針來遍歷即可。
|