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

C 指標深度復習:交換值的三種傳遞語義

     編輯:關於C語言

實驗場景

定義兩個函數嘗試交換 int i=0, j=1:

  • s(int*, int*) — 嘗試交換**指標變數本身**(地址值)
  • s2(int*, int*) — 交換**指標指向的記憶體內容**

完整代碼與輸出

#include <stdio.h>

void s(int *i, int *j);   // 嘗試交換指標變數
void s2(int *i, int *j);  // 交換所指內容
void p(int i, int j);     // 印值
void pp(int *i, int *j);  // 印址

int main() {
    int i = 0, j = 1;
    p(i, j);      // 0 -- 1
    pp(&i, &j);   // 0xbfe2fccc -- 0xbfe2fcc8
    s(&i, &j);    // 嘗試交換指標
    p(i, j);      // 0 -- 1  【值未變】
    pp(&i, &j);   // 位址未變
    s2(&i, &j);   // 交換內容
    p(i, j);      // 1 -- 0  【值交換成功】
    pp(&i, &j);   // 位址仍未變
    return 0;
}

void s(int *i, int *j) {
    int tmp = *i;
    i = j;          // 只改區域副本 i
    j = &tmp;       // j 指向區域變數 tmp(危險!函數返回成懸空指標)
    printf("address change\n");
    p(*i, *j);
    pp(i, j);
}

void s2(int *i, int *j) {
    int tmp = *i;
    *i = *j;
    *j = tmp;
}

void p(int i, int j) { printf("%d -- %d\n", i, j); }
void pp(int *i, int *j) { printf("%p -- %p\n", (void*)i, (void*)j); }

輸出解析

0 -- 1
0xbfe2fccc -- 0xbfe2fcc8
address change
1 -- 0
0xbfe2fcc8 -- 0xbfe2fc9c   ← s 內部 tmp 位址(已失效)
0 -- 1
0xbfe2fccc -- 0xbfe2fcc8   ← 回到 main,i/j 位址值未變
1 -- 0
0xbfe2fccc -- 0xbfe2fcc8   ← s2 真正交換了內容

核心結論

傳遞方式能改變實參的值?能改變實參的位址?
傳值 void f(int x)否(改副本)否
傳指標 void f(int *p)是(*p = ...)否(p = ... 只改副本)
傳指標的指標 void f(int **p)是是(*p = ...)
C++ 參考 void f(int &r)是否(參考綁定後不可重綁)

記憶體模型圖解

main 棧框:          i @ 0xbfe2fccc (值 0)
                    j @ 0xbfe2fcc8 (值 1)

s(&i, &j) 棧框:     i (區域副本) @ ...  初值 0xbfe2fccc
                    j (區域副本) @ ...  初值 0xbfe2fcc8
    → i = j  只把區域 i 改成 0xbfe2fcc8
    → j = &tmp 把區域 j 改成 tmp 位址
    → 函數返回,區域銷毀,main 的 i/j 完全不受影響

最佳實踐

  1. **要修改呼叫端變數的值** → 傳址(指標或 C++ 參考)。
  2. **要修改呼叫端指標指向** → 傳指標的指標(int**)或參考到指標(int*&)。
  3. **絕不**在函數內返回區域變數位址(如 j = &tmp)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved