11

大学の内部スレッドを読んでください。

#include <iostream>
using namespace std;

union zt
{
 bool b;
 int i;
};

int main()
{
 zt w;
 bool a,b;
 a=1;
 b=2;
 cerr<<(bool)2<<static_cast<bool>(2)<<endl;                      //11
  cerr<<a<<b<<(a==b)<<endl;                                      //111
 w.i=2;
 int q=w.b;
 cerr<<(bool)q<<q<<w.b<<((bool)((int)w.b))<<w.i<<(w.b==a)<<endl; //122220
 cerr<<((w.b==a)?'T':'F')<<endl;                                 //F
}

したがってabw.bはすべて として宣言されboolます。ais assigned 1bis assigned 2、および の内部表現が( を使用して)w.bに変更されます。2union

このように、 と のすべてはになりaますが、とは等しくないので、これは宇宙が壊れていることを意味する可能性があります ( )bw.btrueaw.btrue!=true

この問題は実用的というよりも理論的なものであることはわかっています (SAKE プログラマーは a の内部表現を変更したくないbool) が、ここに質問があります。

  1. これでいいですか?(これは g++ 4.3.3 でテストされました)つまり、コンパイラは、ブール値の比較中にゼロ以外の値が true を意味する可能性があることに注意する必要がありますか?
  2. このコーナーケースが実際の問題になる可能性があるケースを知っていますか? (たとえば、ストリームからバイナリ データをロードするとき)

編集:

三つのこと:

  1. bool大きさintが違うので大丈夫です。しかし、char代わりにint. またはいつsizeof(bool)==sizeof(int)

  2. 可能であれば、私が尋ねた2つの質問に答えてください。私の正直な意見では、組み込みシステム (8 ビット システムである可能性があります) では、これは本当の問題である (またはそうでない) 可能性があるためです。

  3. 新しい質問: これは本当に未定義の動作ですか? はいの場合、なぜですか? そうでない場合、なぜですか?仕様にブール比較演算子に関する仮定はありませんか?

4

8 に答える 8

17

書き込まれた最後のメンバーとは異なるメンバーであるユニオンのメンバーを読み取ると、未定義の動作が発生します。int メンバーを書き込んでから共用体の bool メンバーを読み取ると、プログラムの後続の時点で何かが発生する可能性があります。

唯一の例外は、共用体が構造体の共用体であり、すべての構造体に共通の初期シーケンスが含まれている場合です。この場合、共通のシーケンスを読み取ることができます。

于 2009-08-09T20:14:41.007 に答える
9
  1. これでいいですか?(これは g++ 4.3.3 でテストされました)つまり、コンパイラは、ブール値の比較中にゼロ以外の値が true を意味する可能性があることに注意する必要がありますか?

ゼロ以外の整数値 (または NULL 以外のポインター) はすべて true を表します。ただし、integer と bool を比較する場合、bool は比較前に int に変換されます。

  1. このコーナーケースが実際の問題になる可能性があるケースを知っていますか? (たとえば、ストリームからのデータのバイナリ読み込み中)

それは常に現実的な問題です。

  1. これでいいですか?

    仕様がこれについて何かを指定しているかどうかはわかりません。コンパイラは常に次のようなコードを作成する可能性があります: ((a!=0) && (b!=0)) || ((a==0) && (b==0)) は、2 つのブール値を比較する場合に使用されますが、パフォーマンスが低下する可能性があります。

    私の意見では、これはバグではなく、未定義の動作です。すべての実装者は、実装でブール比較がどのように行われるかをユーザーに伝える必要があると思います。

最後のコード サンプルを見ると、a と b の両方がブール値であり、1 と 2 を適切に割り当てることで true に設定されます (いいえ、1 と 2 が消えると、それらは単に true になります)。

だからあなたの表現を分解する:

a!=0      // true (a converted to 1 because of auto-type conversion)
b!=0      // true (b converted to 1 because of auto-type conversion)

((a!=0) && (b!=0)) => (true && true)  // true ( no conversion done)

a==0      // false (a converted to 1 because of auto-type conversion)
b==0      // false (b converted to 1 because of auto-type conversion)

((a==0) && (b==0)) => (false && false) // false ( no conversion done)

((a!=0) && (b!=0)) || ((a==0) && (b==0)) => (true || false) => true

したがって、上記の式が明確に定義され、常に真であることを常に期待しています。

しかし、これが元の質問にどのように当てはまるかわかりません。整数を bool に代入すると、整数は bool に変換されます (何度か説明したように)。true の実際の表現は標準で定義されておらず、bool に適合する任意のビット パターンである可能性があります (特定のビット パターンを仮定しない場合があります)。

bool を int と比較する場合、bool はまず int に変換されてから比較されます。

  1. あらゆる現実のケース

    私の頭に浮かぶ唯一のことは、誰かがファイルからブールメンバーを持つ構造体にバイナリデータを読み取った場合です。bool の場所に 1 ではなく 2 を書き込んだ他のプログラムでファイルが作成された場合、問題が発生する可能性があります (別のプログラミング言語で作成された可能性があります)。

    しかし、これは悪いプログラミング手法を意味するかもしれません。

バイナリ形式でのデータの書き込みは、知識がなければ移植できません。
各オブジェクトのサイズに問題があります。
表現には問題があります:

  • 整数 (エンディアンあり)
  • Float (未定義の表現 ((通常は基盤となるハードウェアに依存))
  • Bool (バイナリ表現は標準で定義されていません)
  • 構造体 (メンバー間のパディングは異なる場合があります)

これらすべてについて、基礎となるハードウェアとコンパイラについて知る必要があります。異なるコンパイラまたはコンパイラの異なるバージョン、または異なる最適化フラグを持つコンパイラでさえ、上記のすべてに対して異なる動作をする可能性があります。

ユニオンの問題

struct X
{
    int  a;
    bool b;
};

人々が「a」への書き込みと「b」からの読み取りについて言及しているように、定義されていません。
理由: このハードウェアで「a」または「b」がどのように表現されるかがわからないためです。「a」に書き込むと「a」のビットが埋められますが、「b」のビットにはどのように反映されますか。システムが 1 バイトの bool と 4 バイトの int を使用し、最下位バイトが下位メモリに最上位バイトが上位メモリにある場合、「a」に 1 を書き込むと「b」に 1 が書き込まれます。しかし、実装はどのようにブール値を表していますか? true は 1 または 255 で表されますか? 'b' に 1 を入れ、それ以外のすべての true の使用に 255 を使用するとどうなりますか?

したがって、ハードウェアとコンパイラの両方を理解していないと、予期しない動作になります。

したがって、これらの使用法は定義されていませんが、標準では禁止されていません。それらが許可されている理由は、調査を行った結果、この特定のコンパイラを使用するシステムでは、これらの仮定を行うことで、気まぐれな最適化を行うことができることがわかったからです。ただし、前提条件を変更すると、コードが破損することに注意してください。

また、2 つの型を比較す​​る場合、コンパイラは比較の前にいくつかの自動変換を行います。2 つの型は比較の前に同じ型に変換されることに注意してください。整数と bool の比較では、bool が整数に変換され、他の整数と比較されます (変換により、false が 0 に、true が 1 に変換されます)。変換されるオブジェクトが両方とも bool である場合、変換は不要であり、ブール論理を使用して比較が行われます。

于 2009-08-09T21:34:25.230 に答える
9

通常、任意の値を に代入するとbool、コンパイラはそれを変換します:

int x = 5;
bool z = x; // automatic conversion here

コンパイラによって生成される同等のコードは、次のようになります。

bool z = (x != 0) ? true : false;

ただし、コンパイラはこの変換を 1 回だけ行います。特にやのような論理演算を行う場合、bool変数内のゼロ以外のビット パターンが と同等であると仮定するのは不合理です。結果のアセンブリ コードは扱いにくくなります。true

データ構造を使用している場合はunion、自分が何をしているのかを知っており、コンパイラを混乱させる能力があると言えば十分です。

于 2009-08-09T20:12:12.777 に答える
2

ブール値は 1 バイト、整数は 4 バイトです。整数に 2 を代入すると、4 番目のバイトの値は 2 になりますが、最初のバイトの値は 0 になります。共用体からブール値を読み取ると、最初のバイトが取得されます。

編集:ああ。Oleg Zhylin が指摘しているように、これはビッグエンディアンの CPU にのみ適用されます。訂正ありがとうございます。

于 2009-08-09T20:23:44.390 に答える
1

あなたがしていることは、タイプパニングと呼ばれていると思います: http://en.wikipedia.org/wiki/Type_punning

于 2009-08-09T20:20:31.700 に答える
0

コードパッドから別の出力が得られます:

11
111
122222
T

コードも正しいように思えますが、コンパイラのバグでしょうか?
こちらをご覧ください

于 2009-08-09T20:11:26.427 に答える
0

私の見解を書き留めるだけです:

  1. これでいいですか?

    仕様がこれについて何かを指定しているかどうかはわかりません。コンパイラは常に次のようなコードを作成する可能性があります: ((a!=0) && (b!=0)) || ((a==0) && (b==0)) は、2 つのブール値を比較する場合に使用されますが、パフォーマンスが低下する可能性があります。

    私の意見では、これはバグではなく、未定義の動作です。すべての実装者は、実装でブール比較がどのように行われるかをユーザーに伝える必要があると思います。

  2. あらゆる現実のケース

    私の頭に浮かぶ唯一のことは、誰かがファイルからブールメンバーを持つ構造体にバイナリデータを読み取った場合です。bool の場所に 1 ではなく 2 を書き込んだ他のプログラムでファイルが作成された場合、問題が発生する可能性があります (別のプログラミング言語で作成された可能性があります)。

    しかし、これは悪いプログラミング手法を意味するかもしれません。

もう 1 つ: 組み込みシステムでは、このバグは「通常の」システムよりも大きな問題になる可能性があります。これは、プログラマーが仕事を成し遂げるためにより多くの「ビットマジック」を行うためです。

于 2009-08-09T20:27:17.677 に答える
-1

提起された質問に対処すると、動作は問題なく、現実の世界では問題にならないと思います。C++ には ^^ がないので、安全な bool 比較手法として !bool == !bool をお勧めします。

このようにして、ブール変数のすべての非ゼロ値がゼロに変換され、すべてのゼロがゼロ以外の値に変換されますが、ほとんどの場合、否定操作は同じです。

于 2009-08-09T20:38:58.480 に答える