2013/12/11

C言語Tips4 無名構造体と共用体

共用体ってあるじゃないですか。

union Data {
    unsigned int value;
    unsigned char byte;
};

これ。
この例では、

union Data data;
data.value = 100;
とかすると、
data.byteは100です。
ところが、
data.value = 256;
とすると、
data.byteは0です。

4バイトを1バイトずつアクセスしたいときってありますよね。

union Data {
    unsigned int value;
    unsigned char byte[4];
};

もちろんこれがいい例ですが、構造体にして名前を付けたい……なんてことも

union Data {
    unsigned int value;
    struct Bytes {
        unsigned char a;
        unsigned char b;
        unsigned char c;
        unsigned char d;
    } bytes;
};

でもこれにアクセスするとき、
data.bytes.a
とbytesが必要です。できれば余分なものは入れたくない……。そんなとき

union Data {
    unsigned int value;
    struct Bytes {
        unsigned char a;
        unsigned char b;
        unsigned char c;
        unsigned char d;
    };
};

と省略できるのです。こうすると、
data.aとかdata.bとかアクセスできるようになります。おお便利。
あとByteなどのタグの名前も省略できますから、

union Data {
    unsigned int value;
    struct {
        unsigned char a;
        unsigned char b;
        unsigned char c;
        unsigned char d;
    };
};
でおkです。

なお、このようにデータをバイト単位で並べるときはアライメントという問題やエンディアンの問題が出てきます。

int型はここでは4byteを想定していますが、コンパイラによって異なります。
例えば、サークルで推奨しているPIC環境でプログラムを書く場合は2byteです。

structはデータを順番に並べることは保証していますが、これらのあいだに隙間がないことは保証されていません。
すなわち、

struct A {
    int x;
    char y;
    int z;
};

と宣言したからと言って、intが2つで4byte×2=8byte、charが1つで1byteだからstruct Aのサイズは9byteということにはなりません。
アライメントといって、データがちょうと2, 4, 8, ...の倍数に来るように調整される可能性があります。
私の持っているVC++コンパイラでは、sizeof(struct A)が12byteとなります。すなわち、char y;の後に3byte空けられていることになります。

VC++コンパイラでは、
#pragma pack(1)
struct A {
    int x;
    char y;
    int z;
};
#pragma pack
とすることでアライメントを1にして詰めてくれます。(XC8でもあるようです)

エンディアンの問題とは、ご存じの通りint型は4byteですから、どの順番に値を入れるかが問題となります。 下の位からすなわち、0xAABBCCDDとあったとき、0xDD, 0xCC, 0xBB, 0xAAと格納する方式をリトルエンディアンと言い、逆に0xAA, 0xBB, 0xCC, 0xDDと格納する方式をビッグエンディアンと言います。XC8はリトルエンディアンです。
これらに注意してint型など2byte以上のコードを書いて下さいね。

0 件のコメント:

コメントを投稿