函數參數傳遞是 C 語言核心機制之一。本文聚焦於**陣列作為形式參數**時的傳遞語義,對比三種寫法的本質差異,並釐清「陣列名退化為指針」的核心規則,助你避開傳遞陷阱,寫出正確高效的代碼。
當陣列名作為實參傳遞給函數時,**不會複製整個陣列**,而是將**指向首元素的指針**傳遞給形參。因此:
int *n 或 int n[](等價)。int 陣列對應 int* 形參)。/* 聲明 */
void disp(int *n); // 或 void disp(int n[]);
/* 調用 */
int m[10];
for(i=0;i<10;i++) m[i]=i;
disp(m); // 傳遞首元素地址
/* 定義:指針算術遍歷 */
void disp(int *n) {
for(j=0;j<10;j++)
printf("%3d", *(n++)); // n 遞增遍歷
printf("\n");
}
特點:高效、原始語義、函數可修改原陣列。
/* 聲明 */
void disp(int n); // 單個 int 形參
/* 調用 */
for(i=0;i<10;i++) {
m[i]=i;
disp(m[i]); // 傳遞第 i 個元素的**值**
}
/* 定義 */
void disp(int n) {
printf("%3d\t", n);
}
特點:值傳遞,函數內修改 n 不影響原陣列;效率較低(多次函數調用開銷),但語義隔離性好。
本質同方式一,僅寫法差異:
void disp(int *n); // 明確指針類型
disp(m); // m 隱式轉換為 &m[0]
/* 函數內用 n[j] 或 *(n+j) 訪問 */
sizeof、&、字符串字面量初始化外)**自動退化為指向首元素的指針**。void f(int a[])、void f(int a[10])、void f(int *a) 在函數簽名中**完全等價**,編譯器均視為 int *a。sizeof(a) 返回指針大小(2/4/8 字節),**而非陣列大小**。若需長度,必須額外傳遞 size 參數。void f(int a[][COLS]) 或 void f(int (*a)[COLS]),首維退化,次維保留,指針算術按行跨度計算。| 場景 | 推薦寫法 | 理由 |
|---|---|---|
| 需修改原陣列、高效遍歷 | void f(int *a, size_t n) | 明確指針語義,攜帶長度 |
| 僅讀取、不修改 | void f(const int *a, size_t n) | const 防呆,文檔化意圖 |
| 需語義隔離、陣列較小 | 逐元素值傳遞或複製陣列 | 避免副作用,代價可控 |
| 多維陣列 | void f(int (*a)[COLS], size_t rows) | 保留類型信息,指針算術正確 |
/* 錯誤:試圖傳遞整個陣列副本 */
void f(int a[10]) { ... } // 實為指針,非副本
f(arr); // 仍傳地址
/* 正確:若真需要副本,手動複製 */
void f(int a[10]) {
int local[10];
memcpy(local, a, sizeof(local)); // 需傳長度或固定大小
...
}
C 語言函數參數**永遠是值傳遞**,但陣列名在傳遞時退化為首元素指針,形成「模擬引用傳遞」的效果。理解「退化規則」、「形參等價性」、「sizeof 失效」三大核心點,配合 const 與顯式長度參數,即可駕馭各類陣列參數場景,寫出既安全又高效的函數接口。