| 特性 | 指針 | 數組 |
|---|---|---|
| 本質 | 存儲地址的復合類型 | 一系列連續變量的集合 |
| 內存佈局 | 指針變量(4/8B) + 可指向任意塊 | 固定大小連續塊,名字綁定首地址 |
| 可變性 | 指向目標可隨時改變 | 地址與容量生命期內不變 |
| 典型用途 | 動態內存、間接訪問 | 靜態存儲、編譯時確定大小 |
void func(int arr[10]); // 等價於
void func(int *arr); // 編譯器內部按此處理
void func(int arr[]); // 同樣退化
函數內部 sizeof(arr) 返回指針大小,**非**數組大小。若需保留大小信息,須額外傳遞長度參數或使用結構體封裝。
char a[] vs char *a
char a[] = "Hi, pig!"; // 數組:棧上分配 9 字節(含\0),內容可改
char *p = "Hi, pig!"; // 指針:指向只讀數據段字面量,內容不可改
// 數組 a 棧內存: [H][i][,][ ][p][i][g][!][\0] ← 9 字節,a 標識此塊 // 指針 p 棧內存: [地址值] ← 4/8 字節,p 存儲地址 只讀數據段: [H][i][,][ ][p][i][g][!][\0]
a[2] vs p[2] 產生的機器碼不同a[2]:基址 a + 偏移 2 → 直接取值p[2]:取 p 存儲的地址 + 偏移 2 → 間接取值
int (*pElement)[2]; // 指向「含 2 個 int 的數組」的指針
int array[2][3] = {{1,2,3},{4,5,6}};
int (*pa)[3] = &array[0]; // pa 指向第 0 行(含 3 個 int)
printf("%d", (*pa)[0]); // 1 → array[0][0]
pa++; // 步長 = sizeof(int[3]) = 12 字節
printf("%d", (*pa)[0]); // 4 → array[1][0]
關鍵點:指針運算步長由指向類型大小決定,pa++ 跳過整行。
struct UT_TEST_STRUCT *pTo[2][MAX_NUM]; // 二維指針數組
pTo[i][j] 直接雙下標訪問
char **ppText = NULL; // 行指針數組(縱向動態)
char *pCurrText = NULL; // 當前行內容(橫向動態)
ULONG ulCurrLines = 0, ulAllocedLines = 0;
while (pCurrText = getline(pFile)) {
if (ulCurrLines >= ulAllocedLines) {
ulAllocedLines += 50;
ppText = realloc(ppText, ulAllocedLines * sizeof(char *));
if (!ppText) return;
}
ppText[ulCurrLines++] = pCurrText; // 橫向:指向不定長字符串
}
realloc 擴展行指針數組malloc 的字符串| 場景 | 結構 | 聲明示例 |
|---|---|---|
| 各維度固定、總量小 | 多維靜態數組 | int a[10][40] |
| 低維固定、高維動態 | 數組指針 | int (*a)[40] → a = malloc(x * sizeof(int[40])) |
| 高維固定、低維動態 | 指針數組 | int *a[10] → 每行 malloc(y * sizeof(int)) |
| 高維、低維均動態 | 指向指針的指針 | int **a → 雙重 malloc |
a vs &a
int a[2] = {1,2};
int *p = a; // p 指向 a[0],類型 int *
p = &a; // ❌ 編譯錯誤:&a 類型是 int (*)[2](指向整個數組的指針)
a / &a[0]:類型 int *,值相同&a:類型 int (*)[2],值相同但類型不同&a[0],語義最清晰| 聲明 | 名稱 | 含義 |
|---|---|---|
int *pF1(); | 指針函數 | 返回 int * 的函數 |
int (*pF2)(); | 函數指針 | 指向「無參返回 int 函數」的指針 |
括號優先級改變語義:() 高於 *。
指針、數組、函數三者交織構成 C 語言最核心的類型系統。理解退化規則、類型語義差異、指針運算步長、多維動態結構選型,是寫出高效、正確、可維護 C 代碼的必經之路。