首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

C 語言指針傳遞陷阱:為什麼函數內修改指針不影響外部?

     編輯:程式設計綜合問答

C 語言「值傳遞」機制導致函數內修改指針變量本身(而非指向的內存)不影響外部,是經典面試題與新手坑。本文通過內存模型圖解、代碼對比、修正方案三維度徹底講透。

問題代碼現場

void change_prt(int *p);  // 聲明

int main() {
    int *prt, b[3] = {1, 2, 3};
    prt = b;                // prt 指向 b[0]
    change_prt(prt);        // 傳遞 prt 的值(地址)
    printf("%d\n", *prt);   // 輸出 1,而非預期 3
    return 0;
}

void change_prt(int *p) {
    p += 2;                 // 修改局部副本 p,指向 b[2]
    // *p = 10;             // 這樣才會修改內容
}

核心原理:值傳遞 + 指針本質


調用前 main 棧幀:           調用時 change_prt 棧幀:
+------------------+         +------------------+
| prt (指針變量)  |  --->   | p (參數,副本)   |  --->  同一地址 (b[0])
| 值: &b[0]        |         | 值: &b[0]        |
+------------------+         +------------------+

change_prt 內執行 p += 2:
+------------------+         +------------------+
| prt (指針變量)  |         | p (參數,副本)   |  --->  &b[2] (新地址)
| 值: &b[0]  不變 |         | 值: &b[2]        |
+------------------+         +------------------+

返回後:p 銷毀,prt 仍指向 &b[0]

關鍵概念

  • 指針是存儲地址的變量:佔 4/8 字節,值為內存地址
  • 函數參數傳遞是值拷貝:change_prt(prt) 將 prt 的值(地址)拷貝給參數 p
  • 修改副本不影響原變量:p += 2 只改變局部參數 p 的值,main 中的 prt 毫不知情
  • 解引用修改內容才生效:*p = 10 修改的是 p 指向的內存,該內存被 prt 共享

兩種修正方案對比

方案一:傳遞指針的指針 (二級指針)

void change_prt(int **pp) {   // 接收指針地址
    *pp += 2;                 // 解引用一次,修改原指針值
}

int main() {
    int *prt, b[3] = {1, 2, 3};
    prt = b;
    change_prt(&prt);         // 傳遞 prt 的地址
    printf("%d\n", *prt);     // 輸出 3 ✓
    return 0;
}

內存模型:pp 指向 main 棧幀中的 prt,*pp 即 prt 本身。

方案二:返回新指針值 (更符合函數式風格)

int* move_prt(int *p, int offset) {
    return p + offset;
}

int main() {
    int *prt, b[3] = {1, 2, 3};
    prt = b;
    prt = move_prt(prt, 2);   // 接收返回值更新 prt
    printf("%d\n", *prt);     // 輸出 3 ✓
    return 0;
}

經典類比:快捷方式與文件

概念對應關系
原始文件 F數組 b (內存數據)
快捷方式 S1指針 prt (指向 b[0])
複製快捷方式 S2參數 p (拷貝 prt 的值)
修改 S2 指向p += 2 (S2 現在指向別處,S1 不變)
用 S1 打開修改內容*prt = 10 (原文件 F 內容變了)
用 S2 打開修改內容*p = 10 (同一文件 F 內容變了)

常見錯誤模式對比

代碼效果是否影響外部
void f(int *p) { p = q; }修改參數指向❌ 否
void f(int *p) { *p = 10; }修改指向的內容✅ 是
void f(int **pp) { *pp = q; }修改原指針指向✅ 是
void f(int *p) { p += 2; }移動參數指向❌ 否

最佳實踐建議

  1. 意圖明確:需修改指針本身 → 傳 ** 或返回新指針;需修改內容 → 傳 *
  2. const 正確性:void f(const int *p) 承諾不修改內容;void f(int *const p) 承諾不修改指向 (參數為常量指針,但仍為副本)
  3. 現代 C++ 替代:用引用 void f(int* &p) 直接綁定原指針,語法清晰無二級指針認知負擔
// C++ 引用傳遞指針 (推薦 C++ 項目使用)
void change_prt(int* &p) {
    p += 2;  // 直接修改外部 prt
}

總結

C 語言只有值傳遞。指針作為參數時,傳遞的是地址值的副本。修改副本(p += 2)不影響原指針;修改副本指向的內存(*p = 10)影響共享內存。理解「指針變量」與「指向的內存」的區別,是掌握 C 內存模型的關鍵一步。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved