堆區(heap): 用于動態記憶體分配,堆在記憶體中位于bss區和堆疊區之間,一般由程式員分配和釋放,若程式員不釋放,程式結束時有可能由OS回收,·
-
3.1.2 堆疊和堆的區別
前面已經介紹過,堆疊是由編譯器在需要時分配的,不需要時自動清除的變數存盤區,里面的變數通常是區域變數、函式引數等,堆是由malloc()函式(C++語言為new運算子)分配的記憶體塊,記憶體釋放由程式員手動控制,在C語言為free函式完成(C++中為delete),堆疊和堆的主要區別有以下幾點:
(1)管理方式不同,
堆疊編譯器自動管理,無需程式員手工控制;而堆空間的申請釋放作業由程式員控制,容易產生記憶體泄漏,
(2)空間大小不同,
堆疊是向低地址擴展的資料結構,是一塊連續的記憶體區域,這句話的意思是堆疊頂的地址和堆疊的最大容量是系統預先規定好的,當申請的空間超過堆疊的剩余空間時,將提示溢位,因此,用戶能從堆疊獲得的空間較小,
堆是向高地址擴展的資料結構,是不連續的記憶體區域,因為系統是用鏈表來存盤空閑記憶體地址的,且鏈表的遍歷方向是由低地址向高地址,由此可見,堆獲得的空間較靈活,也較大,堆疊中元素都是一一對應的,不會存在一個記憶體塊從堆疊中間彈出的情況,
(3)是否產生碎片,
對于堆來講,頻繁的malloc/free(new/delete)勢必會造成記憶體空間的不連續,從而造成大量的碎片,使程式效率降低(雖然程式在退出后作業系統會對記憶體進行回收管理),對于堆疊來講,則不會存在這個問題,
(4)增長方向不同,
堆的增長方向是向上的,即向著記憶體地址增加的方向;堆疊的增長方向是向下的,即向著記憶體地址減小的方向,
(5)分配方式不同,
堆都是程式中由malloc()函式動態申請分配并由free()函式釋放的;堆疊的分配和釋放是由編譯器完成的,堆疊的動態分配由alloca()函式完成,但是堆疊的動態分配和堆是不同的,他的動態分配是由編譯器進行申請和釋放的,無需手工實作,
(6)分配效率不同,
堆疊是機器系統提供的資料結構,計算機會在底層對堆疊提供支持:分配專門的暫存器存放堆疊的地址,壓堆疊出堆疊都有專門的指令執行,堆則是C函式庫提供的,它的機制很復雜,例如為了分配一塊記憶體,庫函式會按照一定的演算法(具體的演算法可以參考資料結構/作業系統)在堆記憶體中搜索可用的足夠大的空間,如果沒有足夠大的空間(可能是由于記憶體碎片太多),就有需要作業系統來重新整理記憶體空間,這樣就有機會分到足夠大小的記憶體,然后回傳,顯然,堆的效率比堆疊要低得多,
作業系統堆管理器管理:
堆管理器是作業系統的一個模塊,堆管理記憶體分配靈活,按需分配,
大塊記憶體:
堆記憶體管理者總量很大的作業系統記憶體塊,各行程可以按需申請使用,使用完釋放,
程式手動申請&釋放:
手工意思是需要寫代碼去申請malloc和釋放free,
臟記憶體:
堆記憶體也是反復使用的,而且使用者用完釋放前不會清除,因此也是臟的,
臨時性:
堆記憶體只在malloc和free之間屬于我這個行程,而可以訪問,在malloc之前和free之后
都不能再訪問,否則會有不可預料的后果,
#include <stdio.h>#include <stdlib.h> int main(void){ int *p = (int *)malloc(20); // 第二步:檢驗分配是否成功 if (NULL == p) { printf("malloc error.\n"); return -1; } *(p+3) = 12; *(p+300000) = 1234; printf("*(p+3) = %d.\n", *(p+3)); printf("*(p+300000) = %d.\n", *(p+300000)); /* int *p1 = (int *)malloc(4); // p2-p1 = 0x10 = 16Byte int *p2 = (int *)malloc(4); printf("p1 = %p.\n", p1); // p2-p1 = 0x10 = 16Byte printf("p2 = %p.\n", p2);*/ /* int *p1 = (int *)malloc(0); int *p2 = (int *)malloc(0); printf("p1 = %p.\n", p1); // p2-p1 = 0x10 = 16Byte printf("p2 = %p.\n", p2); *//* // 需要一個1000個int型別元素的陣列 // 第一步:申請和系結 int *p = (int *)malloc(1000*sizeof(int)); // 第二步:檢驗分配是否成功 if (NULL == p) { printf("malloc error.\n"); return -1; } // 第三步:使用申請到的記憶體 //p = NULL; //p = &a; // 如果在free之前給p另外賦值,那么malloc申請的那段記憶體就丟失掉了 // malloc后p和回傳的記憶體相系結,p是那段記憶體在當前行程的唯一聯系人 // 如果p沒有free之前就丟了,那么這段記憶體就永遠丟了,丟了的概念就是 // 在作業系統的堆管理器中這段記憶體是當前行程拿著的,但是你也用不了 // 所以你想申請新的記憶體來替換使用,這就叫程式“吃記憶體”,學名叫記憶體泄漏 *(p+0) = 1; *(p+1) = 2; printf("*(p+0) = %d.\n", *(p+0)); printf("*(p+1) = %d.\n", *(p+1)); *(p+222) = 133; *(p+223) = 222; // 第四步:釋放 free(p); p = NULL; printf("*(p+222) = %d.\n", *(p+222)); printf("*(p+223) = %d.\n", *(p+223));*/ return 0;}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
堆的結構資訊
#include <stdlib.h>#include <stdio.h>#include <string.h>int main(){ int *pInt = (int *)malloc(sizeof(int[4])); memset(pInt, 0, sizeof(int[4])); for (int i = 0; i < 4; i++) { pInt[i] = i; } system("pause"); return 0;}

運行后如上圖,回傳值0x007bcbb8 指向的是堆的正文
往上數兩行(0x20)
0x007bcb98 :堆塊的起始位置,此處用四個位元組保存著當前堆之前申請的堆的起始位置(為0則表示當前堆為第一個申請的堆)
0x007bcb9c: 下一堆塊的起始地址 (00 沒有下一個堆塊)
0x007bcba0:表示申請堆所在原始碼的全路徑(指標) 00 表示沒有加除錯資訊
0x007bcba4:申請堆所在原始碼對應的行數,
0x007bcba8:使用堆空間大小,malloc 的引數
0x007bcbac:
0x007bcbb0:
0x007bcbb4:上溢標志 標志位
0x007bcbc8:下溢標志, 標志位
規范的堆操作 //規范的動態資源操作
運行時才調入的資源叫做動態資源, malloc free 動態申請函式
int *pInt = NULL; (malloc 錯誤時候回傳NULL) // 1.資源的參考變數初始化為無效(或者錯誤)值,//2.申請資源后必須按照正確的方式去檢查申請結果是否失敗, if (pInt == NULL){//3.如果申請失敗,流程轉移到錯誤處理和釋放資源處,ErrorProc();goto EXIT_PROC; //釋放資源}//4.流程到達這里說明資源申請成功,現在可以開始使用資源了,EXIT_PROC://5.釋放資源時,先檢查資源的狀態是否有效if(pInt != NULL){//6.按正確方法釋放資源free(pInt);//7.將資源的參考變數重置為無效(或者錯誤)狀態pInt = NULL;}
后申請的先釋放,先申請的后釋放
d參考資料:
1.系統內核之堆管理:https://blog.csdn.net/roger_ranger/article/details/78893000
2.C語言中記憶體分配:https://blog.csdn.net/youoran/article/details/10990815
3.淺談C語言中的堆:https://www.cnblogs.com/lanjianhappy/p/6047902.html
4.程式員的自我修養十記憶體:https://www.cnblogs.com/Tan-sir/p/7652149.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/69212.html
標籤:C
上一篇:桶排序(C語言)
