「C言語キーワード一覧」を見ると、まだ説明していない命令が2つあります。
union |
と
volatile |
です。
今回は、共用体(union)について説明します。
union については「小数の表現4」で一度使いました。
共用体とは、1つのアドレスに複数の名前が付けられるというものです。
まずは1つプログラムを書いてみます。
<sample program 182-01>
#include <stdio.h> union Sample { int data1; int data2; }; int main(void) { union Sample sample; return 0; } |
基本的には構造体と同じ宣言方法です。
※typedefも使えます。
こう宣言した場合のメモリの状態を図にしてみます。
+------------* sample.data1 | | ← sample.data2でもある +------------*
イメージするのが難しいと思いますので、プログラムを追加します。
<sample program 182-02>
#include <stdio.h> union Sample { int data1; int data2; }; int main(void) { union Sample sample; sample.data1 = 15; printf("data = %d\n", sample.data2); return 0; } |
<実行結果>
data2 = 15 続行するには何かキーを押してください・・・
15は data1 に入れたはずですが、data2 に15が入っています。
共用体では、data1 も data2 も同じメモリ領域を指しています。
と言う事は、data1 に 入れたデータは data2 にも入っていると言う事です
要はメモリ領域は1つしか確保されておらず、名前だけが2つ付いています。
しかし、重要なのは「使い道」です。
正直、私自身は余り使う場面がありません・・・
「エンディアン」の説明をしなければならない時くらいでしょうか。
「エンディアン」とは何か、先にプログラムを書いて動かしてみましょう。
<sample program 182-03>
#include <stdio.h> union Sample { long data; unsigned char byte[4]; }; int main(void) { union Sample sample; sample.data = 0x89ABCDEF; printf("data = %08X\n", sample.data); return 0; } |
<実行結果>
data = 89ABCDEF 続行するには何かキーを押してください・・・
今回は、共用体のメンバ変数の型を変えてみました。
longは私の環境では32ビットです。
unsigned charは4つの配列になっていますので、8ビット×4で32ビットです。
これをメモリの図として書いてみます。
+------------* sample.data | | sample.byte[0] +------------* | | sample.byte[1] +------------* | | sample.byte[2] +------------* | | sample.byte[3] +------------*
1バイトごとにアドレスが割り振られていますので、↑の図のようになります。
では、実際の中身はどうなっているのでしょうか。
<sample program 182-04>
#include <stdio.h> union Sample { long data; unsigned char byte[4]; }; int main(void) { union Sample sample; sample.data = 0x89ABCDEF; printf("data = %08X\n", sample.data); printf("byte[0] = %02X\n", sample.byte[0]); printf("byte[1] = %02X\n", sample.byte[1]); printf("byte[2] = %02X\n", sample.byte[2]); printf("byte[3] = %02X\n", sample.byte[3]); return 0; } |
<実行結果>
data = 89ABCDEF byte[0] = EF byte[1] = CD byte[2] = AB byte[3] = 89 続行するには何かキーを押してください・・・
と言う事は、メモリの中身は、
+------------* sample.data | EF | sample.byte[0] +------------* | CD | sample.byte[1] +------------* | AB | sample.byte[2] +------------* | 89 | sample.byte[3] +------------*
となっています。
順番に並べてみると、
EF CD AB 89
になっており、バイト単位で考えるとデータが逆に入っています。
何となくですが、
89 AB CD EF
というデータは、
+------------* sample.data | 89 | sample.byte[0] +------------* | AB | sample.byte[1] +------------* | CD | sample.byte[2] +------------* | EF | sample.byte[3] +------------*
と格納されているように思えます。
しかし、私の環境では逆でした。
この「エンディアン」というのは、データをメモリに格納する際の順番の事です。
私の環境の、
+------------* sample.data | EF | sample.byte[0] +------------* | CD | sample.byte[1] +------------* | AB | sample.byte[2] +------------* | 89 | sample.byte[3] +------------*
この状態を「リトルエンディアン」と言い、
+------------* sample.data | 89 | sample.byte[0] +------------* | AB | sample.byte[1] +------------* | CD | sample.byte[2] +------------* | EF | sample.byte[3] +------------*
こちらを「ビッグエンディアン」と言います。
これは、CPUによっても違う可能性がありますし、ネットワーク上で扱われるデータでも違いがあります。
プログラムを移植するとか、ネットワーク上でデータを扱う等、状況に応じて「エンディアン」を確認していく必要があります。