★残りの命令(共用体)★


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によっても違う可能性がありますし、ネットワーク上で扱われるデータでも違いがあります。

プログラムを移植するとか、ネットワーク上でデータを扱う等、状況に応じて「エンディアン」を確認していく必要があります。

次へ

戻る

目次へ