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

  1. Code Complete II《軟體建構之道 2》#10 讀書心得與整理
    1. 第十章 使用變數的一般問題 General Issues in Using Variables
      1. 10.1 資料認知 Data Literacy
        1. 資料認知測試 The Data Literacy Test
        2. 資料型別的其它資源 Additional Resources on Data Types
      2. 10.2 輕鬆掌握變數宣告 Making Variable Declarations Easy
      3. 10.3 初始化變數的原則 Guidelines for Initializing Variables
      4. 10.4 範圍 Scope
        1. 區域化變數參考 Localize References to Variables
        2. 盡量縮短變數的「壽命」 Keep Variables Live for As Short a Time As Possible
        3. 測量變數的生存時間 Measuring the Live Time of a Variable
        4. 減少Scop的一般原則 General Guidelines for Minimizing Scope
        5. 最小化Scop的心得 Comments on Minimizing Scope
      5. 10.5 持續性 Persistence
      6. 10.6 繫結時間 Binding Time
      7. 10.7 資料型別與控制結構的關係 Relationship Between Data Types and Control Structures
      8. 10.8 為變數指定單一用途 Using Each Variable for Exactly One Purpose
        1. 每個變數只用在單一用途
        2. 避免變數有隱喻
        3. 確定已使用所有已宣告的變數

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

原文連結: https://darkblack01.blogspot.com/2013/04/code-complete-ii-210.html
移植時的最後更新日期: 2013-04-18T16:17:51.608+08:00

第十章 使用變數的一般問題
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代表輸出的磁碟機代號。

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