指針本質是「存儲內存地址的變量」。理解指針的關鍵在於區分值傳遞(傳遞副本,函數內修改不影響原值)與地址傳遞(傳遞地址,函數內可修改原值)。指針存在的核心價值:實現函數對外部變量的修改、動態內存管理、高效數據結構構建。
int a[2][5] = {
{1, 3, 5, 7, 9},
{2, 4, 6, 8, 10}
};
int *p = &a[0][0]; // 普通指針指向首元素
// 遍歷輸出
for (int i = 0; i < 10; i++) {
printf("%d ", *(p + i)); // 指針算術:p+i 偏移 i*sizeof(int) 位元組
}
關鍵點:二維數組在內存中連續存儲(行優先),a[0][0] 到 a[1][4] 首尾相連。普通指針 int *p 可按一維方式線性訪問所有元素。
void modify_value(int x) {
x = 20; // 修改副本
}
void modify_ref(int *x) {
*x = 20; // 通過地址修改原值
}
int main() {
int a = 10;
modify_value(a);
printf("值傳遞後: %d\n", a); // 輸出 10
modify_ref(&a);
printf("指針傳遞後: %d\n", a); // 輸出 20
return 0;
}
這正是指針不可替代的原因:突破函數作用域限制,實現輸出型參數。
char *names[] = {"Alice", "Bob", "Charlie"}; // 指針數組
// 等價於
char name0[] = "Alice";
char name1[] = "Bob";
char name2[] = "Charlie";
char *names[] = {name0, name1, name2};
// 遍歷
for (int i = 0; i < 3; i++) {
printf("%s: 長度=%zu\n", names[i], strlen(names[i]));
}
注意:字符串字面量存儲在只讀區,char *p = "abc" 指向只讀內存,不可修改;若需修改請用 char p[] = "abc"。
// 定義函數指針類型:接受兩個 int,返回 int
typedef int (*calc_func)(int, int);
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
void calculate(int a, int b, calc_func func) {
printf("結果: %d\n", func(a, b));
}
int main() {
calculate(10, 5, add); // 輸出 15
calculate(10, 5, sub); // 輸出 5
return 0;
}
函數指針是策略模式、事件回調、狀態機的 C 語言基石,廣泛用於 qsort 比較函數、信號處理、GUI 事件循環。
// 動態分配 3 行 4 列的 int 二維數組
int **matrix = malloc(3 * sizeof(int *));
for (int i = 0; i < 3; i++) {
matrix[i] = malloc(4 * sizeof(int));
}
// 使用
matrix[1][2] = 99;
// 釋放
for (int i = 0; i < 3; i++) free(matrix[i]);
free(matrix);
int ** 指向指針數組,每個指針再指向一行數據。這種「指針的指針」結構靈活但內存不連續,訪問需兩次間接引用。
| 錯誤模式 | 後果 | 正確做法 |
|---|---|---|
| 野指針使用 | 段錯誤/數據損壞 | 定義即初始化 NULL,free 後置 NULL |
| 指針類型不匹配 | 訪問錯誤內存/未定義行為 | 嚴格匹配類型,void* 僅作通用容器 |
| 返回局部變量地址 | 棧內存失效 | 動態分配或靜態存儲 |
| const 正確性缺失 | 意外修改只讀數據 | const char* 指向不可修改數據 |
print *ptr 查看指向值,print ptr 查看地址-Wall -Wextra -fsanitize=address 開啟地址消毒器