導讀:原文作者 Eric Lippert 是微軟一名資深軟體設計工程師,從1996年起一直在微軟開發部門任職,協助設計並實現VBScript、JScript、JScript .NET、Windows Script Host、Visual Studio Tools for Office 和 C#。 這篇文章是《閱讀程式碼真的很難》的續文。
昨天,我對於這個問題思考了好一會,還跟Larry Osterman進行了討論,終於想出了一些也許會在閱讀和除錯別人的程式碼時有用的建議。
首先,你正在除錯的機器程式碼,很有可能會與原始碼不匹配,或者只是因為你得到的原始碼不是最新的,或者是因為程式碼被過度優化以至於原始碼和二進位制檔案之間的關係變得不夠明顯——像瑞士乳酪那樣鬆散,又或者是因為你遇到符號表錯誤等等。我當然不是個彙編程式設計師,但我瞭解必要的彙編知識和呼叫規則,這樣我可以試著除錯那些原始碼和二進位制之間不是那麼同步的程式碼。即使知道“‘this”指標很有可能在ECX暫存器中”可以對正在進行的除錯會話大有幫助。
第二,你還記得那部卡通片Calvin and Hobbes中Calvin偷偷溜到Hobbes的身後嚇他,然後學到了嚇一隻正在睡覺的老虎並不明智嗎?Calvin喃喃自語道:“我得開始聆聽那沒有聲音卻不停冒出的疑問才行。”
那部卡通片改變了我的生活。我意識到自己常常會犯一個錯誤,在回顧之前我應該能夠提前意識到,但不知因為什麼,我卻不顧沒有聲音卻不停冒出的疑問而執意地冒失地繼續下去。我決定絕不讓自己的墓碑上寫著“此人死於愚蠢的但本可以完全避免的事情上。”
相信你的頭腦。是的,預感有可能是錯的,但當它真的是錯誤的時候,發現它是錯的並不需要很大的代價。而如果它是對的,你可以省很多時間。當我在除錯我不知道的程式碼時,我試著去聆聽那些沒有聲音卻不停冒出的疑問。在記憶體顯示視窗有一個位元組瞬間變成了紅色,表明它已經改變了。嗯。在這次函式呼叫中我是否期望記憶體發生改變?其自身擁有的記憶體是改變了,還是隻是堆疊錯誤?嗯,這個區域性變數突然變值—這是不是個符號錯誤?這個變數是否是shadow變數而我則剛剛離開了內部作用域?順著這類的線索追蹤下去。
譯者注:shadow變數:當一個變數所在的作用域之外還有一個同名的變數,稱為shadow變數;shadow變數只在自己的作用域有效,詳細解釋和示例參見variable shadowing.
最近我憑直覺在Excel中找到了一個極不起眼的漏洞—我有發現有一個數位的堆分配的緩衝器通過了一個函式但其長度沒有通過。嗯。可疑!果真,只要你追蹤所有的邏輯,你就會發現更深十級的函式對於緩衝範圍作了錯誤的假設並且造成了一個錯誤。幸好這個假設十分保守,受到了許多限制,所以沒有造成緩衝溢位的後果。還有,幸好這個漏洞極不起眼—一般使用者都不會馬上就陷入其中。
那麼我的第三個建議又會是什麼呢?昨天我說過,你需要知道程式碼是幹什麼的和開發者為什麼要讓它幹這些。但你還需要知道更多資訊—你還要知道程式碼是怎樣隨著時間的流逝而在發生改變的。顯然在這個漏洞中,程式一開始佔用一小塊固定大小的緩衝。有人修改了緩衝區分配程式碼以分配可變範圍的緩衝區,但卻忘記了這程式碼所表示的程式是寫來假定一個固定的小範圍緩衝的。漏洞的發生通常由於在其它合理的程式碼重整中發生的極小的錯誤。明顯地,沒有人會把這樣的漏洞寫進從一開始就已經有了可變範圍的緩衝區程式碼的程式碼。當你在閱讀一片段的程式碼時,試著瞭解不變數將會是什麼,然後想想那些情況會違反常量的假定。
當然,要想知道那些沒有聲音卻不停冒出的疑問來自哪裡是很困難的,而且可能教也教不來。不管你怎樣試圖克服它,它都會很困難。