错误内存【读书笔记】C程序中常见的内存操作有关的典型编程错误
題記:寫這篇博客要主是加深自己對(duì)錯(cuò)誤內(nèi)存的認(rèn)識(shí)和總結(jié)實(shí)現(xiàn)算法時(shí)的一些驗(yàn)經(jīng)和訓(xùn)教,如果有錯(cuò)誤請(qǐng)指出,萬分感謝。
? ? ? ? ?對(duì)C/C++程序員來講,內(nèi)存管理是個(gè)不小的挑戰(zhàn),絕對(duì)值得慎之又慎,否則讓由上萬行代碼構(gòu)成的模塊跑起來后才出現(xiàn)內(nèi)存崩潰,是很讓人痛苦的。因?yàn)楸罎⒌奈恢迷跁r(shí)間和空間上,通常是在距真正的錯(cuò)誤源一段距離以后才表現(xiàn)出來。頭幾天線上模塊因堆內(nèi)存寫越界1個(gè)字節(jié)引起各種詭異崩潰,定位問題過程當(dāng)中的折騰仍歷歷在目,今天讀到《深刻理解計(jì)算機(jī)系統(tǒng)》第9章-虛擬存儲(chǔ)器,發(fā)明書中總結(jié)了C程序中常見的內(nèi)存操縱有關(guān)的10種典型編程錯(cuò)誤,總結(jié)的比擬全面。故作為筆記,記載于此。
????1. 間接引用無效指針
? ? ? ? 進(jìn)程虛擬地址空間的某些地址范圍可能沒有映射到任何有意義的數(shù)據(jù),如果我們?cè)噲D間接引用一個(gè)指向這些地址的指針,則操縱系統(tǒng)會(huì)以Segment Fault終止進(jìn)程。而且,虛擬存儲(chǔ)器的某些區(qū)域是只讀的(如.text或.rodata),試圖寫這些區(qū)域會(huì)以掩護(hù)異常中止當(dāng)前進(jìn)程。
? ? ? ? 如從stdin讀取一個(gè)int變量時(shí),scanf("%d", &val)是準(zhǔn)確用法,若誤寫為scanf("%d", val)時(shí),val的值會(huì)被解釋為一個(gè)地址,并試圖向該地址寫數(shù)據(jù)。在最好的情況下,進(jìn)程立即異常中止。在最壞的情況下,val的值恰好對(duì)應(yīng)于虛擬存儲(chǔ)器的某個(gè)正當(dāng)?shù)木哂凶x/寫權(quán)限的內(nèi)存區(qū)域,于是該內(nèi)存單元會(huì)被改寫,而這通常會(huì)在相當(dāng)長的一段時(shí)間后形成災(zāi)難性的、令人困惑的后果。
????2. 讀未初始化的存儲(chǔ)器
? ? ? ? C語言的malloc并不負(fù)責(zé)初始化申請(qǐng)到的內(nèi)存區(qū)域,因此,常見的錯(cuò)誤是假設(shè)堆存儲(chǔ)器被初始化為0,例如:
? ? ? ? ? ?上述代碼中,錯(cuò)誤地假設(shè)了y被初始化為0。準(zhǔn)確的實(shí)現(xiàn)方式是顯式將y[i]置為0或者應(yīng)用calloc。
????3. 棧緩沖區(qū)溢出
? ? ? ? ?例如:
? ? ? ? ? ?
????上面的代碼致使棧緩沖區(qū)溢出,安全的做法是:1)根據(jù)需求定義適合的buffer;2)采取snprintf(buf, sizeof(buf), "%s", "hello world")來實(shí)時(shí)截?cái)唷?/p>
????4. 誤以為指針與其指向的對(duì)象是雷同巨細(xì)的
? ? ? ? 例如:
????? ? ? ? 上述代碼目的是創(chuàng)立一個(gè)由n個(gè)指針構(gòu)成的數(shù)組,每一個(gè)指針均指向一個(gè)包含m個(gè)int的數(shù)組,但誤將sizeof(int *)寫成sizeof(int)。這段代碼只有在int和int *的size雷同的機(jī)器上運(yùn)行良好。如果在像Core i7這樣的機(jī)器上運(yùn)行這段代碼,由于指針變量的size大于sizeof(int),則會(huì)引發(fā)代碼中的for循環(huán)寫越界。因?yàn)檫@些字中的一個(gè)很多是已分配塊的邊界標(biāo)記腳部,所以我們可能不會(huì)立即發(fā)明這個(gè)錯(cuò)誤,直到進(jìn)程運(yùn)行很久釋放這個(gè)內(nèi)存塊時(shí),此時(shí),分配器中的合并代碼會(huì)戲劇性地失敗,而沒有任何明顯的原因。這是"在遠(yuǎn)處起作用"(action at distance)的一個(gè)隱秘示例,這類"在遠(yuǎn)處起作用"是與存儲(chǔ)器有關(guān)的編程錯(cuò)誤的典型情況。
????5. 形成錯(cuò)位錯(cuò)誤
? ? ? ? ?錯(cuò)位(Off-by-one)錯(cuò)誤是另一種常見的覆蓋錯(cuò)誤來源:
全部世界,因?yàn)橛辛岁柟?#xff0c;城市有了生機(jī);細(xì)小心靈,因?yàn)橛辛岁柟?#xff0c;內(nèi)心有了舒暢。明媚的金黃色,樹叢間小影成像在葉片上泛有的點(diǎn)點(diǎn)破碎似的金燦,海面上直射反映留有的隨波浪層層翻滾的碎片,為這大自然創(chuàng)造了美景,惹人醉的溫馨之感,濃濃暖意中夾雜著的明朗與柔情,讓雨過天晴后久違陽光的心靈重新得到了滋潤!
int ** makeArray(int n, int m){int i;int **A = (int **)Malloc(n * sizeof(int *));for(i = 0; i <= n; i++) {A[i] = (int *)Malloc(m * sizeof(int));}return A;}
? ? ? ? ?
????很明顯,for循環(huán)次數(shù)分歧預(yù)期,致使寫越界。榮幸的話,進(jìn)程會(huì)立即崩潰;不幸的話,運(yùn)行很長時(shí)間才拋出各種詭異問題。
????6. 引用指針,而不是它所指向的對(duì)象
? ? ? ? 如果不注意C操縱符的優(yōu)先級(jí)和結(jié)合性,就會(huì)錯(cuò)誤地操縱指針,而不是指針?biāo)赶虻膶?duì)象。
? ? ? ? 比如上面的函數(shù),其目的是刪除一個(gè)有*size項(xiàng)的二叉堆里的第一項(xiàng),然后對(duì)剩下的*size-1項(xiàng)重建堆:
????? ? ? ?上述代碼中,由于--和*優(yōu)先級(jí)雷同,從右向左結(jié)合,所以*size--其實(shí)增加的是指針自己的值,而非其指向的整數(shù)的值。因此,服膺:當(dāng)你對(duì)優(yōu)先級(jí)和結(jié)合性有疑問時(shí),就應(yīng)該應(yīng)用括號(hào)。
????7. 誤解指針運(yùn)算
? ? ? ? 在C/C++中,指針的算術(shù)操縱是以它們指向的對(duì)象的巨細(xì)為單位來進(jìn)行的。例如上面函數(shù)的功能是掃描一個(gè)int的數(shù)組,并返回一個(gè)指針,指向val的初次出現(xiàn):
????8. 引用不存在的變量
? ? ? ? ? ?
????C/C++新手不理解棧的規(guī)矩時(shí),可能會(huì)引用不再正當(dāng)?shù)漠?dāng)?shù)刈兞?#xff0c;例如:
int * stackref(){int val;return &val;}????? ? ? ? 函數(shù)返回的指針(假設(shè)為p)指向棧中的局部變量,但該變量在函數(shù)返回后隨著stackref棧幀的銷毀已經(jīng)不再有效。也即:盡管函數(shù)返回的指針p仍然指向一個(gè)正當(dāng)?shù)拇鎯?chǔ)器地址,但它已經(jīng)不再指向一個(gè)正當(dāng)?shù)淖兞苛恕.?dāng)程序后續(xù)調(diào)用其它函數(shù)時(shí),存儲(chǔ)器將重用剛才銷毀棧幀處的存儲(chǔ)器區(qū)域。再后來,如果程序分配某個(gè)值給*p,那么它可能實(shí)際上正在修改另一個(gè)函數(shù)棧幀中的數(shù)據(jù),從而潛在地帶來災(zāi)難性的、令人困惑的后果。
????9. 引用閑暇堆塊中的數(shù)據(jù)
? ? ? ? 典型的錯(cuò)誤為:引用已經(jīng)被釋放了的堆塊中的數(shù)據(jù),例如:
????10. 內(nèi)存泄露
? ? ? ?內(nèi)存泄露是遲緩、隱性的殺手,當(dāng)程序員忘記釋放已分配塊時(shí)會(huì)產(chǎn)生這類問題,例如:
????? ? ? ?如果leak在程序全部生命周期內(nèi)只調(diào)用數(shù)次,則問題還不是很嚴(yán)峻(但還是會(huì)浪費(fèi)存儲(chǔ)器空間),因?yàn)殡S著進(jìn)程結(jié)束,操縱系統(tǒng)會(huì)回收這些內(nèi)存空間。但如果leak()被經(jīng)常調(diào)用,那就會(huì)產(chǎn)生嚴(yán)峻的內(nèi)存泄露,最壞的情況下,會(huì)占用全部虛擬地址空間。對(duì)于像守護(hù)進(jìn)程和服務(wù)器這樣的程序來講,內(nèi)存泄露是嚴(yán)峻的bug,必須加以看重。
????【參考資料】
《深刻理解計(jì)算機(jī)系統(tǒng)》第9章 — 虛擬存儲(chǔ)器
????============== EOF ==================
????
文章結(jié)束給大家分享下程序員的一些笑話語錄: 一個(gè)合格的程序員是不會(huì)寫出 諸如 “摧毀地球” 這樣的程序的,他們會(huì)寫一個(gè)函數(shù)叫 “摧毀行星”而把地球當(dāng)一個(gè)參數(shù)傳進(jìn)去。
--------------------------------- 原創(chuàng)文章 By
錯(cuò)誤和內(nèi)存
---------------------------------
轉(zhuǎn)載于:https://www.cnblogs.com/xinyuyuanm/p/3150400.html
總結(jié)
以上是生活随笔為你收集整理的错误内存【读书笔记】C程序中常见的内存操作有关的典型编程错误的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最近学到的一点东西
- 下一篇: Starling 2D框架简介