C語言系列(五)記憶體的分配與釋放
首先我們來科普一下:
什麼是堆?說到堆,又忍不住說到了棧!什麼是 棧?
1、什麼是堆:堆是大家共有的空間,分全域性堆和區域性堆。全域性堆就是所有沒有分配的空間,區域性堆就是使用者分配的空間。堆在作業系統對程序 初始化的時候分配,執行過程中也可以向系統要額外的堆,但是記得用完了要還給作業系統,要不然就是記憶體洩漏。
2、什麼是棧:棧是執行緒獨有的,儲存其執行狀態和區域性自動變數的。棧線上程開始的時候初始化,每個執行緒的棧互相獨立。每個函式都有自己的棧,棧被用來在函式之間傳遞引數。作業系統在切換執行緒的時候會自動的切換棧,就是切換SS/ESP暫存器。棧空間不需要在高階語言裡面顯式的分配和釋放。
C語言程式編譯的記憶體分配,堆與棧的區別:
- 棧是由編譯器自動分配釋放,存放函式的引數值、區域性變數的值等。操作方式類似於資料結構中的棧。
堆一般由程式設計師分配釋放,若不釋放,程式結束時可能由OS回收。注意這裡說是可能,並非一定。再強調一次,記得要釋放! - 棧區(stack) :
//windows下,棧記憶體分配2M(確定的常數),超出了限制,提示stack overflow錯誤
//編譯器自動分配釋放,主要存放函式的引數值,區域性變數值等; 堆區(heap):程式設計師手動分配釋放,作業系統80%記憶體
全域性區或靜態區:存放全域性變數和靜態變數;程式結束時由系統釋放,分為全域性初始化區和全域性未初始化區;
字元常量區:常量字串放與此,程式結束時由系統釋放;
程式程式碼區:存放函式體的二進位制程式碼。
栗子:
int a=0; //全域性初始化區
char *p1; //全域性未初始化區
void main()
{
int b; //棧
char s[]="bb"; //棧
char *p2; //棧
char *p3="123"; //其中,“123\0”常量區,p3在棧區
static int c=0; //全域性區
p1=(char*)malloc(10); //10個位元組區域在堆區
strcpy(p1,"123"); //"123\0"在常量區,編譯器 可能 會優化為和p3的指向同一塊區域
}
棧記憶體
void stackFun(){
int a[1024];
//棧記憶體自動釋放
}
堆記憶體
void heapFun(){
//40M記憶體
//位元組
//void *任意型別的指標
int* p = malloc(1024 * 1024 * 10 * sizeof(int));
//釋放
free(p);
}
void main(){
//在堆記憶體上,分配40M的記憶體
while (1){
Sleep(1000);
stackFun();
}
getchar();
}
建立一個數組,動態指定陣列的大小(在程式執行過長中,可以隨意的開闢指定大小的記憶體,以供使用,相當於Java中的集合)
靜態記憶體分配,分配記憶體大小的是固定,問題:1.很容易超出棧記憶體的最大值 2.為了防止記憶體不夠用會開闢更多的記憶體,容易浪費記憶體
動態記憶體分配,在程式執行過程中,動態指定需要使用的記憶體大小,手動釋放,釋放之後這些記憶體還可以被重新使用(活水)
函式:calloc() 分配記憶體空間並初始化
calloc() 函式用來動態地分配記憶體空間並初始化為 0,其原型為:
void* calloc (size_t num, size_t size);
calloc() 在記憶體中動態地分配 num 個長度為 size 的連續空間,並將每一個位元組都初始化為 0。所以它的結果是分配了 num*size 個位元組長度的記憶體空間,並且每個位元組的值都是0。
【返回值】分配成功返回指向該記憶體的地址,失敗則返回 NULL。
函式:malloc() 動態地分配記憶體空間
malloc() 函式用來動態地分配記憶體空間(如果你不瞭解動態記憶體分配,請檢視:C語言動態記憶體分配及變數儲存類別),其原型為:
void* malloc (size_t size);
應用在程式中程式碼如下:
void main(){
//靜態記憶體分配建立陣列,陣列的大小是固定的
//int i = 10;
//int a[i];
int len;
printf("輸入陣列的長度:");
scanf("%d",&len);
//開闢記憶體,大小len*4位元組
int* p = malloc(len * sizeof(int));
//p是陣列的首地址,p就是陣列的名稱
//給陣列元素賦值(使用這一塊剛剛開闢出來的記憶體區域)
int i = 0;
for (; i < len - 1; i++){
p[i] = rand() % 100;
printf("%d,%#x\n", p[i], &p[i]);
}
//手動釋放記憶體
//free()釋放動態分配的記憶體空間
free(p);
getchar();
}
realloc 重新分配記憶體
void main(){
int len;
printf("第一次輸入陣列的長度:");
scanf("%d", &len);
//int* p = malloc(len * sizeof(int));
int* p = calloc(len, sizeof(int));
int i = 0;
for (; i < len; i++){
p[i] = rand() % 100;
printf("%d,%#x\n", p[i], &p[i]);
}
int addLen;
printf("輸入陣列增加的長度:");
scanf("%d", &addLen);
//記憶體不夠用,擴大剛剛分配的記憶體空間
//1.原來記憶體的指標 2.記憶體擴大之後的總大小
int* p2 = realloc(p, sizeof(int) * (len + addLen));
if (p2 == NULL){
printf("重新分配失敗,世界那麼大,容不下我。。。");
}
新分配記憶體的兩種情況:
//縮小,縮小的那一部分資料會丟失
//擴大,(連續的)
1.如果當前記憶體段後面有需要的記憶體空間,直接擴充套件這段記憶體空間,realloc返回原指標
2.如果當前記憶體段後面的空閒位元組不夠,那麼就使用堆中的第一個能夠滿足這一要求的記憶體塊,將目前的資料複製到新的位置,並將原來的資料庫釋放掉,返回新的記憶體地址
3.如果申請失敗,返回NULL,原來的指標仍然有效
//接著上面的程式碼重新賦值
i = 0;
printf("--------------------------\n");
for (; i < len + addLen; i++){
p2[i] = rand() % 200;
printf("%d,%#x\n", p2[i], &p2[i]);
}
//手動釋放記憶體
if (p != NULL){
free(p);
p = NULL;
}
if (p2 != NULL){
free(p2);
p2 = NULL;
}
getchar();
}
記憶體分配的幾個注意細節
1.不能多次釋放;
2.釋放完之後(指標仍然有值),給指標置NULL,標誌釋放完成;
3.記憶體洩露(p重新賦值之後,再free,並沒有真正釋放記憶體);
void main(){
int len;
printf("輸入陣列的長度:");
scanf("%d", &len);
int* p = malloc(len * sizeof(int));
int i = 0;
for (; i < len; i++){
p[i] = rand() % 100;
printf("%d,%#x\n", p[i], &p[i]);
}
if (p != NULL){
free(p);
p = NULL;
}
getchar();
}
以上就是C語言中對記憶體的分配與釋放,常用的幾個函式~