C#:「自分を足で撃つ」方法

本日は、 論理値を操作する際に、C#で「。



このコンソールアプリケーションによって表示される行は何ですか?

Visual Studio Community 2013環境で以前にアセンブルしたアプリケーションを起動すると、次の結果が得られます。



Unsafe Mode 01: b1 : True 02: b2 : True 03: b1 == b2: False 04: !b1 == !b2: True 05: b1 && b2 : True 06: b1 & b2 : True 07: b1 ^ b2 : True 08: b1 && b3 : True 09: b1 & b3 : False 10: b1 ^ b3 : True Safe Mode 11: b1 : True 12: b2 : True 13: b1 == b2: True 14: !b1 == !b2: True 15: b1 && b2 : True 16: b1 & b2 : True 17: b1 ^ b2 : False 18: b1 && b3 : True 19: b1 & b3 : True 20: b1 ^ b3 : False
      
      





論理変数b1、b2、b3のそれぞれには、「true」値または「false」値以外の値があります(したがって、「true」?-これらのブール変数は?)、という仮定に基づいて、質問:

  1. UnsafeブロックとSafe Modeブロックでは、位置03と13、07と17、09と19、10と20でそれぞれ異なる結果になるのはなぜですか?

    (そして、なぜ、安全でないブロックと安全なブロックで互いに対応する他の位置の値が一致するのですか?)
  2. 位置05と06の結果はUnsafeブロック内で同じですが、08と09では異なるのはなぜですか?

    そして、なぜ08と09の結果が異なるのですか?


把握してみましょう:



おそらく誰もが最初はプログラミング言語には特別な論理(ブール)データ型がなかったことを知っています。

ブール型として、整数データ型が使用されました。



ゼロは偽値(False)、ゼロ以外の値-真(True)として扱われました。

したがって、if分岐演算子を整数オペランドに適用できます。



代入(=)演算子と等価(==)演算子を混同することでC / C ++で簡単に犯される間違いは広く知られています。

次のコードは、文字列「i == 1」を常に表示します。

 int i = 0; if (i = 1) printf("i == 1"); else printf("i == 0");
      
      





これは、オペランド「i = 1」のif分岐演算子で、等価演算子(==)ではなく代入演算子(=)が誤って使用されたためです。

その結果、値「1」が変数「i」にそれぞれ書き込まれ、「=」演算子は値「1」を返し、整数値「1」はif演算子のオペランドとして使用され、論理(ブール)値として解釈され、常に実行されます。最初のブランチからのコード(printf( "i == 1"))。



したがって、C / C ++では、次のように比較演算子を使用するのが慣例です。

 int i = 0; if (1 == i) printf("i == 1"); else printf("i == 0");
      
      



「直感的」ではなく:

 int i = 0; if (i == 1) printf("i == 1"); else printf("i == 0");
      
      



その理由は、演算子「1 == i」では間違えて「1 = i」と書くことができないためです。コンパイラは定数(1)に新しい値(i)を割り当てることを許可しません。



どうやら、ある時点で、プログラミング言語の開発者は、言語の「完全な」論理型のサポートを追加することを決定しました。

そのため、Turbo / Borland PascalおよびDelphiでは、 ブール型が登場しました。 このタイプの変数は、FalseおよびTrueの値を取ることができます。 さらに、型のサイズは1バイトであり、 Ord関数によって返される順序(整数)値は、それぞれFalseとTrueの場合0と1であることが文書化されています。



しかし、他の可能なゼロ以外の内部値はどうでしょうか? この場合の動作はあいまいになる可能性があり、ドキュメント/書籍ではブール値をこの方法でテストする必要があることが明確になっています。

 var b: Boolean; begin b := True; if b then WriteLn('b = True') else WriteLn('b = False'); end
      
      



しかし、これは好きではありません:

 var b: Boolean; begin b := True; if b = True then WriteLn('b = True') else WriteLn('b = False'); end
      
      



変数「b」は、ユニティ以外のゼロ以外の値を持つ可能性があり、比較「b = True」の結果は未定義になります-結果が偽になる可能性がありますパフォーマンス上の理由で)。



一方、論理変数に0と1以外の内部コードを含めることができる場合と、常に正しく処理できるわけではないが、ゼロ以外の値は「真」と見なされる場合が間接的に認識されました。



その後、Delphiは、C / C ++、COMオブジェクト、およびその他のサードパーティコードで記述されたコードを操作する際に、ブール型との互換性のためにサイズが1、2、4バイトのブール型ByteBoolWordBoolLongBoolを追加しました。

それらの場合、 ブール型とは異なり、ゼロ以外の値はすべて「真」と見なされると判断されます。



C ++では、「ネイティブ」型ブールが同じ方法で追加され(この変数は値falseおよびtrueを取ることができます )、そのサイズは非決定的です (おそらくプラットフォームのビット深度に依存します-パフォーマンス上の理由またはその他の理由により、特定のバージョンのデータ型の次元Microsoftコンパイラーは、 ここここにリストさています )。

また、内部コードfalseおよびtrueの明示的な定義はありませんが、定義falseおよびtrueに関連するコード例から間接的に、 falseには内部数値コード0があり、 trueには内部数値コード1があります。



このような一見シンプルなデータ型(論理(ブール)型)で作業する場合の落とし穴を確認するために、ブール型の起源の歴史的ツアーを実施し、問題を理解して、C#の論理データ型の内部構造の検討に取り組み、テストで理由を議論しますこのプログラムは、結果が判明し、C#でブール値を使用してアンマネージコードと対話するときに正しく動作する方法を取得しました。

リトリートは非常に膨大であることが判明したため、次回これらの問題を検討します。



All Articles