上回《生死疆界(上)》說到: 咱們對著微軟在背後做的手腳深感困惑,於是備好車馬糧草 ,一路追殺進去,最後走進了死胡同,剩下的最後希望就是在這裡摸摸,那裡敲敲,看有沒 有暗藏機關。
這正是:探源碼身陷絕境,求解脫心系參數。
還記得我們在delete p;前設的斷點嗎?好,讓我們重新開始調試,按F5,從控制台輸入 "abcd",然後到這條語句前停止了,查看變量p的值,是0x00342c40,那好,打開 vc監視內存的窗口memory,我們查看這個地址的值:
此後的內存情況不再用圖片顯示。只用紅色標志的內存表示發生了變化的內存
看到了嗎?你的寶貝"abcd"乖乖地躺在內存中,其後跟了一個0x00,那表 示''\0'',字符串結束標志。一切都很正常,到底哪裡出錯了?難道是delete p用錯了,而應 該用delete[] p?try it,你會發現依然有相同的錯誤。
從這段內存中仍然看不出問題,仿佛一切風平浪靜,其實是我們來晚了,在delete p前, 內存早已經發生了翻天覆地的變化。 再一次重新進入程序,這次我們從一開始就監視內存。
00342C40 EE FE EE FE EE FE EE 铪铪铪.
00342C47 FE EE FE EE FE EE FE .
這是char *p = new char,執行前的內存。下面是執行後的:
00342C40 CD FD FD FD FD F0 AD 妄.
00342C47 BA 0D F0 AD BA 0D F0 ..瓠...
看不出什麼問題,再往下執行吧:(cin>>p, 這次我們輸入ab)
00342C40 61 62 00 FD FD F0 AD ab.瓠
00342C47 BA 0D F0 AD BA 0D F0 ..瓠...
ab正確地放進了內存中,而且你可以看到cin還體貼地在ab後為你放了一個''\0''
還是沒有什麼問題?再往下走就是cout<<p了,它不會改動內存,再往下就到了 delete p,到那時一切都晚了。 沒錯,就是這一步,靜靜的內存中早已經翻江倒海。
還記得static int __cdecl CheckBytes(unsigned char * pb, unsigned char bCheck, size_t nSize)中的bCheck, nSize嗎? 如果當初你也監視變量的話,會發現bCheck = 253, nSize = 4。這就是這個內存偵測機制的命門。小時候喜歡看武打片,有一部叫做〈鷹爪鐵布 衫〉的,當時令我如癡如醉啊,看過的人一定還記得最後殺那老頭的時候是先在他天靈上一 拍,接著再在褲裆上捏一把,呵呵,bCheck就是天靈,nSize就是褲裆。
把253轉換為16進制,是什麼,沒錯,是FD。呵呵,別忙往下看,想一想,你找到真相了 嗎? 再看一眼char *p = new char執行後的內存,你發現了什麼?p指向0x00342c40那個字 節的值為CD,這是屬於你的內存,看看後邊跟的是什麼,不多不少,恰恰是4個FD,恰恰是 nSize個bCheck!
這個偵測內存非法訪問的機制現在已經被我們開膛破肚了。微軟在你申請的空間後加上四 個FD,如果你訪問了你非法訪問內存,那麼這些內存的內容將被改變(有一個問題我沒有解決 ,我不知道FD代表什麼,望知道的兄弟教我),在delete時,將檢查由new產生的''\0''結束符 後是否有連續四個字節都是FD,如果有證明沒有發生非法內存訪問,如果沒有,那就該讓 _RPT3老兄出馬了。
對於
char *p = new char;
這段代碼,如果只輸入一個字符a,cin>>p執行後的內存為
cin>>p;
cout<<p;
delete p;
00342C40 61 00 FD FD FD F0 AD a..
00342C47 BA 0D F0 AD BA 0D F0 ..瓠...
雖然你只用了你申請的內存,但是cin為了討好你給你加那個''\0'',覆蓋了一個FD,這樣 ,delete時照樣報錯,如果你這樣做char *p = new char[2];
那麼 cin>>p後內存為
00342C40 61 00 FD FD FD FD AD a..
00342C47 BA 0D F0 AD BA 0D F0 ..瓠...
那麼程序將不會報錯。 四個FD就是內存的生死疆界,超過這個疆界,呵呵,聽見遠方傳 來的崩潰的聲音了嗎? 到此,一切真相大白,山高月小,水落石出!
(尾聲:以上所有內容皆來自筆者獨立分析,其中難免有錯,更甚者,也許我大錯特錯, 壓根就不是這樣的機制。如果你發現其中有不正確的地方,請指出,謝謝,在下感激不盡) 。