「&」ではなく「&&」を使用する理由


135

なぜが&&&、そしてにが||望ましいの|か?

私は何年もプログラミングをしている人に尋ねました、そして彼の説明は:

例えば、でif (bool1 && bool2 && bool3) { /*DoSomething*/ }bool1テストにそれのために真である必要がありますbool2に移動する前に、真である必要がありbool3、私はシングルを使用したい場合など、&代わりにそれらのすべてがに忠実でなければならない場合でも、テストへの順序はありません次の行に進むので、なぜとにかくそれが重要なのですか?

注:私は幼児のプログラミングと同等であり、これは深刻な問題や緊急の問題ではないことを指摘しておきます。それは、物事を別の方法ではなく特定の方法で行うべき理由を理解することの問題です。


55
&および&& | そして|| 完全に異なるオペレーター
フェリーチェポラーノ2011

3
これはC#にのみ適用されるわけではないため、タグを付け直しました
Jonathan Dickinson

12
元に戻されました。回答はすでにC#に固有であり、内部の仕組みは、一般的に同じ概念を持つ他の言語では少し異なる場合があるためです。
Daniel Hilgarth、2011

8
@Felice:それらは異なりますが、完全に異なることはほとんどありません彼らは実際には非常に似ていますx & yし、x && yxとyはboolean型の式である場合、常に同じ結果に評価されます。実際、その場合の唯一の違いは、x & yではyが常に評価されることです。
Joren

1
@slawekin:答えを読むことをお勧めします。一部は、パフォーマンスの違いについて広範囲に書き込みます。答えはあなたを驚かせるかもしれません。
アベル、

回答:


183

多くの場合、&&および||よりも好ましい&|前者が短絡しているので、結果は明らかなように評価はすぐにキャンセルされたことを意味しています。

例:

if(CanExecute() && CanSave())
{
}

CanExecute返される場合、の戻り値に関係なくfalse、完全な式はになりfalseますCanSave。このため、CanSave実行されません。

これは、次の状況で非常に便利です。

string value;
if(dict.TryGetValue(key, out value) && value.Contains("test"))
{
    // Do Something
}

TryGetValuefalse指定されたキーが辞書にない場合に返されます。の短絡性のため&&value.Contains("test")TryGetValue返されたときにのみ実行され、trueしたがってvalueは実行されませんnull。代わりにビットごとのAND演算子を使用&するNullReferenceException場合、式の2番目の部分がどのような場合でも実行されるため、キーがディクショナリに見つからない場合はaが返されます。

これの類似しているがより単純な例は、次のコードです(TJHeuvelで言及)。

if(op != null && op.CanExecute())
{
    // Do Something
}

CanExecute場合にのみ実行されるopではありませんnullopisの場合null、式(op != null)の最初の部分はに評価されfalse、残りの部分(op.CanExecute())の評価はスキップされます。

:これとは別に、技術的に、彼らはあまりにも、異なっている
&&||のみに使用することができbool、一方、&および|(任意の整数型で使用することができboolintlongsbyte彼らはビット演算子であるため、...)。&ビットごとのAND演算子で|ビットごとのOR 演算子です。

正確に言うと、C#では、これらの演算子(&|[および^])は「論理演算子」と呼ばれます(C#仕様の 7.11章を参照)。これらの演算子にはいくつかの実装があります。

  1. 整数の場合(intuintlongおよびulong、章7.11.1):
    彼らは、オペランドと演算子のビットごとの結果を計算するために実装されている、すなわち&ビット単位の論理計算するために実装しているANDなど
  2. 列挙型の場合(7.11.2章):
    列挙型の基礎となる型の論理演算を実行するために実装されています。
  3. boolおよびnullable boolの場合(7.11.3および7.11.4章):
    結果はビットごとの計算を使用して計算されません。可能性の数が非常に少ないため、結果は基本的に2つのオペランドの値に基づいて検索されます。
    両方の値がルックアップに使用されるため、この実装はショートサーキットではありません。

31
これは、何かがnullかどうかを確認するのにも便利です。例:if(op != null && op.CanExecute())。最初の条件が真ではない場合、2番目の原因は評価されないため、これは有効です。
TJHeuvel、2011

2
@TJHeuvel:これは基本的に私が私のTryGetValue例で説明したのと同じ使用法です。しかし、はい、これはこの別の良い例です。
Daniel Hilgarth、2011

4
いい答えだ。たぶん、あなたはまた、例を追加する方法&|、新たなすべての人の利益のために(事業者が行うすなわち何)非ブール引数で使用されています。
Zabba

81

これが何を意味するかを非常に明確に説明します(他の回答がそれを示唆していますが、おそらくあなたが理解していない用語を使用してください)。

次のコード:

if (a && b)
{
   Foo();
}

本当にこれにコンパイルされています:

if (a)
{
    if (b)
    {
        Foo();
    }
}

次のコードは、表されているとおりにコンパイルされています。

if (a & b)
{
   Foo();
}

これは短絡と呼ばれます。一般的に、あなたは常に使用する必要があります&&し、||あなたの条件インチ

ボーナスマーク:すべきでないシナリオが1つあります。パフォーマンスが重要な場合(そしてこれはナノ秒が重要です)、必要な場合(たとえば、nullチェック)にのみ短絡を使用します-短絡は分岐/ジャンプなので、これにより、CPUで分岐が誤って予測される可能性があります。&はるかに安いです&&。短絡が実際にロジックを破壊する可能性があるシナリオもあります- 私のこの答えを見てください。

Diatribe / Monologue幸いにも無視するブランチの誤予測について。Andy Firth(13年間ゲームに取り組んでいる)を引用する:「これは、人々が彼らが行く必要があると考えるより低いレベルかもしれません...しかし、彼らは間違っているでしょう。あなたがプログラミングしているハードウェアがブランチを扱う方法を理解するパフォーマンスに大きな影響を与えます...ほとんどのプログラマーがre:1000カットによる死を高く評価するよりもはるかに大きいのです。」

  • ゲーム開発者(および極端なリアルタイム条件で作業している他の開発者)は、予測子により適するようにロジックを再構築します。逆コンパイルされたmscorlibコードにもこれの証拠があります。
  • .NETがこのタイプのものからユーザーを保護するからといって、それが重要ではないという意味ではありません。ブランチの誤予測は、60 Hzで非常に高くつきます。または10,000リクエスト/秒。
  • インテルには、予測ミスの場所を特定するためのツールがなく、Windowsにはこのためのパフォーマンスカウンターもありません。問題がなければ、それを説明する言葉もありません。
  • 下位レベルとアーキテクチャについての無知は、それらに気づいている誰かを間違っていることにはしません。
  • 作業しているハードウェアの制限を常に理解するようにしてください。

これは、非信者のベンチマークです。スケジューラーの影響を軽減するために、プロセスをRealTime / Highで実行することをお勧めします。https//gist.github.com/1200737


7
「ボーナスマーク」について:時期尚早の最適化から生まれる良い点は誰でも知っています。:)
CVn

6
@Michael-それが 'nano-seconds critical'が太字になっている理由です:)。AAAゲーム開発者は、通常、このようなことを心配します-誰が答えを読むかはわかりません。したがって、境界線/極端なケースでさえも文書化することが常に最善です。
ジョナサンディキンソン、

1
そのボーナスマークはC#に有効ですか?MSILが解釈されるので、式がマシンコードにコンパイルされない限り、私は考えていませんでした。
Jeremy McGee

7
@Jeremy MSILは解釈されません。
ジョナサンディキンソン、

2
@TheDの回答を再確認します-なぜこれについて心配する必要があるのか​​についての独白を追加しました。そして、参考までに、はに(x && y)変換されるLOAD x; BRANCH_FALSE; LOAD y; BRANCH_FALSE;場所に(x & y)変換されLOAD x; LOAD y; AND; BRANCH_FALSE;ます。1つのブランチと2つのブランチ。
ジョナサンディキンソン

68

論理演算子(||および&&)とビット単位演算子(|および&)。

論理演算子とビット演算子の最も重要な違いは、論理演算子が2つのブール値を取得してブール値を生成するのに対し、ビット演算子は2つの整数を取得して整数値を生成することです(注:整数は整数だけでなく整数データ型を意味します)。

わかりやすくするために、ビットごとの演算子はビットパターン(たとえば、01101011)を取り、各ビットに対してビットごとのAND / ORを実行します。したがって、たとえば、2つの8ビット整数があるとします。

a     = 00110010 (in decimal:    32+16+2   = 50)
b     = 01010011 (in decimal: 64+   16+2+1 = 83)
----------------
a & b = 00010010 (in decimal:       16+2   = 18)
a | b = 01110011 (in decimal: 64+32+16+2+1 = 115)

論理演算子は以下でのみ機能しboolます。

a      = true
b      = false
--------------
a && b = false
a || b = true

2番目に、trueとfalseはそれぞれ1と0に相当するため、boolでビットごとの演算子を使用することがよくあります。trueを1に、falseを0に変換すると、ビットごとの演算が行われ、ゼロ以外の値に変換されます。真からゼロ、偽から 論理演算子を使用した場合と同じ結果になることがあります(これを確認してください)。

もう1つの重要な違いは、論理演算子が短絡していることです。したがって、一部のサークル[1]では、人々がこのようなことをしているのをよく見ます:

if (person && person.punch()) {
    person.doVictoryDance()
}

これは、「人が存在する場合(つまり、nullでない場合)、パンチすることを試み、パンチが成功した場合(つまり、trueを返す)、勝利のダンスを行う」に変換されます

代わりにビットごとの演算子を使用した場合、これは次のとおりです。

if (person & person.punch()) {
    person.doVictoryDance()
}

「人が存在し(つまり、nullではない)、パンチが成功した(つまり、trueを返す)場合は、勝利のダンスを行います」に変換されます

短絡論理演算子では、nullのperson.punch()場合、コードがまったく実行されない可能性があることに注意してくださいperson。場合、実際には、この特定の場合において、第二のコードはヌル参照エラーを生成するpersonヌルであるが呼び出さないしようとするので、person.punch()人がヌルであるか否かに関係なく。正しいオペランドを評価しないこの動作は、短絡と呼ばれます。

[1]一部のプログラマーは、副作用のある関数呼び出しをif式の内部に置くことを非難し、他のプログラマーは一般的で非常に便利なイディオムです。

ビットワイズ演算子は一度に32ビットで動作するため(32ビットマシンを使用している場合)、膨大な数の条件を比較する必要がある場合、よりエレガントで高速なコードにつながる可能性があります。

int CAN_PUNCH = 1 << 0, CAN_KICK = 1 << 1, CAN_DRINK = 1 << 2, CAN_SIT = 1 << 3,
    CAN_SHOOT_GUNS = 1 << 4, CAN_TALK = 1 << 5, CAN_SHOOT_CANNONS = 1 << 6;

Person person;
person.abilities = CAN_PUNCH | CAN_KICK | CAN_DRINK | CAN_SIT | CAN_SHOOT_GUNS;

Place bar;
bar.rules = CAN_DRINK | CAN_SIT | CAN_TALK;

Place military;
military.rules = CAN_SHOOT_CANNONS | CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT;

CurrentLocation cloc1, cloc2;
cloc1.usable_abilities = person_abilities & bar_rules;
cloc2.usable_abilities = person_abilities & military_rules;

// cloc1.usable_abilities will contain the bit pattern that matches `CAN_DRINK | CAN_SIT`
// while cloc2.usable_abilities will contain the bit pattern that matches `CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT`

論理演算子を使用して同じことを行うと、厄介な量の比較が必要になります。

Person person;
person.can_punch = person.can_kick = person.can_drink = person.can_sit = person.can_shoot_guns = true;
person.can_shoot_cannons = false;

Place bar;
bar.rules.can_drink = bar.rules.can_sit = bar.rules.can_talk = true;
bar.rules.can_punch = bar.rules.can_kick = bar.rules.can_shoot_guns = bar.rules.can_shoot_cannons = false;

Place military;
military.rules.can_punch = military.rules.can_kick = military.rules.can_shoot_guns = military.rules.can_shoot_cannons = military.rules.can_sit = true;
military.rules.can_drink = military.rules.can_talk = false;

CurrentLocation cloc1;
bool cloc1.usable_abilities.can_punch         = bar.rules.can_punch         && person.can_punch,
     cloc1.usable_abilities.can_kick          = bar.rules.can_kick          && person.can_kick,
     cloc1.usable_abilities.can_drink         = bar.rules.can_drink         && person.can_drink,
     cloc1.usable_abilities.can_sit           = bar.rules.can_sit           && person.can_sit,
     cloc1.usable_abilities.can_shoot_guns    = bar.rules.can_shoot_guns    && person.can_shoot_guns,
     cloc1.usable_abilities.can_shoot_cannons = bar.rules.can_shoot_cannons && person.can_shoot_cannons
     cloc1.usable_abilities.can_talk          = bar.rules.can_talk          && person.can_talk;

bool cloc2.usable_abilities.can_punch         = military.rules.can_punch         && person.can_punch,
     cloc2.usable_abilities.can_kick          = military.rules.can_kick          && person.can_kick,
     cloc2.usable_abilities.can_drink         = military.rules.can_drink         && person.can_drink,
     cloc2.usable_abilities.can_sit           = military.rules.can_sit           && person.can_sit,
     cloc2.usable_abilities.can_shoot_guns    = military.rules.can_shoot_guns    && person.can_shoot_guns,
     cloc2.usable_abilities.can_talk          = military.rules.can_talk          && person.can_talk,
     cloc2.usable_abilities.can_shoot_cannons = military.rules.can_shoot_cannons && person.can_shoot_cannons;

ビットパターンとビットごとの演算子が使用される古典的な例は、Unix / Linuxファイルシステムのアクセス許可です。


3
サイドの+1が問題に影響します。それは前に言及されなかったことに驚いた
コンラッドフリックス

3
この例は少し暴力的に見えますが、他の回答は短絡に焦点を合わせすぎており、整数とブール値の演算の違いに十分ではないようです。
R0MANARMY 2011

実装の詳細(回路/副作用)を実行する前に、機能を理解する必要があります。短絡ではなく、ブールロジックと整数ロジックの主な違いを解消できてうれしいです。
アベル

@ROMANARMY-暴力的、私はあなたのモンキーカーを与えられた皮肉が大好きです。いい仕事
brumScouse

8

の場合:

if (obj != null && obj.Property == true) { }

期待どおりに動作します。

だが:

if (obj != null & obj.Property == true) { }

null参照例外がスローされる可能性があります。


2

短くてシンプル:

1 && 2= true
なので、
1 = Cで
true(非ゼロ)2 = Cでtrue(非ゼロ)

trueANDSと論理的trueに与えるtrue

だが

1 & 2= 0 =偽
なぜなら
バイナリ1 = 0001
バイナリで= 0010 2

0001 ANDをビット単位で0010とANDすると、10進数で0000 = 0になります。

同様に|| と| 演算子も...!


2
-1:ここでC#について話しています... C#1 && 2では違法です
ダニエルヒルガース

しかし、これは非常に重要な例であり、&と&&を単純に交換できない理由を説明しています(多くの人が考えているようです)。
bobobobo 2012年

1

&&はの短絡バージョンです&

を評価している場合false & true、最初の引数を見ると、結果がfalseになることがわかっています。&&演算子のバージョンは、式全体を評価するのではなく、できるだけ早く結果を返します。|演算子の同様のバージョンもあり||ます。


1
if (list.Count() > 14 && list[14] == "foo")

安全です

if (list.Count() > 14 & list[14] == "foo")

リストが適切なサイズでない場合はクラッシュします。


誰かが「if(list.Count()> 14 && list [14] ==」の代わりに「if(list.Count()> 14&list [14] == "foo")」と書けるとは思えません。 foo ")"。&この場合、&が確実に安全であっても、&&に対して単純かつ自然に使用することはできません(たとえば、list [1])。
Tien Do

1

C#オペレーターは理由を説明する必要があります。

本質的に2つ&以上の|'s'が存在するということは、それが論理的というより条件付きであることを意味するので、2つの間の違いを見分けることができます。

&演算子には、1つのを使用する例があり&ます。


両方のリンクが(事実上)壊れています(「Visual Studio 2005 Retired documentation」にリダイレクトされます)。
Peter Mortensen

1

OK、額面通り

    Boolean a = true;
    Boolean b = false;

    Console.WriteLine("a({0}) && b({1}) =  {2}", a, b, a && b);
    Console.WriteLine("a({0}) || b({1}) =  {2}", a, b, a || b);
    Console.WriteLine("a({0}) == b({1}) =  {2}", a, b, a == b);

    Console.WriteLine("a({0}) & b({1}) =  {2}", a, b, a & b);
    Console.WriteLine("a({0}) | b({1}) =  {2}", a, b, a | b);
    Console.WriteLine("a({0}) = b({1}) =  {2}", a, b, a = b);

同じ答えを出す。しかし、あなたが示したように、もっと複雑な質問がある場合は、

if (a and b and c and d) ..

場合はa真実ではない、多分bそれは、オフに行く何かに接続し、これを取得する必要があり機能である、それを行う、なぜわざわざ...決定を下しますか?時間の無駄、あなたはそれがすでに失敗したことを知っています。なぜマシンをオフにして余分な無駄な作業をさせるのですか?

&&失敗する可能性が最も高い、エルゴ、少ない計算を最初に置いて、意味がないときに進む前に、常に使用してきました。次のように、データの出力を制限するブール値があるなど、可能性の低い選択肢を予測する方法がない場合:

if (limit && !MyDictionary.ContainsKey("name")) 
    continue;

そうでない場合limitは、キーのチェックを行わないでください。チェックに時間がかかる場合があります。


1

ifステートメントなどの論理式で使用する場合&&は、最初の偽の結果が検出されるとすぐに式の評価が停止するため、推奨されます。false値は式全体をfalseにするため、これは可能です。同様に(論理式でも)||、真の式に遭遇するとすぐに式の評価を停止するため、真の値があると式全体が真になるため、これが望ましいです。

ただし式は-EDまたはまたはと-EDは一緒に副作用を持っている、とあなたは(関係なく、論理式の結果の)あなたの式の結果として発生するこれらのすべてをしたいこと、そして、&そして|使用することができます。逆に、&&and ||演算子は、不要な副作用(例外がスローされる原因となるnullポインターなど)から保護するのに役立ちます。

&そして|オペレータはまた、整数で使用され、この場合、それらは二つのオペランドと-EDまたはであるか、または-EDビットレベルで一緒整数結果を生成することができます。これは、整数値のバイナリビットがtrue値とfalse値の配列として使用される場合に役立ちます。特定のビットがオンであるかオフであるかをテストするために、ビットマスクは値とビット単位でANDされます。ビットをオンにするには、同じマスクを値ごとにビット単位でoredできます。最後に、ビットをオフにするため~に、マスクのビット単位の補数(を使用)は、値とビット単位でANDされます。

int a = 0; // 0 means all bits off
a = a | 4; // set a to binary 100
if ((a & 4) != 0) {
    // will do something
}
a = a & (~4) // turn bit off again, a is now 000

C#以外の言語では、&と|の論理モードとビットモードの比較に注意する必要があります。上記のコードでは、ifステートメントの条件式(a & 4) != 0はこの条件を安全に表す方法ですが、C言語のような多くの言語では、条件ステートメントはゼロ整数値を偽として、ゼロ以外の整数値を真として扱うことができます。(これの理由は、使用可能な条件付きブランチプロセッサ命令と、整数演算ごとに更新されるゼロフラグとの関係に関連しています。)したがって、 ìfステートメントのゼロのテストを削除して、条件をに短縮できます(a & 4)

これは混乱を引き起こし、ビットワイズと演算子の戻り値を使用して式を組み合わせると、ビットが揃っていない場合でも問題が発生する可能性があります。2つの関数の副作用が望まれる次の例を検討してください。2つの関数が両方とも(0以外の値を返すことで定義されているように)成功した​​ことを確認する前に、

if (foo() & bar()) {
    // do something
}

Cでは、foo()1をbar()返し、2 を返す場合、「何か」1 & 2はゼロであるため実行されません。

C#ではif、ブール値のoeprandを使用するなどの条件付きステートメントが必要であり、この言語では整数値をブール値にキャストすることを許可していません。したがって、上記のコードはコンパイラエラーを生成します。より正確には次のように表現されます。

if (foo() != 0 & bar() != 0) {
    // do something
}

1

ベテランCプログラマーの場合は注意してください。C#は本当に私を驚かせました。

MSDN|オペレーターに次のように言っています:

バイナリ| 演算子は、整数型とboolに対して事前定義されています。整数型の場合| オペランドのビットごとのORを計算します。boolオペランドの場合、| オペランドの論理ORを計算します。つまり、両方のオペランドがfalseの場合にのみ、結果はfalseになります。

(強調は私のものです。)ブール型は特別に処理されます。このコンテキストでは、質問が意味を持ち始めただけで、違いは他の回答で既に説明されているとおりです。

&&||短絡しています。両方のオペランド&|評価ます。

何が望ましいかは、副作用、パフォーマンス、コードの読みやすさなどの多くの要素に依存しますが、一般に、短絡演算子は、私のような同じような背景を持つ人々にも理解されやすいため、望ましいものです。

その理由は次のとおりです。Cには実際のブール型がないため、ビット単位の演算子|を使用して、if条件で結果を真または偽として評価できます。しかし、これはC#の間違った態度です。なぜなら、ブール型の特別なケースが既にあるからです。


0

重要なのは、(たとえば)bool2の評価のコストは高いがbool1がfalseの場合、&& over&を使用することでかなりの計算量を節約できたからです。


0

ため&&||に使用されているフロー制御だけのようif/elseです。それは常に条件文についてではありません。次のように、または条件としてはなく、ステートメントとして記述することは完全に合理的です。ifwhile

 a() && b() && c() && d();

あるいは

 w() || x() || y() || z();

同等のif/elseバージョンよりもタイプしやすいというだけではありません。また、読みやすく、理解しやすくなっています。


0

&&と&は2つの非常に異なることを意味し、2つの異なる答えを示します。

1 && 21( "true")
1 & 2は0( "false")を返します

&&論理演算子です。これは、「両方のオペランドがtrueの場合はtrue」
&がビットごとの比較であることを意味します。これは、「両方のオペランドに設定されているビットを教えてください」という意味です。


2
問題はC#についてです。C#では、数値をブール値にキャストする方法がないため、0は「偽」ではなく、ゼロ以外は「真」ではありません。等価性はありません。
Nate CK

数値をブール値に変換するには、1がtrue、0がfalseを意味するように、「n!= 0」と言います(私はC#についてはあまり詳しくありません)。よく調査されていないので、このコメントを撤回したかったのですが、以前のコメントと比べて役に立たなかったり、実際に関連しているとは思わないのですが、たまたまEnterキーを押しただけで、 :-)何でもそれの価値があるため、屋が行くので、ここでそれをキャンセル
ドンハッチ

1 && 2コンパイラー・エラー:「エラー4演算子 '&&'は、タイプ 'int'および 'int'のオペランドには適用できません」
Peter Mortensen

0

これを行うときにコードの正確な操作を知る必要がない人にこれを説明する最も簡単な(そして少し気を抜く)方法は

&&は、これらの各条件についてチェックを行っています。falseが見つかり、結果全体がfalseとして返されるまで

|| は、それらの条件のそれぞれについてチェックを行っています。trueが見つかるまで、結果全体をtrueとして返します。

MATHSベースの武器の両方/すべての条件を実行し、結果に対処しています。

| MATHSベースの武器の両方/すべての条件を実行し、結果に対処しています。

または|を使用する必要があるポイントに出くわしたことはありませんifステートメント内。私は主に、ビット単位のシフトを使用して16進値をそのコンポーネントカラーに切り分けるために使用します。

例えば:

r = fullvalue >> 0xFF & 0xFF;
g = fullvalue >> 0xF & 0xFF;
b = fullvalue & 0xFF;

この操作の中で、 "&0xFF"はバイナリ値のみを見ることを強制しています。個人的に私は用途を見つけていません| まだ。


0

単に、

if exp1 && exp2

exp1がexp2をflase チェックしない場合

だが

if exp1 & exp2

exp1が次の場合falseまたはtrue exp2をチェック

&exp1が次の場合にexp2をチェックすることはめったにないため、めったに使用されませんfalse

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.