代碼示例
#include
int main() {
// 1. 二維字符數組:連續內存塊,每行固定 4 字節
char strs[3][4] = {"123", "abc", "edf"};
// 內存佈局:
// strs[0]: '1','2','3','\0', (可能有填充)
// strs[1]: 'a','b','c','\0'
// strs[2]: 'e','d','f','\0'
// 2. 字符指針數組:存儲 3 個指針(各 8 字節於 64 位系統)
char *ptr_arr[3];
// 3. 指向指針的指針
char **pp;
// 4. 建立映射:指針數組指向二維數組各行
for (int i = 0; i < 3; i++)
ptr_arr[i] = strs[i]; // strs[i] 退化為 char* (行首地址)
pp = ptr_arr; // pp 指向指針數組首元素
// 5. 遍歷輸出
for (int i = 0; i < 3; i++)
puts(*pp++); // *pp 獲取 char*,遞增 pp
return 0;
}
內存模型對比圖解
char strs[3][4] (二維數組) char *ptr_arr[3] (指針數組)
┌─────────────────────────┐ ┌─────────────────────────┐
│ strs[0] │ '1' '2' '3' '\0' │ │ ptr_arr[0] │ ──────────────┐
├─────────────────────────┤ ├─────────────────────────┤ │
│ strs[1] │ 'a' 'b' 'c' '\0' │ │ ptr_arr[1] │ ──────────────┼──▶ 各行數據
├─────────────────────────┤ ├─────────────────────────┤ │
│ strs[2] │ 'e' 'd' 'f' '\0' │ │ ptr_arr[2] │ ──────────────┘
└─────────────────────────┘ └─────────────────────────┘
連續 3×4=12 字節 3×8=24 字節 (64位) 存地址
關鍵差異總結表
| 特性 | 二維字符數組 char a[M][N] | 字符指針數組 char *a[M] |
| 內存佈局 | 連續塊,行主序 | 指針數組 + 分散的字符串 |
| 行長度 | 固定 N (含\0) | 可變(每行指向不同長度字符串) |
| 修改內容 | 直接修改 a[i][j] | 修改指向的字符串(若指向字面量則不可修改) |
| 賦值/複製 | 需 strcpy 逐行 | 直接賦值指針 a[i] = "new" |
| sizeof | M*N | M*sizeof(char*) |
| 參數傳遞 | 退化為 char (*)[N] | 退化為 char ** |
經典面試題:參數傳遞與 sizeof
void func1(char a[3][4]) {
// a 類型:char (*)[4] (指向 4-char 數組的指針)
printf("%zu\n", sizeof(a)); // 8 (指針大小),非 12!
}
void func2(char *a[3]) {
// a 類型:char **
printf("%zu\n", sizeof(a)); // 8
}
int main() {
char strs[3][4] = {"123","abc","edf"};
char *ptrs[3] = {"123","abc","edf"}; // 指向字面量
printf("main sizeof(strs)=%zu\n", sizeof(strs)); // 12
printf("main sizeof(ptrs)=%zu\n", sizeof(ptrs)); // 24 (64位)
func1(strs); // 退化為指針,sizeof 失效
func2(ptrs);
}
動態分配對比
二維數組動態分配(連續內存)
// 真正的連續二維數組
char (*matrix)[4] = malloc(3 * sizeof(*matrix)); // 3 行,每行 4 char
strcpy(matrix[0], "123");
// 釋放:free(matrix);
指針數組動態分配(不連續)
char **matrix = malloc(3 * sizeof(char*));
for (int i = 0; i < 3; i++)
matrix[i] = strdup("dynamic"); // 每行獨立分配
// 釋放:逐行 free(matrix[i]),再 free(matrix)
實際選型指南
| 場景 | 推薦 | 理由 |
| 固定長度字符串集合(如命令表、錯誤碼) | 二維數組 | 緊湊、連續、缓存友好、無碎片 |
| 可變長度字符串(如參數解析、配置項) | 指針數組 | 靈活、易擴展、支持字面量直接賦值 |
| 需頻繁增刪字符串 | 指針數組 + 動態分配 | 避免大塊內存移動 |
| 高性能遍歷/拷貝 | 連續二維數組 | memcpy 一次性拷貝、SIMD 友好 |
總結
char a[M][N] 是連續內存塊,行長固定;char *a[M] 是指針集合,指向分散字符串。理解內存佈局、退化規則、sizeof 行為、修改權限(字面量只讀)的差異,是 C 語言字符串處理進階的分水嶺。根據數據特性(固定/可變長、靜態/動態)選擇合適結構,能顯著提升代碼清晰度與性能。