目錄
成都創(chuàng)新互聯(lián)公司基于成都重慶香港及美國(guó)等地區(qū)分布式IDC機(jī)房數(shù)據(jù)中心構(gòu)建的電信大帶寬,聯(lián)通大帶寬,移動(dòng)大帶寬,多線BGP大帶寬租用,是為眾多客戶提供專業(yè)綿陽(yáng)服務(wù)器托管報(bào)價(jià),主機(jī)托管價(jià)格性價(jià)比高,為金融證券行業(yè)服務(wù)器托管,ai人工智能服務(wù)器托管提供bgp線路100M獨(dú)享,G口帶寬及機(jī)柜租用的專業(yè)成都idc公司。1、為什么要有動(dòng)態(tài)內(nèi)存分配
2、動(dòng)態(tài)內(nèi)存函數(shù)介紹
1、malloc
2、free
3、calloc
?編輯
4、realloc
3、動(dòng)態(tài)內(nèi)存常見(jiàn)的錯(cuò)誤
4、動(dòng)態(tài)內(nèi)存開(kāi)辟相關(guān)好題
5、c/c++程序內(nèi)存開(kāi)辟示意圖
int a,? int arr[10]? 是固定地向內(nèi)存申請(qǐng)連續(xù)的一塊空間,但不能變長(zhǎng)或變短隨時(shí)調(diào)整。
在我們之前寫(xiě)的靜態(tài)版通訊錄中,我們創(chuàng)建了一個(gè)peopleinfo類型的數(shù)組date[100]用來(lái)存放100個(gè)人的個(gè)人信息,但是當(dāng)我們的人員信息較小時(shí),100個(gè)結(jié)構(gòu)體顯得有些浪費(fèi),而當(dāng)我們所需存放的信息超過(guò)100時(shí)又不夠用,隨之我們就得修改這個(gè)數(shù)字,但是學(xué)了動(dòng)態(tài)內(nèi)存管理之后,我們可以動(dòng)態(tài)地分配內(nèi)存空間變大或變小,從而有效利用空間。
1、為什么要有動(dòng)態(tài)內(nèi)存分配int val = 20;//在棧空間上開(kāi)辟四個(gè)字節(jié)
char arr[10] = {0};//在??臻g上開(kāi)辟10個(gè)字節(jié)的連續(xù)空間
但是上述的開(kāi)辟空間的方式有兩個(gè)特點(diǎn):
1. 空間開(kāi)辟大小是固定的。
2. 數(shù)組在申明的時(shí)候,必須指定數(shù)組的長(zhǎng)度,它所需要的內(nèi)存在編譯時(shí)分配。
但是對(duì)于空間的需求,不僅僅是上述的情況。有時(shí)候我們需要的空間大小在程序運(yùn)行的時(shí)候才能知道,
那數(shù)組的編譯時(shí)開(kāi)辟空間的方式就不能滿足了。
這時(shí)候就只能使用動(dòng)態(tài)內(nèi)存開(kāi)辟了。
#include
這個(gè)函數(shù)向內(nèi)存申請(qǐng)一塊連續(xù)可用的空間,并返回指向這塊空間的指針。
如果開(kāi)辟成功,則返回一個(gè)指向開(kāi)辟好空間的指針。
如果開(kāi)辟失敗,則返回一個(gè)NULL指針,因此malloc的返回值一定要做檢查。
返回值的類型是 void* ,所以malloc函數(shù)并不知道開(kāi)辟空間的類型,具體在使用的時(shí)候使用者自己
來(lái)決定。
如果參數(shù) size 為0,malloc的行為是標(biāo)準(zhǔn)是未定義的,取決于編譯器。
先在堆區(qū)上動(dòng)態(tài)申請(qǐng)一定空間,使用后應(yīng)該還給操作系統(tǒng),如果不主動(dòng)還,程序結(jié)束后會(huì)自動(dòng)還,但是如果程序一直不結(jié)束,就一直“占著不用”,就會(huì)造成空間的浪費(fèi)。
2、free用來(lái)釋放、歸還申請(qǐng)的內(nèi)存
int main()
{
//申請(qǐng)40個(gè)字節(jié),用來(lái)存放10個(gè)整型
int* p = (int*)malloc(40);
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//存放 1--10
int i = 0;
for (i = 0; i< 10; i++)
{
*(p + i) = i + 1;
}
for (i = 0; i< 10; i++)
{
printf("%d ", *(p+i));
}
//free釋放申請(qǐng)的內(nèi)存
free(p);
return 0;
}
用malloc申請(qǐng)后,內(nèi)存中為隨機(jī)值,使用時(shí)可給它們賦值,當(dāng)free后,又變?yōu)殡S機(jī)值。
仔細(xì)觀察,我們可以發(fā)現(xiàn),雖然這塊空間的值發(fā)生了變化,但是指針p指向的地址free前后沒(méi)有變化。因此,當(dāng)我們free還給操作系統(tǒng)后,p仍指向這塊空間。此時(shí)*p就會(huì)導(dǎo)致非法訪問(wèn),因此我們需要將p制為NULL,避免非法訪問(wèn)。
free(p);
p = NULL;
總結(jié):malloc申請(qǐng)空間后不會(huì)初始化,使用前要判斷是否成功申請(qǐng)(是否返回NULL),使用后要free還給操作系統(tǒng),然后將用于接收這塊內(nèi)存空間的指針p置為NULL。
申請(qǐng)失敗時(shí)返回NULL,并打印錯(cuò)誤原因(沒(méi)有足夠空間)。
void? free(void * ptr)
free函數(shù)用來(lái)釋放動(dòng)態(tài)開(kāi)辟的內(nèi)存。
如果參數(shù) ptr 指向的空間不是動(dòng)態(tài)開(kāi)辟的,那free函數(shù)的行為是未定義的。
如果參數(shù) ptr 是NULL指針,則函數(shù)什么事都不做。
由上圖我們可以知道,malloc不會(huì)初始化,calloc會(huì)將每個(gè)元素先初始化為0,可以按需用。由于calloc需要初始化,效率比malloc稍低,此外沒(méi)有其它區(qū)別,都需要進(jìn)行相關(guān)步驟。
4、reallocint main()
{
int* p = (int*)malloc(5 * sizeof(int));
if (NULL == p)
{
perror("malloc");
return 1;
}//使用
int i = 0;
for (i = 0; i< 5; i++)
{
p[i] = 1;
}//空間不夠,增加5個(gè)整型的空間
//此時(shí)不能用p接收
int* ptr = (int*)realloc(p, 10 * sizeof(int));
//先用ptr接收,再賦給p,防止返回NULL,p找不到原來(lái)的數(shù)據(jù)
if (ptr != NULL)
{
p = ptr;
ptr = NULL;//釋放但不置空,需手動(dòng)置空
}
//繼續(xù)使用
for (i = 0; i< 10; i++)
{
printf("%d ", p[i]);
}//realloc會(huì)把舊的空間釋放,不用自己釋放
free(p);
p = NULL;
}
3、動(dòng)態(tài)內(nèi)存常見(jiàn)的錯(cuò)誤1、對(duì)NULL解引用(未判斷是否為空)
2、非法訪問(wèn)內(nèi)存,越界訪問(wèn)(解引用野指針)
3、對(duì)非動(dòng)態(tài)開(kāi)辟內(nèi)存的空間用free釋放
void test()
{
int a = 10;
int *p = &a;
free(p);//ok?
}
對(duì)棧區(qū)的空間進(jìn)行釋放(x)
4、使用free釋放一塊動(dòng)態(tài)開(kāi)辟內(nèi)存的一部分
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向動(dòng)態(tài)內(nèi)存的起始位置
p=NULL;
}
5、對(duì)同一塊內(nèi)存多次釋放
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重復(fù)釋放
}
free一個(gè)NULL空指針時(shí),什么事都不會(huì)發(fā)生。
malloc等函數(shù)是申請(qǐng)一塊空間,獲得使用它的權(quán)限,free釋放后,將權(quán)限還給操作系統(tǒng),如果再訪問(wèn)或是釋放,就是非法訪問(wèn)。
6、動(dòng)態(tài)開(kāi)辟的內(nèi)存忘記釋放(內(nèi)存泄漏)
malloc和free要成對(duì)出現(xiàn),防止出現(xiàn)內(nèi)存泄漏
int* test()
//函數(shù)內(nèi)部進(jìn)行了malloc操作,返回了malloc開(kāi)辟的空間的起始地址
//誰(shuí)接收了 要記得釋放和置空
{
int* p = (int*)malloc(100);
if (NULL == p)
{
return 1;
}
return p;
}
int main()
{
int* ptr = test();
free(ptr);
ptr = NULL;
}
4、動(dòng)態(tài)內(nèi)存開(kāi)辟相關(guān)好題void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
int main()
{
Test();
}
這里GetMemory函數(shù)是值傳遞,且沒(méi)有返回值,對(duì)str無(wú)影響。因此str還是指向NULL,即0地址處,然后調(diào)用strcpy需訪問(wèn)0地址處內(nèi)容,導(dǎo)致非法訪問(wèn)。
同時(shí),上面用malloc申請(qǐng)了一塊空間,但是函數(shù)內(nèi)沒(méi)有釋放,函數(shù)銷毀后,p也銷毀,這塊空間就會(huì)內(nèi)存泄漏。記得malloc要與free搭配使用。
修改后,可傳入&str,用char**p接收,*P=(char*)malloc(100),使p指向開(kāi)辟的100byte,進(jìn)而存放拷貝的內(nèi)容,打印后free(str) str=NULL
或者將p(char*)直接返回,用str接收,因?yàn)闆](méi)有free,所以malloc(100)仍然存在,進(jìn)而可以strcpy,如果此時(shí)不是malloc申請(qǐng),而是利用數(shù)組,函數(shù)銷毀后,數(shù)組的空間也會(huì)釋放,如果再進(jìn)行打印就不行了。(如下圖)這類問(wèn)題簡(jiǎn)稱為 返回??臻g地址的問(wèn)題
可以在p前面加上static使其變?yōu)殪o態(tài)區(qū)變量,函數(shù)銷毀后它不會(huì)銷毀,或者去掉[],p變?yōu)閏har*類型,即從數(shù)組變?yōu)槌A孔址?,常量字符串也是在靜態(tài)區(qū),也就是把在棧區(qū)存儲(chǔ)的數(shù)據(jù)放入靜態(tài)區(qū)中存儲(chǔ),從而避免了返回??臻g地址的問(wèn)題。
printf(str)括號(hào)內(nèi)直接加str是可以的,str是一個(gè)地址,例如printf(“hehe”),括號(hào)內(nèi)有引號(hào)+字符串,也就是首字符的地址,相當(dāng)于括號(hào)內(nèi)直接加一個(gè)地址,最終結(jié)果都是打印字符串。
void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
int main()
{
Test();
}
這段代碼的整體思路沒(méi)有問(wèn)題,但是會(huì)導(dǎo)致內(nèi)存泄漏,? malloc等函數(shù)需要與free共同使用
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, "hello");
free(str);
}
if(str != NULL)
{
strcpy(str, "world");
printf(str);
}
free(str)后已經(jīng)沒(méi)有權(quán)限訪問(wèn)了,但后面又調(diào)用strcpy訪問(wèn)空間,導(dǎo)致非法訪問(wèn)
開(kāi)辟--釋放--置空
習(xí)題來(lái)自《高質(zhì)量C/C++編程》
5、c/c++程序內(nèi)存開(kāi)辟示意圖1. 棧區(qū)(stack):在執(zhí)行函數(shù)時(shí),函數(shù)內(nèi)局部變量的存儲(chǔ)單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)
束時(shí)這些存儲(chǔ)單元自動(dòng)被釋放。棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中,效率很高,但是
分配的內(nèi)存容量有限。 棧區(qū)主要存放運(yùn)行函數(shù)而分配的局部變量、函數(shù)參數(shù)、返回?cái)?shù)據(jù)、返
回地址等。
2. 堆區(qū)(heap):一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時(shí)可能由OS回收 。分
配方式類似于鏈表。
3. 數(shù)據(jù)段(靜態(tài)區(qū))(static)存放全局變量、靜態(tài)數(shù)據(jù)。程序結(jié)束后由系統(tǒng)釋放。
4. 代碼段:存放函數(shù)體(類成員函數(shù)和全局函數(shù))的二進(jìn)制代碼,只讀常量(不能被修改),字符串常量。
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧