1. 共用体(union)の定義とメモリ構造
共用体は、複数のメンバが同一のメモリ領域を共有するためのデータ構造です。構造体とは異なり、ある時点で有効なメンバは1つのみとなります。
主な特徴
- メモリ共有: すべてのメンバが同じ開始アドレスを共有します。
- サイズ計算: 共用体のサイズは、最大のメンバのサイズに基づいて決定されます。
- メモリ境界調整(アライメント): 最大の型サイズの整数倍になるよう、パディングが追加されることがあります。
#include <stdio.h>
#include <string.h>
union Storage {
int ival;
double dval;
char text[20];
};
int main() {
union Storage node;
// サイズの確認(最大のメンバ text[20] と double のアライメントを考慮)
printf("共用体のサイズ: %zu バイト\n", sizeof(node));
// int型として使用
node.ival = 1024;
printf("int: %d\n", node.ival);
// double型として使用(intの値は破壊される)
node.dval = 3.14159;
printf("double: %.5f\n", node.dval);
// 文字列として使用
strcpy(node.text, "C-Language");
printf("text: %s\n", node.text);
return 0;
}
2. 列挙型(enum)による定数管理
列挙型は、整数定数に名前を付けて管理するための型です。コードの可読性を高めるのに有効です。
列挙型の規則
- デフォルトでは最初の要素が0から始まり、以降1ずつ増加します。
- 任意の値を手動で割り当てることが可能です。値を指定しない場合、直前の要素の値+1となります。
- 列挙型のメンバは定数であるため、ドット演算子によるアクセスなどは行いません。
#include <stdio.h>
enum DeviceStatus {
IDLE, // 0
CONNECTING = 5, // 5
CONNECTED, // 6
DISCONNECTING, // 7
ERROR = -1 // -1
};
int main() {
enum DeviceStatus current = CONNECTED;
if (current == CONNECTED) {
printf("ステータス値: %d\n", current);
}
return 0;
}
3. ビット演算子の活用
ビット単位の操作を行うことで、メモリ節約や高速な計算、フラグ管理が可能になります。
主要な演算子
- & (論理積): 両方のビットが1の場合のみ1。特定のビットを「抽出」したり「クリア」するのに使用。
- | (論理和): 少なくとも一方のビットが1なら1。特定のビットを「セット」するのに使用。
- ^ (排他的論理和): ビットが異なる場合のみ1。値の反転や入れ替えに使用。
- ~ (否定): ビットを反転。
- << (左シフト): 1ビットシフトするごとに値を2倍にする。
- >> (右シフト): 1ビットシフトするごとに値を1/2にする。
排他的論理和(^)を用いた変数スワップ
#include <stdio.h>
int main() {
int x = 10, y = 20;
// 一時変数を使わないスワップ
x = x ^ y;
y = x ^ y;
x = x ^ y;
printf("x: %d, y: %d\n", x, y); // x=20, y=10
return 0;
}
4. 構造体と共用体のネストによるメモリ計算
メモリサイズを計算する際は、アライメント規則に注意が必要です。以下の例では、各メンバがどのようにメモリを占有するかを考慮します。
#include <stdio.h>
struct Record {
union {
char tag[5]; // 5バイト
short code[2]; // 4バイト
} header; // 共用体サイズは最大要素の5バイトだが、short(2)のアライメントで6バイト
char name[10]; // 10バイト
float score; // 4バイト
};
/*
計算例 (アライメント単位を4とした場合):
1. header: 6バイト (アライメント調整含む)
2. name: 10バイト
3. 調整: 16バイト経過後、float(4バイト)は4の倍数位置から開始可能
4. score: 4バイト
合計: 6 + 10 + 4 = 20バイト
*/
構造体内の共用体は、データプロトコルや可変長メッセージのパースにおいて非常に強力なツールとなります。ビットフィールドと組み合わせることで、ハードウェア制御に近いレイヤーでの実装も容易になります。