指標是 C 語言靈魂,掌握「指標與陣列的等價關係」、「多維陣列位址計算」、「函式指標回調機制」是邁向系統級開發的必經之路。本文從一維陣列指標切入,層層深入至二維陣列指標算術、指標陣列與陣列指標區別、函式指標實戰,並揭示 a[i][j] 編譯期如何轉譯為 *(*(a+i)+j)。
int a[5] = {0,1,2,3,4};
int *p = a; // p 指向 a[0]
// 以下完全等價:
a[i] == *(a + i) == *(p + i) == p[i]
&a[i] == a + i == p + i
關鍵差異:a 是常量指標(不可修改),p 是變數指標(可 p++、p = other)。
// 1. 索引風格
for (int i=0; i<5; ++i) p[i] = i;
// 2. 指標算術風格
for (int *p=a; p
float average(float *arr, int n) {
float sum = 0;
for (int i=0; i
函式簽名 float *arr 與 float arr[] 完全等價,皆為指標。
int a[3][4] = {
{0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11}
};
// 連續儲存:0,1,2,3, 4,5,6,7, 8,9,10,11
// 假設基址 1000,int 4 bytes
// a[0] @1000, a[1] @1016, a[2] @1032
| 表示式 | 型別 | 值(位址) | 含義 |
|---|---|---|---|
a | int (*)[4] | 1000 | 整個陣列位址 = 第 0 列位址 |
a[0] / *a / &a[0][0] | int* | 1000 | 第 0 列首元素位址 |
a+1 / &a[1] / *(a+1) | int (*)[4] / int* | 1016 | 第 1 列位址 / 第 1 列首元素位址 |
a[i] / *(a+i) / &a[i][0] | int* | 1000+i*16 | 第 i 列首元素位址 |
a[i]+j / *(a+i)+j / &a[i][j] | int* | 1000+i*16+j*4 | 第 i 列第 j 元素位址 |
*(*(a+i)+j) / a[i][j] | int | 值 | 第 i 列第 j 元素值 |
int (*p)[4] = a; // p 型別:指向 4 個 int 陣列的指標
// p+1 移動 16 bytes(跳過一整列)
// *p 等價 a[0](int*)
// p[i][j] 合法,編譯器知曉列寬 4
錯誤示範:int *p = a; 型別不匹配(int* vs int(*)[4]),雖然位址相同但指標算術會錯誤。
// 指標陣列:每列長度可不同,記憶體不連續
char *names[] = {"Alice", "Bob", "Charlie"};
// names[0] → "Alice\0" (某處)
// names[1] → "Bob\0" (另一處)
// 二維陣列:固定列寬,記憶體連續
char names2[][10] = {"Alice", "Bob", "Charlie"};
// names2[0] @基址, names2[1] @基址+10, ...
int add(int a, int b) { return a+b; }
int sub(int a, int b) { return a-b; }
int (*op)(int, int) = add; // 函式指標變數
op = sub; // 執行期切換行為
printf("%d\n", op(3, 2)); // 1
void sort(void *base, size_t n, size_t sz,
int (*cmp)(const void*, const void*)) {
// 簡易氣泡排序示意
char *p = base;
for (size_t i=0; i 0) {
// swap bytes
for (size_t k=0; k
這正是 qsort 的運作原理。
void print_transpose(int (*a)[4], int rows, int cols) {
for (int c = 0; c < cols; ++c) {
for (int r = 0; r < rows; ++r)
printf("%3d", a[r][c]);
puts("");
}
}
int main() {
int a[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}};
print_transpose(a, 3, 4);
// 輸出:
// 0 4 8
// 1 5 9
// 2 6 10
// 3 7 11
}
p+1 移動 sizeof(*p) bytes,二維陣列指標 int(*)[4] 移動一整列。* 將指標型別降一維,*(*(a+i)+j) 逐層解參考取值。sizeof、&、字串常數初始化除外。內化這三條,指標不再是魔法,而是精確的記憶體導航儀。