首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言 swap 交換函數五種寫法深度對比:值傳遞 vs 指針 vs 數組退化

     編輯:關於C語言

一個經典面試題,揭示 C 語言參數傳遞與指針解引用的核心機制

交換兩個變量值看似簡單,但在 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

深度機制剖析

swap1:值傳遞 — 隔離的拷貝世界

主調棧幀:  x=4 | y=3
             ↓  拷貝值
被調棧幀:  x=4 | y=3  (獨立內存單元)
             ↓  交換局部變量
被調棧幀:  x=3 | y=4
             ↓  函數返回,棧幀銷毀
主調棧幀:  x=4 | y=3  (完全不變)

**結論**:C 語言**只有值傳遞**。形參是實參的**位拷貝**,操作形參不影響實參。

swap2:傳指針但交換指針變量 — 「換了門牌號,屋裡的人沒動」

主調: 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` 中是**局部指針變量**,交換它們只改變「局部變量存儲的地址值」,不解引用寫入內存。

swap3:傳指針並解引用 — 「順著門牌號進屋換人」

被調: x=0x100, y=0x200
      ↓ t = *x  (讀取 0x100 處的值 4)
      ↓ *x = *y (將 0x200 處的值 3 寫入 0x100)
      ↓ *y = t  (將 4 寫入 0x200)
主調: x=3 @0x100, y=4 @0x200  ✅ 成功交換

**本質**:通過地址**間接訪問並修改**實參所在內存單元。

swap4/swap5:數組參數退化 — `int a[]` 實際是 `int *a`

C 標準規定:函數參數聲明中的 `T a[]` / `T a[N]` **調整為** `T *a`。

  • `swap4(&x, &y)` → `a` 接收 `&x` (類型 `int*`),`b` 接收 `&y`。
  • `a[0]` 等價於 `*a`,`b[0]` 等價於 `*b`。

**為什麼 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` 成功交換。若實測失敗,請檢查:

  1. 是否誤寫為 `swap4(int *a[], int *b[])` (指針數組)?
  2. 調用處是否傳遞 `&x` (類型 `int*`) 而非 `x`?
  3. 編譯器是否對 `int a[]` 參數有非標準處理?

**標準結論**:`swap3`、`swap4`、`swap5` **語義完全等價**,均為正確交換寫法。

總結:C 語言交換函數的鐵律

最佳實踐建議

  1. 首選 `swap3` 風格:`void swap(int *a, int *b)` + `*a` `*b` 解引用,語義最清晰,無數組退化歧義。
  2. 泛型宏版本 (類型安全需 `_Generic` C11):
    #define SWAP(type, a, b) do { type t = (a); (a) = (b); (b) = t; } while(0)
    // 使用:SWAP(int, x, y);  // 直接操作實參,無函數調用開銷
  3. C++ 參考版本:`void swap(int &a, int &b)` 隱式解引用,調用 `swap(x, y)` 無需取址。

掌握「值傳遞拷貝、指針間接訪問、數組退化為指針」三大核心機制,swap 題及其變體將不再是障礙。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
寫法參數類型核心操作能否交換備註
swap1int x, int y交換局部拷貝❌值傳遞隔離
swap2int *x, int *y交換局部指針變量❌未解引用寫內存
swap3int *x, int *y解引用交換 *x *y✅標準正解
swap4int a[], int b[]a[0]/b[0] 下標交換✅退化為指針,等價 swap3
swap5int a[], int b[]*a/*b 解引用交換✅退化為指針,等價 swap3