動態記憶體管理詳解
C語言中開闢記憶體有很多種方式,目前我們最常用的也就是陣列,但陣列是在我們用到他之前就得設定好它的長度,有時很不方便。
我們知道,c語言規定,不允許設定一個未知長度的陣列。(但在Linux下可以設定,但也不支援這樣做)
下面這段程式碼就會報錯喲!!!
int x = 5;
int arr[x];
所以,為了填補這一缺口,c語言有了動態記憶體,c語言提供了幾個函式來管理我們的動態記憶體,這幾個函式非常重要,分別為:
一:
1:malloc
void* malloc(size_t size);
可以看到這個函式的返回型別為void*,為一個空指標(我們前面瞭解到,void*可以作為返回值和傳參,但不能直接解引用,所以我們在運用它時需要先將它強制轉化為我們想要的指標型別
這個函式向記憶體申請一塊連續可用的空間,並返回指向這塊空間的指標;
如果開闢成功,則返回一個指向這塊空間的地址;
如果開闢失敗,則返回NULL,因此對malloc函式的返回值一定要先檢查再使用;
我們知道,以前我們定義的區域性變數、函式、陣列所需要的空間都是在棧上開闢的,但切記malloc申請的記憶體是在堆上開闢的。棧上的可用資源遠遠小於堆上的,所以malloc的專長是用來提供大記憶體需求,而且方便管理;
(這裡提到的棧和堆是什麼,我們可將計算機的記憶體結構畫成如下以便我們理解)
再提供一張棧空間的結構示意圖:
可以看到,堆空間是向上生長的
2.free
void free(void* ptr);
有了開闢記憶體,則就必須要有釋放記憶體!!!!!!!!!!!!
c語言提供了這樣一個函式來釋放我們所開闢的記憶體;
注意:如果ptr不是指向動態記憶體的,則這種行為未定義;如果ptr為NULL,則函式什麼都不會做;
下面來看一個簡單的動態記憶體開闢例子:
#include<stdio.h> #include<stdlib.h> int main() { int* ptr = NULL; int num = 0; scanf("%d", &num); ptr = (int*)malloc(num * sizeof(int)); if (NULL != ptr) //必須判斷 { int i = 0; for (i = 0; i < num; i++) { *(ptr + i) = i; } } else { perror("malloc"); } int* p = ptr; for (; p < ptr + num; p++) { printf("%d\n", *p); } free(ptr); //勿忘 ptr = NULL; //有必要這樣做,不然ptr成了野指標; return 0; }
perror函式是一個自動生成錯誤資訊的函式;可以自己去c庫裡檢視。
3.calloc
void* calloc(size_t num, size_t size);
該函式的功能為為num個大小為size的元素開闢一塊空間,並且把空間的每個位元組初始化為零;
該函式與malloc函式基本上沒有啥區別,唯一區別就是可以把開闢的記憶體自動初始化;(初始化每個位元組為0)
所以如果要求申請記憶體需要初始化,這個函式就很方便。
4.realloc
void* realloc(void* ptr, size_t size);
當我們申請的記憶體需要擴充套件或者縮減時,怎麼辦呢?
這時,c語言提供了realloc這個函式,讓動態記憶體管理變得更加靈活;
引數介紹:
ptr是要調整的記憶體地址,size為調整之後的新大小;
返回值為調整之後的記憶體起始位置;
這個函式調整原記憶體空間大小的基礎上,還會將原來記憶體中的資料移動到新的空間;
realloc在調整記憶體時存在兩種情況:
1.當原有空間的後面有足夠大的空間的時候 : 要擴充套件記憶體就直接原有記憶體之後直接追加空間,原來空間的資料不發生變化;
2.當原有空間的後面沒有足夠空間時 : 在堆空間上另找一個合適大小的連續空間來 使用。這樣函式返回的是一個新的記憶體地址;
來看一個例子:
#include<stdio.h>
int main()
{
int* ptr = malloc(100);
if (ptr != NULL)
{
;//執行任務
}
else
{
perror("malloc");
//或者 exit(EXIT_FAILURE);
}
//第一種方法擴充套件
//ptr = realloc(ptr, 1000); //第一種方法;(可能會造成記憶體洩漏)
//第二種方法擴充套件
int *p = realloc(ptr, 1024);
if (p != NULL)
{
ptr = p;
}
else
{
perror("realloc");
}
//執行任務
free(ptr);
return 0;
}
我們在使用realloc時,一定要判斷他返回的值,不然如果開闢失敗,則會返回NULL,第一種方法的話,ptr就指向了NULL,原來的指標就找不到了,free不掉了,就造成了記憶體洩漏;
二:我們知道,只要開闢記憶體就會有限制,我們來寫一個程式碼檢視自己電腦大概最多能開闢多少棧上記憶體;
int main()
{
int* ptr = NULL;
int num = 0;
scanf("%d", &num);
ptr = (int*)malloc(num*1024*1024*sizeof(char));
if (NULL != ptr) //必須判斷
{
;
}
else
{
perror("malloc");
}
free(ptr); //勿忘
ptr = NULL; //有必要這樣做,不然ptr成了野指標;
return 0;
}
程式碼中的1024*1024*sizeof(char)代表一兆大小的記憶體;
可以自己去測試,當想要獲取的記憶體不足以提供時,則會執行else語句,這裡的perror函式是stdio.h中的一個庫函式,它的作用是列印一個錯誤資訊;
這些都是動態記憶體管理的基礎必備知識,下次會引入一些深入概念以及一些經典題型!!