指針是存儲內存地址的變量,本身佔用固定大小(32 位系統 4 字節,64 位 8 字節)。*p 解引用操作其指向的內存。
const int *p; // p 可變,*p 不可變(指向常量的指針)
int *const p; // p 不可變,*p 可變(常量指針)
const int *const p; // 皆不可變
口訣:const 在 * 左修飾指向值,在右修飾指針本身。
數組是連續內存塊,數組名在多數表達式中退化為指向首元素的常量指針(不可修改)。
int a[5];
a // 類型 int* ,值 &a[0]
&a // 類型 int(*)[5],值同 a 但步長為整個數組大小
sizeof(a) // 20 (整個數組大小)
sizeof(&a) // 4/8 (指針大小)
p + n 等價於 (char*)p + n * sizeof(*p)。ptrdiff_t)。C 無字符串類型,以 '\0' 結尾的字符數組模擬字符串。
| 無長度限制 | 有長度限制 | 說明 |
|---|---|---|
| strcpy | strncpy | 複製,strncpy 不保證以 \0 結尾 |
| strcat | strncat | 拼接,strncat 保證以 \0 結尾 |
| strcmp | strncmp | 比較,返回 <0, 0, >0 |
| strlen | - | 計算長度(不含 \0),返回 size_t |
原則:優先使用標準庫函數(常經過汇編優化),避免手寫循環。
int *p[5]; // 指針數組:5 個 int* 元素
int (*p)[5]; // 數組指針:指向含 5 個 int 的數組
int (*p)[3][4]; // 指向 3x4 二維數組
函數參數退化規則:
void f(int a[]); // 等價 void f(int *a);
void f(int a[][5]); // 等價 void f(int (*a)[5]); // 第二維必須給定
void f(int a[][5][6]); // 等價 void f(int (*a)[5][6]);
行優先連續存儲。int a[3][4] 在內存中為 12 個連續 int。
// 方法1:單塊連續內存 + 指針數組
int (*p)[4] = malloc(3 * sizeof(int[4])); // p 為數組指針
// 方法2:指針數組 + 分行分配(非連續)
int **p = malloc(3 * sizeof(int*));
for (int i=0;i<3;i++) p[i] = malloc(4 * sizeof(int));
int (*cmp)(const void*, const void*); // 函數指針類型
typedef int (*CmpFunc)(const void*, const void*); // 別名
void sort(void *base, size_t n, size_t sz, CmpFunc cmp) {
// ...
if (cmp(a, b) > 0) swap(...);
}
// 調用
sort(arr, n, sizeof(int), int_cmp);
實現調用者與具體比較邏輯解耦。
| 函數 | 作用 | 注意 |
|---|---|---|
| malloc(size) | 分配未初始化內存 | 失敗返回 NULL,必須檢查 |
| calloc(n, size) | 分配並零初始化 | 防止整數溢出,內部乘法 |
| realloc(ptr, new_size) | 調整大小 | 可能移動內存,必須用返回值更新指針 |
| free(ptr) | 釋放 | 釋放後置 NULL 防野指針 |
static 變量、字符串常量。程序啟動分配,結束釋放。| 錯誤類型 | 表現 | 防範措施 |
|---|---|---|
| 野指針 | 指向已釋放/未初始化/超出作用域內存 | 初始化為 NULL,free 後置 NULL,避免返回局部變量地址 |
| 內存洩漏 | malloc 無對應 free | 所有權清晰,RAII 模式,工具檢測 |
| 越界訪問 | 數組下標超界 | 邊界檢查,使用柔性數組/結構體封裝長度 |
| 雙重釋放 | 同一指針 free 兩次 | free 後置 NULL,智能指針思想 |