はかせのラボ

私の頭の中を書いていく雑記ブログです

C++ ビットフィールド ~ビット単位でメモリ管理を行う~

あいさつ

どうも、hakaseです。
最近は遊戯王マスターデュエルにお熱でずっとランクマにこもっています。
そのうちマスターデュエルの記事も上げだすかもしれません。
いつぞやのポケモンみたく、
またなんかゲームの記事上げてんなくらいに思って置いてください。

ただ今回はちゃんと技術記事です。
(技術系とそれ以外でブログ分けることも検討中・・
ただ管理がめんどいので結局やんない可能性高)

今回は表題にもある「ビットフィールド」という機能の紹介です。

ビットフィールド

端的に言うと構造体の各変数に割り当てるビット数を指定できる機能です。
細かい能書きの前にさっさとコードをば

// ビットフィールド無
struct Data
{
    unsigned int dataA;
    unsigned int dataB;
    unsigned int reserve;
}

// ビットフィールド有
struct Data2
{
    unsigned int dataA : 4;
    unsigned int dataB : 4;
    unsigned int reserve : 24; // 別に必須ではない
}

違いがわかりやすいように
ビットフィールド無(Data)/有(Data2)でそれぞれ書いてみました。

それぞれの違いは変数の後に
「コロン+数値」のよくわからんものがついてるかぐらいです。

ではここで唐突ですが問題です。
DataとData2のサイズはどれくらいでしょう?
ここでのサイズは素直にsizeofで得られる数値のことを指します。

コードに起こすとこんな感じのコードで得られる出力

auto size = sizeof(Data);
auto size2 = sizeof(Data2);
std::cout << size << "\n" << size2;

特にシンキングタイム用に結果を隠したり
長~い空白を置くこともなく結果です。
f:id:hakase0274:20220321004915p:plain

Data : 12byte
Data2 : 4byte
約3倍の差ですね。

Dataはunsigned int(以下uint)の変数を3つ持っています。
みなさんご存知uintは4byteの変数です。
それが3つあるので当然構造体のサイズは12byteになります。

Data2も持っている変数の型と数は同じです。
ただ各変数に割り当てるビット数を指定しています。
上から順に
4bit
4bit
24bit
以上の計32bit=4byteです。
そのため実際に割り当てられるサイズは4byteとなっているわけです。

ちなみにコメントで必須ではないと書いたreserveがなけりゃ
8bit=1byteになんじゃないのと思いませんか?
まぁそうなる環境もあると思いますが、
我々が良く使うvsのコンパイラは基本32bit単位で割り振るようなので
仮に8bitの構造体だろうが問答無用で32bit振られます。
コメントアウトするなりしてreserveを消して試せばその環境でどうなるかわかります。

なのであっても無くても変わりませんが、
「この分今余ってるから追加するならここから取ってってね!」
ぐらいの温度感でこの変数は置いてます。

割り振るビット数以上の値入れようとしたら?

さてビット数を指定できるということは
実質的に値制限をかけているようなものになります。
では気になるのはオーバーフローした際はどうなるのか?

さっきから出してる4bitでとれる値範囲は
0~15で16以上の値は入らないはずです。

ということで16を突っ込んでみましょう。

auto data2 = new Data2();
data2->dataA = 16;
std::cout << data2->dataA;
delete data2;

結果
(vs2019のデバッガでの表示)
f:id:hakase0274:20220321012733p:plain
ちゃんと0ですし、
次の変数にメモリがあふれているようなこともないです。
うっかりビット数以上の値を入れようとしても
大事故になったりはしなさそうですね。

実際使うん?

近年のマシンはみんなメモリが潤沢なので
こんなビット単位でメモリを管理する必要があるのかという
疑問が当然出てくると思います。

ぶっちゃけよくあるソフト開発とかであれば
無縁かなぁと思います。
PCやiOSAndroidとかでビット単位でメモリ切り詰めなきゃ
メモリ足りないなんて事態そう起こらないでしょうし、
もし起こってるんならそれは多分別のところで
なんかやばいことが起こってる可能性が高いです。

あとビット単位の管理になると
コンパイラとか環境の影響もモロに受けますし、
その影響で移植性も低くなります。

じゃあ必要な場面ないやんけと思うじゃないですか。
あるんですよ。必要な場面が。
ゲーム開発(コンシューマ機)っていうんですけど。

この分野はなぜかメモリがいくら増えても
メモリが足りないとかいうバグみたいな現象が常日頃起きるので
詰めれる場所は詰めておきたいんですよ。
そうたとえ1bitであっても。
1bitを8回詰めれば1byte空きますからね。

あとがき

今回はビットフィールドの紹介でした。
実は今回はあいさつでも書いた
マスターデュエルの記事書こうと思ってたんですが、
ビットフィールド紹介を実は書いてなかったという
衝撃の事実に気づいてしまい急遽こっちに舵を取った形です。

いやぁ・・
書いてたつもりだったんですがねぇ・・
まぁ気づいて書いたのだから結果オーライ。
記事書けてエライ。

それでは今回はこの辺でノシ