組み込みC/C++

C/C++リテラシー向上のためのページ

ビットフィールドを掘り下げる②

ビットフィールドを掘り下げる①では主にビットフィールドで宣言した構造体のサイズに注目しました。今回は符号修飾 signed, unsigned, 符号なしに注目したいと思います。

 struct StBit{
    unsigned char foo: 4;
    unsigned char bar: 4;
};

通常、上記のようにビットフィールドのメンバ変数はunsigned で宣言します。ではこれを、

 struct StBit{
    unsigned char foo: 4;
    signed char bar: 4;
};

 としたらどうなるでしょうか?結論から言うと、メンバ変数barは4bitの中でちゃんとsingedとして動作します。符号bitありという事ですからsinged char bar: 4; で表現できる数は -8 ~ 7です。一方unsigned char foo :4; で表現できる数は 0 ~ 15 になります。

ではテストプログラムを書いて確認してみましょう。

 

#include <stdio.h>
struct StBit{
    unsigned char foo: 4;
    signed char bar: 4;
};

int _tmain(int argc, _TCHAR* argv[])
{
	StBit stBit;
	stBit.foo = 15;
	stBit.bar = 15;

	printf("sum = %d\n", stBit.foo + stBit.bar );

	return 0;
}

 このテストプログラムのsumの値は何になりますか? 15は0b1111ですからfooにもbarにも0b1111が代入されます。しかしbarはsignedですから最上位bitが立っている場合、マイナスをつけた2の補数で表されます。この場合0b1111 の2の補数なので0b0001(ビット反転して1足す) なのでシステム的には-1という認識をします。よって15+(-1)となりコンソールには sum = 14 と表示されます。なお、signed char bar : 1; とした場合bar = 1は-1と認識されます(1の2の補数は1)基本的にビットフィールドでsignedにする事なんてないかと思うのでunsignedをつけるのを標準化しているわけです。そうでなければ演算をした場合に思わぬバグが出る可能性があります。最後に

 struct StBit{
    char foo: 4;
    char bar: 4;
};

これだと、fooとbarの符号はどうなりますか?これは何度か説明した通り、システム依存になります。Visual Studioではcharが標準でsignedになるので、やっぱりunsignedをつけておかないと演算で失敗する可能性があります。

 

まとめ

・ビットフィールドにはunsignedをつける。