3

3 つの値のうちの 1 つしか持てない変数を作成する方法はありますか?

3 つの可能な状態のいずれかを返す関数を作成したいのですが、この場合に整数を使用すると、不必要に多くのメモリ領域が必要になると思います。

では、メモリに 3 ビットのみを格納し、関数の戻り値として使用できる変数を作成する方法はありますか? もしそうなら、これをより良いものに書き直す方法を提案してください:

int ReturnOneOfThreeStates(){
     return 0 //Let's say 0=green, 1=red, 2=blue
}
4

5 に答える 5

13

3 つの値のうちの 1 つしか持てない変数を作成する方法はありますか?

列挙型を使用できます:

enum TriState { One, Two, Three };

C++11 では、列挙を表す基になる整数型を指定できます。

enum TriState : char { One, Two, Three};

C++11 のさらなる優れた点は、強く型付けできることです。

enum class TriState : char { One, Two, Three};
于 2013-01-28T21:53:49.107 に答える
1

int唯一の実際の解決策は、 (または他の整数型) に値を格納するサブレンジ クラスを作成し、値を変更する可能性のあるすべての関数に範囲不変条件を適用することです。

space 引数は、そのタイプの大きな配列がある場合にのみ適用されることに注意してください。この場合、MyValueTypeVector値ごとに 2 ビットのみを使用するクラスを作成することができます (最大で 3 つの値がある場合)。大きな配列の外では、値を抽出して挿入するために必要な追加のコードは、値自体よりも多くのスペースを占有します。多くの場合、コンパイラは、int領域をまったく占有しないレジスタに an を配置します。また、大きな配列であっても、(インデックスからの) アクセス時間が桁違いに増加する可能性があることを忘れないでください。

于 2013-01-28T22:01:08.443 に答える
1

答えには 2 つの部分があります。3 つの値のいずれかを返す方法と、そのような複数の値をメモリにパックする方法が必要です。

列挙型の値を使用すると、最初の問題を解決できます。 を定義するenumことで、関数から返される値の宣言範囲を 3 つの値に制限できます。ただし、これではメモリを節約enumできません。s は 8 ビットよりも小さくすることはできません。

メモリを節約するには、小さい値を大きい値にパックする必要があります。これは、パッキングが意味を成すのに十分なサイズの値の配列を宣言する場合にのみ機能します。基本的なアプローチは、必要なビットを切り取り、それらを所定の位置に移動しORて、目的の場所に挿入することです。

2 ビットの数値を使用する方法は次のとおりです。8 ビットの値は、そのような数値を 4 つ格納できます。2 ビット値の配列には、 8Nビット値が必要(N+3)/4です。position の要素xは、x/4バイト内の位置x%4(つまり、、、、0または)に1あります。次のように、目的の 2 ビット要素を取得します。23

int twoBit = (array[x/4] >> (2*(x%4))) & 3;
//            ^^^^^^^^^^     ^  ^^^      ^
//                 |         |   |       +-- Get the last two bits
//                 |         |   +---------- Get sub-element 0..3
//                 |         +-------------- Multiply by 2, because there are 2 bits per subelement
//                 +------------------------ Get the desired 8-bit element

値を設定すると、逆になります。

uint8_t mask = 3 << (2*(x%4));           // Prepare the mask
array[x/4] &= ~mask;                     // Clear out the desired two bits
array[x/4] |= (twoBit & 3) << (2*(x%4)); // OR in the desired bits

配列は必要ないが、複数の 2 ビット値をより大きなstructや a内にパックしたい場合は、 Bit Fieldsclassを使用できます。この場合、データのパックが可能であれば、コンパイラーがすべてのパックを行います。データ メンバーを配置する順序が重要になり、保存するメモリの量が変わる可能性があることに注意してください。

于 2013-01-28T22:07:44.323 に答える
1

はいといいえ。別のクラスまたは構造体の一部である場合、はい、ビットフィールドを使用して効果的な利点を得ることができます。

// bit_fields1.cpp
struct Date
{
   unsigned nWeekDay  : 3;    // 0..7   (3 bits)
   unsigned nMonthDay : 6;    // 0..31  (6 bits)
   unsigned nMonth    : 5;    // 0..12  (5 bits)
   unsigned nYear     : 8;    // 0..100 (8 bits)
};

そして、単一のメンバー ビットフィールド (たとえば、上記のフィールドの 1 つだけ) のみを持つ構造体を返すことができます --- しかし、それがあなたのケースに対応するかどうかはわかりません., 関数の戻り値は、少なくとも 8 ビット レジスタを消費します。気になるターゲットマシンを想像してみてください....

于 2013-01-29T01:40:13.373 に答える
0

ここで 2 つの質問があります。

1つは、あなたが話している値だけを含む関数の引数と戻り値などに関するものです。これらが1文字未満になることはありません。そして、実際には、整数よりも少ないスペースを使用することは、ほとんどの場合、あまり役に立ちません。

もう一つは収納についてです。クラスのメンバーとしてのストレージ、またはこれらの値のある種のコレクション。そこでは、さまざまなテクニックやトリックを使用して、1 バイトもかからないようにすることができます。

実際、それらの大きなコレクションがある場合、または値の範囲が非常に制限された他の多くのタイプと組み合わせてデータ構造にそれらを格納する必要がある場合は、それらにほんの少しの割合を占めることさえできます。これには、スペースを共有するより大きなデータ型から値を抽出するための CPU のトレードオフが伴います。しかし、それは可能です。

ある程度、この狭い範囲の値を他の値と同じ組み合わせで常に渡している場合は、関数の引数の問題をストレージの問題に変えることができます。ただし、引数として直接渡す必要がある場合は、これを行うことはできません。

用途ごとにこの型を定義する方法の例をいくつか簡単に投稿します。

値の受け渡しについては、この回答にそれがあります。を使用しenumます。enumC++11 では、を で可能な限り最小のデータ型として表現することを選択できますenum Foo : char { Val1, Val2, Val3 };

それらの配列全体を保存するには、次の手法を使用できます。

#include <limits>

enum Foo : char { Val1, Val2, Val3 };

template <unsigned int NumFoos>
class FooArray {
   static constexpr unsigned int foos_in_x(unsigned long long x) noexcept(true) {
       return (x > 3) ? (1 + foos_in_x(x / 3)) : 0;
   }
   static constexpr unsigned int foos_per_ll = foos_in_x(::std::numeric_limits<unsigned long long>::max());
   static constexpr unsigned long long array_size() noexcept(true) {
       return (NumFoos + (foos_per_ll - 1)) / foos_per_ll;
   }
   static constexpr unsigned int div_value(unsigned int n) {
       return (n == 0) ? 1 : (3 * div_value(n - 1));
   }

 public:
   explicit FooArray(Foo defaultval = Val1);
   ~FooArray();

   Foo operator[](unsigned long long pos) const {
       const unsigned long long arypos = pos / foos_per_ll;
       const unsigned int fooval = (foovals_[arypos] / div_value(pos % foos_per_ll)) % 3;
       return (fooval == 0) ? Val1 : ((fooval == 1) ? Val2 : Val3);
   }

 private:
   unsigned long long foovals_[array_size()];
};

もちろん、非 constoperator []は、割り当てられている場合に配列内の適切な値をいじる特別な参照型を返す必要があります。その方法がわかりにくい場合は、代入関数がどのように機能するかの例を書くことができます。これは、非 const から::std::bitset型を返す方法に似ています。referenceoperator []

于 2013-01-28T22:07:17.603 に答える