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

C 指針基礎完全掌握:聲明解讀、算術運算、類型轉換與安全陷阱

     編輯:關於C語言

指針聲明讀法:右左法則(優先級)

int       *p;    // p 是指針 → 指向 int
int       *p[3]; // p 是數組 → 元素是指針 → 指向 int
int       (*p)[3]; // p 是指針 → 指向數組[3] → 元素是 int
int       **p;   // p 是指針 → 指向指針 → 指向 int
int       (*p)(int); // p 是指針 → 指向函數(參數int) → 返回int
  1. 從變量名開始
  2. 先看右邊 [] ()(高優先級)
  3. 再看左邊 *
  4. 最後看基礎類型

指針四要素

要素含義例子:int *p = &a;
指針類型指針變量本身的類型int *
指向類型解引用後得到的類型int
指針值存儲的地址數值&a (如 0x7ffc...)
指針自身內存指針變量佔用的存儲空間32位機 4 字節

指針算術運算

  • p + n:地址增加 n * sizeof(*p) 位組
  • p++ 移動到下一個同類型對象
  • 指針減法:p2 - p1 返回元素個數差(ptrdiff_t),要求同一數組
  • 非法:指針相加、指針乘除、指針與整數乘除

經典陷阱:二級指針算術

char *p = "hello";
char **pp = &p;
pp++; // pp 增加 sizeof(char*) = 4/8 字節
// *pp 現在指向 p 後面的未知內存,解引用導致 UB

運算符 & 與 *

  • &a:類型在原類型前加 *,值為 a 的地址
  • *p:類型為 p 的指向類型,左值(可賦值),佔用 p 指向的內存
  • 多級遞推:**pp 先 *pp 得到 char*,再 * 得到 char

指針與數組

  • 數組名 a 在表達式中轉換為 &a[0](指向首元素指針)
  • 例外:sizeof(a)、&a、字符串字面量初始化
  • a[i] ⇔ *(a+i) ⇔ *(i+a) ⇔ i[a]
  • 二維數組:int a[3][4] → a 類型 int(*)[4],a+1 跳過一行(16 字節)

指針與結構體

struct S { int a, b, c; };
struct S s = {1,2,3};
struct S *p = &s;
p->a; // 等價於 (*p).a,標準寫法

// 錯誤示範:按整數指針訪問
int *pi = (int*)&s;
pi[1]; // 可能訪問到填充字節,因結構體可能有對齊填充!

指針類型轉換

  • (NewType*)ptr:生成新指針,類型改為 NewType*,指向類型為 NewType,地址不變
  • 安全原則:sizeof(原指向類型) >= sizeof(新指向類型) 相對安全(如 int*→char*)
  • 危險:char*→int* 可能導致未對齊訪問、越界讀寫
  • 整數↔指針:(uintptr_t)ptr、(void*)addr(C99 stdint.h),需確保地址合法

指針安全紅線

  1. 未初始化:int *p; *p = 1; → 野指針,UB
  2. 釋放後使用:free(p); *p = 1; → 懸垂指針
  3. 越界訪問:數組下標超出範圍、指針算術超出對象邊界
  4. 類型懲罰:float f; int *p = (int*)&f; *p = 0; 違反嚴格別名規則,編譯器優化導致邏輯錯誤
  5. 棧地址外逃:返回局部變量地址

最佳實踐清單

  • 聲明即初始化:int *p = NULL; 或 = &var;
  • 使用 const 保護不可修改內容:const char *p、char *const p
  • 動態內存:malloc 後檢查 NULL,free 後置 NULL
  • 函數參數傳遞指針時註明所有權與生命週期
  • 啟用編譯器警告:-Wall -Wextra -Wpedantic -fsanitize=address
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved