指針是 C 語言的靈魂,也是無數開發者的「分水嶺」。本文通過三個遞進案例,徹底講透 二維數組下標轉指針訪問、雙重指針 與 函數中修改指針指向 的核心機制,並給出記憶體佈局圭解。
#include <stdio.h>
int main() {
int a[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
// a[i][j] 等價寫法
printf("%d ", *(*(a + i) + j));
}
printf("\n");
}
return 0;
}
a → 首行首元素地址 (int (*)[3]) 類型:指向含 3 個 int 的數組的指針
a + i → 第 i 行首地址,類型仍為 int (*)[3],偏移 i * sizeof(int[3])
*(a + i) → 解引用,得到第 i 行數組,退化為 int* 指向行首元素
*(a + i) + j → 指向第 i 行第 j 個元素
*(*(a + i) + j) → 解引用取值,即 a[i][j]
a 是「行指針」,步長 = 一行大小(3 * sizeof(int))。*(a+i) 使類型從「行指針」降級為「元素指針」。#include <stdio.h>
int main() {
int a = 1;
int *p = NULL; // 一級指針
int **pp = NULL; // 二級指針
pp = &p; // pp 指向 p 的地址
*pp = &a; // 解引用 pp 得到 p,賦值 &a → 等價於 p = &a
printf("%d\n", **pp); // 輸出 1
return 0;
}
內存地址 變量 值
0x7ffe... a 1
0x7ffe... p → 0x7ffe... (a 的地址)
0x7ffe... pp → 0x7ffe... (p 的地址)
**pp = *(*pp) = *(p) = a = 1
C 語言只有值傳遞。若需在函數內修改主調函數的指針指向(如 realloc 失敗後重新分配、縮容),必須傳遞 指針的指針。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* 重置緩衝區大小
* @param p 指向緩衝區指針的指針
* @param old_size 原大小
* @param new_size 新大小
* @return 1 成功,0 失敗
*/
int reset_buffer(char **p, int old_size, int new_size) {
if (!p || new_size <= 0) return 0;
char *new_buf = (char*)malloc(new_size);
if (!new_buf) return 0; // 分配失敗,原緩衝區不變
int copy_len = (old_size < new_size) ? old_size : new_size;
if (*p) {
memcpy(new_buf, *p, copy_len); // 複製舊數據
free(*p); // 釋放舊緩衝區
}
*p = new_buf; // 關鍵:修改主調函數的指針指向
return 1;
}
int main() {
char *buf = (char*)malloc(5);
strcpy(buf, "hello");
printf("Before: %p -> %s\n", (void*)buf, buf);
if (reset_buffer(&buf, 5, 10)) {
printf("After expand: %p -> %s\n", (void*)buf, buf);
}
if (reset_buffer(&buf, 10, 3)) {
printf("After shrink: %p -> %s\n", (void*)buf, buf);
}
free(buf);
return 0;
}
Before: 0x55a1b2c0 -> hello
After expand: 0x55a1b2e0 -> hello
After shrink: 0x55a1b300 -> hel
| 需求 | 參數類型 | 函數內操作 | 主調傳遞 |
|---|---|---|---|
| 修改指針指向的內容 | T* | *p = val | &var |
| 修改指針本身指向 | T** | *p = new_addr | &ptr |
| 修改二維數組/指針數組 | T** 或 T(*)[N] | *(*p+i)+j | 數組名/指針數組 |
*pp = &a 先解引用 pp 得到 p,再賦值。切勿寫成 **pp = a(這是修改 a 的值)。const char **pp vs char *const *pp vs char **const pp,分別限制不同層級可修改性。free(*p); *p = NULL; 防止野指針。*(a+i)+j 是訪問元素的通用指針寫法。T**,並遵循「先分配新、再釋放舊、最後切換」原則。熟練掌握指針層級轉換,是寫出健壯、高效 C 代碼的關鍵門檻。