C言語では、malloc、calloc、realloc といった関数を用いてヒープ領域に動的メモリを確保できます。これらに関連する特殊なデータ構造として、「柔軟配列(Flexible Array Member)」があります。
柔軟配列は、構造体の末尾に要素数を明示せずに宣言される配列です。以下のように定義します:
struct Data {
int header;
char flag;
int payload[]; // 柔軟配列(C99以降で標準化)
};
この payload[] は通常の配列とは異なり、構造体定義内ではサイズが指定されません。実際のメモリは、構造体本体と柔軟配列の領域をまとめて動的に確保します。
メモリ確保と使用例
10個の整数を格納できる柔軟配列を持つ構造体を確保するには、次のようにします:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
struct Data {
int header;
char flag;
int payload[];
};
int main(void) {
size_t count = 10;
struct Data *obj = malloc(sizeof(struct Data) + count * sizeof(int));
if (!obj) {
fprintf(stderr, "Allocation failed: %s\n", strerror(errno));
return EXIT_FAILURE;
}
obj->header = 42;
obj->flag = 'X';
for (size_t i = 0; i < count; ++i) {
obj->payload[i] = (int)(i + 1);
}
printf("Header: %d, Flag: %c\n", obj->header, obj->flag);
for (size_t i = 0; i < count; ++i) {
printf("%d ", obj->payload[i]);
}
putchar('\n');
free(obj);
return EXIT_SUCCESS;
}
サイズ変更の例
必要に応じて、realloc を使って柔軟配列のサイズを拡張できます:
// 初期確保
struct Data *obj = malloc(sizeof(struct Data) + 10 * sizeof(int));
// ... 初期化 ...
// サイズを20要素に拡張
struct Data *tmp = realloc(obj, sizeof(struct Data) + 20 * sizeof(int));
if (!tmp) {
free(obj);
fprintf(stderr, "Reallocation failed: %s\n", strerror(errno));
return EXIT_FAILURE;
}
obj = tmp;
// 新しい要素を初期化
for (size_t i = 10; i < 20; ++i) {
obj->payload[i] = (int)(i + 1);
}
// ... 使用後 free(obj);
柔軟配列の利点
柔軟配列を使わずに同様の機能を実現する場合、構造体内にポインタを置き、別途配列用のメモリを確保する方法が考えられます:
struct Alternative {
int header;
char flag;
int *payload; // ポインタ
};
この場合、メモリ解放時に payload と構造体本体の両方を個別に free する必要があります。順序を誤ると未定義動作やメモリリークを引き起こす可能性があります。
一方、柔軟配列は単一の malloc 呼び出しで構造体と配列を連続したメモリ上に配置するため、以下の利点があります:
- メモリ管理が簡潔:解放は一度の
freeで済む。 - メモリの局所性が高い:構造体とデータが物理的に近接しており、キャッシュ効率が向上する。
- フラグメンテーションの低減:複数回の
mallocを避けることで、ヒープの断片化を抑制できる。
これらの特性により、柔軟配列はパフォーマンスやコードの信頼性が求められる場面で有用です。