C言語には、構造体(struct
)と似て非なる、少し特殊なデータ型を定義する方法として**「共用体(union)」と「列挙体(enum)」**があります。これらは特定の場面で使うと、コードをより効率的で読みやすくしてくれる強力なツールです。
今回は、この共用体と列挙体の基本的な使い方と、それぞれの便利な活用シーンについて解説します。
共用体(union) 〜 1つのメモリ領域を共有する仕組み
共用体(union)は、複数のメンバー(変数)が同じ一つのメモリ領域を共有する特殊なデータ型です。構造体が「各メンバーに専用の部屋が割り当てられた家」だとすれば、共用体は「一つの部屋を、使う人や目的によって模様替えして使う多目的ルーム」のようなイメージです。
これにより、同時に使わない複数のデータのうち、どれか一つだけを格納する、という場合にメモリを節約できます。
基本的な使い方と注意点
共用体のサイズは、その中で最も大きいメンバーのサイズになります。そして、重要なルールとして**「最後に値を代入したメンバーのみが有効な値を保持する」**という点があります。
サンプルコード:
#include <stdio.h>
// ひとつのデータ領域を整数、浮動小数点数、文字として共用する
typedef union {
int as_integer;
float as_float;
char as_character;
} DataValue;
int main(void) {
DataValue value;
// 1. 整数として値を代入
value.as_integer = 12345;
printf("--- 整数を代入後 ---\n");
// 最後に代入したas_integerは有効な値
printf("整数として: %d\n", value.as_integer);
// 他のメンバーは無効な値(メモリの解釈が変わるため)
printf("浮動小数点数として: %f\n", value.as_float);
printf("文字として: %c\n", value.as_character);
printf("\n");
// 2. 浮動小数点数として値を代入
value.as_float = 98.76f;
printf("--- 浮動小数点数を代入後 ---\n");
// 最後に代入したas_floatが有効になる
printf("整数として: %d\n", value.as_integer); // 値が壊れている
printf("浮動小数点数として: %f\n", value.as_float);
printf("文字として: %c\n", value.as_character); // 値が壊れている
return 0;
}
実行結果(例):
--- 整数を代入後 ---
整数として: 12345
浮動小数点数として: 0.000000
文字として: 9
--- 浮動小数点数を代入後 ---
整数として: 1120281313
浮動小数点数として: 98.760002
文字として: Q
このように、as_integer
に値を入れた後、as_float
に値を入れると、as_integer
が持っていた値は壊れてしまいます。これは、同じ場所にあるメモリを、異なるデータ型として無理やり解釈しようとするためです。
どんな時に使うの?
共用体は、**「このデータは整数かもしれないし、文字列ポインタかもしれない」**といった、複数の型のうちどれか一つが格納されることが分かっている場合に役立ちます。例えば、通信データパケットの解析や、低レベルなハードウェア制御などでメモリを厳密に管理したい場合に利用されます。
列挙体(enum) 〜 番号に分かりやすい名前を付ける
**列挙体(enum)**は、0, 1, 2, ...
といった連続する整数に、分かりやすい名前(識別子)を付けて管理するための仕組みです。
プログラム中で status = 0;
や signal = 2;
のように数字(マジックナンバー)を直接書くと、後からコードを見たときに「0
ってどういう状態だっけ?」と分かりにくくなってしまいます。列挙体は、この問題を解決し、コードの可読性を劇的に向上させます。
基本的な使い方
列挙体で定義した名前は、内部的には0
から始まる整数値として扱われます。
サンプルコード:
#include <stdio.h>
// 信号機の色を列挙体として定義
typedef enum {
RED, // 0
YELLOW, // 1
GREEN // 2
} SignalColor;
// 信号の色に応じたメッセージを表示する関数
void print_signal_action(SignalColor color) {
switch (color) {
case RED:
printf("信号は「赤」です。止まれ。\n");
break;
case YELLOW:
printf("信号は「黄」です。注意。\n");
break;
case GREEN:
printf("信号は「緑」です。進め。\n");
break;
default:
printf("不明な信号です。\n");
break;
}
}
int main(void) {
SignalColor current_signal = RED;
print_signal_action(current_signal);
current_signal = GREEN;
print_signal_action(current_signal);
// 列挙体の実体は整数
printf("\nGREENの内部的な値は %d です。\n", GREEN);
return 0;
}
実行結果:
信号は「赤」です。止まれ。
信号は「緑」です。進め。
GREENの内部的な値は 2 です。
switch
文と組み合わせることで、case 0:
や case 1:
と書くよりも、case RED:
や case GREEN:
と書くほうが、処理の内容が断然分かりやすくなりますね。
構造体(struct)と共用体(union)の決定的な違い
構造体 (struct) | 共用体 (union) | |
メモリ | 全メンバーが別々の領域を確保 | 全メンバーが同じ領域を共有 |
サイズ | 全メンバーの合計サイズ(+α) | 最も大きいメンバーのサイズ |
値の保持 | 全メンバーが同時に値を保持できる | 最後に代入したメンバーのみが値を保持 |
イメージ | 各部屋がある家 | 1つの多目的ルーム |
Google スプレッドシートにエクスポート
この違いを理解し、用途に応じて適切に使い分けることが重要です。
まとめ
今回は、共用体と列挙体という2つの便利なデータ型について学びました。
- 共用体(union): 複数のデータ型でメモリ領域を共有する仕組み。メモリを節約したい、または複数の型を取りうる値を扱いたい場合に使う。
- 列挙体(enum): 連続する整数に分かりやすい名前を付ける仕組み。マジックナンバーをなくし、コードの可読性を高めるために使う。
これらの機能を適切に活用することで、より洗練された、メンテナンスしやすいC言語プログラムを書くことができます。