「不懂指針,不懂 C。」指針將內存地址提升為一級數據類型,賦予程序員對內存的直接控制權。本文從底層原理到實戰模式,構建完整指針認知體系。
int x = 10;
int *px = &x; // px 類型:int* (指向 int 的指針)
// px 值:x 的地址 (如 0x7ffe...)
double d = 3.14;
double *pd = &d; // pd 類型:double*,值:d 的地址
// 關鍵:px 和 pd 值可能相同(地址數值),但類型不同!
// 類型決定:解引用訪問多少字節、指針算術步長、類型安全檢查
int *ip; // 未初始化,野指針 (危險)
int *ip = NULL; // 空指針,明確無效
int i = 10;
int *ip = &i; // 初始化為 i 的地址
// 多指針定義陷阱
int *p1, *p2; // 兩個指針
int *p3, p4; // p3 是指針,p4 是 int!建議一行一個。
int i = 10, x;
int *ip = &i;
x = *ip; // 1. 取 ip 的值 (地址) 2. 訪問該地址的 int 內容 3. 賦給 x
// 等價於 x = i;
*ip = 20; // 修改 ip 指向的內存,i 變為 20
ip (地址 0x1000) i (地址 0x2000) +--------+ +--------+ | 0x2000 | ─────────────→ | 10 | +--------+ +--------+
int i = 'a', j = 'b';
int *p1 = &i, *p2 = &j;
p2 = p1; // 指針賦值:p2 現在也指向 i
// p1、p2 共享同一目標
*p2 = *p1; // 解引用賦值:將 p1 指向的值複製到 p2 指向的位置
// j 變為 'a',p2 仍指向 j
關鍵區別:p2=p1 改變指向;*p2=*p1 改變數據。
y = *px + 5; // (*px) + 5
y = ++*px; // ++(*px) : 先增再用
y = *px++; // *(px++) : 先用再增 (px 移動到下一個 int)
| 運算 | 條件 | 結果 |
|---|---|---|
ptr + n | ptr 指向數組元素 | 指向第 n 個後續元素 |
ptr - n | ptr 指向數組元素 | 指向第 n 個前序元素 |
ptr1 - ptr2 | 同一數組 | 元素距離 (ptrdiff_t) |
==, !=, <, >... | 同一數組/對象 | 位置比較 |
❌ 指針+指針、指針*指針、指針/整數 皆非法。
char : 1
short/int : 2/4 (視平台)
long/float : 4/8
double : 8
結構體 : sizeof(struct)
// 編譯器自動處理:p+1 實際地址增量 = 1 * sizeof(*p)
const int *p1; // 指向常量的指針:不能通過 p1 修改數據,*p1=10 錯誤
int *const p2; // 常量指針:p2 本身不可修改指向,p2=&x 錯誤
const int *const p3; // 雙重常量
口訣:const 在 * 左 → 數據常量;在 * 右 → 指針常量。
| 陷阱 | 後果 | 防範 |
|---|---|---|
| 使用未初始化指針 | 隨機崩潰/數據破壞 | 定義即初始化 (NULL 或合法地址) |
| 釋放後不置 NULL | 野指針,二次釋放崩潰 | free(ptr); ptr = NULL; |
| 返回局部變量地址 | 棧內存失效,訪問垃圾 | 用靜態/堆/輸出參數 |
| 指針越界訪問 | 緩衝區溢出,安全漏洞 | 邊界檢查,使用 size_t 索引 |
| 類型懲罰違規 | 未定義行為 (嚴格別名規則) | 用聯合體/char*/memcpy |
// header.h - 公開接口
struct WidgetImpl; // 前向聲明
typedef struct WidgetImpl *Widget;
Widget widget_create();
void widget_do(Widget w);
void widget_destroy(Widget w);
// source.c - 實現隱藏
struct WidgetImpl {
int id;
char *name;
// 私有數據...
};
Widget widget_create() {
Widget w = malloc(sizeof(*w));
w->id = 0; w->name = NULL;
return w;
}
優勢:編譯防火牆、二進制兼容、封裝實現細節。
指針 = 類型化地址 + 間接訪問機制。掌握三要素:
T *p,讀法「p 是指向 T 的指針」。p = &obj / malloc / NULL。*p 訪問目標對象。嚴守生命週期、邊界、const 正確性,指針將成為你手中最鋒利的編程利器。