程式除錯

**程式除錯**（又或者跟英文叫 ，）係程式編寫上嘅一個重要工序，指寫好咗源碼之後搵同排除電腦程式或者電子架生當中嘅程式錯誤（bug）嘅過程，最簡單嘅做法係俾個程式行幾次，睇吓個程式 output 嚟嗰啲結果有冇異常。

除錯嘅工序可能會嘥好多時間－複雜啲嘅程式要行嗮可能要用成好幾日咁耐，但就對程式編寫嚟講不可或缺：將一個軭咗嘅程式放出嚟俾人用後果可以好大鑊，例如係一隻負責用嚟分析科研數據嘅軟件如果有錯，就好有可能會累到用嗰隻軟件做研究嘅科學家跟住錯，就會搞到好大件事。

搵到錯誤之後要點處理有好多技巧。一個可能嘅做法係喺搵到一個錯誤之後，首先試吓將個程式嘅 input 簡化，睇吓個程式係邊忽衰咗。舉個例說明，如果家吓喺處理一個大檔案嘅編譯嗰陣，個編譯器有錯誤搞到輕咗機，跟手個編程員可以試吓將嗰個大檔案斬件做 5 份，（例如）發現淨係喺個編譯器編譯緊第 3 份嗰陣，先至會有錯誤－噉就知道，搞到個編譯器輕機嘅問題喺嗰份嘢入面；然後編程員可以繼續用同樣嘅方法將範圍慢慢縮窄，最後搵出錯誤嘅成因。除錯係寫程式嗰陣近乎必定要做嘅工序，所以專業寫程式嘅人（編程員）實會對除錯技巧有返咁上下熟識。
## 基礎

一位設計者喺寫好咗一個電腦程式之後，通常會俾個程式試行吓先，然後設計者就可以觀察得到個程式嘅 input 同 output 之間嘅關係，並且可以透過觀察個程式嘅 input-output 關係啱唔啱嚟得知個程式有冇出錯；如果測試過程顯示個程式有錯誤（bug），就表示個程式有問題。不過由測試嘅結果淨係得知個程式係咪有問題，但測試唔會話俾設計者知個程式係邊一忽有問題，所以設計者就要有方法嚟搵出問題嘅徵結所在－呢個過程就係所謂嘅**程式除錯**，而電腦界有好多慣常嘅做除錯技巧。程式除錯技巧可以分做兩大類：「郁手寫個程式中途一路避免出錯」同埋「出咗錯之後補救」。

## 避免錯誤

要避免錯誤，其中一個最重要嘅技巧係防禦式編程：防禦式編程意思係寫個程式嘅源碼嗰陣要一路諗埋「最壞情況可能會係點」－一個程式實會攞一啲數據做 input，而出錯好多時都係因為啲 input 唔啱而起嘅，採取防禦式編程嘅編程員會諗吓「如果個 input 唔啱樣，點先可以避免出錯」；舉個簡單例子說明，想像以下嘅短虛擬碼程式，編寫嚟做加法嘅，個程式會攞兩個 input 整數 `a` 同 `b`，並且將兩個數加埋得出嘅數俾嚟做 output，個編程員有考慮到「如果 `a` 同 `b` 唔係一個有效嘅數值」嘅情況，所以就寫段碼，教部電腦喺呢種情況下要點做。

```c
function add(a, b) { // add 呢個子程序，攞 a 同 b 做 input...
  if (a is not a valid number) { // 如果 a 唔係一個有效嘅數值...
    throw new Error('invalid first number to add ' + a); // ... 就出段字
  }
  if (b is not a valid number) { // 如果 b 唔係一個有效嘅數值...
    throw new Error('invalid second number to add ' + b); // ... 就出段字
  }
  return a + b; // 俾 a + b 嘅結果做 output。 
}
```

## 補救錯誤

要做除錯，一個編程員首先要知程式錯誤主要有啲乜嘢類型。一般嚟講，一個程式會出錯，通常都係衰以下呢幾樣嘢當中是但一樣：
***[[句法 (程式語言)|句法]]錯誤**：句法係指一款程式語言入面指定「邊啲符號（包括數字同字母）嘅組合算係有意思」嘅法則，好似係喺 C 程式語言入面，`printf("xxx");` 呢行碼會教部電腦顯示出 `xxx` 噉嘅字，因為 C 語言嘅句法指明咗 `printf` 嘅陳述式係教部電腦做「顯示段字或者變數嘅數值出嚟」嘅作業，而如果一個編程員打錯字，將段碼打咗做（例如）`prinf("xxx");`（少噉咗個 `t`），就會搞到個程式因為句法問題而出錯。基本上，句法上嘅錯誤係咁多種錯誤當中最易搵得到嘅：喺編譯器將段源碼轉換成機械碼嗰陣，實會捉到句法上嘅錯誤，而廿一世紀初嘅高級程式語言實會有功能，喺捉到句法錯誤嗰時俾個信息個用家，話明俾佢知段碼碼邊行有句佢唔識睇（即係句法有問題）嘅陳述式；除咗噉，現代嘅源碼編輯器仲會有句法突顯嘅功能，即係例如將 `printf` 等嘅命令冚唪唥顯示做一隻色，變數就冚唪唥顯示做第隻色... 所以如果有段碼嘅色水唔啱，編程員就可以一眼睇得出嗰個位有句法錯誤。

***編譯器探測唔到嘅錯字**：有陣時，編程員可能打錯字，搞到段碼做嘅嘢同佢嘅意願唔夾，而錯嘅唔係段碼嘅句法，所以個編譯器唔曉探測到個錯；舉個例，想像一個編程員想個程式計 `(x + y) * z` 嘅結果（計 `x + y` 嘅結果，再將個結果乘 `z`），但佢因為手快等嘅原因，將段碼打咗做 `x + y * z`（變咗做「計 `y * z` 嘅結果，再將個結果加落去 `x` 嗰度」），搞到最後計出嚟嘅結果唔啱。
***[[算術]]上嘅錯誤**：就算一個程式句法冇錯、而且亦冇任何形式嘅打錯字，個程式都有可能喺算術上出錯，好似係除以 0（任何數除 0）、整數過溢（要處理嘅數值嘅位多得滯，搞到部電腦記唔到佢）同算術下溢（要處理嘅數值細過部電腦能夠表示嘅最細值）... 等嘅錯誤都會搞到個程式行行吓出錯（例：行吓行吓，段碼叫部電腦將個變數嘅數值除 0，跟手部電腦就出錯），亦都反映個編程員喺寫個程式嗰陣考慮得唔夠周詳，冇諗到個程式行到呢度有可能會出現個噉嘅數值。要應付呢種錯誤，編程界嘅一種常見做法係叫個程式定時定候將啲變數嘅數值顯示出嚟（可以睇返 C 程式語言嘅 `printf`），等編程員可以一路睇到個程式行嗰陣每個變數嘅數值點樣變，即係個程式會出好似以下噉嘅 output ：

```cpp
t=0:    position = 0      velocity = 0 // 喺時間點（t）0 嗰陣，position 嘅值係 0，velocity 嘅值係 0 
t=1:    position = 0      velocity = 10 // 喺時間點（t）1 嗰陣，position 嘅值係 0，velocity 嘅值係 10 
t=2:    position = 10     velocity = 20 // ... 如此類推。
t=3:    position = 30     velocity = 30
t=4:    position = 60     velocity = 40
t=5:    position = 100    velocity = 50
```

***[[程式迴圈|迴圈]]同[[遞歸 (電腦科學)|遞歸]]上嘅錯誤**：迴圈（loop）係一種好常用嘅編程技術；一個迴圈會掕住若干句陳述式同埋重複條件，個程式會按住條重複條件，重複噉行掕住嗰柞陳述式若干次，直至重複條件達到為止，例子有 while 迴圈同 for 迴圈都係噉。有陣時，編程員可以打錯字，搞到個程式出現無限迴圈或者無限遞歸等嘅問題，好似係以下嘅碼噉：

```matlab
n = 10; % 設 n 做 10。
    f = n; % 設 f 等同 n。
    while n > 1 % 只要 n 大過 1，就一路做以下嘅嘢：
        n = n + 1; % 編程員會來係想將 n 數值下降 1 嘅，但佢打錯字，變咗令 n  數值上升 1。
        f = f * n; % 將 f 變成 f 乘 n。
    end % 因為編程員打錯字，如果 n 一開始嗰陣大過 1，噉佢嘅數值永遠會大過 1，個迴圈永遠都唔會完－出現無限迴圈。
```

***資源上嘅錯誤**：有返咁上下複雜嘅程式基本上實會用到多件資源，包括用部電腦嘅記憶體記住處理緊嗰啲變數嘅數值；呢個過程可以引起好多錯誤，例如係編程員喺初始化嗰陣唔記得咗要設定某個變數嘅數值，然後個程式要攞個變數做運算，跟住搞到部電腦因為個變數冇數值而出錯。
... 等等。

## 睇埋
*電腦程式
*軟件工程
*複雜系統
*紀錄檔案
*容錯能力
*斷言

## 註釋

## 引咗

## 拎

* CS 312 Lecture 26 - Debug 技巧
* 學 debug 嘅基礎，IBM developerWorks