4

C++ でのメモリ管理について詳しく学ぶために、6 つの可能な値のうちの 1 つを保持できる独自のデータ型を定義したいと考えています。数値では、0 から 5 までを保持できるようにしたいと考えています。また、データ型はできるだけメモリを消費しないようにする必要があります。

これを達成する方法がわかりません。まず、すべてのフィールドに値が定義された列挙型を試しました。私の知る限り、値は16進数であるため、1つの「16ビット」で0から15を格納できるはずです。間違っていなければ、char は 0 から 255 までを保持します。

#include <iostream>

enum Foo
{
    a = 0x0, 
    b = 0x1,
    c = 0x2,
    d = 0x3,
    e = 0x4,
    f = 0x5,
};

int main()
{
    Foo myfoo = a;
    char mychar = 'a';

    std::cout << sizeof(myfoo); // prints 4
    std::cout << sizeof(mychar); // prints 1

    return 1;
}

私は明らかに何かを誤解していますが、何がわからないので、SOに目を向けます。:)

また、この記事を書いているときに、語彙の一部が明らかに不足していることに気付きました。この投稿をコミュニティ ウィキにしました。すべての正しい単語を学習できるように編集してください。

4

10 に答える 10

10

Acharは可能な最小の型です。

このような 3 ビット値が 1 つの場所に複数必要であることがわかっている場合は、ビットフィールド構文の構造体を使用できます。

struct foo {
  unsigned int val1:3;
  unsigned int val2:3;
};

したがって、1バイト以内に2つ取得します。理論的には、10 個のそのようなフィールドを 32 ビットの「int」値にパックできます。

于 2009-04-11T14:57:45.380 に答える
3

C ++ 0xには、基になるデータ型(例ではchar)を指定できる強い型の列挙型が含まれますが、現在のC++はこれをサポートしていません。ここでのcharの使用についての標準は明確ではありませんが(例はint、short、longを使用しています)、基礎となる整数型について言及しており、charも含まれます。

今日の時点で、問題のクラスを作成するというNeil Butterworthの答えは、値にシンボリック名が必要な場合はネストされた列挙を含むように拡張できるため、最も洗練されているようです。

于 2009-04-11T16:43:54.860 に答える
2

C++ は、バイトより小さいメモリ単位を表現しません。一度に 1 つずつ作成する場合は、それが最善の方法です。あなた自身の例はうまくいきます。少しだけ取得する必要がある場合は、Alnitak が提案するようにビット フィールドを使用できます。一度に 1 つずつ割り当てることを計画している場合は、さらに悪い結果になります。ほとんどのアーキテクチャではページ サイズ単位が割り当てられ、16 バイトが一般的です。

もう 1 つの選択肢は、入札を行うために std::bitset をラップすることです。このような値が多数必要な場合は、8 ごとに約 1 ビットしか使用しないため、スペースはほとんど無駄になりません。

問題を基数 6 で表される数値として考え、その数値を基数 2 に変換する場合、おそらく無制限の精度整数 (GMP など) を使用すると、ビットをまったく無駄にしません。

もちろん、これは、値が均一でランダムに分布していることを前提としています。それらが別のディストリビューションに従っている場合は、gzip のようなものを使用して、最初の例を一般的に圧縮することをお勧めします。

于 2009-04-11T15:06:24.250 に答える
2

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 を使用します。すべてのフィールドが同じ範囲である必要はありません。

于 2009-04-11T22:26:39.980 に答える
1

使用できる最小サイズ - 1 バイト。

ただし、列挙値のグループを使用する場合 (ファイルへの書き込みまたはコンテナーへの保存など)、このグループを値ごとに 3 ビットでパックできます。

于 2009-04-11T15:00:43.423 に答える
1

最良の解決策は、char を使用して実装された独自の型を作成することです。これは sizeof(MyType) == 1 である必要がありますが、これは保証されていません。

#include <iostream>
using namespace std;

class MyType {

    public:

        MyType( int a ) : val( a ) {
            if ( val < 0 || val > 6 ) {
                throw( "bad value" );
            }
        }

        int Value() const {
            return val;
        }

    private:

        char val;
};

int main() {

    MyType v( 2 );
    cout << sizeof(v) << endl;
    cout << v.Value() << endl;
}
于 2009-04-11T15:02:19.720 に答える
1

列挙型の値を列挙する必要はありません:

enum Foo
{
    a, 
    b,
    c,
    d,
    e,
    f,
};
Foo myfoo = a;

Fooこれは のエイリアスでint、マシンでは 4 バイトかかります。

最小の型はcharで、ターゲット マシンでアドレス可能な最小のデータとして定義されます。CHAR_BITマクロは a のビット数を生成し、でchar定義されていlimits.hます。

[編集]

一般的に言えば、そのような質問を自問するべきではないことに注意してください。[unsigned] int大量のメモリを割り当てる場合を除いて、十分な場合は常に使用してください (例: int[100*1024]vs 。代わりにchar[100*1024]使用を検討してください)。std::vector

于 2009-04-11T15:03:21.933 に答える
1

奇妙なサイズの値をビットフィールドにパックすると、アーキテクチャがビットレベルの操作をサポートしていないため、パフォーマンスが大幅に低下する可能性があります (したがって、操作ごとに複数のプロセッサ命令が必要です)。そのような型を実装する前に、できるだけ少ないスペースを使用することが本当に必要なのか、それとも時期尚早の最適化であるプログラミングの重大な罪を犯しているのかを自問してください。せいぜい、何らかの理由で最後のバイトをすべてスクイーズする必要がある場合は、バッキングストアを透過的に変更できるクラスに値をカプセル化します。

于 2009-04-11T22:36:22.703 に答える
0

列挙型のサイズは int と同じになるように定義されています。ただし、コンパイラによっては、より小さな列挙型を作成するオプションがある場合があります。たとえば、GCC では次のように宣言できます。

enum Foo {
    a, b, c, d, e, f
}
__attribute__((__packed__));

さて、 sizeof(Foo) == 1.

于 2009-04-11T17:02:18.200 に答える
0

unsigned char を使用できます。おそらくそれをBYTEにtypedefします。1 バイトだけを占有します。

于 2009-04-11T14:56:32.823 に答える