內(nèi)存的靜態(tài)分配和動態(tài)分配的區(qū)別主要是兩個:
一是時間不同。靜態(tài)分配發(fā)生在程序編譯和連接的時候。動態(tài)分配則發(fā)生在程序調(diào)入和執(zhí)行的時候。 二是空間不同。堆都是動態(tài)分配的,沒有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動態(tài)分配由函數(shù)malloc進(jìn)行分配。不過棧的動態(tài)分配和堆不同,他的動態(tài)分配是由編譯器進(jìn)行釋放,無需我們手工實現(xiàn)。 對于一個進(jìn)程的內(nèi)存空間而言,可以在邏輯上分成3個部份:代碼區(qū),靜態(tài)數(shù)據(jù)區(qū)和動態(tài)數(shù)據(jù)區(qū)。動態(tài)數(shù)據(jù)區(qū)一般就是“堆?!??!皸?stack)”和“堆(heap)”是兩種不同的動態(tài)數(shù)據(jù)區(qū),棧是一種線性結(jié)構(gòu),堆是一種鏈?zhǔn)浇Y(jié)構(gòu)。進(jìn)程的每個線程都有私有的“?!?,所以每個線程雖然代碼一樣,但本地變量的數(shù)據(jù)都是互不干擾。一個堆??梢酝ㄟ^“基地址”和“棧頂”地址來描述。全局變量和靜態(tài)變量分配在靜態(tài)數(shù)據(jù)區(qū),本地變量分配在動態(tài)數(shù)據(jù)區(qū),即堆棧中。程序通過堆棧的基地址和偏移量來訪問本地變量。
一般,用static修飾的變量,全局變量位于靜態(tài)數(shù)據(jù)區(qū)。函數(shù)調(diào)用過程中的參數(shù),返回地址,EBP和局部變量都采用棧的方式存放。
所謂動態(tài)內(nèi)存分配就是指在程序執(zhí)行的過程中動態(tài)地分配或者回收存儲空間的分配內(nèi)存的方法。動態(tài)內(nèi)存分配不象數(shù)組等靜態(tài)內(nèi)存分配方法那樣需要預(yù)先分配存儲空間,而是由系統(tǒng)根據(jù)程序的需要即時分配,且分配的大小就是程序要求的大小。
例如我們定義一個float型數(shù)組:float score[100]; 但是,在使用數(shù)組的時候,總有一個問題困擾著我們:數(shù)組應(yīng)該有多大?在很多的情況下,你并不能確定要使用多大的數(shù)組,比如上例,你可能并不知道我們要定義的這個數(shù)組到底有多大,那么你就要把數(shù)組定義得足夠大。這樣,你的程序在運行時就申請了固定大小的你認(rèn)為足夠大的內(nèi)存空間。即使你知道你想利用的空間大小,但是如果因為某種特殊原因空間利用的大小有增加或者減少,你又必須重新去修改程序,擴(kuò)大數(shù)組的存儲范圍。這種分配固定大小的內(nèi)存分配方法稱之為靜態(tài)內(nèi)存分配。但是這種內(nèi)存分配的方法存在比較嚴(yán)重的缺陷,特別是處理某些問題時:在大多數(shù)情況下會浪費大量的內(nèi)存空間,在少數(shù)情況下,當(dāng)你定義的數(shù)組不夠大時,可能引起下標(biāo)越界錯誤,甚至導(dǎo)致嚴(yán)重后果。 我們用動態(tài)內(nèi)存分配就可以解決上面的問題. 所謂動態(tài)內(nèi)存分配就是指在程序執(zhí)行的過程中動態(tài)地分配或者回收存儲空間的分配內(nèi)存的方法。動態(tài)內(nèi)存分配不象數(shù)組等靜態(tài)內(nèi)存分配方法那樣需要預(yù)先分配存儲空間,而是由系統(tǒng)根據(jù)程序的需要即時分配,且分配的大小就是程序要求的大小。從以上動、靜態(tài)內(nèi)存分配比較可以知道動態(tài)內(nèi)存分配相對于景泰內(nèi)存分配的特點: 1、不需要預(yù)先分配存儲空間; 2、分配的空間可以根據(jù)程序的需要擴(kuò)大或縮小。 要實現(xiàn)根據(jù)程序的需要動態(tài)分配存儲空間,就必須用到malloc函數(shù). malloc函數(shù)的原型為:void *malloc (unsigned int size) 其作用是在內(nèi)存的動態(tài)存儲區(qū)中分配一個長度為size的連續(xù)空間。其參數(shù)是一個無符號整形數(shù),返回值是一個指向所分配的連續(xù)存儲域的起始地址的指針。還有一點必須注意的是,當(dāng)函數(shù)未能成功分配存儲空間(如內(nèi)存不足)就會返回一個NULL指針。所以在調(diào)用該函數(shù)時應(yīng)該檢測返回值是否為NULL并執(zhí)行相應(yīng)的操作。 靜態(tài)內(nèi)存是在程序一開始運行就會分配內(nèi)存,直到程序結(jié)束了,內(nèi)存才被釋放。
動態(tài)內(nèi)存是在程序調(diào)用在程序中定義的函數(shù)時才被分配,函數(shù)調(diào)用結(jié)束了,動態(tài)內(nèi)存就釋放。 static int a;這是定義了一個靜態(tài)的變量 int a;這是定義了一個動態(tài)的變量; 靜態(tài)內(nèi)存可以用于求階層。 例如: jiechen(int i) {static int a=1; for(;a<=i,a++) return a*i; } #include"stdio.h" main() {int a,i; printf("enter number:") scanf("%d",&a); for(i=1;i<=a;i++) printf("i!=%d\n",jiechen(i)); } 運行 輸入3 結(jié)果為1!=1 由malloc系統(tǒng)函數(shù)分配的內(nèi)存就是從堆上分配內(nèi)存。從堆上分配的內(nèi)存一定要自己釋放。用free釋放,不然就是術(shù)語——“內(nèi)存泄露”(或是“內(nèi)存漏洞”)—— Memory Leak。于是,系統(tǒng)的可分配內(nèi)存會隨malloc越來越少,直到系統(tǒng)崩潰。還是來看看“棧內(nèi)存”和“堆內(nèi)存”的差別吧。
棧內(nèi)存分配 ————— char* AllocStrFromStack() { char pstr[100]; return pstr; } 堆內(nèi)存分配 ————— char* AllocStrFromHeap(int len) { char *pstr; if ( len <= 0 ) return NULL; return ( char* ) malloc( len ); } 對于第一個函數(shù),那塊pstr的內(nèi)存在函數(shù)返回時就被系統(tǒng)釋放了。于是所返回的char*什么也沒有。而對于第二個函數(shù),是從堆上分配內(nèi)存,所以哪怕是程序退出時,也不釋放,所以第二個函數(shù)的返回的內(nèi)存沒有問題,可以被使用。但一定要調(diào)用free釋放,不然就是Memory Leak! 在堆上分配內(nèi)存很容易造成內(nèi)存泄漏,這是C/C++的最大的“克星”,如果你的程序要穩(wěn)定,那么就不要出現(xiàn)Memory Leak。所以,我還是要在這里千叮嚀萬囑付,在使用malloc系統(tǒng)函數(shù)(包括calloc,realloc)時千萬要小心。 記得有一個UNIX上的服務(wù)應(yīng)用程序,大約有幾百的C文件編譯而成,運行測試良好,等使用時,每隔三個月系統(tǒng)就是down一次,搞得許多人焦頭爛額,查不出問題所在。只好,每隔兩個月人工手動重啟系統(tǒng)一次。出現(xiàn)這種問題就是Memery Leak在做怪了,在C/C++中這種問題總是會發(fā)生,所以你一定要小心。一個Rational的檢測工作——Purify,可以幫你測試你的程序有沒有內(nèi)存泄漏。 我保證,做過許多C/C++的工程的程序員,都會對malloc或是new有些感冒。當(dāng)你什么時候在使用malloc和new時,有一種輕度的緊張和惶恐的感覺時,你就具備了這方面的修養(yǎng)了。 對于malloc和free的操作有以下規(guī)則:
1) 配對使用,有一個malloc,就應(yīng)該有一個free。(C++中對應(yīng)為new和delete) 2) 盡量在同一層上使用,不要像上面那種,malloc在函數(shù)中,而free在函數(shù)外。最好在同一調(diào)用層上使用這兩個函數(shù)。 3) malloc分配的內(nèi)存一定要初始化。free后的指針一定要設(shè)置為NULL。 注:雖然現(xiàn)在的操作系統(tǒng)(如:UNIX和Win2k/NT)都有進(jìn)程內(nèi)存跟蹤機(jī)制,也就是如果你有沒有釋放的內(nèi)存,操作系統(tǒng)會幫你釋放。但操作系統(tǒng)依然不會釋放你程序中所有產(chǎn)生了Memory Leak的內(nèi)存,所以,最好還是你自己來做這個工作。(有的時候不知不覺就出現(xiàn)Memory Leak了,而且在幾百萬行的代碼中找無異于海底撈針,Rational有一個工具叫Purify,可能很好的幫你檢查程序中的Memory Leak) 第一個例子也講得不清楚。所謂系統(tǒng)釋放,應(yīng)該是指系統(tǒng)在自己的表里把這段內(nèi)存標(biāo)記為可以使用,以后可以被別的程序使用,所以第一個例子會造成程序能訪問到已經(jīng)釋放的內(nèi)存空間,是越界,會造成不可預(yù)測的情況。
系統(tǒng)一般不會自動去清除釋放空間內(nèi)的數(shù)據(jù),而是由以后的程序來覆蓋。所以很多程序開頭都會做MEMSET(...),就是為了防止這種垃圾數(shù)據(jù)的情況。 如果在程序運行中要改變內(nèi)存塊的大小,可以用RALLOC()函數(shù),它能在原來地址上重新分配一塊空間,不過是用的時候要小心,也是比較容易出問題 |
|