C語(yǔ)言中動(dòng)態(tài)內(nèi)存管理初學(xué)者容易犯的6個(gè)錯(cuò)誤分享
我在這篇文章中,詳細(xì)講解了C語(yǔ)言中的4個(gè)動(dòng)態(tài)內(nèi)存管理函數(shù)。本篇文章,我會(huì)講解初學(xué)者使用這4個(gè)函數(shù)時(shí)最容易犯的6個(gè)錯(cuò)誤,以及如何避免這些錯(cuò)誤。這6個(gè)容易犯的錯(cuò)誤分別是:
- 對(duì)NULL指針的解引用操作。
- 對(duì)動(dòng)態(tài)內(nèi)存的越界訪(fǎng)問(wèn)。
- 忘記free。
- 對(duì)同一塊空間free兩次。
- free動(dòng)態(tài)內(nèi)存的一部分。
- free非動(dòng)態(tài)開(kāi)辟的內(nèi)存。
我希望,當(dāng)你閱讀完本篇文章后,不要犯類(lèi)似的錯(cuò)誤。
1.對(duì)NULL指針的解引用操作
當(dāng)你malloc一塊空間時(shí),是有可能開(kāi)辟失敗的。一旦失敗,malloc會(huì)返回NULL指針。如果不判斷malloc的返回值,就直接使用malloc返回的指針,有可能導(dǎo)致對(duì)NULL指針的解引用操作。例如:
int* p = (int*)malloc(10 * sizeof(int)); for (int i = 0; i < 10; i++) { p[i] = i + 1; }
由于malloc可能返回NULL指針,一旦p為NULL,對(duì)其解引用是非常危險(xiǎn)的!
解決方法:每次使用malloc, calloc, realloc等函數(shù)時(shí),一定要判斷返回值是否為NULL。
int* p = (int*)malloc(10 * sizeof(int)); if (p == NULL) { // 錯(cuò)誤處理 perror("malloc"); exit(-1); } // 此時(shí)p一定不為NULL for (int i = 0; i < 10; i++) { p[i] = i + 1; }
2.對(duì)動(dòng)態(tài)內(nèi)存的越界訪(fǎng)問(wèn)
假設(shè)你使用malloc申請(qǐng)了40個(gè)字節(jié)的空間,一定要記住這塊空間的大小,不能越界訪(fǎng)問(wèn)。比如:
int* p = (int*)malloc(10 * sizeof(int)); if (p == NULL) { // 錯(cuò)誤處理 perror("malloc"); exit(-1); } for (int i = 0; i <= 10; i++) { p[i] = i + 1; }
上面的代碼中,當(dāng)i==10時(shí),p[i]等價(jià)于*(p+10),已經(jīng)超出了申請(qǐng)的40個(gè)字節(jié)的空間,這是非常危險(xiǎn)的!
解決方法:無(wú)論如何,在用指針訪(fǎng)問(wèn)內(nèi)存時(shí),一定要觀察其是否越界!比如上面的代碼,只能訪(fǎng)問(wèn)p[0]~p[9]的空間。
3.忘記free
如果你使用malloc等函數(shù),開(kāi)辟了空間,但是確沒(méi)有free,就會(huì)導(dǎo)致內(nèi)存泄漏!對(duì)于程序員來(lái)說(shuō),這種情況是非常不舒服的,一定要避免。
解決方法:對(duì)于每一塊動(dòng)態(tài)內(nèi)存開(kāi)辟的空間,使用完后都要free。比如:
int* p = (int*)malloc(10 * sizeof(int)); if (p == NULL) { perror("malloc"); exit(-1); } // 使用 // ... free(p); p = NULL;
4.對(duì)同一塊空間free兩次
如果已經(jīng)free過(guò)一塊空間了,如果再free一次,那不多此一舉嗎!不僅如此,如果第一次free之后沒(méi)有把這個(gè)指針置為NULL,第二次free的時(shí)候,這個(gè)指針是一個(gè)野指針,是非常危險(xiǎn)的!
int* p = (int*)malloc(10 * sizeof(int)); if (p == NULL) { perror("malloc"); exit(-1); } // 使用 // ... free(p); // ... free(p);
解決方法:你free一次就行了,干啥要free兩次呀!
值得說(shuō)明的是,由于free(NULL);時(shí),free函數(shù)啥也不干,所以下面的代碼是不會(huì)有問(wèn)題的,但也不建議這么寫(xiě)(有點(diǎn)吃飽了撐著的感覺(jué))。
// ... free(p); p = NULL; free(p);
5.free動(dòng)態(tài)內(nèi)存的一部分
注意,free一個(gè)指針的時(shí)候,這個(gè)指針必須指向動(dòng)態(tài)內(nèi)存的起始位置!比如下面的代碼中,如果這個(gè)指針已經(jīng)不指向起始位置了,free的時(shí)候也是非常危險(xiǎn)的。
int* p = (int*)malloc(10 * sizeof(int)); if (p == NULL) { perror("malloc"); exit(-1); } // ... p += 5; // ... free(p); p = NULL;
由于在malloc之后,free之前,指針p已經(jīng)不指向動(dòng)態(tài)內(nèi)存的起始位置了,此時(shí)再free掉p就會(huì)出問(wèn)題。
解決方法:由于free的時(shí)候需要知道動(dòng)態(tài)內(nèi)存的起始地址,在使用這塊內(nèi)存的時(shí)候不建議改變p的指向。除此之外,如果使用realloc擴(kuò)容,一定要更新p,時(shí)刻讓p指向動(dòng)態(tài)內(nèi)存的起始地址,方便free釋放。
int* p = (int*)malloc(10 * sizeof(int)); if (p == NULL) { perror("malloc"); exit(-1); } // ... // 使用時(shí)時(shí)刻讓p指向動(dòng)態(tài)內(nèi)存的起始位置! int* tmp = (int*)realloc(p, 20 * sizeof(int)); if (tmp == NULL) { free(p); p = NULL; perror("realloc"); exit(-1); } else { p = tmp; } // ... free(p); p = NULL;
6.free非動(dòng)態(tài)開(kāi)辟的內(nèi)存
free是用來(lái)釋放動(dòng)態(tài)開(kāi)辟的內(nèi)存的。所以,如果這塊內(nèi)存空間不是用malloc, calloc, realloc動(dòng)態(tài)開(kāi)辟的,就別free啦。
int arr[10] = {0}; // ... int a = 0; int* pa = &a; // ... free(arr); arr = NULL; free(pa); pa = NULL;
解決方法:不要寫(xiě)出這樣的代碼,free的時(shí)候得看清楚,這塊空間到底是不是動(dòng)態(tài)開(kāi)辟的空間,時(shí)刻保持頭腦清醒。
總結(jié)
C語(yǔ)言動(dòng)態(tài)內(nèi)存管理時(shí),容易犯的6個(gè)錯(cuò)誤及解決方法:
1.對(duì)NULL指針的解引用操作。這是由于malloc等函數(shù)有可能返回NULL,所以需要檢查返回值。
2.對(duì)動(dòng)態(tài)內(nèi)存的越界訪(fǎng)問(wèn)。時(shí)時(shí)刻刻記住,這塊空間究竟有多大,不要越界了。
3.忘記free。別忘記就行。
4.對(duì)同一塊空間free兩次。free一次就行啦,再free一次是多此一舉。
5.free動(dòng)態(tài)內(nèi)存的一部分。時(shí)時(shí)刻刻讓一個(gè)指針記住這塊動(dòng)態(tài)內(nèi)存的起始地址。
6.free非動(dòng)態(tài)開(kāi)辟的內(nèi)存。保持頭腦清醒,只有malloc, calloc, realloc出來(lái)的空間才需要free。
到此這篇關(guān)于C語(yǔ)言中動(dòng)態(tài)內(nèi)存管理初學(xué)者容易犯的6個(gè)錯(cuò)誤分享的文章就介紹到這了,更多相關(guān)C語(yǔ)言動(dòng)態(tài)內(nèi)存管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言中break與continue的用法和區(qū)別詳解
當(dāng)我們使用while或for循環(huán)時(shí),如果想提前結(jié)束循環(huán)(在不滿(mǎn)足結(jié)束條件的情況下結(jié)束循環(huán)),可以使用break或continue關(guān)鍵字,這篇文章主要給大家介紹了關(guān)于C語(yǔ)言中break與continue的用法和區(qū)別的相關(guān)資料,需要的朋友可以參考下2021-10-10C語(yǔ)言中讀寫(xiě)交替時(shí)出現(xiàn)的問(wèn)題分析
讀寫(xiě)命令交替,一定要使用fseek重新定位,否則出現(xiàn)輸入顯示混亂,這篇文章主要介紹了C語(yǔ)言中讀寫(xiě)交替時(shí)出現(xiàn)的問(wèn)題分析,需要的朋友可以參考下2022-12-12基于C++浮點(diǎn)數(shù)(float、double)類(lèi)型數(shù)據(jù)比較與轉(zhuǎn)換的詳解
本篇文章是對(duì)C++中浮點(diǎn)數(shù)(float、double)類(lèi)型數(shù)據(jù)比較與轉(zhuǎn)換進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C++中string轉(zhuǎn)換為char*類(lèi)型返回后亂碼問(wèn)題解決
這篇文章主要介紹了C++中string轉(zhuǎn)換為char*類(lèi)型返回后亂碼問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07用C++實(shí)現(xiàn)一個(gè)鏈?zhǔn)綏5膶?shí)例代碼
本篇文章是對(duì)使用C++實(shí)現(xiàn)一個(gè)鏈?zhǔn)綏5拇a進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C語(yǔ)言開(kāi)發(fā)簡(jiǎn)易版掃雷小游戲
本文給大家分享的是一個(gè)使用C語(yǔ)言開(kāi)發(fā)的命令行下的簡(jiǎn)易版掃雷小游戲,本身沒(méi)有什么太多的技術(shù)含量,只不過(guò)是筆者的處女作,所以還是推薦給大家,希望對(duì)大家學(xué)習(xí)C能夠有所幫助。2015-12-12