共用体


さて、構造体の次に出てくるのが共用体だろう。

これが、今思うと、自分には、超難解だった。ネット上の説明も、まあまあ、デタラメだと思う。

私がこれまで携わってきた中では、以下の二つのケースが大半だったので、私見だが、使う必要が出てきた時に考えればよいと思う。

可変サイズのデータの格納

typedef union {
    char charval;
    int intval;
    long longval;
    double doubleval;
} variant_t;


int main(int argc, char *argv[])
{
    variant_t v;

    v.charval = 1;
    v.intval = 2;
    v.longval = 3;
}

一般的には、こういう説明だろう。

図示すると次のようになるだろうか。
union

しかし、ネット上の説明がデタラメだと思うのは、いったい、どの共用体メンバーにアクセスしてよいかわからない点だ。

実際には、構造体と組み合わせて使うのではないかと思う。

#include <stdio.h>

// char でも int でも long でも double でも入るデータ構造を考えてみる
typedef struct {
     int valtype; // 1=charval, 2=intval, 3=longval、 4=doubleval
     union {
         char charval;
         int intval;
         long longval;
         double doubleval;
    } u;
} variant_t;

int
main(int argc, char *argv[])
{
    variant_t v;

    v.valtype = 3;
    v.u.longval = 1234567890;

    switch (v.valtype) {
    case 3:
        printf("%ld\n", v.u.longval);
        break;
    default:
        printf("not implemented yet\n");
        break;
    }
    return 0;
}

おそらく、こういった、データの中身に何が入ってもよいような構造を作りたい場合に union を使うことがあるかもしれない。

もちろん、ベタに、

typedef struct {
     int valtype; // 1=charval, 2=intval, 3=longval、 4=doubleval
     struct {
         char charval;
         int intval;
         long longval;
         double doubleval;
     } s;
} variant_t;

とか、

typedef struct {
     int valtype; // 1=charval, 2=intval, 3=longval、 4=doubleval
     char charval;
     int intval;
     long longval;
     double doubleval;
} variant_t;

でもよいのだが、不要なメモリを確保してしまうことになるので、通信など、サイズに制約があるようなデータは、union のようなデータ構造になっているだろう。

ビットフィールド

組み込み用のマイコンでは、ある領域を、バイト単位でもビット単位でもアクセスしたい場合があり、そういった場合に union を使うことも多い。

#include <stdio.h>
#include <stdint.h>

typedef union {
    struct {
        unsigned int b7: 1;
        unsigned int b6: 1;
        unsigned int b5: 1;
        unsigned int b4: 1;
        unsigned int b3: 1;
        unsigned int b2: 1;
        unsigned int b1: 1;
        unsigned int b0: 1;
     } bit;
     uint8_t byte;
} reg_t;

int
main(int argc, char *argv[])
{
    reg_t reg;

    reg.byte = 0xaa;

    // ベタでごめん
    printf("%d ", reg.bit.b7);
    printf("%d ", reg.bit.b6);
    printf("%d ", reg.bit.b5);
    printf("%d ", reg.bit.b4);
    printf("%d ", reg.bit.b3);
    printf("%d ", reg.bit.b2);
    printf("%d ", reg.bit.b1);
    printf("%d\n", reg.bit.b0);

    return 0;
}