close

大概去年底解答一位網友的問題,沒想到今天我自己也碰上了。唉,我已經很少犯這類錯誤了說。

執行環境是 VC2008 的 debug 模式

症狀:

1. 程式執行到一半發生錯誤,跳出 「中止/重試/略過」的對話方塊:
Debug Error!

HEAP CORRUPTION DETECTED: before Normal block(#80) at 0x003B3190.
CRT detected that application wrote to memory before start of heap buffer.

(Press Retry to debug the application)

2. 按「重試」之後程式暫停,中斷點停在某個 delete/free() 陳述上。

由於發出錯誤的陳述是 delete/free(),加上很多人根本沒看第一個對話方塊的英文就按掉,所以很容易讓人誤以為錯誤是釋放記憶體造成的,就朝著指標不當更動方面去 debug,例如說該指標已經不指向被配置的記憶體起點,甚至是指向非 heap alloc 的記憶體上。

這樣想的人 debug 通常經驗比較不夠。這個訊息雖然是從 delete/free() 丟出來的,不過錯誤並非 delete/free() 造成的,就像關店之前盤點發現貨物數量缺少,錯誤並不是負責盤點的店員造成的,他只是發現這件事而已。

細心一點的人會稍微讀一下對話方塊的英文,然後發現「application wrote to memory before start of heap buffer」這句話,以為誤寫超出整個程式 heap 區的最前面,所以準備開始找值小過 heap 起點的指標。

會這樣想的人通常水準都不錯,只是被VC的 assertion message 給誤導了。雖然「memory before start of heap buffer」聽起來很像是超出整個 heap 區的最前面,但其實不是。如果是這樣的話比較有可能在中途就會出現runtime error,而不是到程式收攤才由主動檢查發現Debug Error(即assertion fail)

真相是,在 VC debug mode下,當使用動態配置記憶體時,實際上配置的記憶體會比申請量還大,其中頭尾各會有一個沒用到的小記憶體當作警戒元素,如果程式的行為正確無誤根本不應該讀寫到警戒值。在debug mode歸還記憶體時,delete/free() 會去 assert 警戒元素是否被人動過手腳,若被更動過就會發出警告。

所以這個 message 真正代表的意思是,你把所配置記憶體之前的警戒值給覆寫掉了。以下程式碼可以重現這個訊息
test = new[100];
*(test-1) = -1;
delete[] test;

只有剛剛好寫到那個警戒值才會有這個訊息,反而是寫到前面兩個、三個、四個WORD.....都不會發出錯誤。
*(test-2) = -1;          // 沒動到警戒值
*(test-3) = -1;

當然我不會被這類笨訊息給騙到,只是已經很少犯這類的錯誤了


arrow
arrow
    全站熱搜

    novus 發表在 痞客邦 留言(0) 人氣()