兩個核心概念
- 字串指標陣列:
char* arr[] = {"abc", "cde", "fgh"}; — 陣列元素類型為 char*,儲存各字串常量的**首地址**。
- 指向指標的指標:
char** p = arr; — 指向陣列首元素(即第一個 char*)的指標。
記憶體佈局圖解
棧區 (Stack) 唯讀資料段 (Read-only Data)
┌─────────────────────┐ ┌──────────────┐
│ arr[0] ─────────────┼──────────────►│ "abc\0" │
│ arr[1] ─────────────┼──────────────►│ "cde\0" │
│ arr[2] ─────────────┼──────────────►│ "fgh\0" │
└─────────────────────┘ └──────────────┘
▲
│
char** p (指向 arr[0] 的指標)
- 陣列
arr 本身在棧上,佔 3 × sizeof(char*) 位元組。
- 字串常量
"abc" 等在唯讀區,**地址不連續**,由編譯器安排。
p 指向 arr[0],p+1 指向 arr[1](跨越 sizeof(char*) 位元組)。
程式碼逐行解析
char *a[] = {"abc", "cde", "fgh"}; // 字串指標陣列
char** b = a; // b 指向 a[0]
cout << *b << "|" // *b = a[0] = "abc" 首址 → 印 "abc"
<< *(b+1) << "|" // *(b+1) = a[1] = "cde" 首址 → 印 "cde"
<< *(b+2) << endl; // *(b+2) = a[2] = "fgh" 首址 → 印 "fgh"
函式傳遞:陣列退化為指向指標的指標
void print_char(char* array[], int len) { // 等價 char** array
for (int i = 0; i < len; ++i)
cout << *array++ << endl; // array 為區域指標變數,可修改
}
int main() {
char* test[] = {"abc", "cde", "fgh"};
int num = sizeof(test) / sizeof(char*); // 編譯期計算元素數 = 3
print_char(test, num); // 傳遞 test 首元素地址(即 &test[0])
}
關鍵觀察
sizeof(test) 在陣列定義作用域內 = 完整陣列大小(12/24 bytes)。
- 傳入函式後,
sizeof(array) = 指標大小(4/8 bytes),**失去長度資訊** → 必須額外傳 len。
*array++ 利用指標遞增遍歷,等價 array[i] 但避免索引運算。
常見錯誤
| 錯誤代碼 | 問題 | 修正 |
char* b = a; | 類型不符:a 是 char*[3],退化為 char**,不可賦值給 char* | char** b = a; |
cout << **(b+1); | 解引用兩次得到 char,印單一字元 'c' | cout << *(b+1); 印整字串 |
函式內 sizeof(array) 取長度 | 得到指標大小,非元素數 | 傳遞長度參數或用哨兵 NULL |
現代 C++ 替代方案
// 推薦:std::vector 或 std::array
std::vector vec = {"abc", "cde", "fgh"};
for (const auto& s : vec) cout << s << endl;
// 僅需唯讀視圖
std::array arr = {"abc", "cde", "fgh"};
優點:自動管理記憶體、知道大小、支援範圍 for、避免手動指標運算錯誤。
總結
char* arr[] 與 char** p 是 C 風格字串集合的經典表示。理解**陣列在棧、字串在唯讀區、指標運算按 sizeof(char*) 位移**的記憶體模型,是正確操作它們的關鍵。新代碼請優先使用標準容器封裝,僅在介接 C API 時轉換。
- 上一篇文章: C/C++ 字串常量比較陷阱與正確複製策略
- 下一篇文章: C/C++ 是程式設計師必修語言嗎?歷史回顧與現代視角
Copyright ©
程式碼之家 All Rights Reserved