日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

malloc的内存分配原理

發(fā)布時間:2025/1/21 编程问答 56 豆豆
生活随笔 收集整理的這篇文章主要介紹了 malloc的内存分配原理 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

0 堆內(nèi)存的在計算機內(nèi)存中的形式

根據(jù)《The?C Programming language》推測得到堆內(nèi)存,圖中的Heap區(qū)域即為堆內(nèi)存塊(Heap區(qū)域的數(shù)目不代表計算機堆內(nèi)存的真實數(shù)目)。

?

[1]?堆內(nèi)存不連續(xù)。只有標識為Heap的才是堆內(nèi)存。

[2] ?在malloc()/free()看來,每個Heap所代表的的堆由兩部分組成:Header +可給用戶使用的堆內(nèi)存。在Header中包含了“指向下一鄰近高地址堆內(nèi)存塊的指針”、“本堆塊的大小”。每次由malloc()函數(shù)分配給用戶的堆內(nèi)存也必須包含Header結構(且所占內(nèi)存就在返回給用戶使用的堆內(nèi)存之前),這樣是為了讓malloc()/free()更好的管理堆內(nèi)存。

[3]?malloc()/free()函數(shù)操作的堆內(nèi)存是如圖所示的一個鏈(Heap1 -> Heap2 ->Heap3 ->Heap4 ->Heap1),可通過此鏈表訪問到任意一段堆內(nèi)存。所以,經(jīng)malloc()函數(shù)實際分配得到的堆內(nèi)存要比用戶實際需求的要大一個Header,只是返回給用戶的堆內(nèi)存大小剛好是用戶所需。free()釋放時,也要根據(jù)Header的內(nèi)容將此段曾供給用戶使用過得堆內(nèi)存釋放到最鄰近的一個堆塊中去。

?

?

這就是內(nèi)存中的堆內(nèi)存。堆內(nèi)存由用戶用代碼分配及回收。堆和棧的區(qū)別不僅在于內(nèi)存的存在形式,在使用時棧一般擁有內(nèi)存名即棧內(nèi)存可以由內(nèi)存名(變量名)直接訪問,也可以通過地址(指針)訪問棧內(nèi)存。但對于堆內(nèi)存來說,堆不存在內(nèi)存名,只有通過地址(指針)訪問。

?

1堆內(nèi)存

Figure1:內(nèi)存中的堆內(nèi)存空間

假設從《The ?C ?Programming ?Language》中推測正確,從未經(jīng)動態(tài)分配的堆內(nèi)存呈現(xiàn)上圖形式。不連續(xù)的堆內(nèi)存以“鏈”的形式聯(lián)系:Heap1 -> Heap2 ->Heap3 ->Heap4->Heap1。筆跡將構成“堆鏈”的每個堆內(nèi)存(如Heap1)稱為“堆塊”。malloc()/free()將每個堆塊看作由兩部分構成:“Header”和“可用堆內(nèi)存”。在Header中包含了“指向下一個堆內(nèi)存塊的指針”、“本堆塊的大小”。這樣malloc()/free()就能更好地管理堆。

2 堆內(nèi)存分配

[1] mallco()分配機制

根據(jù)C中malloc(n)函數(shù)動態(tài)分配堆的機制:分配堆內(nèi)存的時候就依序由低到高的地址搜索“堆鏈”中的堆塊,搜索到“可用堆內(nèi)存”滿足n的堆塊(如Heap1)為止。若Heap1的“可用堆內(nèi)存”剛好滿足n,則將Heap1從“堆鏈”中刪除,同時重新組織各堆塊形成新的“堆鏈”;若Heap1的“可用堆內(nèi)存”大小大于n,則將malloc(n)申請到的“Header” + "可用堆內(nèi)存"部分從Heap1中分裂,將剩余的Heap1堆內(nèi)存塊重新加入“堆鏈”中。經(jīng)分裂后的堆內(nèi)存也包含“Header”和“可用堆內(nèi)存”兩部分(如圖Figure 2),然后將由malloc()分配得到的“可用堆內(nèi)存”返回給用戶。若某塊堆內(nèi)存空間比較大(如Heap1),能夠滿足較小內(nèi)存的多次申請,那么由malloc(n)多次申請的堆內(nèi)存塊都是連續(xù)被分配給用戶的(因為有Header,所以用戶使用的堆地址不連續(xù))。 為方便free()釋放堆空間,經(jīng)malloc(n)分配給用戶的堆空間也隱含一個Header。如下圖所示: Figure2:malloc()分配的堆內(nèi)存結構

由于Header的構成的內(nèi)存對齊,C中malloc(n)函數(shù)分配的堆內(nèi)存會大于等于Header + n。

3 malloc()分配內(nèi)存

可先參見位經(jīng)malloc()函數(shù)申請分配的堆內(nèi)存在計算機中的形式:計算機中的堆。

?

經(jīng)malloc()分配過得堆內(nèi)存結構如下:

Read From《The?C?Programming?Language》。

?

可用的堆內(nèi)存塊以“可用堆內(nèi)存鏈表”的形式存在。malloc()進行動態(tài)分配的特點:

  • malloc()根據(jù)用戶所需分配內(nèi)存的大小n (bytes)在“堆鏈表”(見未使用過得堆內(nèi)存)里搜索。直到搜索到一個大于等于n字節(jié)的堆內(nèi)存塊為止。如果此堆內(nèi)存塊的大小剛好為n,則直接將首地址返回給用戶;如果此內(nèi)存塊的大小大于n,則將此塊堆內(nèi)存分裂,將大于n部分的堆內(nèi)存留在可用堆內(nèi)存中,以“堆鏈表”的形式和其它未分配的堆內(nèi)存發(fā)生聯(lián)系。
  • 如果整個堆鏈表所代表的堆內(nèi)存塊都沒有大于等于n的堆內(nèi)存塊,系統(tǒng)將給“堆鏈表”鏈接一個更大的區(qū)域供其使用。要是這一步也失敗了,malloc()函數(shù)就返回NULL給用戶。

malloc()函數(shù)分配內(nèi)存成功則返回可用堆內(nèi)存塊的首地址,若分配失敗則返回空。在使用malloc()后一定要判斷堆內(nèi)存是否成功。若對內(nèi)存分配未成功使用指針操作內(nèi)存也會使程序出現(xiàn)異常。動態(tài)分配內(nèi)存時要采取以下結構:

?

[cpp]?view plaincopyprint?
  • char?*pL?=NULL;??
  • ……??
  • pL???????=?(char?*)malloc(?sizeof(char)?*?size);??
  • if(pL)??
  • {??
  • ??????…??
  • }??
  • ?

    分配成功后,得到的堆內(nèi)存首地址一定要保存,不然后來無法釋放堆內(nèi)存而造成內(nèi)存泄露。而且不可使用未初始化的pL指向的內(nèi)存塊。

    ?

    4 用指針來使用堆空間

    • 定義指針后,釋放堆空間后都應將指針賦值為NULL。若指針之上有地址值,而以此地址值為起始地址的內(nèi)存空間不再可用,則就形成了野指針,野指針有潛在的危險。
    • 在上一點的基礎之上,使用指針前判斷其值是否為NULL。
    • 以指針為索引(堆內(nèi)存無名),若malloc分配內(nèi)存成功,初始化堆內(nèi)存(malloc時,大小要不為0)。malloc前的強制轉換類型規(guī)定了申請的堆內(nèi)存將要存的數(shù)據(jù)類型。
    • free堆內(nèi)存后,指針保存的地址值還在,只是那塊內(nèi)存已經(jīng)被回收了,所以需要再次將指針的值設為NULL,避免使用野指針。free內(nèi)存時,按照邏輯來,防止內(nèi)存泄露。

    ?

    指針名所代表的4 bytes內(nèi)存上存了堆內(nèi)存的首地址后,訪問這塊堆內(nèi)存內(nèi)容跟平時使用指針差不多。可以以指針的形式訪問(甚用p++ || ++p,堆內(nèi)存首地址可不要丟失,留著釋放),也可以使用下標的形式訪問。


    5 free()內(nèi)存

    當使用free()函數(shù)釋放堆內(nèi)存的時候,free()函數(shù)將堆內(nèi)存插入到于要釋放堆內(nèi)存地址最鄰近的一個位置上,盡可能的使堆內(nèi)存以大塊的形式存在而不至于讓堆內(nèi)存稱為碎片。

    ?

    釋放未指向任何堆內(nèi)存塊的指針也會造成內(nèi)存泄露。所以在釋放指針前的一個基本操作是判斷指針內(nèi)容是否為空,free(p)后只是將p指向的內(nèi)存回收,p的值依舊存在,為避免再次使用p的值還需要將p賦值為NULL(因為使用指針前都會判斷是否為NULL)。釋放堆內(nèi)存塊采取這樣的程序結構:

    ?

    [cpp]?view plaincopyprint?
  • if(pL)??
  • {??
  • ??????free(pL);??
  • ??????pL??????=?NULL;??
  • }??
  • 6 指針賦值為NULL的道理

    有筆記“C中的void和NULL”表面引用NULL指針的后果。為了更好的利用指針,避免野指針(指針所指的內(nèi)存塊不可用)的使用在所有使用指向堆內(nèi)存塊的指針前都采取如此的結構:

    ?

    [cpp]?view plaincopyprint?
  • if(p)??
  • {??
  • ??????//通過指針操作堆內(nèi)存??
  • ……??
  • }??
  • 定義指針后將其值賦值為NULL。此時指針指向的內(nèi)存地址為NULL,NULL對指針的賦值是將指針置成空指針(什么也沒有指向)還是將指針指向了一段特殊的地址取決于編譯器,編程中我們不需要了解NULL到底代表什么,只需要用NULL來避免指針帶來的后果。

    ?

    定義指針后將其賦值為NULL之后的好處在于避免系統(tǒng)給予局部指針變量的隨機值,我們在使用指針前(malloc()除外)都判斷一下指針的值是否為NULL,只有在不為空的情況下才能對此進行操作,如free(p),若在不判斷p是否為空的情況下進行free(p)操作則會造成內(nèi)存泄露。

    7 在含指針參數(shù)的函數(shù)內(nèi)使用斷言

    (1)用斷言判斷指針是否為NULL

    判斷指針是否為NULL的主要針對對象是指向堆內(nèi)存的指針。比如在以下內(nèi)存拷貝函數(shù)中:

    ?

    [cpp]?view plaincopyprint?
  • flag??my_strcpy(char??*StrTo,??char??*StrFrom)??
  • {??
  • ??????if(!StrTo?||?!StrFrom)??
  • ??????{??
  • ????????????return??-1;??
  • ??????}??
  • ??????char??*StrToL,?*StrFromL;??
  • ??????StrToL??????=?StrTo;??
  • ??????StrFromL????=?StrFrom;??
  • ??????while(*StrToL++?=?*?StrFromL++)??
  • ????????????NULL;??
  • ??????return?0;??
  • }??
  • ?

    程序中首先判斷兩個地址是否為空。判斷StrTo是為了了解StrTo是否指向一段空間。當然若StrTo指向一個常數(shù),往后拷貝操作還得出錯。

    ?

    像這樣帶指針參數(shù)的子函數(shù)內(nèi)都很有必要有這么一段判斷指針是否為NULL的語句,故而可以將這樣的代碼寫成函數(shù)來供大家使用,再考慮此代碼段比較小可以用宏代替。這樣的(帶參數(shù))宏可稱為斷言,因為當指針未空時就退出子函數(shù)(如assert())。

    ?

    如以上一段判斷子函數(shù)是否為空可以用如下宏代替,形成一個斷言:

    ?

    [cpp]?view plaincopyprint?
  • #define??MY_ASSERT(pStrTo,?pStrFrom)?????if(!StrTo?||?!StrFrom)?????\??
  • ?????????????????????{??????????????????????????\??
  • ??????????????????????????????????????????????return??-1;???????????\??
  • ?????????????????????}??
  • ?

    然后在每個程序中直接調(diào)用MY_ASSERT(pStrTo, pStrFrom);即可。由于這樣的宏(斷言)可能供許多函數(shù)的使用,所以一定要保證它的正確性。

    ?

    (2)內(nèi)存塊重疊

    內(nèi)存塊重疊指多個指針指向的內(nèi)存有重疊的情況。對內(nèi)存塊的操作是否會影響源內(nèi)存塊的內(nèi)容(如內(nèi)存數(shù)據(jù)拷貝)。

    兩指針指向的內(nèi)存塊重疊

    如上圖將p2指向內(nèi)存的數(shù)據(jù)拷貝給p1代表的內(nèi)存中去后,p2指向的內(nèi)存塊數(shù)據(jù)也被改變。堆內(nèi)存塊的操作不要有副作用。

    ?

    8 總結

    (1)用NULL(因其特殊性)來統(tǒng)一標識指針的可用性。使用指針前都應該判斷一下指針是否為NULL。

    (2)將局部指針變量初始化為NULL(消除系統(tǒng)給其賦的隨機值,系統(tǒng)為其賦隨機值也就造就了野指針)。

    (3)指針用于指向堆內(nèi)存時需要注意:

    • malloc()后一定要判斷是否malloc()成功。malloc()成功后一定要保存所分配堆內(nèi)存塊的首地址。

    • 使用堆內(nèi)存塊前要初始化。

    • 使用堆內(nèi)存塊不可越界。

    • 正確釋放每個堆內(nèi)存塊。且釋放后將指針的值重新賦值為NULL。

    (4)使用指針前都應該判斷一下指針是否為NULL。

    ?

    ?

    完全使用完某個指針或釋放指向堆內(nèi)存的指針后,將其值賦值為空。指向堆內(nèi)存的指針在釋放完需要賦值為空的理由見free ()堆內(nèi)存。對于指針定義時初始化和完全使用完指針后再將其值賦為NULL的道理在于所有使用指針的語句前都會有有判斷指針是否為NULL的語句。尤其是在子函數(shù)內(nèi)判斷指向堆內(nèi)存塊的指針實參是否為NULL。


    9 malloc()分配總結

    對于C中的malloc(n)分配,有以下進一步的結論:

    • 實際分配的堆內(nèi)存是Header + n結構。返回給用戶的是n部分的首地址。
    • 由于內(nèi)存對齊值8,實際分配的堆內(nèi)存大于等于sizeof(Header) + n。
    所以在編程過程中,需要用內(nèi)存對齊的知識合理的讓malloc()分配的內(nèi)存變得小一些。 轉載:http://blog.csdn.net/misskissc/article/details/17717717

    總結

    以上是生活随笔為你收集整理的malloc的内存分配原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。