「指針數組」與「數組指針」一字之差,本質天壤之別。本文從定義語法、優先級解析、二維數組訪問演示、內存佈局圖解四個維度徹底釐清。
| 名稱 | 聲明 | 本質 | 優先級結合 |
|---|---|---|---|
| 指針數組 | int *p[8]; | **數組**,元素為指針 | p 先結合 [] → 數組,再結合 * → 元素是指針 |
| 數組指針 | int (*p)[8]; | **指針**,指向數組 | p 先結合 * → 指針,再結合 [8] → 指向 8 元素數組 |
p 出發。[] 或 () (高優先級) 先結合。* 或類型關鍵字結合。() 反向,解析完跳出繼續。例:int (*p)[8] → p 右無高優先級 → 左 * (指針) → 跳出括號 → 右 [8] (指向 8 元素數組) → 左 int (元素類型)。
int arr[4][4] = { ... }; // 行主序連續存儲
地址遞增: arr[0][0] → arr[0][1] → ... → arr[0][3] → arr[1][0] → ...
每行佔 4*4=16 字節
行起始地址: arr, arr+1, arr+2, arr+3 (類型 int(*)[4],步長 16 字節)
等價表達: arr+i, &arr[i], *(arr+i), arr[i] (均為第 i 行首地址,類型 int*)
int arr[4][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11},{12,13,14,15}};
// 方式一:數組指針
int (*p1)[4] = arr; // p1 指向首行
for (int i=0; i<4; ++i)
for (int j=0; j<4; ++j)
printf("%d ", *(*(p1+i)+j)); // p1+i → 第i行首地址,解引用 → 行首元素,+j → 第j列
// 方式二:指針數組
int *p2[4];
for (int i=0; i<4; ++i) p2[i] = arr[i]; // 每個元素指向對應行首
for (int i=0; i<4; ++i)
for (int j=0; j<4; ++j)
printf("%d ", *(p2[i]+j)); // p2[i] 已是行首指針,直接 +j
| 特性 | 數組指針 int (*p)[N] | 指針數組 int *p[N] |
|---|---|---|
| 類型 | 單一指針 | 指針集合 (N個) |
| sizeof(p) | 指針大小 (4/8) | N * 指針大小 |
| 算術步長 | 整行大小 (N*sizeof(int)) | 指針大小 |
| 初始化 | p = arr; 一次性 | 需循環 p[i] = arr[i]; |
| 適用場景 | 函數參數傳遞二維數組、遍歷整個矩陣 | 行長度不一 (鋸齒數組)、需單獨操作某幾行 |
// 推薦:數組指針,保留列數信息,編譯器可做邊界檢查
void process(int (*matrix)[COLS], int rows);
// 避免:指針數組 (除非鋸齒數組)
void process(int **matrix, int rows, int cols); // 退化為指針的指針,丟失列數