許多初學者將「程式能跑出正確結果」等同於「程式寫對了」。本文透過一個多項式加法程式的真實除錯過程,展示調試器如何揭露隱藏在看似正確輸出背後的邏輯缺陷,並說明養成調試習慣對程式設計能力的質變影響。
以陣列儲存兩個多項式,項目結構:
struct polynomial { float coef; int expon; };
struct polynomial terms[MAX_TERMS];
int avail = 0; // 下一可用位置
核心函數 padd(starta, finisha, startb, finishb, &startd, &finishd) 合併兩多項式,結果存入 terms 起始處。
輸入:
4x³ + 2x²
5x³ + 3x²
輸出:
9x³ + 5x² ← 完全正確!
開發者高興地以為大功告成。
輸入:
4x⁴ + 4
3x¹ + 1
預期:4x⁴ + 3x + 5
實際輸出:錯誤結果 (截圖顯示指數/係數錯亂)。
在 attach() 函數設中斷點,觀察 avail 與 terms[] 變化:
void attach(float coefficient, int exponent) {
if (avail > MAX_TERMS) { /* 錯誤處理 */ }
terms[avail].coef = coefficient;
terms[avail].expon = exponent; // ← 關鍵行
avail++;
}
發現:terms[avail].expon = exponent; 這行在處理 4x⁴ + 4 時,將常數項 4 (指數 0) 寫入陣列,但因邏輯順序問題,導致後續比較指數時讀取到錯誤值,進而誤判項目合併邏輯。
padd 中的合併迴圈:
while (starta <= finisha && startb <= finishb) {
switch (COMPARE(terms[starta].expon, terms[startb].expon)) {
case -1: attach(terms[startb].coef, terms[startb].expon); startb++; break;
case 0: /* 係數相加 */ attach(...); starta++; startb++; break;
case 1: attach(terms[starta].coef, terms[starta].expon); starta++; break;
}
}
當第一個多項式為 4x⁴ + 4 (指數 4, 0),第二個為 3x + 1 (指數 1, 0) 時:
starta 推進到常數項 4 (指數 0)。startb 推進到常數項 1 (指數 0)。邏輯本身正確!真正錯誤在 attach 寫入順序:原始碼中 terms[avail].expon = exponent; 被註解標記為「此處存在風險」,實際執行時因指標/陣列狀態導致寫入位置錯位 (需結合完整原始碼上下文分析)。調試器精確捕捉到 terms[avail] 寫入瞬間的記憶體狀態,揭露了靜態閱讀無法發現的時序問題。
感謝陳正沖老師教導:不調試,不寫程式。