C 語言多維數組在內存中按行優先順序連續存儲。int a[3][4] 實際是 12 個 int 的連續塊,編譯器按 a[i][j] ≡ *(a + i*4 + j) 計算地址。這決定了:
// 完全初始化
int a[2][3] = {{1,2,3}, {4,5,6}};
// 部分初始化,餘下自動置 0
int b[2][3] = {{1,2}, {4}}; // 等效 {{1,2,0}, {4,0,0}}
// 遍歷訪問
for (int i=0; i<2; ++i)
for (int j=0; j<3; ++j)
printf("%d ", a[i][j]);
// 原矩陣 2×3 → 轉置 3×2
int a[2][3] = {{1,5,9}, {3,2,8}};
int b[3][2];
for (int i=0; i<2; ++i)
for (int j=0; j<3; ++j)
b[j][i] = a[i][j]; // 核心:索引對調
int max = a[0][0], max_r = 0, max_c = 0;
for (int i=0; i<3; ++i)
for (int j=0; j<4; ++j)
if (a[i][j] > max) {
max = a[i][j];
max_r = i; max_c = j;
}
char *strs[5] 是 5 個 char* 的數組,每個指針指向字符串字面量(只讀區)或堆/棧上的字符數組。
char *strs[6] = {"banana", "apple", "pear", "orange", "grape", "kiwi"};
char *temp;
for (int i=0; i<5; ++i)
for (int j=0; j<5-i; ++j)
if (strcmp(strs[j], strs[j+1]) > 0) {
temp = strs[j];
strs[j] = strs[j+1];
strs[j+1] = temp;
}
// 輸出:apple banana grape kiwi orange pear
size_t max_len = 0;
char *longest = NULL;
for (int i=0; i<6; ++i) {
size_t len = strlen(strs[i]);
if (len > max_len) {
max_len = len;
longest = strs[i];
}
}
// 3×4×5 三維數組
int cube[3][4][5];
// 訪問:cube[i][j][k] ≡ *(cube + i*20 + j*5 + k)
// 遍歷三重循環
for (int i=0; i<3; ++i)
for (int j=0; j<4; ++j)
for (int k=0; k<5; ++k)
cube[i][j][k] = i*100 + j*10 + k;
| 聲明形式 | 本質 | 適用場景 |
|---|---|---|
void f(int a[3][4]) | 退化為 int (*a)[4] | 列數固定、行數可變 |
void f(int a[][4]) | 同上,語義更清晰 | 推薦風格 |
void f(int (*a)[4]) | 指向含4個int數組的指針 | 本質表達,利於理解 |
// 正確調用
void printMat(int (*mat)[4], int rows) {
for (int i=0; i
動態分配二維數組:兩種方案對比
方案一:連續塊(緊湊、緩存友好)
int (*mat)[cols] = malloc(rows * sizeof *mat); // 一次分配
// 訪問同靜態數組:mat[i][j]
free(mat);
方案二:指針數組(行長度可變、非連續)
int **mat = malloc(rows * sizeof *mat);
for (int i=0; i
經典陷阱與最佳實踐
- 越界不報錯:C 不做邊界檢查,嚴謹編碼需斷言或手動檢查
- 字符串字面量不可修改:
char *s = "abc"; s[0]='x'; // UB,需用 char s[] = "abc";
- sizeof 陷阱:參數退化後
sizeof(arr) 失效,必須傳長度
- 大數組放棧:
int a[10000][10000] 棧溢出,改靜態/堆分配
- 字符串數組排序:交換指針而非拷貝內容,O(1) 交換成本
總結
多維數組本質是帶有編譯器計算偏移語法糖的一維連續內存。掌握行優先佈局、退化規則、動態分配兩種模式、字符串數組即指針數組,配合 memcpy/memset、標準庫算法,即可高效解決矩陣運算、文本處理、圖論鄰接矩陣等實際問題。