為什麼要進行動态記憶體管理?
普通的記憶體開辟方式有下面兩種:
int val = 1;//在棧空間上開辟4個位元組的空間
char array[10] = {0};//在棧空間上開辟10個位元組的空間
但是,上面開辟空間的方式有缺陷:
- 開辟出來的空間大小是固定的;
- 數組在聲明時,必須指定數組的長度,它所需要的記憶體在編譯時配置設定;
是以,如果我想要靈活的申請空間,上面的方法顯然不能滿足,這是就需要利用動态記憶體開辟的方式來申請空間了。
進行動态記憶體管理的幾個函數
開辟空間的函數
1.malloc()函數
函數原型:
void* malloc ( size_t size );
malloc函數的作用就是在堆上申請一塊連續可用的空間,并傳回指向這片空間位址的指針。
在使用malloc函數時需要注意的地方:
- 如果開辟成功,則傳回一個指向開辟好空間的指針。
- 如果開辟失敗,則傳回一個NULL指針,是以malloc的傳回值一定要做檢查。
- 傳回值的類型是 void* ,是以malloc函數并不知道開辟空間的類型,具體在使用的時候使用者自己來決定。
- 如果參數 size 為0,malloc的行為是标準是未定義的,取決于編譯器。
2.calloc()函數
函數原型:
void* calloc ( size_t num, size_t size );
- num:要配置設定的元素個數;
- size:每個元素的大小;
calloc函數的功能與malloc函數的功能相同,都是用來在堆上申請空間的。不同的地方是:
calloc函數申請出來的空間會初始化為0 ;
除此之外,和malloc一樣。
3.realloc()函數
函數原型:
void* realloc ( void * ptr, size_t size );
- ptr是要調整的記憶體的位址;
- size是調整之後記憶體的大小;
- 傳回值為調整之後的記憶體的起始空間位置;
realloc函數的也是用來在堆上申請空間的,但是realloc函數使動态記憶體管理更加的靈活。
realloc函數可以在已經申請好的記憶體空間上在追加記憶體空間。
在realloc函數申請記憶體空間時有兩種情況:
-
原有空間後有足夠大的空間;
此時直接在原空間的後邊追加空間,傳回原有空間的首位址;
-
原有空間後的空間不夠所要追加的空間大小;
在對空間上重新找一塊大小合适的連續空間,将原空間的内容拷貝一份,儲存到這塊大小合适的連續空間中,傳回這塊大小合适的空間的首位址;
上面三個申請空間的函數有個必須要注意的地方:
- 使用時,傳回值要強轉;
- 使用這三個函數時,要包含stdlib,h頭檔案;
釋放記憶體空間的函數
4.free()函數
函數原型:
void* free ( void* ptr ) ;
free函數的作用是:釋放由malloc函數,calloc函數,realloc函數動态開辟的記憶體空間;
需要注意:
動态申請的記憶體空間,必須釋放,如果不釋放,會産生記憶體洩漏;
常見的動态記憶體錯誤
- 對空指針的解引用;
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就會有問題
free(p);
}
- 對動态開辟空間的越界通路;
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//當i是10的時候越界通路
}
free(p);
}
- 對非動态開辟記憶體使用free釋放;
void test()
{
int a = 10;
int *p = &a;
free(p);//ok?
}
- 使用free函數釋放動态開辟記憶體的一部分;
void test()
{
int * p = (int*) malloc (100);
p++;
free(p);//p不再指向動态記憶體的起始位置
}
- 對同一塊動态記憶體多次釋放;
void test()
{
int *p = (int*) malloc(100);
free(p);
free(p);//重複釋放
}
- 動态開辟記憶體忘記釋放(記憶體洩漏);
void test()
{
int* p = (int*) malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main ()
{
test():
return 0;
}
keep Running