交換兩個變量值看似簡單,但在 C 語言中卻藏著參數傳遞語義、指針運算、數組退化等核心考點。本文分析五種 `swap` 實現,通過代碼、內存圖解、運行結果,徹底講清**為什麼只有傳地址並解引用才能真正交換主調函數變量**。
// 1. 值傳遞:形參拷貝,無法影響實參
void swap1(int x, int y) {
int t = x; x = y; y = t;
}
// 2. 傳指針但交換指針本身:修改形參指針變量,不影響實參指向的內存
void swap2(int *x, int *y) {
int *t = x; x = y; y = t; // 只是交換了局部指針變量的值
}
// 3. 傳指針並解引用交換指向的值:正解
void swap3(int *x, int *y) {
int t = *x; *x = *y; *y = t;
}
// 4. 數組參數退化為指針,下標訪問交換:等價 swap3
void swap4(int a[], int b[]) {
int t = a[0]; a[0] = b[0]; b[0] = t;
}
// 5. 數組參數退化為指針,解引用交換:等價 swap3/4
void swap5(int a[], int b[]) {
int t = *a; *a = *b; *b = t;
}
int main() {
int x = 4, y = 3;
swap1(x, y); printf("swap1: x=%d, y=%d\n", x, y); // 4, 3
swap2(&x, &y); printf("swap2: x=%d, y=%d\n", x, y); // 4, 3
swap3(&x, &y); printf("swap3: x=%d, y=%d\n", x, y); // 3, 4 ✅
swap4(&x, &y); printf("swap4: x=%d, y=%d\n", x, y); // 4, 3 ❌
swap5(&x, &y); printf("swap5: x=%d, y=%d\n", x, y); // 3, 4 ✅
return 0;
}
swap1: x=4, y=3
swap2: x=4, y=3
swap3: x=3, y=4
swap4: x=4, y=3
swap5: x=3, y=4
主調棧幀: x=4 | y=3
↓ 拷貝值
被調棧幀: x=4 | y=3 (獨立內存單元)
↓ 交換局部變量
被調棧幀: x=3 | y=4
↓ 函數返回,棧幀銷毀
主調棧幀: x=4 | y=3 (完全不變)
**結論**:C 語言**只有值傳遞**。形參是實參的**位拷貝**,操作形參不影響實參。
主調: x=4 @0x100, y=3 @0x200
↓ 傳地址 &x, &y
被調: x=0x100, y=0x200 (形參指針指向實參)
↓ 交換指針變量 t=x; x=y; y=t;
被調: x=0x200, y=0x100 (形參指針互換了指向)
↓ 返回
主調: x=4 @0x100, y=3 @0x200 (實參內存未被讀寫!)
**關鍵**:`x` `y` 在 `swap2` 中是**局部指針變量**,交換它們只改變「局部變量存儲的地址值」,不解引用寫入內存。
被調: x=0x100, y=0x200
↓ t = *x (讀取 0x100 處的值 4)
↓ *x = *y (將 0x200 處的值 3 寫入 0x100)
↓ *y = t (將 4 寫入 0x200)
主調: x=3 @0x100, y=4 @0x200 ✅ 成功交換
**本質**:通過地址**間接訪問並修改**實參所在內存單元。
C 標準規定:函數參數聲明中的 `T a[]` / `T a[N]` **調整為** `T *a`。
**為什麼 swap4 失敗而 swap5 成功?**
swap4: int t = a[0]; a[0] = b[0]; b[0] = t;
// 調用 swap4(&x, &y) 時:
// a = &x (int*), b = &y (int*)
// a[0] 訪問 *a → x 的值
// b[0] 訪問 *b → y 的值
// 看似正確,但實測失敗?
**原文結果 `swap4: x=4, y=3` 疑為筆誤或特定編譯器行為差異**。標準語義下 `swap4` 應等價 `swap3/5` 成功交換。若實測失敗,請檢查:
**標準結論**:`swap3`、`swap4`、`swap5` **語義完全等價**,均為正確交換寫法。
| 寫法 | 參數類型 | 核心操作 | 能否交換 | 備註 |
|---|---|---|---|---|
| swap1 | int x, int y | 交換局部拷貝 | ❌ | 值傳遞隔離 |
| swap2 | int *x, int *y | 交換局部指針變量 | ❌ | 未解引用寫內存 |
| swap3 | int *x, int *y | 解引用交換 *x *y | ✅ | 標準正解 |
| swap4 | int a[], int b[] | a[0]/b[0] 下標交換 | ✅ | 退化為指針,等價 swap3 |
| swap5 | int a[], int b[] | *a/*b 解引用交換 | ✅ | 退化為指針,等價 swap3 |