48章 ビットフィールド

http://www.geocities.jp/ky_webid/c/048.html

前回の記事でに共用体について調べてたときにビットフィールドについて少し書いてる記事を読んだのでなんとなくわかります。

あるデータを1ビット単位で操作できるようにするための構文です。

問題1

1ビットのビットフィールドを1つ持つだけの構造体を作り、そのサイズをsizeof演算子を使って表示させてみて下さい。結果はどうなるでしょう。

試す問題ですね。さっそく表示させてみましょう。

int main (int argc,char *argv[]) {
    struct {
        unsigned int num : 1;
    }s;
    
    printf("%d",sizeof(s));
    return 0;
}
$ main
4

4バイトと表示されました。指定しているサイズは1ビットだけのはずなんですが、確保されるサイズはintの4バイト分になるんですね。

答え合わせ

出力結果は、環境によって異なります。小さい領域しか取っていないようでも、実際にはある程度の大きさのサイズの領域が取られます。例えば1バイト単位とか、4バイト単位とかというようになります。この決められた大きさをアラインメントと呼びます。

環境によって異なるらしい。こういうのをアライメントって言うんだって。

問題2

次のプログラムの実行結果を考えてみて下さい。

int main(void){
    struct STRUCT
    {
        unsigned num1 : 4;
        signed   num2 : 4;
    }test;
    int i;
    
    test.num1 = 0;
    test.num2 = 0;
    
    for(i=0; i<100; ++i)
    {
        printf( "%2d %2d\n", test.num1, test.num2 );
        ++test.num1;
        ++test.num2;
    }

    return 0;
}

考える問題ですね。さっそく考えてみます。

num1、num2ともに4ビット分しか表現できないように設定されています。

なので4ビットで表現できる範囲を超えた場合、切り捨てられてまた0からのカウントになるはずです。

そしてsignedとunsignedとの違いにより、signedの場合はunsignedよりも半分の位置からマイナス値になるんじゃないかと思われます。

答え合わせ

あってましたね。具体的にどの数値までかはちょっとわからなかったんですが、予想は的中しました。

何故15までなのか、また調査が必要です。

問題3

ビットフィールドを使って、2進数の01100を2の補数に変換するプログラムを作って下さい。

2の補数ですか。説明はありましたが計算が難しそうです。

union uBIT {
    int num : 5;
    struct {
        unsigned int b1 : 1;
        unsigned int b2 : 1;
        unsigned int b3 : 1;
        unsigned int b4 : 1;
        unsigned int b5 : 1;
    }bit;
};

void bit (union uBIT *u) {
    // ビットの反転処理
    if ( u->bit.b1 == 0 ) {
        u->bit.b1 = 1;
    }
    else {
        u->bit.b1 = 0;
    }
    if ( u->bit.b2 == 0 ) {
        u->bit.b2 = 1;
    }
    else {
        u->bit.b2 = 0;
    }
    if ( u->bit.b3 == 0 ) {
        u->bit.b3 = 1;
    }
    else {
        u->bit.b3 = 0;
    }
    if ( u->bit.b4 == 0 ) {
        u->bit.b4 = 1;
    }
    else {
        u->bit.b4 = 0;
    }
    if ( u->bit.b5 == 0 ) {
        u->bit.b5 = 1;
    }
    else {
        u->bit.b5 = 0;
    }
    
    // +1する処理
    if ( u->bit.b1 == 1 ) {
        if ( u->bit.b2 == 1 ) {
            if ( u->bit.b3 == 1 ) {
                if ( u->bit.b4 == 1 ) {
                    if ( u->bit.b5 == 1 ) {
                        u->bit.b5 = 0;
                    }
                    else {
                        u->bit.b5 = 1;
                    }
                    u->bit.b4 = 0;
                }
                else {
                    u->bit.b4 = 1;
                }
                u->bit.b3 = 0;
            }
            else {
                u->bit.b3 = 1;
            }
            u->bit.b2 = 0;
        }
        else {
            u->bit.b2 = 1;
        }
        u->bit.b1 = 0;
    }
    else {
        u->bit.b1 = 1;
    }
}

int main (int argc,char *argv[]) {
    union uBIT u;
    int i;
    
    for(i=0;i<20;++i) {
        u.num = i;
        printf("%d => ",u.num);
        bit(&u);
        printf("%d\n",u.num);
    }
    
    return 0;
}
$ main
0 => 0
1 => -1
2 => -2
3 => -3
4 => -4
5 => -5
6 => -6
7 => -7
8 => -8
9 => -9
10 => -10
11 => -11
12 => -12
13 => -13
14 => -14
15 => -15
-16 => -16
-15 => 15
-14 => 14
-13 => 13

要件満たしてるかな・・・?ぶっちゃけもの凄いコードになってしまいました。わかってます。正直このコードはないなと思いながらも他に思いつかなかったのでこんな実装になってしまいました。

答え合わせ

あー、そっかぁ。1ビットのデータなので0と1しか表現できないからインクリメントすれば0なら1に、1なら0になるので辻褄が合うわけなんですね。こりゃ一本取られました。

でもそれ以外の2の補数の考えたとしてはあってたので今回はまあ良しとします。




というわけでビットフィールドの話でした。ちょっといきなり難易度が上がって来た感じはするんですがまだまだ頑張れそうです。(大丈夫かな・・・)