web-dev-qa-db-ja.com

ビットフィールドに値を代入しても同じ値が返されないのはなぜですか。

私は このQuoraの投稿 に以下のコードを見ました。

#include <stdio.h>

struct mystruct { int enabled:1; };
int main()
{
  struct mystruct s;
  s.enabled = 1;
  if(s.enabled == 1)
    printf("Is enabled\n"); // --> we think this to be printed
  else
    printf("Is disabled !!\n");
}

C&C++の両方で、コードの出力は予期しないであり、

無効になっています !!

「符号ビット」に関する説明がそのポストに与えられているが、私は私たちが何かを設定し、それがあるとして、それが反映されない可能性があり、どのように理解することができません。

誰かがより精巧な説明を与えることはできますか?


cc ++ のタグは両方とも必須です。なぜなら、それらの標準はビットフィールドを記述するためにわずかに異なるからです。 C specification および C++ specification の回答を参照してください。

95
iammilind

ビットフィールドは、標準では非常に不十分に定義されています。このコードをstruct mystruct {int enabled:1;};とすると、 don't know:

  • これがどのくらいのスペースを占有するか - パディングビット/バイトがあり、それらがメモリ内のどこにあるか。
  • ビットがメモリのどこにあるか。定義されておらず、エンディアンにも依存します。
  • int:nビットフィールドが符号付きまたは符号なしと見なされるかどうか。

最後の部分に関して、C17 6.7.2.1/10はこう言います:

ビットフィールドは、指定されたビット数で構成される符号付きまたは符号なし整数型を持つものとして解釈されます。 125)

上記を説明する非規範的な注意:

125) 上の6.7.2で指定されているように、使用される実際の型指定子がintまたはintとして定義されたtypedef-nameである場合、それはビットフィールドが符号付きか符号なしかにかかわらず実装定義です。

ビットフィールドがsigned intと見なされ、サイズが1である場合、データ用のスペースはなく、符号ビット用のみです。これがあなたのプログラムがいくつかのコンパイラで奇妙な結果を与えるかもしれない理由です。

いい練習:

  • いかなる目的にもビットフィールドを使用しないでください。
  • いかなる形式のビット操作にも符号付きint型を使用しないでください。
76
Lundin

私は理解することができません、我々が何かを設定して、そしてそれがそれが現状のように現れないことがどのように可能であることが可能であるか。

なぜそれがコンパイルするのに対してあなたにエラーを与えるのか尋ねていますか?

はい、理想的にはエラーが発生します。あなたのコンパイラの警告を使うなら、それはそうなります。 GCCでは、-Werror -Wall -pedanticで:

main.cpp: In function 'int main()':
main.cpp:7:15: error: overflow in conversion from 'int' to 'signed char:1' 
changes value from '1' to '-1' [-Werror=overflow]
   s.enabled = 1;
           ^

これが実装定義のエラーとエラーのせいにされる理由は、キャストを必要とすることが古いコードを壊すことを意味する歴史的な使い方と関係があるかもしれません。規格の作者は、警告が関係者のための弛みを拾うのに十分であると信じるかもしれません。

ある程度の規範主義を投入するために、私は@ Lundinの声明を反省する。 「いかなる目的にもビットフィールドを使用しない」。 最初にビットフィールドを必要としていると考えるようになるような、メモリレイアウトの詳細について低レベルで具体的な理由がある場合は、他の関連する要件が仕様不足と対抗することになります。 。

(TL; DR - あなたが正当にビットフィールドを "必要とする"ために十分洗練されているなら、それらはあなたに役立つために十分には定義されていません。)

57
HostileFork

これは実装定義の動作です。これを実行しているマシンでは2の補数の符号付き整数を使用し、この場合はintを符号付き整数として扱い、ifステートメントのif部分に入力しない理由を説明することを前提にしています。

struct mystruct { int enabled:1; };

enableを1ビットのビットフィールドとして宣言します。署名されているので、有効な値は-10です。フィールドを1に設定すると、そのビットはオーバーフローして-1に戻ります(これは未定義の動作です)。

基本的に符号付きビットフィールドを扱うときの最大値は2^(bits - 1) - 1で、この場合は0です。

22
NathanOliver

あなたはそれを2の補数体系で、一番左のビットが符号ビットであると考えることができました。したがって、左端のビットが設定されている符号付き整数は負の値です。

1ビットの符号付き整数がある場合、それは符号ビットだけを持ちます。そのため、その単一ビットに1を代入しても符号ビットしか設定できません。そのため、読み返すと、値は負と解釈され、-1になります。

1ビット符号付き整数が保持できる値は-2^(n-1)= -2^(1-1)= -2^0= -12^n-1= 2^1-1=0です。

10
Paul Ogilvie

C++標準のn4713 に従って、非常によく似たコードスニペットが提供されています。使用される型はBOOL(カスタム)ですが、どの型にも適用できます。

12.2.4

4 値trueまたはfalseが任意のサイズのbool型のビットフィールド(1ビットのビットフィールドを含む)に格納されている場合、元のboolの値とビットフィールドの値は同じになります。 列挙子の値が同じ列挙型のビットフィールドに格納され、そのビットフィールドのビット数がその列挙型のすべての値を保持するのに十分な大きさである場合(10.2)、元の列挙子値とビットフィールドの値は等しい を比較します。 [例:

enum BOOL { FALSE=0, TRUE=1 };
struct A {
  BOOL b:1;
};
A a;
void f() {
  a.b = TRUE;
  if (a.b == TRUE)    // yields true
    { /* ... */ }
}

- 終了例]


一見すると、太字部分は解釈のために開いているように見えます。ただし、enum BOOLintから派生している場合は、正しい意図が明確になります。

enum BOOL : int { FALSE=0, TRUE=1 }; // ***this line
struct mystruct { BOOL enabled:1; };
int main()
{
  struct mystruct s;
  s.enabled = TRUE;
  if(s.enabled == TRUE)
    printf("Is enabled\n"); // --> we think this to be printed
  else
    printf("Is disabled !!\n");
}

上記のコードでは-Wall -pedanticなしで警告を出します。

警告:「mystruct :: enabled」は小さすぎて「enum BOOL」struct mystruct { BOOL enabled:1; };のすべての値を保持できません

出力は以下のとおりです。

無効になっています !! (enum BOOL : intを使用する場合)

enum BOOL : intを単純なenum BOOLにすると、出力は上記の標準的なパス指定のとおりになります。

有効になっている(enum BOOLを使用している場合)


したがって、他の回答のほとんどと同じように、int型は、値 "1"を1ビットのビットフィールドに格納するのに十分な大きさではないと結論付けることができます。

8
iammilind

私が見ることができるビットフィールドのあなたの理解に問題はありません。最初にmystructを のように再定義したことがわかります。 そして次に struct mystruct s; 。あなたがコーディングすべきものは次のとおりです。

#include <stdio.h>

struct mystruct { int enabled:1; };
int main()
{
    mystruct s; <-- Get rid of "struct" type declaration
    s.enabled = 1;
    if(s.enabled == 1)
        printf("Is enabled\n"); // --> we think this to be printed
    else
        printf("Is disabled !!\n");
}
1
ar18