前言
- 本博文基于VC++6.0開發調試
- 隻是對這四個函數的一個總結;
- 動态記憶體配置設定這個知識經常和動态資料連結清單(如連結清單)結合使用;
什麼是記憶體的動态配置設定
C語言中的全局變量和局部變量分别配置設定記憶體的棧(stack) 中的靜态存儲區和動态存儲區,關于靜态存儲區和動态存儲區之前的部落格也有介紹(https://blog.csdn.net/wuyuzun/article/details/82354885)
在括号裡的這篇博文裡介紹過靜态存儲區和動态存儲區内的變量是有生命周期的;全局變量被編譯器編譯時,開始配置設定其記憶體單元,生命周期持續到程式運作結束;局部變量比如函數參數,當程式被調用時為參數配置設定記憶體單元,函數調用結束生命周期也結束,所占用的記憶體單元被釋放;那麼除此之外還有一種情況,就是生命周期由使用者自己決定,需要多少記憶體就開辟多少記憶體,用完了的時候釋放就可以了,并且這些記憶體一般是用來存儲一些臨時資料,是以稱為記憶體的動态配置設定,而這個存儲區成為堆(heap);
是以将C語言中常用的存儲區分類:
區名 | 生命周期 | 存儲資料類型 |
---|---|---|
靜态存儲區 | 從定義處到程式結束 | 全局變量,靜态變量(static) |
文字常量區 | 從定義處程式結束釋放 | 常量字元串 |
程式代碼區 | 從開始執行程式的開始 ,到程式結束 | 二進制代碼 |
棧(stack) | 一般為函數結束 | 函數參數值,臨時變量,局部變 |
堆(heap) | 由程式猿開辟,由程式猿釋放或程式結束釋放 | 記憶體的動态存儲區,存放程式運作中的臨時資料 |
注意:記憶體的動态配置設定是向系統申請記憶體空間,但是由于這個記憶體空間不像數組一樣有數組名,是以對這段記憶體的讀寫都是靠指針來實作的;
怎樣建立記憶體的動态配置設定
系統提供的庫函數:
函數名 | 全稱 | 功能 |
---|---|---|
malloc(n) | 動态記憶體配置設定函數 | 在堆總開辟n個位元組的存儲空間 |
calloc(n,m) | 動态記憶體配置設定并清零函數 | 在堆中開辟n*m個位元組空間(),為每個元素清零 |
free§ | 動态記憶體釋放函數 | 釋放指針p所指向的開辟的記憶體空間 |
realloc(p,n) | 動态記憶體調整函數 | 修改p所指向的動态記憶體空間為n個位元組(p的值不變) |
**注意:**這些函數定義在庫函數stdlib.h中,在使用時需要調用其頭檔案;
動态記憶體配置設定函數malloc
函數原型:void *malloc(unsigned int size);
函數特點:
- 函數為無符号指針函數,傳回值是不指向任何資料類型的此函數所開辟的記憶體空間的首位址;
- 參數為無符号整型,因為要開辟的記憶體空間數不會是負值;
- 如果開辟空間失敗(記憶體不足或其他情況),函數傳回值為NULL;
-
此段記憶體空間中的值為任意值,也就是沒有進行初始化,memset函數可以完成對這個空間的初始化;
舉例:
#include <stdio.h>
#include <stdlib.h>
void main()
{
void *p = NULL;
p = malloc(100);
printf("%d\n",p);
}
運作結果:(這個位址是随機的,每次運作結果并不相同)
圖像表示"p = malloc(100)":
動态記憶體配置設定并清零函數calloc
函數原型:void *calloc(unsigned int n , unsigned int size);
函數特點:
- 函數同樣為空類型指針類型,傳回的位址同樣為所開辟空間的首位址,如果開辟失敗,則傳回NULL;
- 開辟的是一個數組空間,此數組一共n個元素,每個元素的存儲位元組數為size;
- 除了開辟空間之外,此函數還為每個元素賦初值為0;
- 函數calloc() 會将所配置設定的記憶體空間中的每一位都初始化為零,也就是說,如果你是為字元類型或整數類型的元素配置設定記憶體,那麼這些元素将保證會被初始化為0;如果你是為指針類型的元素配置設定記憶體,那麼這些元素通常會被初始化為空指針;如果你為實型資料配置設定記憶體,則這些元素會被初始化為浮點型的零.
舉例:
#include <stdio.h>
#include <stdlib.h>
void main()
{
void *p = NULL;
p = calloc(4,100); //在棧中配置設定4個長度為100位元組的記憶體空間;
printf("%d\n",p);
}
運作結果:(這個位址是随機的,每次運作結果并不相同)
圖檔解釋“p = calloc(4,100)”:
動态記憶體釋放函數free
函數原型:void free(void *p);
函數特點:
- 此函數用來釋放指針p所指針的動态記憶體空間;
- 一般和malloc函數搭配使用;
- 一般free函數釋放的是最近開辟的一個記憶體空間;
- 當程式運作過程中malloc了,但是沒有free的話,會造成記憶體洩漏.一部分的記憶體沒有被使用,但是由于沒有free,是以系統認為這部分記憶體還在使用,造成不斷的向系統申請記憶體,使得系統可用記憶體不斷減少.但是記憶體洩漏僅僅指程式在運作時,程式退出時,OS将回收所有的資源.是以,适當的重起一下程式,有時候還是有點作用.
舉例說明:
#include <stdio.h>
#include <stdlib.h>
void main()
{
void *p = NULL;
p = malloc(4,100);
printf("%d\n",p);
free(p); //需要注意的是,記憶體釋放後,p本身的值并沒有發生改變;
}
運作結果:(這個位址是随機的,每次運作結果并不相同)
動态記憶體調整函數realloc
函數原型:void *realloc(void *p,unsigned int size)
函數特點:
- 函數傳回值為指針,如果操作失敗(記憶體不足或其他)傳回為NULL;
- 函數用來重新配置設定由malloc和calloc函數所開辟的首位址為p記憶體空間,修改其大小為size;
- 重新配置設定後,p的值可能會發生改變;
- 此函數是堆中開辟的記憶體;
- realloc是從堆上配置設定記憶體的.當擴大一塊記憶體空間時,realloc()試圖直接從堆上現存的資料後面的那些位元組中獲得附加的位元組,如果能夠滿足,自然天下太平;如果資料後面的位元組不夠,問題就出來了,那麼就使用堆上第一個有足夠大小的自由塊,現存的資料然後就被拷貝至新的位置,而老塊則放回到堆上.這句話傳遞的一個重要的資訊就是資料可能被移動.
- realloc可以對給定的指針所指的空間進行擴大或者縮小,無論是擴張或是縮小,原有記憶體的中内容将保持不變.當然,對于縮小,則被縮小的那一部分的内容會丢失.realloc并不保證調整後的記憶體空間和原來的記憶體空間保持同一記憶體位址.相反,realloc傳回的指針很可能指向一個新的位址.
舉例:
#include <stdio.h>
#include <stdlib.h>
void main()
{
void *p = NULL;
p = malloc(100);
realloc(p,120);
printf("%d\n",p);
free(p);
}
運作結果:(這個位址是随機的,每次運作結果并不相同)
圖檔解釋“realloc(p,120)”:
但是realloc函數不僅可以增加拓展記憶體空間,還可以縮短記憶體空間;
例如:realloc(p,80)代表:
注意:
那麼,這些記憶體的開辟幹什麼用哪?
用來存臨時資料, 而且是一般存儲的資料往往都是有一定量的,但是這裡有個問題:配置設定的臨時存儲空間内并沒有資料類型,例如malloc開辟的空間,就隻是一個由若幹個位元組組成的記憶體空間塊,如何像數組一樣去存儲資料,并按照位址去通路它們?解決這個問題的辦法是強制類型轉換;并且有一點要說明的是,在C 99之前,malloc和calloc函數的資料類型是字元指針型(char *)的,但即使是這樣,這些記憶體空間并不一定是用來存字元的;
舉例說明:
#include <stdio.h>
#include <stdlib.h>
void main()
{
void Chack(int *p);
int i,*p = NULL;
p = (int *) malloc(5*sizeof(int));
/*
1.這裡發生了強制性類型轉化,但即使不進行強制轉化,編譯器也會進行預設轉化
2.sizeof()函數解決了程式的健壯性和可已知項以及所開辟空間位元組數和所轉化資料類型的不比對問題;
*/
printf("請輸入5個人的成績:\n");
for(i=0;i<5;i++)
scanf("%d",p+i);
Chack(p);
free(p);
}
void Chack(int *p)
{
int i=0;
for(;i<5;i++)
if(p[i]<60)
printf("不合格的成績有:%d\n",p[i]);
}
運作結果:
圖檔解釋:“p = (int ) malloc(5sizeof(int)); ”
顯然,所開辟的空間被轉換成了數組一樣的記憶體空間,并存儲了資料,各個小存儲區可以利用指針的算術運算存取;
寫部落格途中有遇到一些問題,拜讀過這位前輩的部落格,寫的很好,很有深度:
https://blog.csdn.net/shuaishuai80/article/details/6140979