8 ビットまたは 32 ビットより小さい値を格納できます。それらを構造体 (またはクラス) にパックし、ビット フィールドを使用するだけです。
例えば:
struct example
{
unsigned int a : 3; //<Three bits, can be 0 through 7.
bool b : 1; //<One bit, the stores 0 or 1.
unsigned int c : 10; //<Ten bits, can be 0 through 1023.
unsigned int d : 19; //<19 bits, can be 0 through 524287.
}
ほとんどの場合、コンパイラは、32 ビット プラットフォームで構造体の合計サイズを 32 ビットに切り上げます。もう 1 つの問題は、ご指摘のとおり、値の範囲が 2 乗でない可能性があることです。これでは無駄なスペースができてしまいます。構造体全体を 1 つの数値として読み取ると、入力範囲がすべて 2 の累乗でない場合、設定できない値が見つかります。
興味深いと思われるもう 1 つの機能は、unionです。それらは構造体のように機能しますが、メモリを共有します。したがって、1 つのフィールドに書き込むと、他のフィールドが上書きされます。
スペースが非常に狭く、各ビットを最大限に活用したい場合は、簡単なエンコード方法があります。それぞれ 0 から 5 までの 3 つの数値を格納したいとします。ビット フィールドは無駄です。それぞれ 3 ビットを使用すると、一部の値が無駄になるからです (つまり、6 や 7 を設定できなくても、それらを保管する部屋)。それでは、例を見てみましょう:
//Here are three example values, each can be from 0 to 5:
const int one = 3, two = 4, three = 5;
それらを最も効率的にまとめるには、基数 6 で考える必要があります (各値は 0 ~ 5 であるため)。したがって、可能な限り最小のスペースに詰め込むと、次のようになります。
//This packs all the values into one int, from 0 - 215.
//pack could be any value from 0 - 215. There are no 'wasted' numbers.
int pack = one + (6 * two) + (6 * 6 * three);
基数 6 でエンコードしているように見えますか? 各数値は、6^n のような桁数で乗算されます。n は桁数です (0 から始まります)。
次にデコードします。
const int one = pack % 6;
pack /= 6;
const int two = pack % 6;
pack /= 6;
const int three = pack;
これらのスキームは、人間が入力するためにバーコードまたは英数字シーケンスでいくつかのフィールドをエンコードする必要がある場合に非常に便利です。これらのいくつかの部分的なビットを言うだけで、大きな違いが生じる可能性があります. また、すべてのフィールドが同じ範囲である必要はありません。1 つのフィールドが 0 ~ 7 の場合、適切な場所で 6 ではなく 8 を使用します。すべてのフィールドが同じ範囲である必要はありません。