4

なぜビットフィールドはunions/structsで機能するのに、やのような正規変数では機能しないのだろうintshort
これは機能します:

struct foo {
    int bar : 10;
};

しかし、これは失敗します:

int bar : 10; // "Expected ';' at end of declaration"

この機能がユニオン/構造体でのみ使用可能であり、変数では使用できないのはなぜですか?技術的には同じではないですか?


編集:

許可されれば、たとえばstruct/unionメンバーを毎回使用せずに3バイトの変数を作成できます。これは私が構造体でそれを行う方法です:

struct int24_t {
    int x : 24 __attribute__((packed));
};

struct int24_t var; // sizeof(var) is now 3
// access the value would be easier:
var.x = 123;
4

5 に答える 5

7

これは主観的な質問です。「なぜスペックはこれを言っているのですか?」しかし、私はそれに私のショットを与えます。

関数内の変数は通常、他の期間(静的期間、スレッド期間、および割り当てられた期間)のいずれかとは対照的に、「自動」ストレージを備えています。

構造体では、オブジェクトのメモリレイアウトを明示的に定義しています。ただし、関数では、コンパイラーは不特定の方法で変数にストレージを自動的に割り当てます。xここに質問があります:スタック上で何バイトを占めますか?

// sizeof(unsigned) == 4
unsigned x;

4バイトを占めるか、8、12、または0を占めるか、同時に3つの異なるレジスター、またはスタックとレジスターに配置されるか、スタック上で4つの場所を取得する可能性があります。 。

重要なのは、コンパイラがあなたに代わって割り当てを行っているということです。スタックのレイアウトを行っていないため、ビット幅を指定しないでください。

拡張された議論:ビットフィールドは実際には少し特別です。仕様では、隣接するビットフィールドが同じストレージユニットにパックされると規定されています。ビットフィールドは実際にはオブジェクトではありません。

  1. sizeof()ビットフィールドはできません。

  2. malloc()ビットフィールドはできません。

  3. &addressofビットフィールドはできません。

これらはすべて、Cのオブジェクトで実行できますが、ビットフィールドでは実行できません。ビットフィールドは、構造物のためだけに作られた特別なものであり、他のどこにもありません。

についてint24_t(更新):一部のアーキテクチャでは機能しますが、他のアーキテクチャでは機能しません。少しでも持ち運びできません。

typedef struct {
    int x : 24 __attribute__((packed));
} int24_t;

Linux ELF / x64、OS X / x86、OS X / x64 、sizeof(int24_t) == 3。しかし、OS X / PowerPCでは、sizeof(int24_t) == 4

GCCがロード用に生成するコードint24_tは、基本的にこれと同等であることに注意してください。

int result = (((char *) ptr)[0] << 16) |
             (((unsigned char *) ptr)[1] << 8) |
             ((unsigned char *)ptr)[2];

単一の値をロードするための、x64での9つの命令です。

于 2012-12-19T18:24:54.677 に答える
3

構造体またはユニオンのメンバーは、保管場所間に関係があります。レイアウトに厳しい制約があるため、コンパイラはスペースを節約するためにそれらを巧妙な方法で並べ替えたりパックしたりすることはできません。基本的に、コンパイラが構造をレイアウトする際に持つ唯一の自由は、整列に必要な量を超えて余分なパディングを追加する自由です。ビットフィールドを使用すると、(1)これらのメンバーのアドレスは不要であり、(2)特定の制限された範囲外の値を格納する必要がないことを約束することにより、コンパイラーに情報を密にパックする自由を手動で与えることができます。

構造体のメンバーではなく個々の変数について話している場合、抽象マシンでは、それらは保管場所の間に関係がありません。それらが関数内のローカル自動変数であり、それらのアドレスが取得されない場合、コンパイラーはそれらをレジスターに保持したり、メモリーにパックしたりすることができます。このようなヒントをコンパイラーに手動で提供することには、ほとんどまたはまったくメリットがありません。

于 2012-12-19T19:06:32.980 に答える
1

意味がないからです。ビットフィールド宣言は、のフィールド間でビットを共有および再編成するために使用されますstruct。メンバーがなく、定数サイズ(実装定義)の変数が1つしかない場合、たとえば、charほぼ確実に8ビット幅のを1ビットまたは12ビット変数として宣言することは矛盾します。

于 2012-12-19T18:21:59.417 に答える
0

4つの2ビットビットフィールドを1つのバイトに結合する構造体がある場合QBLOB、その構造体を使用するたびに、タイプの4つのフィールドを単純に含む構造体と比較して3バイトの節約になりますunsigned char。配列を宣言するQBLOB myArray[1000000]と、そのような配列は1,000,000バイトしかかかりません。QBLOBが4つのunsigned charフィールドを持つ構造体であった場合、さらに3,000,000バイトが必要になります。したがって、ビットフィールドを使用する機能は、メモリを大幅に節約できる可能性があります。

対照的に、ほとんどのアーキテクチャでは、単純な変数を最適なサイズのビットフィールドタイプとして宣言すると、最小の適切な標準整数型として宣言する場合と比較して、最大15ビットを節約できます。通常、ビットフィールドにアクセスするには、標準の整数型の変数にアクセスするよりも多くのコードが必要になるため、個々の変数をビットフィールドとして宣言すると利点が得られる場合はほとんどありません。

ただし、この原則には1つの注目すべき例外があります。一部のアーキテクチャには、バイトの読み取りと書き込みよりもさらに効率的に個々のビットを設定、クリア、およびテストできる機能が含まれています。このようなアーキテクチャの一部のコンパイラには型が含まれておりbit、その型の8つの変数をストレージの各バイトにパックします。このような変数は、静的スコープまたはグローバルスコープに制限されることがよくあります。これは、それらを処理する特殊な命令が特定のメモリ領域の使用に制限される場合があるためです(リンカは、このような変数が必要な場所に配置されるようにすることができます)。

于 2012-12-19T20:14:26.110 に答える
0

すべてのオブジェクトは1つ以上の連続したバイトまたはワードを占有する必要がありますが、ビットフィールドはオブジェクトではありません。これは、単語のビットをマスクするためのユーザーフレンドリーな方法です。structビットフィールドを含むものは、整数のバイトまたはワードを占める必要があります。ビットフィールドサイズが完全なワードにならない場合に備えて、コンパイラは必要なパディングを追加するだけです。

構造体(AFAIK)の外部でビットフィールドを定義するためにC構文を拡張できなかった技術的な理由はありませんが、関連する作業の量については疑わしい有用性があります。

于 2012-12-19T20:30:08.833 に答える