隨筆 動態記憶體分配

2021-09-12 15:47:30 字數 2499 閱讀 9704

在cpu中,cpu的職責就是執行運算而已,資料的**則來自於記憶體區。但是由於cpu運算速度遠遠高於記憶體的讀寫速度,為了平衡這種速度差,cpu中引入了快取機制。但是,cpu 快取還是不夠快,另外資料在快取裡面的位址是不固定的,cpu 每次讀寫都要定址也會拖慢速度。因此cpu就加入了cpu registers(暫存器)這樣乙個東西。它通常被稱為「零級快取」。cpu在高速運算下,會優先讀寫暫存器,再由暫存器跟記憶體交換資料。按照儲存介質速度的不同,下圖把它們簡單做了乙個排列,它們的特點是:速度越快記憶體越小,速度越慢記憶體越大。

接下來,再來分別簡單介紹一下這些儲存介質

1.暫存器:我們常常看到 32位 cpu、64位 cpu 這樣的名稱,其實指的就是暫存器的大小。32 位 cpu 的暫存器大小就是4個位元組。

4.磁碟:這個用過電腦的應該都比較熟悉,磁碟分為固態硬碟和機械硬碟。機械硬碟讀寫速度相對較慢,不過**比較實惠。

現在來介紹一下記憶體空間:

乙個記憶體空間裡面包含了四個區域:**區,全域性變數和靜態變數區,區域性變數區(即棧區)以及堆區。

**區和全域性變數和靜態變數區就和它們的名字一樣,儲存的就是**和全域性變數和靜態變數。

下面重點介紹一下棧區和堆區:

乙個程式在執行之前,每乙個**原始檔會被編譯器編譯為可執行檔案,在編譯時期編譯器則會預先在記憶體區分配所需靜態記憶體,這些靜態記憶體一般被指定上面提到的前三個區域。那麼是什麼樣的變數會儲存在棧裡面呢?

基本資料型別都是儲存在棧中的(當然前提是它不是全域性的,下面預設都不是全域性的),基礎型別包括byte,char,short,int,float,double,long等,而陣列型別要麼是在宣告期就指定它的大小,這時陣列的內容都是儲存在棧中的。還有一種情況是無法在編譯期確定陣列的實際大小,而是需要在程式執行時再動態分配記憶體,這時陣列的內容就會儲存在堆中了。不過又一點需要注意的是:雖然這個陣列時存在堆中,但是陣列變數是儲存在棧中的,比如

int a[10] = malloc(sizeof(int) * 10);
這個a變數是乙個指標,儲存的是堆中動態分配內容的首位址。a變數是儲存在棧中的,和所有指標型別一樣,它所占用的位元組數是4個位元組。值得一提的是,陣列在堆中的記憶體分配是連續的,因此程式可以很輕鬆地通過(首位址+偏移量*下標)的方式得以訪問到陣列內的任何乙個元素。在c語言中,動態分配的記憶體需要使用free函式手動釋放掉。指向堆記憶體的指標變數會自動被釋放掉。除了malloc函式,還有calloc或realloc函式也能動態分配記憶體。它們的區別是:

malloc函式的原型是void *malloc (unsigned int size) ,其作用是在記憶體的動態儲存區中分配乙個長度為size的連續空間。不過只是分配而已,並不會對所分配的記憶體做初始化操作。分配成功的話會返回分配記憶體的首位址,實際使用時需要做型別強轉。如果分配記憶體失敗,比如記憶體空間不足,那麼就會返回乙個null指標。所以在使用該函式返回的值時,需要先判斷一下記憶體是否分配成功。

calloc函式的原型是void *calloc(size_t n, size_t size),其作用和malloc函式差不多,區別在於它比malloc函式多做了乙個初始化所分配記憶體區域的步驟,會全部初始化為0。 這當然會造成額外的開銷,所以如果不需要初始化的話,可以只使用malloc函式即可。

realloc函式的原型是extern void *realloc(void *mem_address, unsigned int newsize),其作用是動態改變已分配記憶體的大小,第乙個引數是原始分配的記憶體首位址,第二個引數是需要分配的記憶體大小。比如用malloc分配了四個int大小的資料,後來因為某些原因需要調整該區域記憶體大小。就可以使用realloc函式。比如

int *a = malloc(4*sizeof(int));

int *b = realloc(a, 3*sizeof(int)); // 縮小

int *c = realloc(a, 5*sizeof(int)); // 擴大

realloc函式的第乙個引數必須是來自於malloc或者calloc或者realloc函式的。realloc函式的第乙個引數如果傳入的是null,那麼它就和malloc函式作用一樣了。realloc函式如果是縮小記憶體的話,返回的指標一般還是原來的指標,只不過縮小之後的內容,被剔除的記憶體被系統**了,如果再訪問,可能會引起野指標異常。而如果realloc函式是擴大記憶體的話,它所返回的指標就不一定是原來的指標內容了。因為記憶體分配的都是連續的位址空間,如果在當前位址下分配不了指定的size大小,則系統會另外開闢一段記憶體空間,然後將之前指標指向的內容複製到新的記憶體位址,並自動釋放掉原來的指標,最後返回的是新的記憶體位址。如果因為某些原因,比如系統記憶體不足,那麼該函式將會返回乙個null指標。所以它在使用之前也需要做判空。

總結一下:使用動態分配的記憶體都是使用malloc函式、calloc函式或者realloc函式分配的,它們都儲存在堆中,而指向它們的指標變數是存在棧中。動態分配的記憶體可能會分配失敗,所以需要做判空操作。動態分配的記憶體必須使用free()函式釋放,否則會造成記憶體洩漏。

動態記憶體分配

在c 中建立乙個物件時,我們必須要為這個物件申請一塊記憶體,而且要用建構函式對這塊記憶體進行初始化。c 中的new和delete相對於c的庫函式malloc和free在這方面有很大的優勢,所以我們主要講的是運算子new和delete。當用new來建立乙個物件時,它會自動在堆裡為物件分配記憶體並且為這...

動態記憶體分配

為什麼使用動態記憶體分配?c語言中的一切操作都是基於記憶體的 變數和陣列都是記憶體的別名,如何分配這些記憶體由編譯器在編譯期間決定 定義陣列的時候必須指定陣列唱的 而陣列長度是在編譯期就必須決定的 需求 程式執行的過程中,可能需要使用一些額外的記憶體空間 malloc和free malloc和fre...

動態記憶體分配

c的儲存類別有4種 自動的 auto 靜態的 statics 暫存器的 register 外部的 extern 全域性變數時分配在記憶體中的靜態儲存區 靜態區域性變數屬於靜態儲存類別,在靜態儲存區內分配儲存單元,是在編譯時賦初值的,只賦初值一次,在程式執行時它已有初值,以後每次呼叫函式時不再重新賦初...