這篇文章主要是針對(duì)c++程序中可能出現(xiàn)的內(nèi)存錯(cuò)誤做一些間單的歸納。是看了Rational Purify的使用和分析之后做的提煉。相信很多初級(jí)的c++程序員也像我一樣曾被這些問(wèn)題困惑,希望對(duì)各位看官有所幫助。
一、內(nèi)存錯(cuò)誤的分類
a.內(nèi)存訪問(wèn)錯(cuò)誤
對(duì)內(nèi)存進(jìn)行讀或?qū)憰r(shí)發(fā)生的錯(cuò)誤,考試大提示:可能是讀未被初始化的內(nèi)存單元,也可能是讀寫(xiě)錯(cuò)誤的內(nèi)存單元。
b.內(nèi)存使用錯(cuò)誤
主要是在動(dòng)態(tài)請(qǐng)求內(nèi)存之后沒(méi)有正確釋放產(chǎn)生的錯(cuò)誤。
二、內(nèi)存剖析(典型的c++內(nèi)存模型)
BSS段:BSS段(bss segment)通常是指用來(lái)存放程序中未初始化的全局變量的一塊內(nèi)存區(qū)域。BSS是英文Block Started by Symbol的簡(jiǎn)稱。BSS段屬于靜態(tài)內(nèi)存分配。
數(shù)據(jù)段:數(shù)據(jù)段(data segment)通常是指用來(lái)存放程序中已初始化的全局變量的一塊內(nèi)存區(qū)域。數(shù)據(jù)段屬于靜態(tài)內(nèi)存分配。(其實(shí)我不太明白既然都是存全局變量的,那為什么要把已初始化的和未初始化的分開(kāi)在兩個(gè)段中進(jìn)行管理)
代碼段:代碼段(code segment/text segment)通常是指用來(lái)存放程序執(zhí)行代碼的一塊內(nèi)存區(qū)域。這部分區(qū)域的大小在程序運(yùn)行前就已經(jīng)確定,并且內(nèi)存區(qū)域通常屬于只讀, 某些架構(gòu)也允許代碼段為可寫(xiě),即允許修改程序。在代碼段中,也有可能包含一些只讀的常數(shù)變量,例如字符串常量等。
堆(heap):堆是用于存放進(jìn)程運(yùn)行中被動(dòng)態(tài)分配的內(nèi)存段,它的大小并不固定,可動(dòng)態(tài)擴(kuò)張或縮減。當(dāng)進(jìn)程調(diào)用malloc等函數(shù)分配內(nèi)存時(shí),新分配的內(nèi)存就被動(dòng)態(tài)添加到堆上(堆被擴(kuò)張);當(dāng)利用free等函數(shù)釋放內(nèi)存時(shí),被釋放的內(nèi)存從堆中被剔除(堆被縮減)
棧(stack):棧又稱堆棧, 是用戶存放程序臨時(shí)創(chuàng)建的局部變量,也就是說(shuō)我們函數(shù)括弧“{}”中定義的變量(但不包括static聲明的變量,static意味著在數(shù)據(jù)段中存放變量)。除此以外,在函數(shù)被調(diào)用時(shí),其參數(shù)也會(huì)被壓入發(fā)起調(diào)用的進(jìn)程棧中,并且待到調(diào)用結(jié)束后,函數(shù)的返回值也會(huì)被存放回棧中。由于棧的先進(jìn)先出特點(diǎn),所以棧特別方便用來(lái)保存/恢復(fù)調(diào)用現(xiàn)場(chǎng)。從這個(gè)意義上講,我們可以把堆??闯梢粋€(gè)寄存、交換臨時(shí)數(shù)據(jù)的內(nèi)存區(qū)。
c++不同于C#、Java的一個(gè)地方是它可以動(dòng)態(tài)管理內(nèi)存,但魚(yú)與熊掌兩者不可兼得,靈活性的代價(jià)是程序員需要花費(fèi)更多的精力保證代碼不發(fā)生內(nèi)存錯(cuò)誤。
三、常見(jiàn)的內(nèi)存訪問(wèn)錯(cuò)誤和內(nèi)存使用錯(cuò)誤
具體來(lái)說(shuō),內(nèi)存訪問(wèn)錯(cuò)誤有下面這幾種:訪問(wèn)未被初始化的內(nèi)存單元、數(shù)組訪問(wèn)錯(cuò)誤、訪問(wèn)無(wú)效的內(nèi)存單元(0x000000,0x000005等)、寫(xiě)無(wú)效內(nèi)存。
而內(nèi)存使用錯(cuò)誤有:1、請(qǐng)求內(nèi)存之后沒(méi)有將它釋放,使new和delete成對(duì)出現(xiàn)可以避免這樣的問(wèn)題。2、釋放一塊內(nèi)存后又再釋放一次。
四、例子
1 #include
2 using namespace std;3 int main(){4 char* str1="four";5 char* str2=new char[4]; //not enough space6 char* str3=str2;7 cout< 由以上的程序,我們可以看到:在第5行分配內(nèi)存時(shí),忽略了字符串終止符"\0"所占空間導(dǎo)致了第8行的數(shù)組越界寫(xiě)(Array Bounds Write)和第9行的數(shù)組越界讀(Array Bounds Read); 在第7行,打印尚未賦值的str2將產(chǎn)生訪問(wèn)未初始化內(nèi)存錯(cuò)誤(Uninitialized Memory Read);在第11行使用已經(jīng)釋放的變量將導(dǎo)致釋放內(nèi)存讀和寫(xiě)錯(cuò)誤(Freed Memory Read and Freed Memory Write);最后由于str3和str2所指的是同一片內(nèi)存,第12行又一次釋放了已經(jīng)被釋放的空間 (Free Freed Memory)。
這個(gè)包含許多錯(cuò)誤的程序可以編譯連接,而且可以在很多平臺(tái)上運(yùn)行。但是這些錯(cuò)誤就像定時(shí)炸彈,會(huì)在特殊配置下觸發(fā),造成不可預(yù)見(jiàn)的錯(cuò)誤。這就是內(nèi)存錯(cuò)誤難以發(fā)現(xiàn)的一個(gè)主要原因。
一、內(nèi)存錯(cuò)誤的分類
a.內(nèi)存訪問(wèn)錯(cuò)誤
對(duì)內(nèi)存進(jìn)行讀或?qū)憰r(shí)發(fā)生的錯(cuò)誤,考試大提示:可能是讀未被初始化的內(nèi)存單元,也可能是讀寫(xiě)錯(cuò)誤的內(nèi)存單元。
b.內(nèi)存使用錯(cuò)誤
主要是在動(dòng)態(tài)請(qǐng)求內(nèi)存之后沒(méi)有正確釋放產(chǎn)生的錯(cuò)誤。
二、內(nèi)存剖析(典型的c++內(nèi)存模型)
BSS段:BSS段(bss segment)通常是指用來(lái)存放程序中未初始化的全局變量的一塊內(nèi)存區(qū)域。BSS是英文Block Started by Symbol的簡(jiǎn)稱。BSS段屬于靜態(tài)內(nèi)存分配。
數(shù)據(jù)段:數(shù)據(jù)段(data segment)通常是指用來(lái)存放程序中已初始化的全局變量的一塊內(nèi)存區(qū)域。數(shù)據(jù)段屬于靜態(tài)內(nèi)存分配。(其實(shí)我不太明白既然都是存全局變量的,那為什么要把已初始化的和未初始化的分開(kāi)在兩個(gè)段中進(jìn)行管理)
代碼段:代碼段(code segment/text segment)通常是指用來(lái)存放程序執(zhí)行代碼的一塊內(nèi)存區(qū)域。這部分區(qū)域的大小在程序運(yùn)行前就已經(jīng)確定,并且內(nèi)存區(qū)域通常屬于只讀, 某些架構(gòu)也允許代碼段為可寫(xiě),即允許修改程序。在代碼段中,也有可能包含一些只讀的常數(shù)變量,例如字符串常量等。
堆(heap):堆是用于存放進(jìn)程運(yùn)行中被動(dòng)態(tài)分配的內(nèi)存段,它的大小并不固定,可動(dòng)態(tài)擴(kuò)張或縮減。當(dāng)進(jìn)程調(diào)用malloc等函數(shù)分配內(nèi)存時(shí),新分配的內(nèi)存就被動(dòng)態(tài)添加到堆上(堆被擴(kuò)張);當(dāng)利用free等函數(shù)釋放內(nèi)存時(shí),被釋放的內(nèi)存從堆中被剔除(堆被縮減)
棧(stack):棧又稱堆棧, 是用戶存放程序臨時(shí)創(chuàng)建的局部變量,也就是說(shuō)我們函數(shù)括弧“{}”中定義的變量(但不包括static聲明的變量,static意味著在數(shù)據(jù)段中存放變量)。除此以外,在函數(shù)被調(diào)用時(shí),其參數(shù)也會(huì)被壓入發(fā)起調(diào)用的進(jìn)程棧中,并且待到調(diào)用結(jié)束后,函數(shù)的返回值也會(huì)被存放回棧中。由于棧的先進(jìn)先出特點(diǎn),所以棧特別方便用來(lái)保存/恢復(fù)調(diào)用現(xiàn)場(chǎng)。從這個(gè)意義上講,我們可以把堆??闯梢粋€(gè)寄存、交換臨時(shí)數(shù)據(jù)的內(nèi)存區(qū)。
c++不同于C#、Java的一個(gè)地方是它可以動(dòng)態(tài)管理內(nèi)存,但魚(yú)與熊掌兩者不可兼得,靈活性的代價(jià)是程序員需要花費(fèi)更多的精力保證代碼不發(fā)生內(nèi)存錯(cuò)誤。
三、常見(jiàn)的內(nèi)存訪問(wèn)錯(cuò)誤和內(nèi)存使用錯(cuò)誤
具體來(lái)說(shuō),內(nèi)存訪問(wèn)錯(cuò)誤有下面這幾種:訪問(wèn)未被初始化的內(nèi)存單元、數(shù)組訪問(wèn)錯(cuò)誤、訪問(wèn)無(wú)效的內(nèi)存單元(0x000000,0x000005等)、寫(xiě)無(wú)效內(nèi)存。
而內(nèi)存使用錯(cuò)誤有:1、請(qǐng)求內(nèi)存之后沒(méi)有將它釋放,使new和delete成對(duì)出現(xiàn)可以避免這樣的問(wèn)題。2、釋放一塊內(nèi)存后又再釋放一次。
四、例子
1 #include
2 using namespace std;3 int main(){4 char* str1="four";5 char* str2=new char[4]; //not enough space6 char* str3=str2;7 cout<
這個(gè)包含許多錯(cuò)誤的程序可以編譯連接,而且可以在很多平臺(tái)上運(yùn)行。但是這些錯(cuò)誤就像定時(shí)炸彈,會(huì)在特殊配置下觸發(fā),造成不可預(yù)見(jiàn)的錯(cuò)誤。這就是內(nèi)存錯(cuò)誤難以發(fā)現(xiàn)的一個(gè)主要原因。

