01-0006 C++記憶體訪問越界 [問題整理]
C++記憶體訪問越界 [問題整理]
1. 相關問題
在嘗試解決上述問題的時候,搜尋的關鍵詞如下:
- ntdll.dll crash
- VS未載入ntdll.pdb可能的錯誤原因
- ntdll.pdb斷點
- C++ delete出錯
- C++動態陣列delete出錯
- VS 觸發斷點,記憶體釋放異常
- C++".exe" 觸發了一個斷點
- C++動態陣列空間的釋放
- C++delete偶爾出錯[rRelese模式]
2. 出現記憶體訪問越界或記憶體洩露可能的原因
2.1 沒有delete或者free
通過new或者malloc分配的記憶體空間沒有及時的釋放,或者釋放不當造成的記憶體洩露。
2.1.1 直接不釋放
函式體內申請記憶體,執行結束後不釋放[也沒有返回,提供其他位置釋放的可能性]
void fun(){
int* a=new int[10];
}
2.1.2 釋放錯誤
釋放不夠徹底,delete使用方法錯誤[僅僅釋放了0號元素或第一層指標]
void fun(){
int* a=new int[10];
delete a;//僅僅釋放了0號元素
}
void fun(){
int** a=new int*[10];
for(int i=0;i<10;i++){
a[i]=new int[10];
}
delete [] a;//第二層的空間並沒有釋放,應該迴圈釋放
}
如果在delete位置出現中斷,是因為將要被delete的這個指標指向的空間發生了訪問越界的情況,導致了堆被破壞,從而使得delete失敗
2.1.3 釋放void*
釋放void* 導致解構函式不能被正常呼叫,這種情況有可能出現:
void fun(){
void* pVoid = new int(2);
delete pVoid;
}
備註:關於上述記憶體的釋放,直接引用原作者寫的一段文字說明,沒有實際驗證。其是否能夠正確釋放,與編譯器所處的模式也有一定的關係,具體看引用的回覆部分。
這時候記憶體會正確釋放嗎?我們知道指標是分型別,特定型別的指標定址的位元組數是不一樣的,比如字元指標加一的時候是移動一個位元組,int型別指 針加一的時候,移動兩個位元組。因為第一次申請的是int型的兩個位元組的記憶體,如果系統釋放記憶體的時候是按型別釋放的話,delete一個int型的指標能 正確釋放int型指標所指向的記憶體,因為系統知道,int型就佔兩個位元組,從指標的起始位置,釋放兩個位元組的指標不就行了?但是,這樣看來,第二段程式碼就 有問題:在第二段程式碼中,從堆中申請了一個int型的兩個位元組的記憶體賦給一個void型的指標,然後釋放這個void型別的指標所指向的記憶體,由於 void型別指標是無型別的,系統無法知道void型別的指標指向多大的記憶體空間,這樣,能正確釋放指標所指向的記憶體空間嗎?會不會造成記憶體洩露?為什麼 呢?
.
這個時候我們就需要對變數的記憶體分配有一定的瞭解。
.
比如,int *pNum = new int(2);所申請的記憶體就是2個位元組嗎?答案是否定的,每當我們從堆裡面申請一塊的記憶體時,系統所分配的記憶體比物件所佔用的記憶體要稍微大一點,就像 OSI協議中每層都要加一些特定的資訊用於辨認一樣,系統也會為記憶體加上一個標識頭,用於標識這塊記憶體的邊界,這種技術成為cookie,不要以為 cookie是http協議中特有的標識資訊,在c++中也有這種技術。其實cookie是一種很古老的技術,最初用來在進城間傳遞一些資訊,後來因為用 於http協議中用來標示使用者資訊而為世人所熟知。扯遠了,回到前面所說。所以當把一個int型別的指標賦給一個void型別的指標,然後用void型別 的指標釋放這塊記憶體,會正確釋放,因為,型別只是給你看的,系統自有自己的一套標識方法。
.
回覆:你說的這種情況只存在於debug,如果你用的是release,那就沒有邊界的。
關於釋放void*,最常見問題出現在類中,用void*指向類的物件,delete的時候不會呼叫解構函式導致記憶體的洩露,具體情況如下:
class A;
void fun(){
void* ptr=new A();
delete ptr;//A的解構函式不會被呼叫
}
原文地址:
C++造成記憶體洩漏的原因彙總
void*指標及delete釋放void*記憶體(轉)
釋放void*指標 [推薦閱讀]
2.2 memset()導致的記憶體越界
程式從堆中分配的記憶體使用完畢後必須顯式釋放,否則這塊記憶體就不能被再次使用,即這塊記憶體洩漏了。記憶體洩漏導致軟體在執行過程中佔用越來越多的記憶體,程式的效率會越來越低。
使用malloc、new等運算子動態分配的記憶體,必須使用free、delete顯式釋放;memset函式可能導致指向某塊記憶體的指標的值發生了變化,從而導致釋放記憶體失敗,造成記憶體越界。
//以下程式碼會造成記憶體越界,但是編譯不會報錯,也有可能不會崩潰[崩潰偶爾會發生]
int * x0 = new int[10];
int * x1 = new int[10];
int * x2 = new int[10];
memset(x0, 0, 3 * 10 * sizeof(int));
int * x3 = new int[10];
相關原文地址:
.exe 已觸發了一個斷點
memset和memcpy使用不當而引起的memory溢位
memset函式導致記憶體洩露的問題
2.3 執行庫的連結可能會導致記憶體越界
msvcrt.lib,可能會導致記憶體越界,因為動態連結的庫和靜態連結的庫可能會過載了new運算子,導致了不同的堆存在,可能會在一個堆上申請的空間在另一個堆上釋放!
這裡有兩點需要注意:
- 儘量不要混合靜態連結和動態連結,因為他們會申請不同的堆。
- 注意任何編譯的警告,[warning LNK4098: 預設庫“msvcrtd.lib”與其他庫的使用衝突;請使用 /NODEFAULTLIB:library]
這裡已經明確提示開發者可能會出現衝突問題了,所以應該解決掉他!
原文連結:C++中delete崩潰的問題
2.4 其他情況
以下幾種情況屬於個人寫程式碼中出現過的問題,坑都踩過,需要記錄:
- 陣列下標訪問越界[自己寫了某種神奇計算索引的方程導致索引越界 ]
- strcpy等系列函式的使用,字元陣列空間不夠[計算size的問題 ]
- delete之後不置位NULL,再次訪問[需要養成良好習慣 ]
- 使用了已經被銷燬的函式返回的地址或者引用[確定自己使用的東西存在 ]
- 指標指向臨時物件,該物件在某語句塊執行完畢之後被析構[指標指向空間不復存在 ]
- 型別強轉導致的訪問越界[派生類指標指向積累物件 ]
參考連結:基類指標指向派生類物件 - 多執行緒訪問共享物件時,可能該物件已經被某執行緒析構掉
原文地址:C++什麼時候出現訪問越界?
3. 個人程式碼出現問題的原因
3.1 描述
VS執行QT程式的過程中是不是會在delete[]的位置出現中斷,沒有中斷資訊的提示,僅僅顯示一個叉號,有的時候會直接斷開在QT main函式中的最後一句,甚至是會出現的ntdll.dll中,但是此時提示沒有相關的pdb訊息無法除錯。
//以下幾處出現中斷
void fun(){
...
delete[] tempPtr1;//maybe crash
...
delete[] tempPtr2;//maybe crash
...
...
delete[] tempPtr3;//maybe crash
}
int main(){
...
return w.exec();//maybe crash
}
3.2 忽略項
Relese模式:我的VS目前是reles模式下,沒有辦法精確的定位出錯的位置。
relese模式下,這種與記憶體有關的bug並不是100%的命中,因此bug並不是每次都會出現,更為奇葩的是,發生中斷的位置並不一定是你寫的可能產生陣列越界的位置,此時,你可能一直在檢查根本沒有發生記憶體越界的那部分程式碼!。一定要調到Debug模式下,才能夠更快的找到錯誤。
言:記憶體一定要處理妥當,要手動的,主動的,準確的進行釋放,否則不僅僅會出現記憶體佔用大的情況,還會出現訪問越界導致程式崩潰,更可怕的是出現記憶體洩露,影響程式及其使用者的安全。
相關文章
- Linux核心筆記005 - 越界訪問記憶體,Linux核心處理過程Linux筆記記憶體
- CVE-2018-4990 Acrobat Reader堆記憶體越界訪問釋放漏洞分析BAT記憶體
- iOS 問題整理07----記憶體管理iOS記憶體
- 一個SMMU記憶體訪問異常的問題記憶體
- 記憶體訪問全過程記憶體
- SQLServer記憶體問題分析SQLServer記憶體
- 探究 iOS 記憶體問題iOS記憶體
- ThreadLocal記憶體洩漏問題thread記憶體
- 排查Java的記憶體問題Java記憶體
- 記憶體分配問題處理記憶體
- ReFlex:讓遠端快閃記憶體訪問擁有本地訪問的效能Flex記憶體
- 排查一個潛在的記憶體訪問問題 — 用 C 寫程式碼的日常記憶體
- JVM堆外記憶體問題排查JVM記憶體
- Java記憶體模型常見問題Java記憶體模型
- redisson記憶體洩漏問題排查Redis記憶體
- 關於java記憶體訪問重排序的思考Java記憶體排序
- C++區域性變數的記憶體訪問:小心技巧與安全邊界C++變數記憶體
- 告別記憶體OOM,解決MySQL記憶體增長問題記憶體OOMMySql
- 記錄一次Halcon C++ ReadShapeModel 反覆呼叫時記憶體洩漏問題C++記憶體
- 不生成core檔案的記憶體越界快速定位方法/記憶體越界定位/地址崩潰定位方法記憶體
- vector clear() 方法 記憶體釋放問題記憶體
- Windbg分析高記憶體佔用問題記憶體
- 記憶體和棧溢位問題定位記憶體
- 小程式白屏問題和記憶體研究記憶體
- 解決git記憶體洩露問題Git記憶體洩露
- Java記憶體問題 及 LeakCanary 原理分析Java記憶體
- 解決golang 的記憶體碎片問題Golang記憶體
- JVM 常見線上問題 → CPU 100%、記憶體洩露 問題排查JVM記憶體洩露
- 基礎學習-記憶體溢位問題記憶體溢位
- BufferedImage記憶體洩漏和溢位問題記憶體
- 小計:引用型別記憶體分配問題型別記憶體
- javascript:變數、作用域和記憶體問題JavaScript變數記憶體
- 利用Windbg分析高記憶體佔用問題記憶體
- 關於PHP記憶體洩漏的問題PHP記憶體
- project中的堆疊記憶體,記憶體地址引用,gc相關問題Project記憶體GC
- Java記憶體模型FAQ(五)舊的記憶體模型有什麼問題?Java記憶體模型
- Swift 5將強制執行記憶體獨佔訪問Swift記憶體
- 物件的例項化、記憶體佈局以及訪問定位物件記憶體