1. 程式人生 > >如何閱讀別人的程式碼2

如何閱讀別人的程式碼2

++++++++++++ 
第一章: 導論 
++++++++++++  
1.要養成一個習慣, 經常花時間閱讀別人編寫的高品質程式碼. 
2.要有選擇地閱讀程式碼, 同時, 還要有自己的目標. 您是想學習新的模式|編碼風格|還是滿足某些需求的方法.
3.要注意並重視程式碼中特殊的非功能性需求, 這些需求也許會導致特殊的實現風格.
4.在現有的程式碼上工作時, 請與作者和維護人員進行必要的協調, 以避免重複勞動或產生厭惡情緒. 
5.請將從開放原始碼軟體中得到的益處看作是一項貸款, 儘可能地尋找各種方式來回報開放原始碼社團. 
6.多數情況下, 如果您想要了解"別人會如何完成這個功能呢?", 除了閱讀程式碼以外, 沒有更好的方法. 
7.在尋找bug時, 請從問題的表現形式到問題的根源來分析程式碼. 不要沿著不相關的路徑(誤入歧途). 
8.我們要充分利用偵錯程式|編譯器給出的警告或輸出的符號程式碼|系統呼叫跟蹤器|資料庫結構化查詢語言的日誌機制|包轉儲工具和Windows的消 
息偵查程式, 定出的bug的位置. 
9.對於那些大型且組織良好的系統, 您只需要最低限度地瞭解它的全部功能, 就能夠對它做出修改. 
10.當向系統中增加新功能時, 首先的任務就是找到實現類似特性的程式碼, 將它作為待實現功能的模板. 
11.從特性的功能描述到程式碼的實現, 可以按照字串訊息, 或使用關鍵詞來搜尋程式碼. 
12.在移植程式碼或修改介面時, 您可以通過編譯器直接定位出問題涉及的範圍, 從而減少程式碼閱讀的工作量. 
13.進行重構時, 您從一個能夠正常工作的系統開始做起, 希望確保結束時系統能夠正常工作. 一套恰當的測試用例(test case)可以幫助您滿 
足此項約束. 
14.閱讀程式碼尋找重構機會時, 先從系統的構架開始, 然後逐步細化, 能夠獲得最大的效益. 
15.程式碼的可重用性是一個很誘人, 但難以理解與分離, 可以試著尋找粒度更大一些的包, 甚至其他程式碼. 
16.在複查軟體系統時, 要注意, 系統是由很多部分組成的, 不僅僅只是執行語句. 還要注意分析以下內容: 檔案和目錄結構|生成和配置過程| 
使用者介面和系統的文件. 
18.可以將軟體複查作為一個學習|講授|援之以手和接受幫助的機會.
&<60;&<60;&<60;
 
++++++++++++++++++++
  
第二章: 基本程式設計元素 
 
++++++++++++++++++++
 
 19.第一次分析一個程式時, main是一個好的起始點.
 
 
20.層疊if-else if-...-else序列可以看作是由互斥選擇項組成的選擇結構.
 
 
21.有時, 要想了解程式在某一方面的功能, 執行它可能比閱讀原始碼更為恰當.
 
 
22.在分析重要的程式時, 最好首先識別出重要的組成部分.
 
 
23.瞭解區域性的命名約定, 利用它們來猜測變數和函式的功能用途.
 
 
24.當基於猜測修改程式碼時, 您應該設計能夠驗證最初假設的過程. 這個過程可能包括用編譯器進行檢查|引入斷言|或者執行適當的測試用例.
 
 
25.理解了程式碼的某一部分, 可能幫助你理解餘下的程式碼.
 
 
26.解決困難的程式碼要從容易的部分入手.
 
 
27.要養成遇到庫元素就去閱讀相關文件的習慣; 這將會增強您閱讀和編寫程式碼的能力.
 
 
28.程式碼閱讀有許多可選擇的策略: 自底向上和自頂向下的分析|應用試探法和檢查註釋和外部文件, 應該依據問題的需要嘗試所有這些方法.
 
 
29.for (i=0; i<n; i++)形式的迴圈執行n次; 其他任何形式都要小心.
 
 
30.涉及兩項不等測試(其中一項包括相等條件)的比較表示式可以看作是區間成員測試.
 
 
31.我們經常可以將表示式應用在樣本資料上, 藉以瞭解它的含義.
 
 
32.使用De Morgan法則簡化複雜的邏輯表示式.
 
 
33.在閱讀邏輯乘表示式時, 問題可以認為正在分析的表示式以左的表示式均為true; 在閱讀邏輯和表示式時, 類似地, 可以認為正在分析的表
 
 
達式以左的表示式均為false.
 
 
34.重新組織您控制的程式碼, 使之更為易讀.
 
 
35.將使用條件執行符? :的表示式理解為if程式碼.
 
 
36.不需要為了效率, 犧牲程式碼的易讀性.
 
 
37.高效的演算法和特殊的優化確實有可能使得程式碼更為複雜, 從而更難理解, 但這並不意味著使程式碼更為緊湊和不易讀會提高它的效率.
 
 
38.創造性的程式碼佈局可以用來提高程式碼的易讀性.
 
 
39.我們可以使用空格|臨時變數和括號提高表示式的易讀性.
 
 
40.在閱讀您所控制的程式碼時, 要養成添加註釋的習慣.
 
 
41.我們可以用好的縮排以及對變數名稱的明智選擇, 提高編寫欠佳的程式的易讀性.
 
 
42.用diff程式分析程式的修訂歷史時, 如果這段歷史跨越了整體重新縮排, 常常可以通過指定-w選項, 讓diff忽略空白差異, 避免由於更改了
 
 
縮排層次而引入的噪音.
 
 
43.do迴圈的迴圈體至少執行一次.
 
 
44.執行算術運算時, 當b=2n-1時, 可以將a&b理解為a%(b+1).
 
 
45.將a<<n理解為a*k, k=2n.
 
 
46.將a>>n理解為a/k, k=2n.
 
 
47.每次只分析一個控制結構, 將它的內容看作是一個黑盒.
 
 
48.將每個控制結構的控制表示式看作是它所包含程式碼的斷言.
 
 
49.return, goto, break和continue語句, 還有異常, 都會影響結構化的執行流程. 由於這些語句一般都會終止或重新開始正在進行的迴圈,
 
 
因此要單獨推理它們的行為.
 
 
50.用複雜迴圈的變式和不變式, 對迴圈進行推理.
 
 
51.使用保持含義不變的變換重新安排程式碼, 簡化程式碼的推理工作. 
 
 
+++++++++++++++++++
 
第三章: 高階C資料型別 
 
+++++++++++++++++++
 
 
 
52.瞭解特定語言構造所服務的功能之後, 就能夠更好地理解使用它們的程式碼.
 
 
53.識別並歸類使用指標的理由.
 
 
54.在C程式中, 指標一般用來構造鏈式資料結構|動態分配的資料結構|實現引用呼叫|訪問和迭代資料元素|傳遞陣列引數|引用函式|作為其他
 
 
值的別名|代表字串|以及直接訪問系統記憶體.
 
 
55.以引用傳遞的引數可以用來返回函式的結果, 或者避免參數複製帶來的開銷.
 
 
56.指向陣列元素地址的指標, 可以訪問位於特定索引位置的元素.
 
 
57.指向陣列元素的指標和相應的陣列索引, 作用在二者上的運算具有相同的語義.
 
 
58.使用全域性或static區域性變數的函式大多數情況都不可重入(reentrant).
 
 
59.字元指標不同於字元陣列.
 
 
60.識別和歸類應用結構或共用體的每種理由.
 
 
61.C語言中的結構將多個數據元素集合在一起, 使得它們可以作為一個整體來使用, 用來從函式中返回多個數據元素|構造鏈式資料結構|對映
 
 
資料在硬體裝置|網路連結和儲存介質上的組織方式|實現抽象資料型別|以及以面向物件的方式程式設計.
 
 
62.共用體在C程式中主要用於優化儲存空間的利用|實現多型|以及訪問資料不同的內部表達方式.
 
 
63.一個指標, 在初始化為指向N個元素的儲存空間之後, 就可以作為N個元素的陣列來使用.
 
 
64.動態分配的內在塊可以電焊工地釋放, 或在程式結束時釋放, 或由垃圾回收器來完成回收; 在棧上分配的記憶體塊當分配它的函式退出後釋放.


  
65.C程式使用typedef宣告促進抽象, 並增強程式碼的易讀性, 從而防範可移植性問題, 並模擬C++和Java的類宣告行為.
 
 
66.可以將typedef宣告理解成變數定義: 變數的名稱就是型別的名稱; 變數的型別就是與該名稱對應的型別.
&<60;&<60;&<60;
 
 
 
 
 
 
&<60;
+++++++++++++++
 
 
第四章: C資料結構
 
 
+++++++++++++++
 
 
 
 
 
 
67.根據底層的抽象資料型別理解顯式的資料結構操作.
 
 
68.C語言中, 一般使用內建的陣列型別實現向量, 不再對底層實現進行抽象.
 
 
69.N個元素的陣列可以被序列for (i=0; i<N; i++)完全處理; 所有其他變體都應該引起警惕.
 
 
70.表示式sizeof(x)總會得到用memset或memcpy處理陣列x(不是指標)所需的正確位元組數.
 
 
71.區間一般用區間內的第一個元素和區間後的第一個元素來表示.
 
 
72.不對稱區間中元素的數目等於高位邊界與低位邊界的差.
 
 
73.當不對稱區間的高位邊界等於低位邊界時, 區間為空.
 
 
74.不對稱區間中的低位邊界代表區間的第一個元素; 高位邊界代表區間外的第一個元素.
 
 
75.結構的陣列常常表示由記錄和欄位組成的表.
 
 
76.指向結構的指標常常表示訪問底層記錄和欄位的遊標.
 
 
77.動態分配的矩陣一般儲存為指向陣列列的指標或指向元素指標的指標; 這兩種型別都可以按照二維陣列進行訪問.
 
 
78.以陣列形式儲存的動態分配矩陣, 用自定義訪問函式定位它們的元素.
 
 
79.抽象資料型別為底層實現元素的使用(或誤用)方式提供一種信心的量度.
 
 
80.陣列用從0開始的順序整數為鍵, 組織查詢表.
 
 
81.陣列經常用來對控制結構進行高效編碼, 簡化程式的邏輯.
 
 
82.通過在陣列中每個位置儲存一個數據元素和一個函式指標(指向處理資料元素的函式), 可以將程式碼與資料關聯起來.
 
 
83.陣列可以通過儲存供程式內的抽象機(abstract machine)或虛擬機器(virtual machine)使用的資料或程式碼, 控制程式的運作.
 
 
84.可以將表示式sizeof(x) / sizeof(x[0])理解為陣列x中元素的個數.
 
 
85.如果結構中含有指向結構自身|名為next的元素, 一般說來, 該結構定義的是單向連結串列的結點.
 
 
86.指向連結串列結點的永續性(如全域性|靜態或在堆上分配)指標常常表示連結串列的頭部.
 
 
87.包含指向自身的next和prev指標的結構可能是雙向連結串列的結點.
 
 
88.理解複雜資料結構的指標操作可以將資料元素畫為方框|指標畫為箭頭.
 
 
89.遞迴資料結構經常用遞迴演算法來處理.
 
 
90.重要的資料結構操作演算法一般用函式引數或模板引數來引數化.
 
 
91.圖的結點常常順序地儲存在陣列中, 連結到連結串列中, 或通過圖的邊連結起來.
 
 
92.圖中的邊一般不是隱式地通過指標, 就是顯式地作為獨立的結構來表示.
 
 
93.圖的邊經常儲存為動態分配的陣列或連結串列, 在這兩種情況下, 邊都錨定在圖的結點上.
 
 
94.在無向圖中, 表達資料時應該將所有的結點看作是等同的, 類似地, 進行處理任務的程式碼也不應該基於它們的方向來區分邊.
 
 
95.在非連通圖中, 執行遍歷程式碼應該能夠接通孤立的子圖.
 
 
96.處理包含迴路的圖時, 遍歷程式碼應該避免在處理圖的迴路進入迴圈.
 
 
97.複雜的圖結構中, 可能隱藏著其他型別的獨立結構.
 
 
 
 
 
 
+++++++++++++++++
 
 
第五章: 高階控制流程
 
 
+++++++++++++++++
 
 
 
 
 
 
98.採用遞迴定義的演算法和資料結構經常用遞迴的函式定義來實現.
 
 
99.推理遞迴函式時, 要從基準落伍測試開始, 並認證每次遞迴呼叫如何逐漸接近非遞迴基準範例程式碼.
 
 
100.簡單的語言常常使用一系列遵循該語言語法結構的函式進行語法分析.
 
 
101.推理互遞迴函式時, 要基於底層概念的遞迴定義.
 
 
102.尾遞迴呼叫等同於一個回到函式開始處的迴圈.
 
 
103.將throws子句從方法的定義中移除, 然後執行Java編譯器對類的原始碼進行編譯, 就可以容易地找到那些可能隱式地生成異常的方法.
 
 
104.在多處理器計算機上執行的程式碼常常圍繞程序或執行緒進行組織.
 
 
105.工作群並行模型用於在多個處理器間分配工作, 或者建立一個任務池, 然後將大量需要處理標準化的工作進行分配.
 
 
106.基於執行緒的管理者/工人並行模型一般將耗時的或阻塞的操作分配給工人子任務, 從而維護中心任務的響應性.
 
 
107.基於程序的管理者/工人並行模型一般用來重用現有的程式, 或用定義良好的介面組織和分離粗粒度的系統模組.
 
 
108.基於流水線的並行處理中, 每個任務都接收到一些輸入, 對它們進行一些處理, 並將生成的輸出傳遞給下一個任務, 進行不同的處理.
 
 
109.競爭條件很難捉摸, 相關的程式碼常常會將競爭條件擴散到多個函式或模組; 因而, 很難隔離由於競爭條件導致的問題.
 
 
110.對於出現在訊號處理器中的資料結構操作程式碼和庫呼叫要保持高度警惕.
 
 
111.在閱讀包含巨集的程式碼時, 要注意, 巨集既非函式, 也非語句.
 
 
112.do…while(0)塊中的巨集等同於控制塊中的語句.
 
 
113.巨集可以訪問在它的使用點可見的所有區域性變數.
 
 
114.巨集呼叫可改變引數的值
 
 
115.基於巨集的標記拼接能夠建立新的標記符.
&<60;&<60;&<60;
 
 
 
 
+++++++++++++++++
 
 
第六章: 應對大型專案
 
 
+++++++++++++++++
 
 
 
 
 
 
116.我們可以通過瀏覽專案的原始碼樹—包含專案原始碼的層次目錄結構, 來分析一個專案的組織方式. 原始碼樹常常能夠反映出專案在構架和
 
 
軟體過程上的結構.
 
 
117.應用程式的原始碼樹經常是該應用程式的部署結構的映象.
 
 
118.不要被龐大的原始碼集合嚇倒; 它們一般比小型的專門專案組織得更出色.
 
 
119.當您首次接觸一個大型專案時, 要花一些時間來熟悉專案的目錄樹結構.
 
 
120.專案的原始碼遠不只是編譯後可以獲得可執行程式的計算機語言指令; 一個專案的原始碼樹一般還包括規格說明|終端使用者和開發人員文件|
 
 
測試指令碼|多媒體資源|編譯工具|例子|本地化檔案|修訂歷史|安裝過程和許可資訊.
 
 
121.大型專案的編譯過程一般宣告性地藉助依賴關係來說明. 依賴關係由工具程式, 如make及其派生程式, 轉換成具體的編譯行動.
 
 
122.大型專案中, 製作檔案常常由配置步驟動態地生成; 在分析製作檔案之前, 需要先執行專案特定的配置.
 
 
123.檢查大型編譯過程的各個步驟時, 可以使用make程式的-n開關進行預演.
 
 
124.修訂控制系統提供從儲存庫中獲取原始碼最新版本的方式.
 
 
125.可以使用相關的命令, 顯示可執行檔案中的修訂標識關鍵字, 從而將可執行檔案與它的原始碼匹配起來.
 
 
126.使用修訂日誌中出現的bug跟蹤系統內的編號, 可以在bug跟蹤系統的資料庫中找到有關的問題的說明.
 
 
127.可以使用修訂控制系統的版本儲存庫, 找出特定的變更是如何實現的.
 
 
128.定製編譯工具用在軟體開發過程的許多方面, 包括配置|編譯過程管理|程式碼的生成|測試和文件編制.
 
 
129.程式的除錯輸出可以幫助我們理解程式控制流程和資料元素的關鍵部分.
 
 
130.跟蹤語句所在的地點一般也是演算法執行的重要部分.
 
 
131.可以用斷言來檢驗演算法運作的步驟|函式接收的引數|程式的控制流程|底層硬體的屬性和測試用例的結果.
 
 
132.可以使用對演算法進行檢驗的斷言來證實您對演算法運作的理解, 或將它作為推理的起點.
 
 
133.對函式引數和結果的斷言經常記錄了函式的前置條件和後置條件.
 
 
134.我們可以將測試整個函式的斷言作為每個給定函式的規格說明.
 
 
135.測試用例可以部分地代替函式規格說明.
 
 
136.可以使用測試用例的輸入資料對原始碼序列進行預演.
 
 
 
 
 
 
+++++++++++++++++++
 
 
第七章: 編碼規範和約定
 
 
+++++++++++++++++++
 
 
 
 
 
 
137.瞭解了給定程式碼庫所遵循的檔案組織方式後, 就能更有效率地瀏覽它的原始碼.
 
 
138.閱讀程式碼時, 首先要確保您的編輯器或優美列印程式的tab設定, 與程式碼遵循的風格規範一致.
 
 
139.可以使用程式碼塊的縮排, 快速地掌握程式碼的總體結構.
 
 
140.對編排不一致的程式碼, 應該立即給予足夠的警惕.
 
 
141.分析程式碼時, 對標記為XXX, FIXME和TODO的程式碼序列要格外注意: 錯誤可能就潛伏在其中.
 
 
142.常量使用大寫字母命名, 單詞用下劃線分隔.
 
 
143.在遵循Java編碼規範的程式中, 包名(package name)總是從一個頂級的域名開始(例如, org, com), 類名和介面名由大寫字母開始, 方法
 
 
和變數名由小寫字母開始.
 
 
144.使用者介面控制元件名稱之前的匈牙利記法的字首型別標記可以幫助我們確定它的作用.
 
 
145.不同的程式設計規範對可移植構造的構成有不同的主張.
 
 
146.在審查程式碼的可移植性, 或以某種給定的編碼規範作為指南時, 要注意瞭解規範對可移植性需求的界定與限制.
 
 
147.如果GUI功能都使用相應的程式設計結構來實現, 則通過程式碼審查可以輕易地驗證給定使用者介面的規格說明是否被正確地採用.
 
 
148.瞭解專案編譯過程的組織方式與自動化方式之後, 我們就能夠快速地閱讀與理解對應的編譯規則.
 
 
149.當檢查系統的釋出過程時, 常常可以將相應發行格式的需求作為基準.
 
 
&<60;
&<60;
++++++++++++
 
 
第八章:文件
 
 
++++++++++++
 
 
 
 
 
 
150.閱讀程式碼時, 應該儘可能地利用任何能夠得到的文件.
 
 
151.閱讀一小時程式碼所得到的資訊只不過相當於閱讀一分鐘文件.
 
 
152.使用系統的規格說明文件, 瞭解所閱讀程式碼的執行環境.
 
 
153.軟體需求規格說明是閱讀和評估程式碼的基準.
 
 
154.可以將系統的設計規格說明作為認知程式碼結構的路線圖, 閱讀具體程式碼的指引.
 
 
155.測試規格說明文件為我們提供可以用來對程式碼進行預演的資料.
 
 
156.在接觸一個未知系統時, 功能性的描述和使用者指南可以提供重要的背景資訊,從而更好地理解閱讀的程式碼所處的上下文.
 
 
157.從使用者參考手冊中, 我們可以快速地獲取, 應用程式在外觀與邏輯上的背景知識, 從管理員手冊中可以得知程式碼的介面|檔案格式和錯誤消
 
 
息的詳細資訊.
 
 
158.利用文件可以快捷地獲取系統的概況, 瞭解提供特定特性的程式碼.
 
 
159.文件經常能夠反映和提示出系統的底層結構.
 
 
160.文件有助於理解複雜的演算法和資料結構.
 
 
161.演算法的文字描述能夠使不透明(晦澀, 難以理解)的程式碼變得可以理解.
 
 
162.文件常常能夠闡明原始碼中識別符號的含義.
 
 
163.文件能夠提供非功能性需求背後的理論基礎.
 
 
164.文件還會說明內部程式設計介面.
 
 
165.由於文件很少像實際的程式程式碼那樣進行測試, 並受人關注, 所以它常常可能存在錯誤|不完整或過時.
 
 
166.文件也提供測試用例, 以及實際應用的例子.
 
 
167.文件常常還會包括已知的實現問題或bug.
 
 
168.環境中已知的缺點一般都會記錄在原始碼中.
 
 
169.文件的變更能夠標出那些故障點.
 
 
170.對同一段原始碼重複或互相沖突的更改, 常常表示存在根本性的設計缺陷, 從而使得維護人員需要用一系列的修補程式來修復.
 
 
171.相似的修復應用到原始碼的不同部分, 常常表示一種易犯的錯誤或疏忽, 它們同樣可能會在其他地方存在.
 
 
172.文件常常會提供不恰當的資訊, 誤導我們對原始碼的理解.
 
 
173.要警惕那些未歸檔的特性: 將每個例項歸類為合理|疏忽或有害, 相應地決定是否應該修復程式碼或文件.
 
 
174.有時, 文件在描述系統時, 並非按照已完成的實現, 而是系統應該的樣子或將來的實現.
 
 
175.在原始碼文件中, 單詞gork的意思一般是指”理解”.
 
 
176.如果未知的或特殊用法的單詞阻礙了對程式碼的理解, 可以試著在文件的術語表(如果存在的話)|New Hacker’s Dictionary[Ray96]|或在
 
 
Web搜尋引擎中查詢它們.
 
 
177.總是要以批判的態度來看待文件, 注意非傳統的來源, 比如註釋|標準|出版物|測試用例|郵件列表|新聞組|修訂日誌|問題跟蹤資料庫|營
 
 
銷材料|原始碼本身.
 
 
178.總是要以批判的態度來看待文件; 由於文件永遠不會執行, 對文件的測試和正式複查也很少達到對程式碼的同樣水平, 所以文件常常會誤導
 
 
讀者, 或者完全錯誤.
 
 
179.對於那些有缺陷的程式碼, 我們可以從中推斷出它的真實意圖.
 
 
180.在閱讀大型系統的文件時, 首先要熟悉文件的總體結構和約定.
 
 
181.在對付體積龐大的文件時, 可以使用工具, 或將文字輸出到高品質輸出裝置上, 比如鐳射印表機, 來提高閱讀的效率.
 
 
 
 
 
 
++++++++++++++
 
 
第九章: 系統構架
 
 
++++++++++++++
 
 
 
 
 
 
182.一個系統可以(在重大的系統中也確實如此)同時出多種不同的構架型別. 以不同的方式檢查同一系統|分析系統的不同部分|或使用不同級
 
 
別的分解, 都有可能發現不同的構架型別.
 
 
183.協同式的應用程式, 或者需要協同訪問共享資訊或資源的半自治程序, 一般會採用集中式儲存庫構架.
 
 
184.黑板系統使用集中式的儲存庫, 儲存非結構化的鍵/值對, 作為大量不同程式碼元件之間的通訊集線器.
 
 
185.當處理過程可以建模|設計和實現成一系列的資料變換時, 常常會使用資料流(或管道—過濾器)構架.
 
 
186.在批量進行自動資料處理的環境中, 經常會採用資料流構架, 在對資料工具提供大量支援的平臺上尤其如此.
 
 
187.資料流構架的一個明顯徵兆是: 程式中使用臨時檔案或流水線(pipeline)在不同程序間進行通訊.
 
 
188.使用圖示來建模面向物件構架中類的關係.
 
 
189.可以將原始碼輸入到建模工具中, 逆向推匯出系統的構架.
 
 
190.擁有大量同級子系統的系統, 常常按照分層構架進行組織.
 
 
191.分層構架一般通過堆疊擁有標準化介面的軟體元件來實現.
 
 
192.系統中每個層可以將下面的層看作抽象實體, 並且(只要該層滿足它的需求說明)不關心上面的層如何使用它.
 
 
193.層的介面既可以是支援特定概念的互補函式族, 也可以是一系列支援同一抽象介面不同底層實現的可互換函式.
 
 
194.用C語言實現的系統, 常常用函式指標的陣列, 表達層介面的多路複用操作.
 
 
195.用面向物件的語言實現的系統, 使用虛方法呼叫直接表達對層介面的多嘴複用操作.
 
 
196.系統可以使用不同的|獨特的層次分解模型跨各種座標軸進行組織.
 
 
197.使用程式切片技術, 可以將程式中的資料和控制之間依賴關係集中到一起.
 
 
198.在併發系統中, 一個單獨的系統元件起到集中式管理器的作用, 負責啟動|停止和協調其他系統程序和任務的執行.
 
 
199.許多現實的系統都會博採眾家之長. 當處理此類系統時, 不要徒勞地尋找無所不包的構架圖; 應該將不同構架風格作為獨立但相關的實體
 
 
來進行定位|識別並瞭解.
 
 
200.狀態變遷圖常常有助於理清狀態機的動作.
 
 
201.在處理大量的程式碼時, 瞭解將程式碼分解成單獨單元的機制極為重要.
 
 
202.大多數情況下, 模組的物理邊界是單個檔案|組織到一個目錄中的多個檔案或擁有統一字首的檔案的集合.
 
 
203.C中的模組, 由提供模組公開介面的標頭檔案和提供對應實現的原始檔組成.
 
 
204.物件的建構函式經常用來分配與物件相關的資源, 並初始化物件的狀態. 函式一般用來釋放物件在生命期中佔用的資源.
 
 
205.物件方法經常使用類欄位來儲存控制所有方法運作的資料(比如查詢表或字典)或維護類運作的狀態資訊(例如, 賦給每個物件一個識別符號的
 
 
計數器).
 
 
206.在設計良好的類中, 所有的欄位都應在宣告為private, 並用公開的訪問方法提供對它們的訪問.
 
 
207.在遇到friend宣告時, 要停下來分析一下, 看看繞過類封裝在設計上的理由.
 
 
208.可以有節制地用運算子增強特定類的可用性, 但用運算子過載, 將類實現為擁有內建算術型別相關的全部功能的類實體, 是不恰當的.
 
 
209.泛型實現不是在編譯期間通過巨集替換或語言所支援的功能(比如C++模板和Ada的泛型包)來實現, 就是在執行期間通過使用資料元素的指標
 
 
和函式的指標|或物件的多型性實現.
 
 
210.抽象資料型別經常用來封裝常用的資料組織方案(比如樹|列表或棧), 或者對使用者隱藏資料型別的實現細節.
 
 
211.使用庫的目的多種多樣: 重用原始碼或目的碼, 組織模組集合, 組織和優化編譯過程, 或是用來實現應用程式各種特性的按需載入.
 
 
212.大型的|分散式的系統經常實現為許多互相協作的程序.
 
 
213.對於基於文字的資料儲存庫, 可以通過瀏覽儲存在其中的資料, 破譯出它的結構.
 
 
214.可以通過查詢資料字典中的表, 或使用資料庫專有的SQL命令, 比如show table, 來分析關係型資料庫的模式.
 
 
215.識別出重用的構架元素後, 可以查詢其最初的描述, 瞭解正確地使用這種構架的方式, 以及可能出現的誤用.
 
 
216.要詳細分析建立在某種框架之上的應用程式, 行動的最佳路線就是從研究框架自身開始.
 
 
217.在閱讀嚮導生成的程式碼時, 不要期望太高, 否則您會感到失望.
 
 
218.學習幾個基本的設計模式之後, 您會發現, 您檢視程式碼構架的方式會發生改變: 您的視野和詞彙將會擴充套件到能夠識別和描述許多通用的形
 
 
式.
 
 
219.頻繁使用的一些模式, 但並不顯式地指出它們的名稱, 這是由於構架性設計的重用經常先於模式的形成.
 
 
220.請試著按照底層模式來理解構架, 即使程式碼中並沒有明確地提及模式.
 
 
221.大多數直譯器都遵循類似的處理構架, 圍繞一個狀態機進行構建, 狀態機的操作依賴於直譯器的當前狀態|程式指令和程式狀態.
 
 
222.多數情況下, 參考構架只是為應用程式域指定一種概念性的結構, 具體的實現並非必須遵照這種結構.
 
 
 
 
+++++++++++++++++
 
 
第十章: 程式碼閱讀工具
 
 
+++++++++++++++++
 
 
 
 
 
 
223.詞彙工具可以高效地在一個大程式碼檔案中或者跨多個檔案查詢某種模式.
 
 
224.使用程式編輯器和正則表示式查詢命令, 瀏覽龐大的原始碼檔案.
 
 
225.以只讀方式瀏覽原始碼檔案.
 
 
226.使用正則表示式 ^function name 可以找出函式的定義.
 
 
227.使用正則表示式的字元類, 可以查詢名稱遵循特定模式的變數.
 
 
228.使用正則表示式的否定字元類, 可以避免非積極匹配.
 
 
229.使用正則表示式 symbol-1. *symbol-2, 可以查找出現在同一行的符號.
 
 
230.使用編輯器的 tags 功能, 可以快速地找出實體的定義.
 
 
231.可以用特定的 tag 建立工具, 增加編輯器的瀏覽功能.
 
 
232.使用編輯器的大綱檢視, 可以獲得原始碼結構的鳥瞰圖.
 
 
233.使用您的編輯器來檢測原始碼中圓括號|方括號和花括號的匹配.
 
 
234.使用 grep 跨多個檔案查詢程式碼模式.
 
 
235.使用 grep 定位符號的宣告|定義和應用.
 
 
236.當您不能精確地表述要查詢的內容時, 請使用關鍵單詞的詞幹對程式的原始碼進行查詢.
 
 
237.用 grep 過濾其他工具生成的輸出, 分離出您要查詢的項.
 
 
238.將 grep 的輸出輸送到其他工具, 使複雜處理任務自動化.
 
 
239.通過對 grep 的輸出進行流編輯, 重用程式碼查詢的結果.
 
 
240.通過選取與噪音模式不匹配的輸出行(grep-v), 過濾虛假的 grep 輸出.
 
 
241.使用 fgrep 在原始碼中查詢字串列表.
 
 
242.查詢註釋, 或識別符號大小寫不敏感的語言編寫的程式碼時, 要使用大小寫不敏感的模式匹配(grep -i).
 
 
243.使用 grep –n 命令列開關, 可以建立與給定正則表示式匹配的檔案和行號的檢查表.
 
 
244.可以使用 diff 比較檔案或程式不同版本之間的差別.
 
 
245.在執行 diff 命令時, 可以使用 diff –b, 使檔案比較演算法忽略結尾的空格, 用–w 忽略所有空白區域的差異, 用–i 使檔案比較對大
 
 
小寫不敏感.
 
 
246.不要對建立自己的程式碼閱讀工具心存畏懼.
 
 
247.在構建自己的程式碼閱讀工具時: 要充分利用現代快速原型語言所提供的能力; 從簡單開始, 根據需要逐漸改進; 使用利用程式碼詞彙結構的
 
 
各種試探法; 要允許一些輸出噪音或寂靜(無關輸出或缺失輸出); 使用其他工具對輸入進行預處理, 或者對輸出進行後期處理.
 
 
248.要使編譯器成為您的: 指定恰當級別的編譯器警告, 並小心地評估生成的結果.
 
 
249.使用C前處理器理清那些濫用前處理器特性的程式.
 
 
250.要徹底地瞭解編譯器如何處理特定的程式碼塊, 需要檢視生成的符號(彙編)程式碼.
 
 
251.通過分析相應目標檔案中的符號, 可以清晰地瞭解原始檔的輸入和輸出.
 
 
252.使用原始碼瀏覽器瀏覽大型的程式碼集合以及物件型別.
 
 
253.要抵制住按照您的編碼規範對外部程式碼進行美化的誘惑; 不必要的編排更改會建立不同的程式碼, 並妨礙工作的組織.
 
 
254.優美列印程式和編輯器語法著色可以使得程式的原始碼為易讀.
 
 
255.cdecl 程式可以將難以理解的C和C++型別宣告轉換成純英語(反之亦然).
 
 
256.實際執行程式, 往往可以更深刻地理解程式的動作.
 
 
257.系統呼叫|事件和資料包跟蹤程式可以增進對程式動作的理解.
 
 
258.執行剖析器可以找出需要著重優化的程式碼, 驗證輸入資料的覆蓋性, 以及分析演算法的動作.
 
 
259.通過檢查從未執行的程式碼行, 可以找出測試覆蓋的弱點, 並據此修正測試資料.
 
 
260.要探究程式動態動作時的每個細節, 需要在偵錯程式中運作它.
 
 
261.將您覺得難以理解的程式碼列印到紙上.
 
 
262.可以繪製圖示來描繪程式碼的動作.
 
 
263.可以試著向別人介紹您在閱讀的程式碼, 這樣做一般會增進您對程式碼的理解.
 
 
264.理解複雜的演算法或巧妙的資料結構, 要選擇一個安靜的環境, 然後聚精會神地考慮, 不要藉助於任何計算機化或自動化的幫助.
 
 
 
 
 
 
+++++++++++++++++++++
 
 
第十一章: 一個完整的例子
 
 
+++++++++++++++++++++
 
 
 
 
 
 
265.模仿軟體的功能時, 要依照相似實體的線路(類|函式|模組). 在相似的現有實體中, 為簡化對原始碼庫的文字查詢, 應選取比較罕見的名
 
 
稱.
 
 
266.自動生成的檔案常常會在檔案的開關有一段註釋, 說明這種情況.
 
 
267.如果試圖精確地分析程式碼, 一般會陷入數量眾多的類|檔案和模組中, 這些內容會很快將我們淹沒; 因此, 我們必須將需要理解的程式碼限定
 
 
在絕對必需的範圍之內.
 
 
268.採用一種廣度優先查詢策略, 從多方攻克程式碼閱讀中存在的問題, 進到找出克服它們的方法為止.