「C語言只有值傳遞」是核心鐵律。要在函數中修改實參,必須傳遞地址(指針)。本文通過「交換兩個整數」這一經典案例,徹底講透兩種傳遞方式的本質區別。
#include <stdio.h>
// 值傳遞:無法修改實參
void swap_val(int x, int y) {
int t = x; x = y; y = t;
printf("In swap_val: x=%d, y=%d\n", x, y);
}
// 指針傳遞:可修改實參
void swap_ptr(int *x, int *y) {
int t = *x; *x = *y; *y = t;
printf("In swap_ptr: x=%d, y=%d\n", *x, *y);
}
int main() {
int a = 5, b = 10;
printf("Before swap_val: a=%d, b=%d\n", a, b);
swap_val(a, b);
printf("After swap_val: a=%d, b=%d\n\n", a, b); // 無變化
printf("Before swap_ptr: a=%d, b=%d\n", a, b);
swap_ptr(&a, &b);
printf("After swap_ptr: a=%d, b=%d\n", a, b); // 交換成功
return 0;
}
Before swap_val: a=5, b=10
In swap_val: x=10, y=5
After swap_val: a=5, b=10 ← 實參未變!
Before swap_ptr: a=5, b=10
In swap_ptr: x=10, y=5
After swap_ptr: a=10, b=5 ← 實參已交換!
main棧幀: swap_val棧幀:
a(地址0x100) = 5 x(地址0x200) = 5 ← 複製值
b(地址0x104) = 10 y(地址0x204) = 10 ← 複製值
函數內交換 x,y 只影響 0x200/0x204
返回後 swap_val棧幀銷毀,main中 a,b 不變
main棧幀: swap_ptr棧幀: a(0x100)=5 x(0x200)=0x100 ← 複製地址值 b(0x104)=10 y(0x204)=0x104 ← 複製地址值 *x 解引用訪問 0x100 → 修改 a *y 解引用訪問 0x104 → 修改 b 實現真正交換
| 特性 | 值傳遞 | 指針傳遞 |
|---|---|---|
| 傳遞內容 | 變量的值副本 | 變量的地址副本 |
| 函數內修改 | 只影響副本 | 通過解引用修改原變量 |
| 實參要求 | 變量/表達式/常量 | 必須是可取地址的左值 |
| 調用語法 | func(a, b) | func(&a, &b) |
| 函數參數 | int x | int *x |
| 函數體訪問 | x | *x |
// 錯誤:修改的是形參指針副本,不影響實參指針
void wrong_swap(int *x, int *y) {
int *t = x; x = y; y = t; // 只交換了局部指針變量
}
// 正確:修改指針指向的內容
void correct_swap(int *x, int *y) {
int t = *x; *x = *y; *y = t;
}
int **int *
#define SWAP(type, x, y) do { \
type tmp = *(y); \
*(y) = *(x); \
*(x) = tmp; \
} while(0)
// 使用
int a=5, b=10;
SWAP(int, &a, &b); // 類型安全,無函數調用開銷
*ptr 表示「ptr 指向的內存位置」,是修改原數據的鑰匙
4. 宏 SWAP 利用 do{...}while(0) 實現語句級封裝,類型安全且零開銷
5. 面試必考:畫棧幀圖、解釋為什麼值傳遞失效、指針傳遞生效
理解參數傳遞機制,是從「會寫代碼」進階到「理解程序運行本質」的關鍵一步。