← C++ Learning Path

C++ V2 · TOOLCHAIN · 54

Compile / Link / Runtime Debug:不要把所有「跑不起來」都叫程式錯

C++ source 不會直接變 process。Build pipeline 中每層都有不同輸入、輸出與 failure evidence。真正高效的 debug 是先找到「最早沒有成功的層」,而不是看到紅字就改 pointer、include 或邏輯。

Learning outcomes

1. Why now:Compile 成功只是 Pipeline 中的一個 Gate

main.cpp
  ↓ compile
main.o
  ↓ link
grade-analyzer
  ↓ OS launch
process
  ↓ execute
stdout/stderr/exit code

同樣一句「程式壞了」,可能發生在完全不同的 layer。

2. Mental model:每一層都要先成功,下一層才存在

source valid?
  ↓ yes
object produced?
  ↓ yes
symbols resolved?
  ↓ yes
executable produced?
  ↓ yes
process launched?
  ↓ yes
behavior correct?

這條 evidence chain 能避免在 executable 根本沒產生時去查 runtime crash。

3. Worked Example A:Compile Error

int main() {
    int score = ;
}

Compiler 無法把 source 轉成有效 object code。第一個 evidence 通常是 file/line/diagnostic。

4. Preprocessor / Include Failure

#include "grade_book.hpp"

如果 header path 不存在或 include configuration 錯,failure 可能在 preprocessing/compile 前段。不要把它誤認成 linker 問題。

5. Worked Example B:Declaration 有了,但 Definition 沒進 Link

// grade.hpp
int gradeOf(int score);

// main.cpp
#include "grade.hpp"

int main() {
    return gradeOf(90);
}

如果沒有提供對應 definition,或 implementation object file 沒加入 link inputs,可能出現 undefined reference / unresolved external symbol。

6. Build Trace:Two Translation Units

g++ -std=c++20
    -c main.cpp
    -o main.o

g++ -std=c++20
    -c grade.cpp
    -o grade.o

g++ main.o grade.o
    -o grade-analyzer

前兩步各自 compile;最後一步 linker 把 symbols 組成 executable。

7. Worked Example C:Runtime Undefined Behavior

int* ptr = nullptr;

std::cout << *ptr;

Source 可能成功 compile/link,但 process 執行到 invalid dereference 時進入 undefined behavior。這是 runtime memory layer。

8. Logic Error:所有技術層都成功,答案仍錯

int total = 7;
int count = 2;

double average =
    total / count;
// 3.0, expected 3.5

Executable 可正常退出 0,不代表 business contract 正確。Regression tests 才能抓這層。

9. Warnings:不是全部都能忽略的「建議」

g++ -std=c++20
    -Wall
    -Wextra
    -pedantic
    main.cpp
    -o grade-analyzer

Warnings 不保證一定是 bug,但它們是高價值 evidence。初學階段應盡量做到「理解每個 warning」,而不是關掉。

10. Sanitizer:把一部分 Runtime Memory Evidence 變明顯

g++ -std=c++20
    -Wall
    -Wextra
    -fsanitize=address,undefined
    -g
    main.cpp
    -o grade-analyzer

AddressSanitizer/UBSan 能幫你把某些 memory/undefined behavior 問題轉成更具體 diagnostics,但不是形式證明,也不會抓到所有 bug。

11. Debugger / Stack Trace:看 Process 正在哪裡失敗

main()
  ↓
run()
  ↓
average()
  ↓
invalid access

Runtime crash 時要看 call stack、local variables、faulting instruction/function,而不是只看最後輸出。

12. Common mistakes

A. Undefined reference 時去改 pointer

這通常是 link/symbol 問題。

B. Compile error 時一直重跑 executable

新的 executable 根本沒產生。

C. Warning 全部忽略

可能放掉 narrowing、unused、lifetime 等重要訊號。

D. Crash 就認為「一定是 null pointer」

out-of-bounds、use-after-free、stack overflow 等也可能。

E. Exit 0 就認為功能正確

logic error 完全可能 exit 0。

13. Debug evidence:固定六步 Failure Classification

1. Did compile succeed?
2. Did link succeed?
3. Was executable updated?
4. Did process launch?
5. Did process terminate abnormally?
6. Is output/behavior correct?

每一步都要用 observable evidence 回答,而不是憑印象。

14. Guided exercise:製造四種 Failure

在最小 Grade Analyzer 裡刻意製造:

compile error
link error
runtime invalid access
logic boundary error

對每種記錄:symptom、第一個 evidence、失敗 layer、修復方式。

15. Independent exercise:Split Files

把:

isPassed()
gradeOf()

移到 grade.hpp / grade.cpp,main.cpp 只呼叫。先正確 build,再故意漏掉 grade.o link input,觀察 link diagnostic。

16. Challenge:Stale Executable

修改 source 後故意讓 compile 失敗,再執行舊 executable。說明為什麼你可能看到「程式還能跑」,但其實跑的是 stale binary;建立避免誤判的方法。

Project checkpoint:CLI Grade Analyzer · Build Evidence v4

src/
├─ main.cpp
├─ grade.hpp
└─ grade.cpp

build evidence:
compile warnings clean
link success
executable timestamp/commit known
boundary regressions pass

完成這一課後,C++ 前半段不再只是會寫 syntax,而是能說明 code 如何真正成為 process。

17. Vocabulary / Summary

18. Further reading

Knowledge check · Mastery

  1. 畫 source → object → executable → process。
  2. 看到 undefined reference 時指出第一個 failure layer。
  3. 完成四類 fault injection 與 split-file build。
  4. 完成頁面下方 C++ build/debug diagnostic practice。