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

C語言二級指針深度解析:從汇編視角理解指針傳遞與修改

     編輯:關於C語言

前言:為什麼要看汇編?

C 語言參數傳遞本質是值傳遞。理解一級/二級指針能否修改外部變量,關鍵在於「值」是什麼。通過 GCC -m32 -O0 生成的 AT&T 汇編,我們一窺究竟。

環境設置


gcc -m32 -O0 test.c -o test   # 32位,關閉優化,便於對照源碼
objdump -d test > test.asm
gdb ./test

第一層:普通變量值傳遞

代碼


void increase(int value) { value = value + 1; }
int main() { int count = 7; increase(count); printf("%d", count); } // 輸出 7

關鍵汇編 (main 調用前)


sub $0x20,%esp          # 分配棧幀
movl $0x7,0x1c(%esp)    # count = 7 存放在 [esp+0x1c]
mov 0x1c(%esp),%eax     # 載入 count 值到 EAX
mov %eax,(%esp)         # **複製值** 到棧頂作為參數
call increase

increase 內部


addl $0x1,0x8(%ebp)     # 操作的是**副本** (位於調用者棧頂)

結論:函數內修改的是副本,外部 count 不變。

第二層:一級指針傳遞 (修改指向的內容)

代碼


void increase(int* ptr) { *ptr = *ptr + 1; }
int main() { int count = 7; increase(&count); printf("%d", count); } // 輸出 8

main 調用前


lea 0x1c(%esp),%eax     # 取 count **地址** 到 EAX
mov %eax,(%esp)         # 壓入地址作為參數
call increase

increase 內部


mov 0x8(%ebp),%eax      # 取出參數 (地址)
mov (%eax),%eax         # 解引用:取地址處的值
lea 0x1(%eax),%edx      # +1
mov 0x8(%ebp),%eax      # 再取地址
mov %edx,(%eax)         # 寫回:修改原 count

結論:傳遞的是地址值 (也是值傳遞),但通過地址可修改目標內存。

第三層:一級指針無法修改指針本身

代碼


void increase(int* ptr) { *ptr = *ptr + 1; ptr = NULL; }
int main() { int count = 7; int* countPtr = &count; increase(countPtr); printf("%p", countPtr); } // countPtr 不為 NULL

原因:countPtr 的地址值被複製給形參 ptr,函數內 ptr = NULL 只修改副本。

第四層:二級指針 — 修改外部指針變量

核心概念

  • 引用:& 取地址,類型加一級 *。
  • 解引用:* 或 ->,類型減一級 *。

關係鏈:

int count      ──&──► int* countPtr  ──&──► int** pp
     │                │                  │
     ▼                ▼                  ▼
   值              地址               指針的地址

代碼


void increase(int** ptr) { **ptr = **ptr + 1; *ptr = NULL; }
int main() {
    int count = 7;
    int* countPtr = &count;
    increase(&countPtr);  // 傳遞一級指針的地址
    // count = 8, countPtr = NULL
}

汇編關鍵點


# main 調用前
lea 0x1c(%esp),%eax     # 取 countPtr 的地址 (即 &countPtr)
mov %eax,(%esp)         # 壓棧

# increase 內部
mov 0x8(%ebp),%eax      # 取出 &countPtr
mov (%eax),%edx         # *ptr → countPtr 的值 (即 &count)
mov (%edx),%ecx         # **ptr → count 的值
# ... 運算 ...
mov %eax,(%edx)         # *ptr = NULL → 修改 countPtr 本身

總結對比表

參數類型傳遞內容能修改外部值能修改外部指針典型場景
int值副本否否只讀輸入
int*地址副本是 (*ptr)否輸出參數、修改結構體字段
int**指針地址副本是 (**ptr)是 (*ptr)修改指針本身 (鏈表頭插入、重新分配內存)

實戰場景:鏈表頭插入


void push(Node** head, int val) {
    Node* newNode = malloc(sizeof(Node));
    newNode->val = val;
    newNode->next = *head;
    *head = newNode;  // 修改外部 head 指針
}

避坑指南

  • void func(int** p) 調用時必須傳 &ptr,而非 ptr。
  • 函數內先判空:if (!ptr || !*ptr) return;。
  • 區分 *ptr = NULL (修改指針) 和 **ptr = 0 (修改值)。

結語

二級指針本質是「指針的指針」,通過雙重解引用觸達外部指針變量。從汇編視角看,一切皆是地址值的傳遞與內存讀寫。掌握這一點,指針難題迎刃而解。

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