2013年4月18日 星期四

Code Complete II《軟體建構之道 2》#10 讀書心得與整理

第十章 使用變數的一般問題
General Issues in Using Variables



10.1 資料認知
Data Literacy


資料認知測試
The Data Literacy Test


熟悉1分,知道又不太確定0.5分,不知道沒聽過0分(依繁體中文版順序)

______ abstract data type ______ heap ______ retroactive synapse
______ array ______ index ______ referential integrity
______ bitmap ______ integer ______ stack
______ boolean variable ______ linked list ______ string
______ B-tree ______ named constant ______ structured variable
______ character variable ______ literal ______ tree
______ container class ______ local variable ______ typedef
______ double precision ______ lookup table ______ union
______ elongated stream ______ member data ______ value chain
______ enumerated type ______ pointer ______ variant
______ floating point ______ private ______ Total Score




分數分析....
0-4 新手,
15-19 中階,
20-24 專業,
25-29 比作者更厲害~可以出書了,
30~32....(買這本書就知道了!)

資料型別的其它資源
Additional Resources on Data Types


Cormen, H. Thomas, Charles E. Leiserson, Ronald L. Rivest. Introduction to Algorithms. New York: McGraw Hill. 1990.
Sedgewick, Robert. Algorithms in C++, Part 5, 3d ed. Boston, Mass.: Addison-Wesley, 2002.
Sedgewick, Robert. Algorithms in C++, Parts 1-4, 3d ed. Boston, Mass.:Addison-Wesley, 1998.


10.2 輕鬆掌握變數宣告
Making Variable Declarations Easy

隱含宣告
Implicit Declarations
定義: 不用自己先宣告變數,compilier會幫你宣告。
關閉隱含宣告
宣告所有變數
使用命名慣例
檢查變數名稱


10.3 初始化變數的原則
Guidelines for Initializing Variables


初始化會出問題的原因在於:
  1. 未賦值
  2. 值已過期
  3. 變數一部份被賦值,一部份沒有
關於第三點,有時是對於指標尚未指定變數,就初始化指標指向的內容
除錯指標的錯誤,比其它錯誤更困難。

為避免上述的問題,採用下列幾種原則

  • 宣告時初始化
  • 靠近第一次使用變數前,就初始化
    • 不支援宣告時初始化的語言
  • 靠近第一次使用變數前,就宣告及初始化←最理想的
    • c語言不支援
  • 在可以的情況之下,加上const或final
    • 限制變數使用權限,會降低出錯的機會
  • 注意計數索引變數和累加索引變數
    • 使用前reset
  • 建構函式中初始化類別的成員資料
    • construct中初始化類別資料。
    • 在construct中new的空間,務必在deconstruct中delete掉。
  • 確認重新初始化的需要
    • 迴圈使用變數多次
    • 常式呼叫之間保留其值
  • 初始化const變數一次
  • 用可執行程式碼來初始化變數;初始化含可執行程式碼的變數
    • 初始化常數的function: Startup()
    • 其它一般的function: AnyPossibleNames()
    • 在此若需要常數(只需初始化一次就永遠不需要修改的變數)就放在Startup()並且在程式執行中,確定呼叫一次
    • 其它變數則放在AnyPossibleNames(),在需要呼叫該函式時再初始化。
  • 使用自動初始化所有變數的編譯器設定
    • 打開compilier的自動初始化變數功能,壞處在提高code依賴專案設定的程度以及培養程式設計人員檢查初始化的習慣。
  • 利用編譯器警告訊息
    • 警告你未初始化(未使用)的變數
  • 檢查輸入參數的有效性
    • 利用assert()
  • 使用記憶體存取檢查程式,檢查錯誤的指標
    • 構買memory-access checker程式
  • 在程式一開始的地方初始化工作記憶體(working memory)
    1. 使用預先程式化記憶體填充工具
      • 填入0
        • 確保未初始化的指標指向記憶體底部,方便偵測出誤用未初始化指標的情況。
      • 填入0xCC
        • 對於x86的處理器來說,填入0xCC是不錯的數值,它是一個中斷點int 03h的mechine code
          71:       ptr = (int*)204;   //0xcc = 11001100
          00405308   mov    dword ptr [ebp-4],0CCh
          在debug tool中執行時,執行一段數據,而不是執行程式碼時就會進入中斷點。
          容易從memory dump的訊息中識別出來,而且很少有合法的用途
      • 填入0xDEADBEEF //11011110101011011011111011101111
        0012FF44  EF  BE  AD  DE
        • 因為容易在debug tool工具上看出來
    2. 如果你正使用記憶體填充工具,可以偶爾改變一下填充的值,可以找到「背景環境保持不變的情況下」無法察覺的錯誤。
    3. 開啟程式時讓程式初始化working memory,這是不同於前兩項曝露缺陷的方式,而是一種要隱藏缺陷的方式。避免程式不會因為記憶體初始值的隨機性而受到影響。


10.4 範圍
Scope


區域化變數參考
Localize References to Variables

定義Span:
span = 第一次使用的行數 - 最後一次使用的行數 - 1
Java Example of Variable Span
a = 0;
b = 0;
c = 0;
a = b + c;
span: a=2, b=1, c=0
Java Example of Spans of One and Zero
a = 0;
b = 0;
c = 0;
b = a + 1;
b = b / c;
span: 第一次b=1, 第二次b=0, avgSpan b=0.5
span愈小,可讓讀程式碼的讀者注意小區域的程式,不會跳來跳去的,可增加可讀性。


盡量縮短變數的「壽命」
Keep Variables Live for As Short a Time As Possible

存活時間,在這我稱為「壽命」(live time)
定義:
live time = 第一次使用的行數 - 最後一次使用的行數 + 1


測量變數的生存時間
Measuring the Live Time of a Variable

用以上的定義算出平均值

減少Scop的一般原則
General Guidelines for Minimizing Scope

在迴圈開始之前再去初始化迴圈內使用的變數,而不是在副程式一開始初始化
變數使用之前再賦值
把相關的陳述式放在一起
把相關的陳述式提取成副程式
以最小權限為最初宣告,需要時再打開權限

最小化Scop的心得
Comments on Minimizing Scope

「便利性」哲學與「智慧管理能力」哲學之間的差異
便利性: 全域變數
智慧管理能力: 權限少、限制多、壽命短的區域變數


10.5 持續性
Persistence

「持續性」就是span的另一個說法
原文:“Persistence”is another word for the life span of a piece of data.
  • 在副程式中:從 { 到 }
  • 在指定的程式段:從 new 到 delete
  • 在整個程式中:從 main 到 return EXIT_SUCCESS;
  • 直到永遠:存到檔案中。
如何避免使用到已消失的物件
  • 使用debug程式碼或assert()
  • 賦上不合理的值,指標賦值0或null(音同now)
  • 使用變數,在進出副程式之後,要假設變數失去上次賦予的值(不適用於static變數)
  • 養成使用變數前檢查是否宣告和賦值的習慣


10.6 繫結時間
Binding Time

定義:
「繫結時間」:變數與變數的值真正聯繫在一起的時間。(參考資料)
也就是「賦值的時間點」。
  1. 編碼時(使用magic number、hard code)
  2. 編譯時(使用const)
  3. 來自檔案
  4. 生成物件(creat時,不同於初始化)
  5. 即時(重畫視窗的值)
由1. 到5. 彈性愈大、效能愈低。


10.7 資料型別與控制結構的關係
Relationship Between Data Types and Control Structures

  • 循序陳述式
  • 選擇性陳述式
  • 重覆迴圈


10.8 為變數指定單一用途
Using Each Variable for Exactly One Purpose


遠離為一個變數賦序多種職責的奇技淫巧。

每個變數只用在單一用途

一個變數,重覆使用,常發生在臨時變數身上,通常是temp、a, b, c, kk或x的這種無意義的變數身上
// Compute roots of a quadratic equation.
// This code assumes that (b*b-4*a*c) is positive.
temp = Sqrt( b*b - 4*a*c );
root[O] = ( -b + temp ) / ( 2 * a );
root[1] = ( -b - temp ) / ( 2 * a );
...
// swap the roots
temp = root[0];
root[0] = root[1];
root[1] = temp;
這例子中的 temp就用在Sqrt()又用在swap中。
事實上沒關係的兩個行為,就會被誤以為有關係

為提昇可讀性,可以改成下面的寫法
// Compute roots of a quadratic equation.
// This code assumes that (b*b-4*a*c) is positive.
discriminant = Sqrt( b*b - 4*a*c );
root[0] = ( -b + discriminant ) / ( 2 * a );
root[1] = ( -b - discriminant ) / ( 2 * a );
...
// swap the roots
oldRoot = root[0];
root[0] = root[1];
root[1] = oldRoot;

避免變數有隱喻

下面有三種要避免使用的例子
  1. pageCount, >= 0代表列印的紙張數量,= -1,代表發生錯誤。
  2. customerId, 1....2...3...代表正常的,500000+1....500000+2...500000+3..代表拖欠帳號的號碼。
  3. bytesWritten, >= 0代表文件的byte數,< 0代表輸出的磁碟機代號。

確定已使用所有已宣告的變數



沒有留言:

張貼留言