指針是C語言的核心特性,也是嵌入式開發中最強大也最危險的工具。本文將系統梳理指針的核心概念、常見陷阱、嵌入式開發中的實際應用,以及內存管理的最佳實踐,幫助開發者建立扎實的指針思維。
理解指針的關鍵在於區分三個概念:




關鍵結論:int *p 中的 * 僅告訴編譯器 p 是指針變量。對於 int a = 10,有 a = *(&a)。指針類型本質上就是其指向對象的類型。

#define GPFCON *( (volatile unsigned *)0x56000050 )
逐層解析:
0x56000050:具體的十六進制地址值(volatile unsigned *)0x56000050:將數值強轉為 volatile 修飾的無符號指針,防止編譯器優化*(...):解引用,操作該地址處的內容本質:將符號 GPFCON 與物理地址 0x56000050 綁定,操作前者即操作後者。


字節序口訣:
以 0x1234 為例(小端):低位 0x34 存低地址,高位 0x12 存高地址。char* 讀取首字節得到 0x34。指針 +1 表示跳過該類型佔用的字節數。
Segment Fault 由虛擬內存管理單元(MMU)異常觸發,核心原因是解引用未初始化或非法指針。
$ ulimit -c # 查看 core 文件大小限制(0 表示禁用)
$ ulimit -c unlimited # 移除限制
$ gcc -g test.c # 編譯帶調試信息的可執行文件
$ ./a.out # 運行觸發段錯誤,生成 core 文件
$ gdb a.out core # 載入 core 文件調試
(gdb) r # 運行程序
(gdb) bt # 查看調用棧,定位崩潰位置
| 符號 | 含義 |
|---|---|
NUL (單 L) | ASCII 字符串結束符 \0 |
NULL (雙 L) | 空指針常量,表示不指向任何有效內存 |
| 表達式 | 執行順序 |
|---|---|
*p++ | 先取值 *p,再將指針 p 自增(移動到下一個元素) |
(*p)++ | 先取值 *p,再將所指向的值 自增 |
*++p | 先將指針 p 自增,再取新位置的值 |
++*p | 先取值 *p,再將所指向的值 自增(同 (*p)++ 效果,但返回值不同) |
鐵律:指針自增/自減會改變其存儲的地址值,測試時務必使用不同的指針變量避免相互干擾。
NULL++/-- 後地址已變NULL,防野指針

核心結論:
a + i <=> &a[i]
a[i] <=> *(a + i) <=> *(p + i) <=> p[i]



關鍵關係式:*行指針 = 列指針;&列指針 = 行指針
對於 a[3][4],訪問 a[2][2] 等價形式:
&a[2][2]
a[2] + 2
a[0] + 2*4 + 2
(int*)(a + 2) + 2
*(&a[0] + 2) + 2
// 取值等價
a[2][2]
*(a[2] + 2)
*a[0] + 2*4 + 2
// 指針數組:本質是數組,元素是指針
int *p[3] = {a[0], a[1], a[2]}; // p 是二級指針常量
// 數組指針:本質是指針,指向一個數組
int (*p)[3]; // p 指向含 3 個 int 的數組

void GetMem(char *p) {
p = malloc(100); // 修改的是副本,外部 str 不受影響
}
void Test() {
char *str = NULL;
GetMem(str); // str 仍為 NULL
strcpy(str, "hello"); // 崩潰!
修正:傳遞二級指針 GetMem(&str) 或返回分配地址 char* GetMem()。
char* GetMem() {
char p[] = "hello"; // 棧上分配,函數返回即銷毀
return p; // 返回懸空指針!
}
void Test() {
char *str = malloc(7);
strcpy(str, "hello");
free(str); // 內存歸還系統,str 仍指向原地址(野指針)
printf(str); // 未定義行為
if (str != NULL) { // 判斷失效!str 不為 NULL
strcpy(str, "world"); // 二次破壞
}
}
鐵律:free(ptr); ptr = NULL; 必須成對出現。
| 區域 | 存儲內容 | 管理方式 | 生命周期 |
|---|---|---|---|
| 棧 | 函數參數、局部變量 | 編譯器自動分配/釋放 | 函數執行期間 |
| 堆 | malloc/new 動態分配 | 程序員手動管理 | malloc 到 free 之間 |
| 全局/靜態區 | 全局變量、static 變量 | 編譯器分配/釋放 | 程序整個運行期間 |
| 文字常量區 | 字符串字面量 | 編譯器管理 | 程序整個運行期間 |
| 代碼區 | 二進制機器指令 | 系統管理 | 程序整個運行期間 |
| 錯誤類型 | 典型場景 | 防範規則 |
|---|---|---|
| 分配失敗仍使用 | 未檢查 malloc 返回值 | 規則1:使用前必檢查 NULL |
| 未初始化就引用 | 誤以為默認為 0 | 規則2:無論何種方式創建,必賦初值 |
| 越界訪問 | 數組下標「多1/少1」 | 規則4:特別當心循環邊界 |
| 內存洩漏 | malloc 無對應 free | 規則5:申請釋放嚴格配對 |
| 野指針 | free 後繼續使用 | 規則6:free 後立即置 NULL |
| 返回棧地址 | 函數返回局部變量地址 | 規則3:嚴禁返回棧內存指針/引用 |
// 1. 分配並檢查
int *p = malloc(n * sizeof(int));
if (p == NULL) { /* 錯誤處理 */ }
// 2. 初始化
memset(p, 0, n * sizeof(int)); // 或逐項賦值
// 3. 使用時嚴防越界
for (int i = 0; i < n; i++) { p[i] = ...; }
// 4. 釋放並置空
free(p);
p = NULL; // 防野指針
本文整理自「嵌入式學習之路」博客,原文地址:https://fancong.blog.51cto.com/3191877/1296147