數組是 C 語言最基礎的聚合類型,看似簡單,實則蘊含豐富的內存佈局規則、指針轉換語義、編譯器優化機會與工程陷阱。本文系統梳理數組聲明初始化、多維數組行主序佈局、數組名與指針的微妙關系、可變長數組(VLA)、柔性數組成員,並給出性能優化實戰指南。
type name[size]; // 固定長度,size 為常量表達式(C89)
type name[] = {init...}; // 推導長度
type name[size] = {init}; // 部分初始化,餘位零初始化
type name[size] = {0}; // 全零初始化(通用模式)
int arr[10] = { [0]=1, [5]=10, [9]=100 }; // C99
int arr[] = { [2]=5, [0]=1 }; // 長度推導為 3
char str[] = "hello"; // 等價於 {'h','e','l','l','o','\0'},長度 6
char str[6] = "hello"; // 同
char str[5] = "hello"; // 警告:截斷 \0,非字符串
int a[2][3] = { {1,2,3}, {4,5,6} };
// 內存連續:1 2 3 4 5 6
// a[0][0] 地址最低,a[1][2] 最高
a[i][j] == *(*(a + i) + j) == *(a[0] + i*3 + j)
編譯器將 a[i][j] 降為 *(a[0] + i*COLS + j),需知道列數 COLS。
// 以下聲明等價,參數實為 int (*)[3] void foo(int a[][3]); void foo(int (*a)[3]); // 調用:foo(arr); // arr 類型 int [2][3] → int (*)[3]
第一維長度丟失,第二維必須保留。
// 模式 1:單塊連續內存(推薦,緩存友好) int (*p)[COLS] = malloc(ROWS * COLS * sizeof(int)); p[i][j] // 直接雙下標訪問 // 模式 2:指針數組(行不連續) int **p = malloc(ROWS * sizeof(int *)); for (i) p[i] = malloc(COLS * sizeof(int)); p[i][j] // 雙重間接訪問 // 模式 3:扁平化一維數組(手動計算索引) int *p = malloc(ROWS * COLS * sizeof(int)); p[i*COLS + j] // 手動計算,最靈活
type *(右值)sizeof(arr):返回整個數組字節大小&arr:類型 type (*)[N],指向整個數組的指針char arr[] = "abc"int arr[10]; int *p = arr; sizeof(arr) // 40 (假設 int 4 字節) sizeof(p) // 8 (64 位指針大小) sizeof(*p) // 4 (int 大小)
void foo(int arr[100]) { // 實為 int *arr
sizeof(arr); // 返回指針大小,非數組大小!
}
// 必須額外傳遞長度參數
void foo(int n) {
int arr[n]; // 棧上分配,n 為運行時值
// ...
} // 作用域結束自動釋放
優點:避免 malloc 開銷、自動釋放。缺點:棧溢出風險、不支持初始化列表、C11 後為可選特性(__STDC_NO_VLA__),Microsoft MSVC 不支持。
struct Buffer {
size_t len;
char data[]; // 最後一個成員,無長度
};
// 分配:一次 malloc 獲取結構體 + 數據區
struct Buffer *buf = malloc(sizeof(struct Buffer) + 1024);
buf->len = 1024;
// 釋放:free(buf) 一次完成
用於變長結構體、網絡包、文件系統 inode 等場景,替代「結構體 + 指針」雙重分配模式。
// 差:列優先(步長大,缺失多) for (j=0; j2. 循環展開與向量化輔助
// 手動展開 4x,幫助編譯器生成 SIMD for (i=0; i3. 對齊分配:aligned_alloc / posix_memalign
int *p = aligned_alloc(64, n * sizeof(int)); // 64 字節對齊,利用 AVX-5124. 避免別名阻礙優化