この共用体のサイズがビットフィールドで2である理由
-
08-07-2019 - |
質問
charが1バイトかかるWindowsでターボCに取り組んでいますが、現在の問題は以下のユニオンにあります。
union a
{
unsigned char c:2;
}b;
void main()
{
printf("%d",sizeof(b)); \\or even sizeof(union a)
}
このプログラムは、出力を2として出力しますが、unionは1バイトのみを使用する必要があります。なぜそうなのですか?
構造体の場合、1バイトを与えることは問題ありませんが、この共用体は不適切に機能しています。
もう1つこれらのビットフィールドにアクセスする方法
scanf("%d",&b.c); //even scanf("%x",b.c);
ビットのアドレスを取得できないため、機能していません。したがって、以下のような別の変数を使用する必要があります
int x;
scanf("%d",&x);
b.c=x;
それを避けられますか??他の方法はありますか?
解決
Turbo Cは、2バイトのワード境界を持つ8086マイクロプロセッサに基づいています。通常、アトミックな読み取りと書き込みはCPUのアーキテクチャにバインドされているため、コンパイラーはデータ構造を調整するためにいくつかのスラックバイトを追加しています。
#pragma pack(1)
を呼び出すと、無効にすることができますが、Turbo Cで動作するかどうかはわかりません。
他のヒント
コンパイラは、構造体と共用体にパディングを追加できますが、完全に許可されている1バイトの構造体を取得できる場合、あなたの共用体が2バイトサイズに切り上げられることは少し驚くべきことです。
2番目の質問への回答:いいえ、回避することはできません。ビットフィールドは構造体のパッキングの最適化であり、パフォーマンスと利便性のペナルティは、ビットフィールドのメンバーが個別にアドレス指定できないことです。
ユニオンを正確に最小サイズにする必要があるという要件がどこにあるかわかりません。オブジェクトは少なくともそのメンバーと同じ大きさでなければなりませんが、それは下限のみです。
ビットフィールドのアドレスを取得することはできません。そのタイプは何でしょうか? int *にはできません。 scanf(%d)は、渡されたint *にsizeof(int)* CHAR_BITビットを書き込みます。これは2ビット以上を書き込みますが、そのスペースはありません。
標準には、構造体の最初のメンバーの前にパディングがないことを示す段落があります。しかし、それは労働組合について明示的に言っていない。サイズの違いは、ユニオンを2バイト境界で整列させたいために発生する可能性がありますが、構造体の最初のメンバーの前にパディングできないため、構造体には1バイトの整列があります。また、ユニオンには異なるタイプのより多くのメンバーが含まれることがあり、これによりユニオンの必要なアライメントが広がる可能性があることに注意してください。コンパイラーに少なくとも2バイトのアライメントを与える理由がある可能性があります。たとえば、ユニオンの必要な適格性に従って処理しなければならないコードを容易にするためです。
とにかく、ユニオンを正確に1バイトにする必要はありません。すべてのメンバーのための場所が必要です。
2番目の質問について、C標準は次のように述べています。
単項&のオペランド演算子は関数
ではないオブジェクトを指定する指定子または左辺値
ビットフィールドであり、レジスタストレージクラスで宣言されていません
指定子。
したがって、最善の策はintを使用する方法を使用することです。コードを中括弧で囲むことができるため、一時変数はローカルに保持されます。
void func(void) { struct bits f; { int x; scanf("%d", &x); f.bitfield = x; } /* ... */ }
答えには多くの誤った情報があるので、明確にします。 2つの理由のいずれかが原因である可能性があります(コンパイラーについて詳しくない)。
-
ビットフィールドストレージユニットは2です。
-
位置合わせはワード(2バイト)境界に強制されます。
ビットフィールドストレージユニットを宣言された「ベース」のサイズとして使用することは一般的な拡張であるため、最初のケースではないでしょう。タイプ。この場合、タイプはcharで、サイズは常に1です。
[標準では、int型またはunsigned int型のビットフィールドと「ストレージユニット」のみを宣言できます。ビットフィールドがグループ化される場所は固定されています(通常はintと同じサイズです)。 1ビットのビットフィールドでも1つのストレージユニットを使用します。]
2番目のケースでは、Cコンパイラが一般的に #pragma pack
を実装して、アライメントの制御を許可します。デフォルトのパッキングは2であると思われます。その場合、結合の最後にパッドバイトが追加されます。これを回避する方法は次を使用することです:
#pragma pack(1)
また、 #pragma pack()
を使用してデフォルトに戻す必要があります(または、コンパイラでサポートされている場合はpushおよびpop引数を使用することをお勧めします)。
コンパイラーの処理に我慢しなければならないと言ったすべての回答者にとって、これはCの精神に反します。ビットフィールドを使用して、ファイル形式やハードウェアマッピングなどを制御します。
もちろん、これは実装によってバイトオーダー、ビットフィールドストレージユニットにビットが追加される順序(上または下)、ストレージユニットのサイズ、デフォルトのアライメントなどが異なるため、移植性が非常に低くなります。
2番目の質問に関しては、問題は見当たりませんが、問題があるため scanf
は使用しません。
「構造体またはユニオンの終わりに無名のパディング」が存在する可能性があることに加えて、コンパイラは、ビットを保持するのに十分な大きさのアドレス可能なストレージユニットにビットフィールドを配置することを許可されています。フィールド"。 (両方の引用はC90標準に由来します-C99標準に類似した、しかし異なる表現があります。)
また、標準では、「ビットフィールドの型はint、unsigned int、またはsigned int」の修飾または非修飾バージョンであるため、char型のビットフィールドは非-標準。
ビットフィールドの動作は不特定のコンパイラ実装の詳細に非常に依存しているため(ビットフィールドには他にも移植性のない問題がいくつかあります)、それらを使用することはほとんど常に悪い考えです。特に、ファイル形式、ネットワークプロトコル、またはハードウェアレジスタでビットフィールドをモデル化しようとする場合、それらは悪い考えです。
一般に、ビットフィールドは避けるべきです 他のマニフェスト定数を使用します (enumsまたは何でも)明示的なビットで アクセスするためのマスキングとシフト フィールド内の「サブフィールド」。
ビットフィールドが必要な理由の1つを次に示します。 避けてください-それらはあまり移植性がありません 同じ場合でもコンパイラ間 プラットフォーム。 C99標準から (C90にも同様の文言があります 標準):
実装は、 十分に大きいアドレス可能なストレージユニット ビットフィールドを保持します。十分なスペースがある場合 残っているビットフィールドはすぐに の別のビットフィールドに続く 構造はに詰められるものとします 同じユニットの隣接ビット。もし 十分なスペースが残っているかどうか、 適合しないビットフィールドが配置されます 次のユニットまたはオーバーラップに 隣接ユニットは 実装定義。の順 ユニット内のビットフィールドの割り当て (高位から低位または低位へ 高次へ)は 実装定義。アライメント アドレス可能なストレージユニットの 指定なし。
少しかどうかは保証できません フィールドはint境界を「スパン」するか、 そうではなく、あなたが指定することはできません ビットフィールドは、 intまたはintのハイエンド(これ かどうかの独立 プロセッサがビッグエンディアンまたは リトルエンディアン)。