學習目標
- 掌握程式碼追蹤的方法
- 理解變數的作用域和生命週期
- 能夠追蹤函數呼叫和返回值
- 掌握常見的除錯技巧
- 理解指標和陣列的操作
1. 程式碼追蹤的基本方法
1.1 什麼是程式碼追蹤?
程式碼追蹤 (Code Tracing) 是逐步執行程式碼,記錄每個步驟的變數值和執行流程。
1.2 追蹤的步驟
- 建立變數表: 記錄所有變數的初始值和變化
- 逐步執行: 按照程式執行順序,一行一行追蹤
- 記錄狀態: 記錄每個步驟後的變數值和輸出
- 檢查邏輯: 驗證程式邏輯是否正確
2. 基本程式追蹤
2.1 範例 1: 簡單計算
#include <stdio.h>
int main() {
int a = 5;
int b = 3;
int c = a + b;
printf("%d\n", c);
return 0;
}
追蹤過程:
| 行號 | 執行內容 | a | b | c | 輸出 |
|---|---|---|---|---|---|
| 4 | int a = 5; |
5 | - | - | - |
| 5 | int b = 3; |
5 | 3 | - | - |
| 6 | int c = a + b; |
5 | 3 | 8 | - |
| 7 | printf("%d\n", c); |
5 | 3 | 8 | 8 |
| 8 | return 0; |
5 | 3 | 8 | - |
2.2 範例 2: 迴圈追蹤
#include <stdio.h>
int main() {
int sum = 0;
for (int i = 1; i <= 3; i++) {
sum += i;
}
printf("%d\n", sum);
return 0;
}
追蹤過程:
| 迭代 | i 的值 | 條件檢查 | sum 的值 | 執行內容 |
|---|---|---|---|---|
| 初始 | - | - | 0 | int sum = 0; |
| 1 | 1 | 1 <= 3 (真) |
1 | sum = 0 + 1 = 1 |
| 2 | 2 | 2 <= 3 (真) |
3 | sum = 1 + 2 = 3 |
| 3 | 3 | 3 <= 3 (真) |
6 | sum = 3 + 3 = 6 |
| 結束 | 4 | 4 <= 3 (假) |
6 | 跳出迴圈 |
輸出: 6
3. 函數呼叫追蹤
3.1 範例: 函數呼叫
#include <stdio.h>
int add(int x, int y) {
return x + y;
}
int main() {
int a = 5;
int b = 3;
int result = add(a, b);
printf("%d\n", result);
return 0;
}
追蹤過程:
main 函數:
| 行號 | 執行內容 | a | b | result |
|:---:|:---|:---:|:---:|:---:|
| 8 | int a = 5; | 5 | - | - |
| 9 | int b = 3; | 5 | 3 | - |
| 10 | int result = add(a, b); | 5 | 3 | 等待... |
add 函數 (被呼叫):
| 行號 | 執行內容 | x | y | 返回值 |
|:---:|:---|:---:|:---:|:---:|
| 3 | int add(int x, int y) | 5 | 3 | - |
| 4 | return x + y; | 5 | 3 | 8 |
main 函數 (繼續):
| 行號 | 執行內容 | a | b | result |
|:---:|:---|:---:|:---:|:---:|
| 10 | result = 8 | 5 | 3 | 8 |
| 11 | printf("%d\n", result); | 5 | 3 | 8 (輸出: 8) |
4. 指標追蹤
4.1 範例: 指標操作
#include <stdio.h>
int main() {
int x = 10;
int *p = &x;
*p = 20;
printf("%d\n", x);
return 0;
}
追蹤過程:
| 行號 | 執行內容 | x | p (位址) | *p | 說明 |
|---|---|---|---|---|---|
| 4 | int x = 10; |
10 | - | - | x 的位址假設為 0x1000 |
| 5 | int *p = &x; |
10 | 0x1000 | 10 | p 指向 x |
| 6 | *p = 20; |
20 | 0x1000 | 20 | 透過指標修改 x |
| 7 | printf("%d\n", x); |
20 | 0x1000 | 20 | 輸出: 20 |
4.2 範例: 指標與陣列
#include <stdio.h>
int main() {
int arr[3] = {1, 2, 3};
int *p = arr;
printf("%d\n", *(p + 1));
return 0;
}
追蹤過程:
| 變數 | 初始值 | 說明 |
|---|---|---|
| arr[0] | 1 | 陣列第一個元素 |
| arr[1] | 2 | 陣列第二個元素 |
| arr[2] | 3 | 陣列第三個元素 |
| p | &arr[0] | p 指向陣列開頭 |
| p + 1 | &arr[1] | 指向第二個元素 |
| *(p + 1) | 2 | 取值,得到 arr[1] |
輸出: 2
5. 常見錯誤與除錯
5.1 錯誤 1: 未初始化的變數
int main() {
int x;
printf("%d\n", x); // 錯誤:x 未初始化
return 0;
}
問題: x 的值是未定義的(垃圾值)
修正:
int x = 0; // 初始化
5.2 錯誤 2: 陣列越界
int main() {
int arr[3] = {1, 2, 3};
printf("%d\n", arr[3]); // 錯誤:越界存取
return 0;
}
問題: 陣列索引範圍是 0-2,arr[3] 越界
修正:
printf("%d\n", arr[2]); // 正確
5.3 錯誤 3: 指標未初始化
int main() {
int *p;
*p = 10; // 錯誤:p 未初始化
return 0;
}
問題: p 指向未知的記憶體位置
修正:
int x;
int *p = &x; // 先讓 p 指向有效的變數
*p = 10;
5.4 錯誤 4: 除零錯誤
int main() {
int a = 10;
int b = 0;
int c = a / b; // 錯誤:除以零
return 0;
}
問題: 除以零會導致程式崩潰
修正:
if (b != 0) {
int c = a / b;
}
6. 除錯技巧
6.1 使用 printf 除錯
int factorial(int n) {
printf("進入 factorial(%d)\n", n); // 除錯輸出
if (n == 0 || n == 1) {
printf("返回 1\n");
return 1;
}
int result = n * factorial(n - 1);
printf("factorial(%d) = %d\n", n, result);
return result;
}
6.2 檢查變數值
int main() {
int a = 5;
int b = 3;
printf("a = %d, b = %d\n", a, b); // 檢查變數值
int c = a + b;
printf("c = %d\n", c);
return 0;
}
6.3 使用斷點
在整合開發環境 (IDE) 中設置斷點,逐步執行程式。
7. 複雜範例追蹤
7.1 範例: 遞迴函數
#include <stdio.h>
int mystery(int n) {
if (n <= 1)
return 1;
return n + mystery(n - 1);
}
int main() {
printf("%d\n", mystery(3));
return 0;
}
追蹤過程:
mystery(3):
mystery(3)
→ 3 + mystery(2)
→ 2 + mystery(1)
→ 1 (基礎情況)
← 2 + 1 = 3
← 3 + 3 = 6
返回值: 6
輸出: 6
7.2 範例: 陣列與指標
#include <stdio.h>
void modify(int *arr, int size) {
for (int i = 0; i < size; i++) {
arr[i] *= 2;
}
}
int main() {
int arr[3] = {1, 2, 3};
modify(arr, 3);
for (int i = 0; i < 3; i++) {
printf("%d ", arr[i]);
}
return 0;
}
追蹤過程:
main 函數: | 步驟 | arr[0] | arr[1] | arr[2] | |:---:|:---:|:---:|:---:| | 初始 | 1 | 2 | 3 |
modify 函數 (被呼叫): | 迭代 | i | arr[i] 修改前 | arr[i] 修改後 | |:---:|:---:|:---:|:---:| | 1 | 0 | 1 | 2 | | 2 | 1 | 2 | 4 | | 3 | 2 | 3 | 6 |
main 函數 (繼續): | 步驟 | arr[0] | arr[1] | arr[2] | |:---:|:---:|:---:|:---:| | modify 後 | 2 | 4 | 6 |
輸出: 2 4 6
8. 練習題
練習題 1
追蹤以下程式碼,寫出輸出:
#include <stdio.h>
int main() {
int x = 5;
int y = x++;
int z = ++x;
printf("%d %d %d\n", x, y, z);
return 0;
}
解答:
| 行號 | 執行內容 | x | y | z |
|---|---|---|---|---|
| 4 | int x = 5; |
5 | - | - |
| 5 | int y = x++; |
6 | 5 | - |
| 6 | int z = ++x; |
7 | 5 | 7 |
輸出: 7 5 7
練習題 2
追蹤以下程式碼,寫出輸出:
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10;
int y = 20;
swap(&x, &y);
printf("%d %d\n", x, y);
return 0;
}
解答:
main 函數: - 初始: x = 10, y = 20
swap 函數: - temp = 10 - a (即 x) = 20 - b (即 y) = 10
main 函數 (繼續): - x = 20, y = 10
輸出: 20 10
練習題 3
找出以下程式碼的錯誤:
int main() {
int arr[5];
for (int i = 0; i <= 5; i++) {
arr[i] = i;
}
return 0;
}
解答:
錯誤在於迴圈條件 i <= 5。陣列索引範圍是 0-4,當 i = 5 時會越界存取 arr[5],這是未定義行為。
修正:
for (int i = 0; i < 5; i++) {
arr[i] = i;
}
9. 考試重點提醒
- 逐步追蹤: 要能一行一行追蹤程式碼執行
- 變數表: 建立變數表記錄每個變數的值
- 函數呼叫: 理解函數呼叫時的參數傳遞和返回值
- 指標操作: 理解指標的取值和取址操作
- 常見錯誤: 識別未初始化變數、陣列越界等錯誤