web-dev-qa-db-ja.com

ビットフィールドで列挙型を使用しても安全ですか?

たとえば、次の構造体があります。

typedef struct my_struct{
    unsigned long       a;
    unsigned long       b;
    char*               c;
    unsigned int        d1  :1;
    unsigned int        d2  :4;
    unsigned int        d3  :4;
    unsigned int        d4  :23;
} my_type, *p_type;

フィールドd3は現在、#defineから0x00まで到達する0x0Dsによって定義されています。

実際、d3は列挙型です。だから先に進んで交換したくなる

    unsigned int        d3  :4;

沿って

    my_enum             d3  :4;

これは安全/許可されていますか?

コードはさまざまなものでコンパイルする必要があります

  • コンパイラー(GCC、Visual Studio、組み込みのもの)
  • プラットフォーム(Win32、Linux、組み込みのもの)
  • 構成(Cとしてコンパイル、C++としてコンパイル)

明らかに、d3の定義をそのままにして、コードで列挙型を使用し、それをd3に割り当てるなどすることはできますが、C++では機能しません。

29
eckes

CとC++では答えが異なりますが、これはCでの答えです。

Cでは、ビットフィールドはsigned intunsigned int_Boolおよびintに制限されています。このコンテキストでは、最初の2つのうちのいずれかになります。コンパイラの実装者は、そのリストに好みに合わせて追加できますが、サポートするタイプを文書化する必要があります。

したがって、質問に答えるために、コードがすべてのCコンパイラに移植可能であることを絶対に確認したい場合は、いいえ、enum型を使用することはできません。

現在の標準の対応する段落は次のとおりです。

ビットフィールドは、_Bool、signed int、unsigned int、またはその他の実装定義の型の修飾または非修飾バージョンである型を持つ必要があります。アトミックタイプが許可されるかどうかは、実装によって定義されます。

13
Jens Gustedt

これは、標準をサポートするすべてのC++コンパイラで許可されています。

C++ 03標準9.6/3

ビットフィールドは、整数型または列挙型(3.9.1)を持つ必要があります。プレーン(明示的に符号付きでも符号なしでもない)char、short、int、またはlongビットフィールドが符号付きか符号なしかは実装定義です。

C++ 03標準9.6/4

列挙型の値が同じ列挙型のビットフィールドに格納されていて、ビットフィールドのビット数がその列挙型のすべての値、元の列挙型の値、および値を保持するのに十分な数である場合ビットフィールドのは等しく比較されます。

enum BOOL { f=0, t=1 };

struct A {
    BOOL b:1;
};

void f() {
    A a;
    a.b = t;
    a.b == t // shall yield true
}

しかし、列挙型に符号なしの基になる型があるとは考えられません。

C++ 03標準7.2/5

列挙型の基になる型は、列挙型で定義されたすべての列挙型値を表すことができる整数型です。列挙型の基礎となる型として使用される整数型は実装によって定義されます。ただし、列挙子の値がintまたはunsigned intに収まらない場合を除き、基礎となる型はintより大きくてはなりません。

22
ForEveR

番号。

ビットフィールドは、コンパイラ間で大幅に異なる方法で実装されます。ゼロと1の2つの値でビットフィールドを定義し、列挙型のビットフィールドを作成しようとすると、次の問題が発生する可能性があります。

ビットフィールドはgccとclangで符号なしになりますが、VC++で符号付きになります。つまり、0と1を格納するには、2ビットのビットフィールドが必要です(1ビットの符号付きビットフィールドは、0と負の1のみを格納できます)。

次に、パッキングについて心配する必要があります。 VC++は、サイズが一致する場合にのみ、隣接するビットフィールドを同じバッキングストアにパックします。 gccとclangのルールはわかりませんが、VC++の場合、ビットフィールドのデフォルトのバッキングストアはintです。したがって、たとえば、boolとenumが混在する一連のビットフィールドは、VC++では非常に不十分にパックされます。

これは、C++ 11型の列挙型で解決することができます。

列挙型Foo:unsigned char {1、two};

しかし、これを1ビットビットフィールドで使用すると、gccは文句を言います。

警告:「bitfieldTest :: g」は小さすぎて「enum Foo」のすべての値を保持できません[デフォルトで有効]

勝てないようです。

6
Bruce Dawson