在絕大多數表達式中,數組名 a 退化為指向首元素的指針 &a[0]。這是數組與指針互換的基石。
int a[10], *p = a; // p = &a[0]
p + i == &a[i] // 第 i 個元素地址
*(p + i) == a[i] // 第 i 個元素值
p[i] == *(p + i) // 指針也可用下標
p + i 的地址增量 = i * sizeof(*p)。編譯器自動根據指向類型縮放。
int a[10]; int *p = a;
p++; // 地址 +4 (假設 int 4 字節),指向 a[1]
double d[10]; double *q = d;
q++; // 地址 +8,指向 d[1]
int *p = &a[5];
p[-2] == a[3]; // 向前訪問,等價 *(p-2)
int a[3][4] 按行優先連續存儲:
a[0][0] a[0][1] a[0][2] a[0][3] // 第 0 行 a[1][0] a[1][1] a[1][2] a[1][3] // 第 1 行 a[2][0] a[2][1] a[2][2] a[2][3] // 第 2 行
| 表達式 | 類型 | 含義 | 地址值(假設基址1000) |
|---|---|---|---|
a | int (*)[4] | 整個數組首地址 / 第 0 行首地址 | 1000 |
a + 1 | int (*)[4] | 第 1 行首地址 | 1016 (+4*4) |
a[i] | int* | 第 i 行首地址 (*(a+i)) | 1000 + i*16 |
&a | int (*)[3][4] | 整個數組地址 (類型不同!) | 1000 |
// a[i][j] 等價形式
*(a[i] + j) // 行指針 + 列偏移
*(*(a + i) + j) // 數組指針 -> 行指針 -> 元素
(*(a + i))[j] // 解引用行指針後下標
int (*p)[4]int a[3][4];
int (*p)[4] = a; // p 指向整行 (4個int)
p++; // 跳過一整行,地址 +16
*p // 當前行首地址 (int*)
(*p)[j] // 當前行第 j 個元素
*(p+i)+j // 第 i 行第 j 個元素地址
| 聲明 | 含義 | 用途 |
|---|---|---|
int *p[3]; | 指針數組:3個 int* | 字符串數組、不規則矩陣、查找表 |
int (*p)[3]; | 數組指針:指向 3個int 的數組 | 二維數組行指針、矩陣運算 |
記憶口訣:括號優先。*p[3] 先結合 p[3] → 數組;(*p)[3] 先結合 *p → 指針。
char *names[] = {"Alice", "Bob", "Charlie"}; // 指針數組
// 內存:names[0]→"Alice\0", names[1]→"Bob\0"...
for (int i = 0; i < 3; i++)
printf("%s: length=%zu\n", names[i], strlen(names[i]));
// 以下三種聲明等價,參數均為 int*
void foo(int a[]);
void foo(int a[10]);
void foo(int *a);
// 二維數組:必須指定列數
void bar(int a[][4]); // OK
void bar(int (*a)[4]); // 等價
void bar(int a[3][4]); // 3 被忽略
// void bar(int **a); // 錯誤!類型不匹配
void transpose(int rows, int cols, int (*src)[cols], int (*dst)[rows]) {
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
dst[j][i] = src[i][j];
}
int main() {
int a[2][3] = {{1,2,3},{4,5,6}};
int b[3][2];
transpose(2, 3, a, b);
// b = {{1,4},{2,5},{3,6}}
return 0;
}
sizeof、&、字符串字面量初始化)。p[i] ≡ *(p+i)。a 是行指針,a[i] 是列指針,int (*p)[cols] 是行指針變量。int *p[N] (指針數組) 與 int (*p)[N] (數組指針)。