許多程式設計師精通 BASIC、Pascal 等語言,但指標是 C/C++ 獨有且最核心的特性。本文不重複教科書內容,專注於書本講不清、講不透的關鍵觀念,助你建立精準的指標心智模型。
理解一個指標,必須搞清四個面向:
(1) int *ptr;
(2) char *ptr;
(3) int **ptr;
(4) int (*ptr)[3];
(5) int *(*ptr)[4];
去掉變數名,剩下的就是指標型別:
(1) int * // 指向 int 的指標
(2) char * // 指向 char 的指標
(3) int ** // 指向 int* 的指標
(4) int (*)[3] // 指向 int[3] 陣列的指標
(5) int *(*)[4] // 指向 int*[4] 陣列的指標
去掉變數名與最左邊的 *,剩下的就是指向型別:
(1) int
(2) char
(3) int *
(4) int [3]
(5) int * [4]
關鍵洞見:指標型別與指向型別是兩個獨立概念。精通指標的關鍵在於將「型別」拆解為這兩個層面。
指標值是一個位址(32 位元系統下為 32 位整數)。指向的記憶體區塊從該位址起始,長度為 sizeof(指向型別)。未初始化的指標指向區塊不存在或無意義。
32 位平台佔 4 位元組。判斷指標運算式是否為左值的依據:指標運算式結果是否已佔據明確記憶體。
指標加減整數 不是 普通數值運算,步長由 sizeof(指向型別) 決定。
char a[20];
int *ptr = a; // 強制轉型,假設 a 位址為 0x1000
ptr++; // ptr 變為 0x1004 ( sizeof(int)=4 )
ptr 原指向 a[0..3],現指向 a[4..7]。
int array[20];
int *ptr = array;
for (i = 0; i < 20; i++) {
(*ptr)++; // 元素值加 1
ptr++; // 移至下一元素
}
ptr + n:位址增加 n * sizeof(指向型別) 位元組ptr - n:位址減少 n * sizeof(指向型別) 位元組運算元型別 + *int a = 12, b;
int *p;
int **ptr;
p = &a; // p: int*, 指向 a
*p = 24; // *p 即 a,a 變 24
ptr = &p; // ptr: int**, 指向 p
*ptr = &b; // *ptr 是 int*,&b 也是 int*,賦值合法
**ptr = 34; // **ptr 即 b,b 變 34
結果為指標的運算式稱為指標運算式。具備四大屬性,其中「本身佔用記憶體」決定是否為左值。
int a, b;
int array[10];
int *pa = &a; // &a 是指標運算式,非左值(臨時結果)
int **ptr = &pa; // &pa 是左值(pa 有固定記憶體)
*ptr = &b; // *ptr 是左值(即 pa)
pa = array; // 陣列名轉型為指標
pa++; // pa++ 是指標運算式,也是左值
宣告 TYPE array[n] 時:
TYPE[n],sizeof(array) 為整個陣列大小TYPE*,指向第 0 元素,*array == array[0],array+3 == &array[3]char *str[3] = {
"Hello, this is a sample!",
"Hi, good morning.",
"Hello world"
};
strcpy(s, str[0]); // 等同 strcpy(s, *str)
strcpy(s, str[1]); // 等同 strcpy(s, *(str+1))
str 型別 char**,指向 char*。*str 型別 char*,指向第一個字串首字元。
int array[10];
int (*ptr)[10] = &array; // ptr 型別 int(*)[10],指向整個陣列
此處 &array 取的是整個陣列位址,型別 int(*)[10],非 int**。
int (*ptr)[10];
sizeof(int(*)[10]) == 4 // 指標大小
sizeof(int[10]) == 40 // 陣列大小
sizeof(ptr) == 4 // 指標變數大小
sizeof 永遠測量運算元「自身型別」大小,不會自動解參考。
指標精髓在於區分「指標型別」與「指向型別」,理解算術運算以「指向型別大小」為步長,掌握 &/* 的型別轉換規則,並看透陣列名在不同上下文的雙重角色。這些核心觀念是寫出正確、高效 C 程式的基石。