C言語に関する補足

Copyright(C)24Aug2008
coskx TNCT

1.ビットフィールド

ビットフィールドは1ビットの変数や4ビットの変数を用いる時に使われる。Cの文法の範囲であるが通常のプログラミングでは,あまり使われていない。マイクロコンピュータプログラミングでは時々使われている。

次のプログラムは1ビットの変数を使って礼である。1ビットの変数なので,とりうる値は0と1しかないため,1ずつ増加させると,すぐに桁あふれするので,0と1を繰り返すこととなる。
ビットフィールドは構造体のメンバ変数としてのみ使用が許されている。

List 1.1 1ビットの変数を使ったプログラム
(PC用のコンパイラでコンパイルしてPC上で実行)

#include<stdio.h>

typedef struct {
    unsigned int test: 1;
} OneBit_t;

int main()
{
    int i;
    OneBit_t x;
    x.test=0;
    for (i=0; i<5; i++) {
        printf("i=%d x.test=%d\n",i,x.test);
        x.test++;
    }
    return 0;
}

実行結果
i=0 x.test=0
i=1 x.test=1
i=2 x.test=0
i=3 x.test=1
i=4 x.test=0

複数の変数の例
変数 test は1ビットの変数
変数 testa は2ビットの変数(とりうる値は0,1,2,3)

List 1.2 1ビットの変数と2ビットの変数を使ったプログラム
(PC用のコンパイラでコンパイルしてPC上で実行)

#include<stdio.h>

typedef struct {
    unsigned int test:  1;
    unsigned int testa: 2;
} OneBit_t;

int main()
{
    int i;
    OneBit_t x;
    x.test=0;
    x.testa=0;
    for (i=0; i<5; i++) {
        printf("i=%d x.test=%d x.testa=%d\n",i,x.test,x.testa);
        x.test++;
        x.testa++;
    }
    return 0;
}

実行結果
i=0 x.test=0 x.testa=0
i=1 x.test=1 x.testa=1
i=2 x.test=0 x.testa=2
i=3 x.test=1 x.testa=3
i=4 x.test=0 x.testa=0

2.共用体(union)

同じメモリを2つ以上の名前で使うことができる。例えば,unsigned long intの変数は4バイトでできているが,同じメモリを,unsigned short int(2バイト)の変数2個,あるいはunsigned char(1バイト)の変数4個として使うことができる。

しかし,多バイト変数をメモリにどのように置くかは,CPU開発者に依存している。WindowsPCなどIntel系のCPUは下位バイトをメモリの若い番地に置くようにしているので,リトルエンディアン(little endian)と呼ばれ,H8のようなMotorola系のCPUは上位バイトをメモリの若い番地に置くようにしているためビッグエンディアン(big endian)と呼ばれている。そのため,同じプログラムでも結果が異なってしまう。

List 2.1 共用体を使ったプログラム(WinPC リトルエンディアン)
(PC用のコンパイラでコンパイルしてPC上で実行)

#include<stdio.h>

typedef union {
    unsigned long int      test;
    unsigned short int test1[2];
    unsigned char      test2[4];
} myUnion_t;

int main()
{
    myUnion_t x;
    x.test=0x12345678;
    printf("%x\n",x.test);
    printf("%x %x\n",x.test1[0], x.test1[1]);
    printf("%x %x %x %x\n",x.test2[0], x.test2[1],x.test2[2], x.test2[3]);
    return 0;
}

実行結果
12345678
5678 1234
78 56 34 12

「x.test=0x12345678;」の実行直後
←メモリの若い番地           大きい番地→

78

56

34

12

List 2.2 共用体を使ったプログラム(H8 ビッグエンディアン)
(H8用クロスコンパイラでコンパイルしてH8上で実行)

#include "h8-01.h"

typedef union {
    unsigned long int      test;
    unsigned short int test1[2];
    unsigned char      test2[4];
} myUnion_t;

int main()
{
    myUnion_t x;
    initSCI1();

    x.test=0x12345678;
    SCI1_printf("%lx\n",x.test);
    SCI1_printf("%x %x\n",x.test1[0], x.test1[1]);
    SCI1_printf("%x %x %x %x\n",x.test2[0], x.test2[1],x.test2[2], x.test2[3]);
    return 0;
}

実行結果
12345678
1234 5678
12 34 56 78

「x.test=0x12345678;」の実行直後
←メモリの若い番地      大きい番地→

12

34

56

78


3.共用体(union)とビットフィールド

共用体とビットフィールドを用いた場合も,エンディアンの違いに気をつけて使わなければならない。

次の例は,1バイト(8ビット)の変数と,2ビット+2ビット+4ビットの変数を共用体としたものである。

List 3.1 共用体とビットフィールドを使ったプログラム(WinPC リトルエンディアン)
(PC用のコンパイラでコンパイルしてPC上で実行)

#include<stdio.h>

typedef struct {
    unsigned int quad  :4;
    unsigned int dual0 :2;
    unsigned int dual1 :2;
} bits_t;

typedef union {
    unsigned char      test;
    bits_t            test1;
} myUnion_t;

int main()
{
    myUnion_t x;
    x.test=0x51;
    printf("%x\n",x.test);
    printf("%x %x %x\n",x.test1.quad, x.test1.dual0, x.test1.dual1);
    x.test1.dual0=3;
    printf("%x\n",x.test);
    printf("%x %x %x\n",x.test1.quad, x.test1.dual0, x.test1.dual1);
    return 0;
}

実行結果
51
1 1 1
71
1 3 1
「x.test=0x51;」の実行直後

x.test

0

1

0

1

0

0

0

1

x.dual1

0

1

x.dual0

0

1

x.quad

0

0

0

1


「x.test1.dual0=3;」の実行直後

x.test

0

1

1

1

0

0

0

1

x.dual1

0

1

x.dual0

1

1

x.quad

0

0

0

1

 

List 3.2 共用体とビットフィールドを使ったプログラム(H8 ビッグエンディアン)
(H8用クロスコンパイラでコンパイルしてH8上で実行)

#include "h8-01.h"

typedef struct {
    unsigned int quad  :4;
    unsigned int dual0 :2;
    unsigned int dual1 :2;
} bits_t;

typedef union {
    unsigned char      test;
    bits_t            test1;
} myUnion_t;

int main()
{
    myUnion_t x;
    initSCI1();

    x.test=0x51;
    SCI1_printf("%x\n",x.test);
    SCI1_printf("%x %x %x\n",x.test1.quad, x.test1.dual0, x.test1.dual1);
    x.test1.dual0=3;
    SCI1_printf("%x\n",x.test);
    SCI1_printf("%x %x %x\n",x.test1.quad, x.test1.dual0, x.test1.dual1);
    return 0;
}

実行結果
51
5 0 1
5d
5 3 1

「x.test=0x51;」の実行直後
x.test

0

1

0

1

0

0

0

1

x.quad

0

1

0

1

x.dual0

0

0

x.dual1

0

1


「x.test1.dual0=3;」の実行直後

x.test

0

1

0

1

1

1

0

1

x.quad

0

1

0

1

x.dual0

1

1

x.dual1

0

1