萬網(wǎng)域名管理平臺登錄廣州seo網(wǎng)站推廣平臺
目錄
- 關(guān)于動(dòng)態(tài)內(nèi)存分配
- malloc和calloc函數(shù)介紹
- 動(dòng)態(tài)內(nèi)存回收----free
- realloc函數(shù)介紹
- 常見的動(dòng)態(tài)內(nèi)存錯(cuò)誤
關(guān)于動(dòng)態(tài)內(nèi)存分配
回想一下我們之前學(xué)過的內(nèi)存開辟方式:
int val = 20;//在??臻g上開辟四個(gè)字節(jié)
char arr[10] = {0};//在棧空間上開辟10個(gè)字節(jié)的連續(xù)空間
在學(xué)習(xí)c語言時(shí)我們知道數(shù)據(jù)結(jié)構(gòu)通常是固定大小的。就拿數(shù)組舉例,一旦程序完成編譯,那么數(shù)組的大小及元素的個(gè)數(shù)就確定了。那么在不修改程序并且再次編譯程序的情況下就無法改變數(shù)據(jù)結(jié)構(gòu)的大小??偨Y(jié)就是下面兩個(gè)特點(diǎn):
- 空間開辟大小是固定的。
- 數(shù)組在申明的時(shí)候,必須指定數(shù)組的長度,數(shù)組空間?旦確定了大小不能調(diào)整。
但是對于空間的需求,不僅僅是上述的情況。有時(shí)候我們需要的空間大小在程序運(yùn)行的時(shí)候才能知道,那數(shù)組的編譯時(shí)開辟空間的方式就不能滿足了。
于是乎C語言便引入了動(dòng)態(tài)內(nèi)存開辟,即讓程序員自己可以申請和釋放空間,下面將對如何動(dòng)態(tài)開辟內(nèi)存進(jìn)行介紹
malloc和calloc函數(shù)介紹
下面是cplusplus對malloc
的定義:
void* malloc (size_t size);
這個(gè)函數(shù)向內(nèi)存申請一塊連續(xù)可以的空間,并返回指向這塊空間的指針。size
即是想要申請內(nèi)存空間的大小,void*
即是指向該申請內(nèi)存的首元素地址,因?yàn)椴恢李愋?#xff0c;所以用void*
,還有以下注意點(diǎn):
- 如果開辟成功,則返回?個(gè)指向開辟好空間的指針
- 如果開辟失敗,則返回?個(gè)NULL指針,因此malloc的返回值?定要做檢查。
- 返回值的類型是 void* ,所以malloc函數(shù)并不知道開辟空間的類型,具體在使用的時(shí)候使用者自己來決定。
- 如果參數(shù) size 為0,malloc 的行為是標(biāo)準(zhǔn)是未定義的,取決于編譯器。
再來看一下cplusplus對calloc
的定義:
void* calloc (size_t num, size_t size);
其實(shí)malloc
與calloc
是極其相似的,ralloc
中參數(shù)size
是想要申請的數(shù)據(jù)類型的每個(gè)的大小,而num
就是想要申請的數(shù)據(jù)類型的個(gè)數(shù),申請的總大小就為num*size
,其實(shí)就可以用malloc
中的size
表示。其余特點(diǎn)也和malloc
相似,這就不多介紹了。
當(dāng)然這兩者也存在區(qū)別,如下:
- 與函數(shù)
malloc
的區(qū)別只在于calloc
會在返回地址之前把申請的空間的每個(gè)字節(jié)初始化為0.
所以如果我們想要對動(dòng)態(tài)申請的內(nèi)存空間初始化為0,那么使用ralloc
就更方便。
動(dòng)態(tài)內(nèi)存回收----free
其實(shí)malloc
,calloc
等動(dòng)態(tài)開辟內(nèi)存的函數(shù),實(shí)則是在堆區(qū)上開辟內(nèi)存。由于這些函數(shù)申請的內(nèi)存空間系統(tǒng)并不會主動(dòng)回收,所以過于頻繁的使用這類函數(shù)開辟空間,就會導(dǎo)致堆耗盡。這時(shí)就需要我們主動(dòng)釋放開辟的空間,于是乎引入free
函數(shù),函數(shù)原型如下:
void free (void* ptr);
關(guān)于這里的ptr
指針,則是指向我們動(dòng)態(tài)開辟的內(nèi)存的首地址,只有指向首地址才能完全釋放動(dòng)態(tài)開辟的內(nèi)存空間。關(guān)于ptr
指針還有以下兩個(gè)特殊情況;
- 如果參數(shù)
ptr
指向的空間不是動(dòng)態(tài)開辟的,那free
函數(shù)的行為是未定義的。- 如果參數(shù)
ptr
是NULL
指針,則函數(shù)什么事都不做。
還有兩個(gè)注意事項(xiàng):
- 在我們釋放開辟的空間后,原來指向這段空間的指針
ptr
還存著此處的地址,為了避免后面不小心對此指針進(jìn)行賦值或解引用,導(dǎo)致野指針問題,所以在釋放完空間后,還需將此指針賦為NULL
。 - 在寫代碼時(shí)最好始終有一個(gè)指向該空間的指針,如果沒有指向該空間的指針,那么這段空間將無法訪問和釋放。對程序而言,不可訪問的空間也被稱為垃圾,留有垃圾的程序存在內(nèi)存泄漏現(xiàn)象。
如上圖所示,原指針p
指向第一個(gè)內(nèi)存塊,操作后p
指向了第二個(gè)內(nèi)存塊。所以由于沒有指針指向第一個(gè)內(nèi)存塊,就再也不能使用此內(nèi)存塊了,這就是上文所說的垃圾,導(dǎo)致了內(nèi)存泄漏。
realloc函數(shù)介紹
有時(shí)會我們發(fā)現(xiàn)過去申請的空間太小了,有時(shí)候我們又會覺得申請的空間過大了,那為了合理的申請內(nèi)存,我們?定會對內(nèi)存的大小做靈活的調(diào)整。此時(shí)realloc
函數(shù)就可以做到對動(dòng)態(tài)開辟內(nèi)存大小的調(diào)整。
函數(shù)原型如下啊:
void* realloc (void* ptr, size_t size);
其中指針ptr
指向的是要調(diào)整的內(nèi)存地址,size
是調(diào)整后的內(nèi)存大小。返回值為調(diào)整后的內(nèi)存的起始地址。
情況1:原有空間之后有足夠大的空間
當(dāng)是情況1 的時(shí)候,要擴(kuò)展內(nèi)存就直接原有內(nèi)存之后直接追加空間,原來空間的數(shù)據(jù)不發(fā)生變化。
情況2:原有空間之后沒有足夠大的空間
當(dāng)是情況2 的時(shí)候,原有空間之后沒有足夠多的空間時(shí),擴(kuò)展的方法是:在堆空間上另找?個(gè)合適大小的連續(xù)空間來使用。這樣函數(shù)返回的是?個(gè)新的內(nèi)存地址。在此情況時(shí),對于原先內(nèi)存上已有的數(shù)據(jù),此函數(shù)則會將那些數(shù)據(jù)拷貝到新的內(nèi)存上,而原內(nèi)存將被釋放。
還有就是為了防止realloc
開辟動(dòng)態(tài)內(nèi)存空間失敗時(shí),將指針賦為NULL
所導(dǎo)致找不到原內(nèi)存空間的問題。我們一般創(chuàng)建一個(gè)新指針來接收地址,判斷不為NULL
后再賦給原指針,如下:
int main()
{int* ptr=(int*)malloc(5*sizeof(int));int* p=(int*)realloc(10*sizeof(int));if(p != NULL)ptr = p;//......(代碼)free(ptr);ptr = NULL;return 0;
}
常見的動(dòng)態(tài)內(nèi)存錯(cuò)誤
- 對NULL指針的解引用操作:
void test(){int *p = (int *)malloc(INT_MAX/4);*p = 20;free(p);}
這此代碼中如果p
的值是NULL
,就會有問題。
- 對動(dòng)態(tài)開辟空間的越界訪問:
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;//當(dāng)i是10的時(shí)候越界訪問}free(p);}
- 對非動(dòng)態(tài)開辟內(nèi)存使用free釋放:
void test()
{int a = 10;int *p = &a;free(p);
}
這里的a
是在棧區(qū)上開辟的,如果用free
釋放系統(tǒng)將會報(bào)錯(cuò)。
- 對同?塊動(dòng)態(tài)內(nèi)存多次釋放:
void test(){int *p = (int *)malloc(100);free(p);free(p);//重復(fù)釋放}
這里malloc
開辟的動(dòng)態(tài)內(nèi)存空間,被重復(fù)釋放,系統(tǒng)同樣會報(bào)錯(cuò)。
- 使用free釋放?塊動(dòng)態(tài)開辟內(nèi)存的?部分:
void test(){int *p = (int *)malloc(100);p++;free(p);}
因?yàn)?code>++符號會改變變量的值,所以這里的p
不再指向動(dòng)態(tài)內(nèi)存的起始位置,這時(shí)使用free
釋放時(shí)并不會釋放完全部的動(dòng)態(tài)內(nèi)存。
- 動(dòng)態(tài)開辟內(nèi)存忘記釋放(內(nèi)存泄漏):
void test(){int *p = (int *)malloc(100);if(NULL != p){*p = 20;}}int main(){test();while(1);}
在調(diào)用完test()
函數(shù)后沒有主動(dòng)釋放開辟的內(nèi)存空間,同樣在棧區(qū)的int* p
在調(diào)用完此函數(shù)后將被回收,所以就無法尋找到malloc
開辟的空間,這就是上文所說的垃圾,而留有垃圾的程序存在內(nèi)存泄漏現(xiàn)象。所以切記動(dòng)態(tài)開辟的內(nèi)存一定要釋放!