C89/C++ 標準禁止零長度陣列 (int arr[0]),但部分編譯器 (GCC/MSVC) 作為擴展支援。C99 引入柔性陣列成員,以標準化方式解決「結構體尾部跟隨變長資料」的需求。
| 寫法 | 標準 | 支援度 | 備註 |
|---|---|---|---|
int arr[0]; | 非標準 | GCC、MSVC、Clang 擴展 | C99 前通用做法 |
int arr[]; | C99 標準 | 所有 C99 相容編譯器 | 柔性陣列成員 (Flexible Array Member) |
sizeof(結構體) 不包含 柔性陣列大小
struct X {
int id;
int data[]; // 柔性陣列成員,無大小
};
// 正確用法
struct X *p = malloc(sizeof(struct X) + n * sizeof(int));
for (int i = 0; i < n; ++i)
p->data[i] = i;
struct PacketHeader {
uint16_t magic;
uint16_t type;
uint32_t length;
uint8_t payload[]; // 柔性陣列:實際封包資料
};
// 發送端建構
void send_packet(int type, const void* data, uint32_t len) {
struct PacketHeader* pkt = malloc(sizeof(struct PacketHeader) + len);
pkt->magic = 0xA5A5;
pkt->type = type;
pkt->length = len;
memcpy(pkt->payload, data, len);
socket_send(pkt, sizeof(struct PacketHeader) + len);
free(pkt);
}
// 接收端解析
void on_receive(struct PacketHeader* pkt, uint32_t total_len) {
if (total_len < sizeof(struct PacketHeader)) return;
uint32_t payload_len = total_len - sizeof(struct PacketHeader);
process_payload(pkt->type, pkt->payload, payload_len);
}
+------------------+ | magic (2 bytes) | +------------------+ | type (2 bytes) | +------------------+ | length(4 bytes) | +------------------+ | payload[0] | ← 柔性陣列開始位置 = 結構體大小 (對齊後) | payload[1] | | ... | | payload[n-1] | +------------------+
關鍵點:sizeof(struct PacketHeader) == 8 (假設 4 字節對齊),不含 payload。動態分配時手動加上資料長度。
// 確保柔性陣列按特定對齊
struct AlignedPacket {
uint32_t header;
// 強制 payload 8-byte 對齊
uint8_t payload[] __attribute__((aligned(8))); // GCC/Clang
// MSVC: __declspec(align(8)) uint8_t payload[];
};
// 只能有一個柔性陣列,需求多段變長資料時:
struct MultiData {
size_t len1, len2;
uint8_t data[]; // 手動分段:data[0..len1-1], data[len1..len1+len2-1]
};
std::vector、std::string 或 std::unique_ptr<T[]>| 錯誤代碼 | 問題 | 修正 |
|---|---|---|
struct S { int a[]; }; | 無其他具名成員 | 加入 int dummy; 或實際欄位 |
struct S { int a; int b[]; int c; }; | 柔性陣列非最後 | 移動 b[] 到最後 |
struct S s = {1, {2,3}}; | 靜態初始化柔性陣列 | 改用動態分配 |
sizeof(s.b) | 柔性陣列無 sizeof | 手動追蹤長度 |
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 柔性陣列成員 | 標準、單次分配、快取友好 | 需手動管理長度、非 C++ 標準 | 網路封包、檔案格式、高效能緩衝區 |
| 指標 + 獨立分配 | 靈活、C++ 友好 | 雙重分配、快取未命中、碎片化 | 一般業務邏輯、長度頻繁變動 |
| 固定大小陣列 | 簡單、靜態檢查 | 浪費空間或長度受限 | 最大長度可預估且較小 |
C99 柔性陣列成員 (int arr[]) 是「結構體尾部變長資料」的標準化解法,取代非標準的零長度陣列。核心價值:單次記憶體分配、連續佈局、零開銷存取。掌握其規範與記憶體佈局,是系統程式設計、協定解析、高效能資料結構的必備技能。