C/C++編譯的程序占用的內(nèi)存分為以下幾個部分
1柄慰、棧區(qū)(stack)—由編譯器自動分配釋放,存放函數(shù)的參數(shù)值,局部變量的值等食侮。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧荷并。
2合砂、堆區(qū)(heap)—一般由程序員分配釋放,若程序員不釋放源织,程序結(jié)束時可能由OS回收翩伪。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事微猖,分配方式倒是類似于鏈表。
棧(stack)和堆(heap)比較
1.申請方式和回收方式不同
棧是系統(tǒng)自動分配空間的缘屹,例如我們定義一個 char a凛剥;系統(tǒng)會自動在棧上為其開辟空間。而堆則是程序員根據(jù)需要自己申請的空間轻姿,例如malloc(10)犁珠;開辟十個字節(jié)的空間。由于棧上的空間是自動分配自動回收的踢代,所以棧上的數(shù)據(jù)的生存周期只是在函數(shù)的運(yùn)行過程中盲憎,運(yùn)行后就釋放掉,不可以再訪問胳挎。而堆上的數(shù)據(jù)只要程序員不釋放空間饼疙,就一直可以訪問到,不過缺點(diǎn)是一旦忘記釋放會造成內(nèi)存泄露慕爬。
2.申請后系統(tǒng)的響應(yīng)
棧:只要棧的剩余空間大于所申請空間窑眯,系統(tǒng)將為程序提供內(nèi)存,否則將報異常提示棧溢出医窿。
堆:首先應(yīng)該知道操作系統(tǒng)有一個記錄空閑內(nèi)存地址的鏈表磅甩,當(dāng)系統(tǒng)收到程序的申請時,會遍歷該鏈表姥卢,尋找第一個空間大于所申請空間的堆結(jié)點(diǎn)卷要,然后將該結(jié)點(diǎn)從空閑結(jié)點(diǎn)鏈表中刪除,并將該結(jié)點(diǎn)的空間分配給程序独榴,另外僧叉,對于大多數(shù)系統(tǒng),會在這塊內(nèi)存空間中的首地址處記錄本次分配的大小棺榔,這樣才能正確的釋放本內(nèi)存空間瓶堕。另外,由于找到的堆結(jié)點(diǎn)的大小不一定正好等于申請的大小症歇,系統(tǒng)會自動的將多余的那部分重新放入空閑鏈表中郎笆。也就是說堆會在申請后還要做一些后續(xù)的工作這就會引出申請效率的問題。
2.申請效率的比較
根據(jù)第0點(diǎn)和第1點(diǎn)可知忘晤。
棧:棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu)宛蚓,計算機(jī)會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執(zhí)行设塔,這就決定了棧的效率比較高苍息。
堆:是由new分配的內(nèi)存,一般速度比較慢,它是通過鏈表的方式尋找可用的內(nèi)存空間竞思,容易產(chǎn)生內(nèi)存碎片,不過用起來最方便表谊,效率低。
3.申請大小的限制
棧:在Windows下,棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)盖喷,是一塊連續(xù)的內(nèi)存的區(qū)域爆办。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的,在 WINDOWS下课梳,棧的大小是2M(也有的說是1M距辆,總之是一個編譯時就確定的常數(shù)),如果申請的空間超過棧的剩余空間時暮刃,將提示overflow跨算。因此,能從棧獲得的空間較小椭懊。
堆:堆是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)诸蚕,是不連續(xù)的內(nèi)存區(qū)域。這是由于系統(tǒng)是用鏈表來存儲的空閑內(nèi)存地址的氧猬,自然是不連續(xù)的背犯,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機(jī)系統(tǒng)中有效的虛擬內(nèi)存盅抚。由此可見漠魏,堆獲得的空間比較靈活,也比較大妄均。
4.堆和棧中的存儲內(nèi)容
棧: 在函數(shù)調(diào)用時柱锹,第一個進(jìn)棧的是主函數(shù)中函數(shù)調(diào)用后的下一條指令(函數(shù)調(diào)用語句的下一條可執(zhí)行語句)的地址,然后是函數(shù)的各個參數(shù)丰包,在大多數(shù)的C編譯器中奕纫,參數(shù)是由右往左入棧的,然后是函數(shù)中的局部變量烫沙。注意靜態(tài)變量是不入棧的。
當(dāng)本次函數(shù)調(diào)用結(jié)束后隙笆,局部變量先出棧锌蓄,然后是參數(shù),最后棧頂指針指向最開始存的地址撑柔,也就是主函數(shù)中的下一條指令瘸爽,程序由該點(diǎn)繼續(xù)運(yùn)行。
堆:一般是在堆的頭部用一個字節(jié)存放堆的大小铅忿。堆中的具體內(nèi)容有程序員安排剪决。
5.分配方式
堆都是動態(tài)分配的,沒有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動態(tài)分配柑潦。靜態(tài)分配是編譯器完成的享言,比如局部變量的分配。動態(tài)分配由alloca函數(shù)進(jìn)行分配渗鬼,但是棧的動態(tài)分配和堆是不同的览露,他的動態(tài)分配是由編譯器進(jìn)行釋放,無需我們手工實(shí)現(xiàn)譬胎。
3差牛、全局區(qū)(靜態(tài)區(qū))(static)—,全局變量和靜態(tài)變量的存儲是放在一塊的堰乔,初始化的全局變量和靜態(tài)變量在一塊區(qū)域偏化,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。
- 程序結(jié)束后由系統(tǒng)釋放镐侯。
4侦讨、文字常量區(qū)—常量字符串就是放在這里的。程序結(jié)束后由系統(tǒng)釋放
5析孽、程序代碼區(qū)—存放函數(shù)體的二進(jìn)制代碼搭伤。
代碼演示
int a = 0; 全局初始化區(qū)
char *p1; 全局未初始化區(qū)
{int b; 棧
char *p3 = "123456"; 123456\0在常量區(qū),p3在棧上袜瞬。
static int c =0怜俐; 全局(靜態(tài))初始化區(qū)
p1 = (char *)malloc(10); p1指向的數(shù)據(jù)分配在堆區(qū)(p1本身分配在靜態(tài)區(qū))。
}
接下來我們探討一下c語言中char *str 與char str[]有什么區(qū)別
char* test(){
char * str = "abcdefg";
return str;
}
等號右邊存放在常量區(qū)邓尤,等號左邊的str是局部變量所以存放在棧內(nèi)存拍鲤,str指向存放"abcdefg"的內(nèi)存區(qū)域。函數(shù)結(jié)束時這塊區(qū)域并不會釋放汞扎。
char* test(){
char? str[] = "abcdefg";
return str;
}
等號右邊依然存放在常量區(qū)季稳,等號左邊是一個字符型數(shù)組,盡管是數(shù)組澈魄,但它是一個局部變量景鼠,所以會在棧區(qū)分配一塊內(nèi)存存儲這個數(shù)組,然后將常量區(qū)的"abcdefg"拷貝過來痹扇,這樣就會導(dǎo)致函數(shù)執(zhí)行完畢之后這塊內(nèi)存會被釋放掉铛漓,那么這個數(shù)組也就不存在了。
總結(jié)
使用棧就象我們?nèi)ワ堭^里吃飯鲫构,只管點(diǎn)菜(發(fā)出申請)浓恶、付錢、和吃(使用)结笨,吃飽了就走包晰,不必理會切菜湿镀、洗菜等準(zhǔn)備工作和洗碗、刷鍋等掃尾工作伐憾,他的好處是快捷勉痴,但是自由度小。
使用堆就象是自己動手做喜歡吃的菜肴塞耕,比較麻煩蚀腿,但是比較符合自己的口味,而且自由度大扫外。