上一篇我們分享了棧內(nèi)存的概念,現(xiàn)在我們分享下堆內(nèi)存的概念。
在一般的編譯系統(tǒng)中,堆內(nèi)存的分配方向和棧內(nèi)存是相反的。當(dāng)棧內(nèi)存從高地址向低地址增長的時候,堆內(nèi)存從低地址向高地址分配。
在C語言中,堆內(nèi)存在分配和釋放的時候,是程序通過調(diào)用C語言的庫函數(shù)完成的。這和棧內(nèi)存的分配有區(qū)別,棧內(nèi)存利用的是處理器的硬件機(jī)制,而堆內(nèi)存的處理使用的是庫函數(shù)。
我們來看下堆內(nèi)存的分配情況:
在堆內(nèi)存的分配過程中,每次分配將返回一個當(dāng)前分配地址的指針。在程序中如果多次分配內(nèi)存,可以得到多個內(nèi)存指針,每個內(nèi)存指針都是本次分配內(nèi)存的地址。在釋放內(nèi)存的時候,只需要對每個指針進(jìn)行操作,那個指針?biāo)赶虻膬?nèi)存就會被釋放,而對其他的內(nèi)存區(qū)域沒有影響。
從內(nèi)存的分配和使用上,可以看出棧內(nèi)存和堆內(nèi)存的區(qū)別:棧內(nèi)存只有一個入口點,就是棧指針,棧內(nèi)存壓入和彈出的時候棧指針將發(fā)生變化,棧指針標(biāo)識當(dāng)前棧區(qū)域中已使用和未使用的界限,程序在訪問棧內(nèi)存的時候都只能通過棧指針及其偏移量;而堆內(nèi)存有多個入口點,每次分配得到的指針是訪問內(nèi)存的入口,每個分配內(nèi)存區(qū)域都可以被單獨釋放,程序?qū)Χ褍?nèi)存可以通過每次分配得到的指針訪問。
堆內(nèi)存有一個整體分配的過程,按照向上的堆內(nèi)存分配方向。隨著堆內(nèi)存使用量的增加,堆內(nèi)存將逐漸向高地址分配。這只是一個大體的增長的方面,在堆內(nèi)存中,已使用的區(qū)域和未使用的區(qū)域是交錯的,而不是像棧區(qū)域那樣有明顯的分界線。
堆內(nèi)存的釋放看下面這個圖:
看到這樣頻繁的使用區(qū)域和釋放,那么很容易看出堆內(nèi)存是不連續(xù)的,跟堆內(nèi)存的使用方式有關(guān)系,這個分配就相對自由靈活了,但是也是會在低地址向高地址發(fā)展的方向分配的。
比如上面釋放后再分配就可以是下面兩種情況:
先看再次分配1的情況:當(dāng)新分配的需求比中間(剛剛釋放)區(qū)域小,那么就會在緊接著的區(qū)域給分配。
再看再次分配2的情況:當(dāng)新分配的需求比中間(釋放的)區(qū)域大,那么只能往后尋求能給的區(qū)域。
當(dāng)頻繁的分配和釋放內(nèi)存的過程中,會很容易出現(xiàn)在兩塊已經(jīng)分配的內(nèi)存之間較小的未分配內(nèi)存區(qū)域,這些其實可以用,但是由于他們的空間比較小,不夠連續(xù)內(nèi)存的分配,所以分配的時候就很難再次使用,這些較小的內(nèi)存就是我們常說的內(nèi)存碎片。
我們再來聊一下在C程序中堆空間的使用。
在C語言中,堆內(nèi)存區(qū)域的分配和釋放是通過調(diào)用庫函數(shù)來完成的,實現(xiàn)的函數(shù)主要有四個:
void *malloc(size_t size); //分配內(nèi)存空間
void free(void *ptr); //釋放內(nèi)存空間
void *calloc(size_t nmemb,size_t size); //分配內(nèi)存空間
void *realloc(void * ptr,size_t size); //重新分配內(nèi)存空間
注意:使用上面這幾個函數(shù)需要包含標(biāo)準(zhǔn)庫文件
那么庫函數(shù)怎么使用呢,內(nèi)存分配了就要有釋放,那么常用的就是malloc()和free()兩個函數(shù)。malloc()函數(shù)的輸入是需要分配內(nèi)存的大小,輸出是分配內(nèi)存的指針。如果分配不成功,則返回NULL。
free()函數(shù)的輸入是需要釋放的指針,可以接受任何形式的指針。這個指針必須是由分配函數(shù)分配出來的。
例如:
int *pa;
pa = (int *)malloc(sizeof(int));//分配一個int大小的指針
if(NULL != pa)
{
free(pa);
}
內(nèi)存使用完成需要釋放,以便分配給其他程序使用。
calloc()也是內(nèi)存分配的,只是可以把分配好的內(nèi)存區(qū)域的初始值全部設(shè)置為0。還有這個分配內(nèi)存有兩個參數(shù),第一個是分配單元的大小,第二個是要分配的數(shù)目。
malloc(sizeof(unsigned int)*10); == calloc(sizeof(unsigned int),10)
realloc()有兩個參數(shù),一個是指向內(nèi)存的地址指針,一個是要重分配內(nèi)存的大小,返回值是指向所分配內(nèi)存的指針。
1、當(dāng)參數(shù)指針為NULL的時候,作為malloc使用,分配內(nèi)存。
2、當(dāng)重分配內(nèi)存大小為0的時候,作為free使用,釋放內(nèi)存。
3、當(dāng)指針和重分配內(nèi)存大小均不為0的時候,根據(jù)指針指向的堆內(nèi)存區(qū)域的情況和指針大小重新分配內(nèi)存。
對于realloc()作為重新分配內(nèi)存的時候,有三種可能出現(xiàn):
1、縮小內(nèi)存
2、擴(kuò)大內(nèi)存,不需要移動指針
3、擴(kuò)大內(nèi)存,需要移動指針(指定內(nèi)存區(qū)域大小不夠)
在堆內(nèi)存的管理上,主要容易出現(xiàn)以下幾個問題:
1、開辟的內(nèi)存沒有釋放,造成內(nèi)存泄漏(系統(tǒng)不會釋放任何用戶分配的內(nèi)存)
2、野指針被使用或釋放(內(nèi)存釋放后,需要將內(nèi)存指針置為NULL)
3、非法釋放指針(分配了有效內(nèi)存才存在釋放,否則是非法的)
在C語言語法的方面對棧內(nèi)存和堆內(nèi)存如何使用沒有限制。然后從使用的角度,棧內(nèi)存更適用于容量較小的單個變量(例如:C語言的基本變量類型、較小的結(jié)構(gòu)體和數(shù)組),堆內(nèi)存則適用于開辟較大塊的內(nèi)存。棧內(nèi)存由編譯器分配和釋放,堆內(nèi)存由程序員分配和釋放。
責(zé)任編輯:lq6
-
內(nèi)存
+關(guān)注
關(guān)注
8文章
3019瀏覽量
74005 -
C語言
+關(guān)注
關(guān)注
180文章
7604瀏覽量
136694
原文標(biāo)題:堆內(nèi)存的那些事
文章出處:【微信號:gh_e7f294a514ca,微信公眾號:單片機(jī)匠人】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論