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

C 語言指針深度理解:從內存模型到函數傳遞、函數指針的完整圖解

     編輯:關於C語言

C 語言指針深度理解:內存模型、類型系統與實戰模式

指針是 C 語言的靈魂,亦是最大的攔路虎。本文從內存地址本質出發,通過生活類比、代碼實驗、圖解分析,系統講解指針聲明、運算、數組關係、const 修飾語義、函數傳遞三種模式、函數指針等核心知識點。

一、指針本質:地址與內存

  • 變量 = 內存區域的別名,編譯器分配地址與大小。
  • 指針 = 存儲地址的變量,指針變量本身佔用內存(32 位 4 字節,64 位 8 字節)。
  • *p:解引用,訪問地址 p 指向的內存單元。
  • &x:取地址,獲取變量 x 的起始地址。

二、生活類比:一級 vs 二級指針

  • 一級指針:教室 A214 直接坐著目標人物 → 地址直接指向數據。
  • 二級指針:A214 門上貼著條子寫著「去 A215」 → 指針指向另一個指針,需兩次解引用才能拿到數據。

三、聲明與初始化

int a = 3;
int *p = &a;        // 推薦:顯式取地址
// int *p = a;      // 錯誤/警告:整數轉指針無意義

float *p;           // 僅聲明,未初始化(野指針,極度危險)
// 通用形式:類型 *變量名;

四、指針運算經典示例

int x, y;
int *px = &x, *py = &y, *p;
x = 21;
y = *px;      // y = 21,解引用 px 獲取 x 的值
p = &y;
printf("%d %d %d %d %d\n", *px, *py, x, y, *p); // 21 21 21 21 21

五、三大核心注意事項

  1. 指針必須指向已定義變量,不可指向未初始化內存或字面常量區(字符串字面量)。
  2. 指針不可直接指向字面值:int *p = 3; 錯誤。
  3. 區分「聲明時的 *」與「運算時的 *」:
    • int *p; → * 屬於類型聲明一部分,表示 p 是指針。
    • *p = 10; → * 是解引用運算符,賦值給指向的內存。

六、指針與數組

  • 數組名 = 首元素地址(int a[3] → a == &a[0]),**但數組名是常量,不可修改**(a++ 錯誤)。
  • 指針遍歷:for (int *p = a; p < a+3; ++p) *p = 0;
  • *(a+i) 等價 a[i],指針算術自動按元素類型大小縮放。

七、指針常量 vs 常量指針

聲明含義可修改指針?可修改所指值?
int *const p = &a;指針常量:p 本身不可變❌✅ (*p = 5)
const int *p = &a;
int const *p = &a;
常量指針:所指值不可通過 p 修改✅ (p = &b)❌ (*p = 5 錯誤)
const int *const p = &a;雙重常量❌❌

口訣:***const 靠右鎖定誰,靠右修飾指針本身,靠左修飾所指類型***。

八、為什麼指針要有類型?

char ch = 1;
char *pc = &ch;   // 讀 1 字節
int *pi = &ch;    // 讀 4 字節(32位),包含 ch 及其後 3 字節垃圾值 → 未定義行為

指針類型決定解引用時訪問的字節數與解釋方式,也是指針算術步長的依據(pi+1 跳過 4 字節,pc+1 跳過 1 字節)。

九、函數參數傳遞三種模式

1. 值傳遞(默認)

void swap(int a, int b) { int t=a; a=b; b=t; } // 僅交換副本
int main() { int x=3,y=4; swap(x,y); /* x=3,y=4 */ }

實參拷貝給形參,函數內操作不影響外部。

2. 地址傳遞(模擬引用)

void swap(int *pa, int *pb) { int t=*pa; *pa=*pb; *pb=t; }
int main() { int x=3,y=4; swap(&x,&y); /* x=4,y=3 */ }

傳遞地址,通過解引用修改原變量。**C 語言實現「引用語義」的標準方式**。

3. C++ 引用傳遞

void swap(int &a, int &b) { int t=a; a=b; b=t; } // 語法糖,本質同地址傳遞
int main() { int x=3,y=4; swap(x,y); /* x=4,y=3 */ }

.c 文件編譯報錯,需重命名為 .cpp 或用 C++ 編譯器。

十、函數指針

void fun(int x) { printf("%d\n", x); }

int main() {
    void (*pFun)(int);   // 聲明:指向「參數 int、返回 void」函數的指針
    pFun = &fun;         // 取函數地址(& 可省略)
    fun(10);             // 直接調用
    (*pFun)(20);         // 通過指針調用
    pFun(30);            // 隱式解引用調用(等價 (*pFun)(30))
    return 0;
}
  • 聲明技巧:將函數聲明 void fun(int); 中的 fun 替換為 (*pFun) 即得指針聲明。
  • 應用場景:回調函數、狀態機、動態分發、插件架構、qsort 比較函數。

總結

指針 = 地址 + 類型。掌握內存佈局、const 修飾位置、三種傳遞語義、函數指針聲明/調用,配合大量實踐與調試(gdb 觀察內存),即可攻克 C 指針難關,寫出高效、健壯的系統級代碼。

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