1 #include <stdio.h> 2 #include <windows.h> 3 4 class AutoExpand 5 { 6 public: 7 AutoExpand(int val, char* pval) 8 { 9 a = val; 10 p = pval; 11 } 12 private: 13 int a; 14 char *p; 15 }; 16 class CantExpand 17 { 18 public: 19 CantExpand(int val, char* pval) 20 { 21 a = val; 22 p = pval; 23 } 24 private: 25 int a; 26 char *p; 27 }; 28 29 int main(void) 30 { 31 int p[4] = {0x31,0x32,0x33,0x34}; 32 int *a = p; 33 34 FILE* fp = fopen("File Not Exist", "r"); 35 DWORD dwError = GetLastError(); 36 37 AutoExpand ae(10, "abc"); 38 CantExpand ce(10, "abc"); 39 40 return 0; 41 }
上面代碼中出現的變量先說明一下:
p: 是整形數組,含四個元素,總共16Byte;
a: 整形指針,指向數組p;
fp: 文件指針,用來標識打開的"File Not Exist",我機器里是沒這個文件的;
dwError: 獲得fopen失敗的錯誤碼。一般來說可以用FormatMessge來格式化這個錯誤原因或者直接用VC自帶的工具errorlookup來查找這個錯誤碼的解釋;
ae和ce: 是自定義的AutoExpand類型的變量和CantExpand類型的變量。注意,這兩種類型只有類型名字不同。
下面看一下我在調試這個程序的時候,watch窗口的顯示:
上圖中,左邊是Context窗口的Locals頁,顯示所有局部變量。右邊是Watch窗口,是我自己添加的要觀察的對象。
好,先看看整形數組p。我們看到Context窗口的顯示p其實只顯示了數組的地址,點了+號展開后,顯示出了4個整形數據。而右邊窗口,我添加了一個p,c。p后面加個",c"是什么意思呢?看看效果,p,c下面的[0],[1],[2],[3]顯示的是這4個整形值對應的ascii字符哈。所以從這里有了第一個小技巧:
1.watch窗口中,在整形變量后面加上",c"可以顯示該變量對應的ASCII字符。實際上,可以直接敲數字這么顯示也行。比如上面右邊窗口中的118,c的對應值是'v'。也就是說118對應的ASCII字符是'v'。那么,反過來,要知道一個字符的ASCII碼值怎么看呢?看上面,'v',d就是顯示字符'v'對應的十進制ASCII碼值是118。 'v',x顯示的是對應的十六進制的ASCII碼值。除了",c" ",d" ",x"外,還有一些其他的參數可以加,見后面的附表。
然后我們看看變量a。a是個指針。看左邊窗口,即使點了它的+號展開,也只看到了它指向的地址的第一個元素(49)。如果想要看得更多的數據,也可以像我一樣,在上面的Memory窗口里看。但是Memory窗口只有一個,要看多個指針指向的數據就麻煩了,切來切去。那就在watch窗口中顯示吧,a,4就可以了,看到我的watch窗口的顯示沒?所以,有了第二個小技巧:
2.watch窗口中,把指針當成數組看,只要在指針名后面加上一個長度,就可以想看數組一樣看到對應的數據了。比如我上面的a,4。那么如果一個指針指向的數據很大,比如一個整形指針a是指向一個1000個整數的大塊內存,我只想看看最后4個數據,要怎樣呢?那就(a+996),4 唄。從第996個數據開始,看4個~
接下來這個小技巧是我最喜歡的一個了。調試中遇到系統函數調用失敗的情況,通常都要加上GetLastError()函數獲得返回值,然后查對應的解釋才知道錯誤原因。比如,我上面的代碼要打開一個不存在的文件,結果fopen失敗。取回了錯誤碼dwError=2,一查才知道是文件不存在。那么可不可以不用查,調試器直接告訴我原因呢?當然可以,不然我寫這干嘛!剛才的錯誤碼2是記錄在dwError中了,所以只要在watch窗口看看dwError,hr看value欄:系統找不到指定的文件!爽吧!總結一下,第三個小技巧:
3. 在watch窗口中察看錯誤原因,只需要在錯誤之后面頰上",hr"就可以了。比如我上面的 dwError,hr 和 2, hr 都能夠顯示錯誤消息。想看某個錯誤碼的解釋,只要后面加上",hr"就輕松搞定,非常方便!這里還要提一下的是,即使不調用GetLastError()也是可以看錯誤原因的。在fopen()調用完后,直接在watch窗口敲err,hr也可以顯示最近一次的錯誤原因。但是我機器重新裝了os,還沒裝vc,現在用的還是安裝前的屍體。所以這個err,hr的顯示有問題。不過還是有應對之法,那就是強大的TIB信息。watch窗口看看*(unsigned long*)(TIB+0x34), hr也是一樣的。至於為什么嗎,那就是GetLastError的機制了。
還剩2個變量,ae和ce。這兩變量類型名不同,但是其他全都一樣。為什么這兩個變量在watch窗口中顯示的不一樣呢?ae直接顯示出了類成員的值,ce就顯示了個......嗯,這就是最后一個小技巧:
4. 在vc安裝目錄的msdev\bin目錄下有個autoexp.dat文件。可以在里面自定義數據的顯示。具體的看看它前面的大段說明,說得很詳細。我就是在文件后面加上一行AutoExpand =int_val=<a,d> sz_val=<p,s>。
附表:
下表說明調試器可識別的格式說明符。
說明符 | 格式 | 值 | 顯示 |
---|---|---|---|
d,i | signed 十進制整數 | 0xF000F065 | -268373915 |
u | unsigned 十進制整數 | 0x0065 | 101 |
o | unsigned 八進制整數 | 0xF065 | 0170145 |
x,X | 十六進制整數 | 61541(十進制) | 0x0000F065 |
l,h | 用於 d、i、u、o、x、X 的 long 或 short 前綴 | 00406042,hx | 0x0c22 |
f | signed 浮點型 | 3./2. | 1.500000 |
e | signed 科學計數法 | 3./2. | 1.500000e+000 |
g | signed 浮點型或 signed 科學計數法,無論哪個都更短 | 3./2. | 1.5 |
c | 單個字符 | 0x0065 | 101 'e' |
s | 字符串 | 0x0012fde8 | "Hello world" |
su | Unicode 字符串 | "Hello world" | |
hr | HRESULT 或 Win32 錯誤代碼。(調試器自動將 HRESULT 解碼,因此這些情況下不需要該說明符。 | 0x00000000L | S_OK |
wc | 窗口類標志。 | 0x00000040 | WC_DEFAULTCHAR |
wm | Windows 消息數字 | 0x0010 | WM_CLOSE |
下表包含用於內存位置的格式化符號。
符號 | 格式 | 顯示 |
---|---|---|
ma | 64 個 ASCII 字符 | 0x0012ffac .4...0...".0W&.......1W&.0.:W..1...."..1.JO&.1.2.."..1...0y....1 |
m | 以十六進制表示的 16 個字節,后跟 16 個 ASCII 字符 | 0x0012ffac B3 34 CB 00 84 30 94 80 FF 22 8A 30 57 26 00 00 .4...0...".0W&.. |
mb | 以十六進制表示的 16 個字節,后跟 16 個 ASCII 字符 | 0x0012ffac B3 34 CB 00 84 30 94 80 FF 22 8A 30 57 26 00 00 .4...0...".0W&.. |
mw | 8 個字 | 0x0012ffac 34B3 00CB 3084 8094 22FF 308A 2657 0000 |
md | 4 個雙倍字長 | 0x0012ffac 00CB34B3 80943084 308A22FF 00002657 |
mq | 2 個四倍字長 | 0x0012ffac 7ffdf00000000000 5f441a790012fdd4 |
mu | 2 字節字符 (Unicode) | 0x0012fc60 8478 77f4 ffff ffff 0000 0000 0000 0000 |
可以使用帶有計算為位置的任何值或表達式的內存位置說明符。