C語言的核心精華無疑是指針,它也是許多程式設計師眼中的難點。C語言因其對底層記憶體操作的便利性,使得優秀的C程式效率可媲美組合語言。本文將系統梳理指針的基礎概念、進階用法及常見陷阱,助你真正掌握C語言的靈魂。
指針是一種特殊的資料類型,其本質是「用來儲存位址值的變量」。理解指針需掌握四個關鍵屬性:
int *pn 中的 int *。* 後的基礎類型,即 int。int 佔 4 位元組)。void* 稱為「通用指針」或「泛指針」,不攜帶類型資訊,可隱式轉換為任意類型指針,常用於泛型程式設計(如 malloc 回傳值)。
面對 int (*p)[3] 或 int *(*p(int))[3] 這類宣告,遵循「從變量名出發,依運算子優先級結合」的原則:
[]、() 優先級高於 *)。*、類型關鍵字)。例如 int (*p)[3]:p 先結合 * → 是指針;再結合 [3] → 指向含 3 個 int 的陣列。
合法運算僅限於:
p + n 表示移動 n * sizeof(*p) 位元組)NULL)比較或賦值非法操作:指針相加、相乘、與浮點數運算等。
& 與 * 運算子本質&n:取址運算,結果類型為 n 的類型加 *,指向 n 的位址。*p:間接存取(解引用),結果類型為 p 指向的類型,佔用 p 指向的位址。陣列名 A 具有雙重含義:
int[3](若 int A[3])。int *const),值不可修改(A++ 錯誤)。作為函數參數時,陣列名「退化」為指針,sizeof 失效(僅得指針大小)。
struct MyStruct { int a; int b; };
struct MyStruct ss = {10, 20};
struct MyStruct *ptr = &ss;
ptr->a; // 等價於 (*ptr).a,推薦使用 ->
宣告函數指針:int (*pfun)(char *, int);。回調函數本質是將函數位址作為參數傳遞,實現調用者與被調用者的解耦,廣泛用於事件驅動、庫函數擴展等場景。
賦值時雙邊類型必須一致,否則需強制轉換:(TYPE *)ptr。轉換產生新指針,原指針屬性不變。函數參數傳遞同理。
int *a[10] vs int (*a)[10]int *a[10]:指針陣列,含 10 個 int* 元素,sizeof(a) = 40(32位)。int (*a)[10]:陣列指針,指向含 10 個 int 的陣列,sizeof(a) = 4。遵循標準步驟:從最內層括號的未定義標識符起始,右→左,遇括號反向,跳出括號繼續,直到解析完畢。此法能機械化解析任意複雜宣告。
參考資源:《C和指針》、CSDN 專欄、Sohu 部落格相關文章。