C言語構造体のフレキシブル配列メンバで動的データを扱う
フレキシブル配列メンバとは
フレキシブル配列メンバ(Flexible Array Member)は、構造体内でサイズを指定せずに配列を扱う仕組みです。配列のサイズは実行時に決定されます。構造体は、異なるデータ型の複数のメンバを1つの名前でまとめたユーザー定義データ型です。
構造体の基本的な宣言構文は以下の通りです:
struct StructName {
data_type member1;
data_type member2;
// ...
};
各メンバは異なるデータ型を持てます。
フレキシブル配列メンバは、固定サイズのメンバの後に可変長配列を配置し、それらをひとまとまりのメモリブロックとして確保できるよう構造体を拡張します。本章では、構造体内での動作を解説します。
構造体におけるフレキシブル配列メンバ
フレキシブル配列メンバは、構造体内で固定サイズを持たない配列であり、malloc()、calloc()などの関数で実行時に動的にメモリを確保します。空の角括弧[]で宣言します。
フレキシブル配列メンバは構造体の末尾に宣言しなければならず、その前に少なくとも1つの別のメンバが必要です。
構造体内での宣言構文:
struct StructName {
data_type member1;
data_type flexible_array[]; // フレキシブル配列メンバ
};
ここでdata_typeは配列の要素型、arrayName[]はサイズ未指定のフレキシブル配列です。
フレキシブル配列メンバのメモリ確保
フレキシブル配列メンバは固定サイズを持たないため、コンパイラは構造体内でそれにメモリを割り当てません。sizeof演算子も、フレキシブル配列メンバを除いた固定メンバのサイズのみを返します。そのため、このような構造体を作成する際は手動でメモリを確保する必要があります。
必要な総メモリ量の計算式:
総メモリ = sizeof(構造体) + (要素数 × sizeof(要素型))
ここで、sizeof(構造体)は固定メンバ分のメモリ、(要素数 × sizeof(要素型))はフレキシブル配列分のメモリです。これらを足した値を確保します。
例1:フレキシブル配列メンバのメモリ確保
固定メンバ1つとフレキシブル配列を持つ構造体を定義し、フレキシブル配列分のメモリを確保する例です。
#include <stdio.h>
#include <stdlib.h>
struct Example {
int id;
int arr[]; // フレキシブル配列メンバ
};
int main() {
int size = 5;
// 構造体 + フレキシブル配列分のメモリ確保
struct Example *e = malloc(sizeof(struct Example) + size * sizeof(int));
if (e != NULL) {
printf("Total allocated memory: %zu bytes\n", sizeof(struct Example) + size * sizeof(int));
free(e);
}
return 0;
}
ここでは、sizeof(struct Example)が4バイト(idメンバ分)、フレキシブル配列分が5 × sizeof(int) = 20バイト。合計4 + 20 = 24バイトが確保されます。出力は:
Total allocated memory: 24 bytes
例2:構造体内のフレキシブル配列へのアクセス
固定メンバ(id)とフレキシブル配列メンバ(marks)を持つStudent構造体を定義し、フレキシブル配列分のメモリを動的確保して要素にアクセスします。確保サイズを超えるアクセスは未定義動作を招くため、marks[0]からmarks[2]のみアクセスします。
#include <stdio.h>
#include <stdlib.h>
struct Student {
int id;
int marks[]; // フレキシブル配列メンバ
};
int main() {
int subjects = 3;
// 構造体 + フレキシブル配列分のメモリ確保
struct Student *s = malloc(sizeof(struct Student) + subjects * sizeof(int));
if (s != NULL) {
s->id = 102;
s->marks[0] = 80;
s->marks[1] = 75;
s->marks[2] = 88;
printf("Student ID: %d\n", s->id);
printf("Marks: %d, %d, %d\n", s->marks[0], s->marks[1], s->marks[2]);
printf("Total allocated memory: %zu bytes\n", sizeof(struct Student) + subjects * sizeof(int));
free(s);
}
return 0;
}
実行結果:
Student ID: 102 Marks: 80, 75, 88 Total allocated memory: 16 bytes
フレキシブル配列メンバの動的リサイズ
フレキシブル配列はrealloc()関数でリサイズ可能です。この関数は既存メモリブロックを拡張するか、新しいブロックを確保して既存データを自動的にコピーします。
リサイズ時は、新しい総メモリサイズを以下の式で計算してrealloc()に渡します:
sizeof(構造体) + (新しい要素数 × sizeof(要素型))
注意:realloc()の戻り値は必ず一時ポインタに受け取り、失敗時は元のポインタが有効なままになるようにしてください。リサイズ後はサイズ管理用のカウンタを更新し、新たに追加された要素のみ初期化してください。
例3:フレキシブル配列メンバの動的リサイズ
Student構造体で最初3科目分のmarksを確保し、後で6科目分にrealloc()で拡張します。既存のmarksデータは保持されるため、手動コピーは不要です。
#include <stdio.h>
#include <stdlib.h>
struct Student {
int id;
int count; // 現在の配列サイズを管理
int marks[]; // フレキシブル配列メンバ
};
int main() {
// Step 1: 初期確保(3要素)
int initial_elements = 3;
struct Student *s = malloc(sizeof(struct Student) + initial_elements * sizeof(int));
if (s != NULL) {
s->id = 101;
s->count = initial_elements; // 現在の配列サイズを記録
// 初期サイズの表示
printf("Structure size: %zu bytes\n", sizeof(struct Student)); // 出力: 8バイト(id + count)
printf("Initial array elements: %d\n", s->count); // 出力: 3
printf("Initial total memory: %zu bytes\n", sizeof(struct Student) + initial_elements * sizeof(int)); // 出力: 20バイト
// Step 2: 6要素にリサイズ
int new_elements = 6;
struct Student *temp = realloc(s, sizeof(struct Student) + new_elements * sizeof(int));
if (temp != NULL) {
s = temp;
s->count = new_elements; // 配列サイズ管理を更新
// リサイズ後のサイズ表示
printf("\nAfter resizing:\n");
printf("Structure size: %zu bytes\n", sizeof(struct Student)); // 変わらず8バイト
printf("New array elements: %d\n", s->count); // 出力: 6
printf("New total memory: %zu bytes\n", sizeof(struct Student) + new_elements * sizeof(int)); // 出力: 32バイト
}
free(s);
}
return 0;
}
構造体サイズと、初期・リサイズ後の総メモリを示す出力:
Structure size: 8 bytes Initial array elements: 3 Initial total memory: 20 bytes After resizing: Structure size: 8 bytes New array elements: 6 New total memory: 32 bytes
本章では、C言語構造体のフレキシブル配列メンバについて学びました。構造体の末尾に宣言し、可変長データを扱い、メモリを節約し、様々なデータサイズに柔軟に対応できます。確保、アクセス、リサイズの方法も確認しました。
C言語
- C++データ構造:配列、構造体、ポインタで効率的なプログラムを構築
- C言語のcalloc():ゼロ初期化メモリ割り当てと実践例
- C#型変換完全ガイド:暗黙・明示的変換、Parse、Convertクラス
- C++関数オーバーライド完全解説:実例とベストプラクティス
- C# 抽象クラスと抽象メソッドをマスターする:実践ガイド
- C++変数タイプ完全解説:メモリ、制限、操作
- C# イベント完全ガイド:ユーザー アクションとシステム通知の処理
- C言語forループ完全マスター:構文、仕組み、実践例
- C# Hello World – 初めてのC#アプリケーション構築
- C言語演算子完全ガイド:種類、機能、実例付き解説
- C++開発環境の構築:完全ガイド