假如一個 AV 的起因源自於試圖訪問沒有初始化的類的屬性(或方法),在沒有多少代碼的情況下這是很輕易識別的。但另一方面假如有一大堆代碼時,或者 AV 出現在多次重復的循環體中的話,就相當有難度了。幸運的是,( C++Builder 內或外的) AV 的錯誤信息是通過窗口顯示的。這有助於辨別由於非法內存訪問引起的錯誤。有兩件事必須在頭腦中牢記: a) 別讓十六進制的符號嚇倒你。 b) 更別害怕 CPU 窗口。
考慮如下的代碼段 :
TEdit *EditBox; EditBox->AutoSelect = false;
這在語法上似乎是正確的,事實上編譯器也不會報錯。當然,問題在運行時就會暴露出來。運行時,會得到類似 "Access Violation at (someaddress) in module Vcl40.bpl. Read of address 000001F4." 的錯誤,試試看。
發生什麼了? Ok ,在內存中一個對象( object )與數組是非常相似的。我們最好還是先看看一個數組吧。 考慮如下的代碼段 :
int myIntegerArray [ 10 ]; myIntegerArray [ 0 ] = 6 ; myIntegerArray [ 1 ] = 88
當我們為 (myIntegerArray[0])的第一個位置賦值時,我們實際上先將編譯器為“myIntegerArray”分配的內存位置設為0。再將6移到這個內存位置。然後好心的編譯器又分配(保留)了下9個int大小的內存單元預備給我們使用。所以,當我們給myIntegerArray[1]賦值88時,我們實際上在myIntegerArray指定的位置增加了sizeof(int)大小,再將88填入。一般的,myIntegerArray[n]可以被認為是myIntegerArray + (n*sizeof(int))。
實體 (n*sizeof(int))可以(也應該)被想像為一個“偏移”(offset)。
這跟訪問沖突 access violations有什麼關系呢?
前面'read of address' 的值(000001F4 and 000001F5)是來自EditBox的偏移! 回顧前面的代碼片斷,好心的C++Builder自動將EditBox初始化為0。當我們試圖訪問EditBox的一個屬性時,我們實際上在類的基指針上加上了屬性的偏移值(就象前面的數組一樣)。但此時基指針的值為0!由於00000000 + AutoSelect 的偏移(000001F4)不是用戶答應訪問的絕對內存地址,我們得到了一個AV。
我們如何依據這些信息來解決 AV?
首先也是最重要的,前面的介紹應該增加了您對各種 AV的綜合理解。其次,在AV消息框中提供的信息可以用來隔離導致問題的高級語言代碼。下面就是具體的步驟:
1.) 記錄下AV發生的地址。就是前面所講的'(someaddress)'。
2.) 在運行的第一個構造函數處設置斷點 (工程的主窗體main form)。
3.) 運行工程。
4.) 當程序在斷點處鎖住時按下ctrl-alt-c,彈出CPU窗口。
5.) 在左上角的包含了匯編代碼的區域右擊鼠標。
6.) 從彈出菜單中選擇'Goto Address'
7.)在彈出窗口的'Enter Address To Position To'處填入'0x(someaddress)'並回車
使用前面的 Tedit例子您將會看到:
vcl40.@Stdctrls@TCustomEdit@SetAutoSize$QQr4bool
意義很簡單。我們正處在 vcl40模塊內並試圖訪問TCustomEdit+SetAutoSize 。 現在就可以開始隔離引起AV的罪魁禍首了。只需在TcustomEdit對象中排除啦。由於訪問的是AutoSize屬性。當你找到這些的時候,基本上可以肯定了你已經找到了引起Av的高級代碼了!
現在快抓住這個骯髒的 AVs!