指針的本質是一個變量,完整名稱為「指針變量」。它與普通變量在結構上並無二致,核心差異在於存儲內容:普通變量存儲具體數值,而指針變量存儲的是內存地址。這一特性使得指針能夠間接訪問和操作數據,成為 C 語言實現動態內存管理、數組操作、函數傳參等核心機制的基石。
語法形式為 類型名 * 指針變量名。星號的位置靈活,以下三種寫法完全等價:
int *p1;int* p2;int * p3;編譯器視角下,星號屬於聲明符的一部分,與類型名或變量名的距離不影響語義。建議採用 int *p; 風格,強調 *p 是 int 類型,符合「聲明模仿使用」的設計哲學。
兩種主要方式:
int *p = &a;int *p; p = &a;警告:未經關聯的指針(野指針)指向隨機地址,解引用會導致未定義行為,嚴重時引發段錯誤或悄無聲息地破壞數據。
星號在指針上下文中有雙重含義:
int *p 中的 * 標識 p 為指針類型*p 表示「p 所指向的那個變量本身」,即間接訪問目標內存單元C 語言中 * 同時服務於三種語境,互不相干:
&變量 構成一個新符號,代表該變量在內存中的起始地址。它是一個右值表達式,不可作為左值出現在賦值號左側。
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
C 標準庫將 NULL 定義為空指針常量。C++ 中簡化為 0,C 中則強制轉型為 void* 以增強類型安全。最佳實踐:指針初始化時統一賦值 NULL,使用前檢查非空,用畢置空,構建「定義-綁定-檢查-清理」四步防禦體系。
編譯器根據上下文決定符號含義:a = 5 中 a 是左值(目標地址),b = a 中 a 是右值(源數值)。
| 類型 | 指向區域 | 後果 | 危險等級 |
|---|---|---|---|
| 段錯誤型 | 內核空間、受保護頁 | 程序立即崩潰(SIGSEGV) | ★☆☆ 最易發現 |
| 沉默破壞型 | 已釋放棧/堆內存 | 程序看似正常,實則數據已損壞 | ★★☆ 最難排查 |
| 誤傷友軍型 | 正在使用的變量區 | 關鍵變量被竄改,引發邏輯錯誤 | ★★★ 破壞力最強 |
const int *p; // (1) p 指向的值不可修改,*p 是只讀
int const *p; // (2) 同 (1),const 修飾 int
int * const p; // (3) p 本身不可修改,p 是常量指針
const int * const p; // (4) 雙重保護:p 和 *p 皆不可修改
記憶口訣:* 左邊的 const 修飾指向的值,右邊的修飾指針本身。
數組在內存中是連續分配的同類型元素集合。int a[4] 保證四個 int 緊湊排列,而分散聲明的 int a,b,c,d 地址不一定相鄰。這種連續性使得指針算術運算成為遍歷數組的最高效方式。
| 表達式 | 左值語義 | 右值語義(數值) | 類型 |
|---|---|---|---|
a (數組名) | 整個數組空間(不可賦值) | 首元素地址 &a[0] | int * |
| 首元素內存空間 | 首元素的值 | int | |
| 常量,不可作左值 | 整個數組首地址 | int (*)[N] | |
| 首元素內存空間 | 首元素地址 | int * |
關鍵結論:a、&a[0]、&a 數值相等但類型不同。&a 是數組指針,步長為整個數組大小;a 退化為元素指針,步長為元素大小。
p + 1 的實際地址增量 = 1 * sizeof(p 指向的類型)。int* +1 增 4 字節,char* +1 增 1 字節,double* +1 增 8 字節。這是編譯器根據靜態類型自動完成的縮放,而非簡單的整數加法。
同一塊內存,按不同類型讀寫會產生截然不同的結果:
指針類型決定了解析方式:所有指針類型(int*, char* 等)自身佔 4 字節(32 位系統),存儲地址,解析方式統一;但 *p 所指向的內存單元如何解析,完全取決於 p 的聲明類型。
—— 文章未完,後續將深入函數指針、多級指針、指針數組等進階主題 ——