このバージョンのCの論理ANDが短絡動作を示さないのはなぜですか?


80

はい、これは宿題の質問ですが、私はこのトピックについて調査とかなりの深い考えを行ったので、これを理解することはできません。この質問は、このコードが短絡動作を示さないことを示し、その理由を尋ねます。しかし、私には短絡動作を示しているように見えますが、なぜそうでないのか誰かが説明できますか?

Cの場合:

それaが間違っている場合、プログラムはまったく評価しようとしないように見えbますが、私は間違っているに違いありません。bこの場合、プログラムが触れる必要がないのに、なぜプログラムが触れるのでしょうか。


11
最も不自然な宿題の質問によくあることですが、プログラマーが意図的に賢くしようとしない限り、本番システムでこの種のコードを目にすることはありません。
ロバートハーベイ

49
@RobertHarvey本番システムでは、このようなコードが常に表示されます。、という名前の関数である可能性は低いですが、AND()値で引数を受け取り、関数のロジックに応じて引数を評価する(またはしない)関数はどこにでもあります。「トリックの質問」であるにもかかわらず、理解することはCの重要な振る舞いです。名前による呼び出しのある言語では、この質問の答えは大きく異なります。
ベンジャクソン

7
@BenJackson:動作ではなく、コードについてコメントしていました。はい、動作を理解する必要があります。いいえ、このようなコードを書く必要はありません。
ロバートハーベイ

3
これは、VBでコーディングしてIIfに遭遇する必要がある場合、実際には非常に重要です。演算子ではなく関数であるため、評価が短絡することはありません。これは、短絡オペレーターに慣れている開発者にとって問題を引き起こす可能性がありますIIf(x Is Nothing, Default, x.SomeValue)
ダンブライアント

3
それはだから@alk教育システムの起訴。
シヴァンドラゴン

回答:


118

これはトリックの質問です。bsc_andメソッドへの入力引数であるため、常に評価されます。他の語でsc_and(a(), b())呼ぶa()とコールb()(保証されないため)、その後、呼び出しsc_andの結果とa(), b()に渡されますa?b:0。絶対に短絡する三項演算子自体とは何の関係もありません。

更新

私がこれを「トリック質問」と呼んだ理由に関して:それは、「短絡」をどこで考慮するかについて明確に定義されたコンテキストがないためです(少なくともOPによって再現されたように)。多くの人は、関数の定義だけが与えられた場合、質問のコンテキストが関数の本体について尋ねていると想定します。多くの場合、関数をそれ自体の式とは見なしません。これが質問の「トリック」です。プログラミング全般では、特にルールに多くの例外があるCライクのような言語では、それを行うことはできません。例、質問がそのように尋ねられた場合:

次のコードについて考えてみます。呼び出されたときにexibit短絡動作をsc_andますメイン

あなたがの思考することになっていることがすぐに明らかであるsc_and中、自分の中に自身のオペレータとしてドメイン固有言語、および場合の評価への呼び出しsc_and定期的に同様の展示短絡動作が&&でしょう。三項演算子に焦点を当てるのではなく、代わりにC / C ++の関数呼び出しメカニズムに焦点を当てる必要があることは明らかであるため、これをトリックの質問とはまったく見なしません(そして、おそらくリードします)関数ではなくsc_andを使用することを含む、短絡を行うを書くためのフォローアップの質問にうまく入り#defineます)。

三項演算子自体が短絡(または「条件付き評価」など)を行うことを呼び出すかどうかは、短絡の定義に依存し、それについての考えについてはさまざまなコメントを読むことができます。私の場合はそうですが、実際の質問や、なぜそれを「トリック」と呼んだのかとはあまり関係がありません。


13
三項演算子は短期循環を行いますか?いいえ。の?ように、に続く式の1つを評価しif (condition) {when true} else {when-false}ます。それは短絡とは呼ばれません。
イェンス


17
@Jens:演算子が1つ以上のオペランドの評価をスキップする場合は、「短絡」の形式と呼びますが、これは実際には、用語の定義方法の問題にすぎません。
R .. GitHub STOP HELPING ICE

13
@Jensこれは、の構文糖衣構文でありif else、ではありませんif。それでも、実際にはそうではありません。?:オペレータは、表現if-elseある声明。三項と同じ効果を生み出す同等のステートメントのセットを作成できる場合でも、これらは意味的に非常に異なるものです。これが短絡と見なされる理由です。他のほとんどの式は、常にすべてのオペランド(など)を評価します。そうでない場合、それは短絡です()。+,-,*,/&&, ||
aruisdante 2014年

9
はい、私にとって重要な違いは、演算子について話しているということです。もちろん、フロー制御ステートメントは、実行されるコードパスを制御します。演算子の場合、CおよびC派生言語に精通していない人にとって、演算子がすべてのオペランドを評価しない可能性があることは明らかではないため、このプロパティについて話すための用語を用意することが重要です。そのために、「short」を使用します。回路」。
R .. GitHub STOP HELPING ICE

43

ステートメントが

実行b++され、オペランドafalse(短絡動作)と評価された場合は評価されません。これは、の副作用が発生しbないことを意味します。

次に、関数を見てください。

これを呼んで

この場合、aistrueか、かはfalse、関数呼び出し中にb++常に1と評価され、副作用bが常に発生します。

同じことが当てはまります

そして


1関数の引数の評価順序は指定されていません。


1
OK、Stephen Quanの答えと協力してください:「boolx = and_fun(a、b ++);」を呼び出すときに、コンパイラが「and_fun」関数をインライン化できる可能性はありますか(合法)。aがtrueの場合、b ++はインクリメントされませんか?
シヴァンドラゴン

4
@ShivanDragonそれは私には観察可能な行動を変えるように聞こえます。
sapi 2014年

3
@ShivanDragon:コンパイラーをインライン化しても、動作は変わりません。これは、関数本体を置き換えるほど単純ではありません。
ジャックAidley

明確にするためint x = a ? b++ : 0に、観察可能な短絡としてを追加する場合があります。
ポールドレイパー

@PaulDraper; 読者が混乱しないように、元のスニペットはそのままにしておきました。
haccks 2014年

19

他の人がすでに指摘しているように、2つの引数として関数に何が渡されても、渡されるときに評価されます。これは、tenary操作の前の方法です。

一方、これは

だろうこのように、「ショート」マクロは関数コールとこれに関数の引数の無い評価(複数可)に行われることを意味するものではありません。


多分理由を説明しますか?それは、opのスニペットとまったく同じように私を探します。インラインが異なるスコープではないことを除いて。
dhein 2014年

5

@cmastersコメントに記載されているエラーを修正するために編集されました。


... returned式は短絡評価を示しますが、関数呼び出しは示しません。

電話してみてください

関数呼び出しは評価されますが1 / 0、使用されることはないため、おそらくゼロ除算エラーが発生します。

(ドラフト)ANSIC標準からの関連する抜粋は次のとおりです。

2.1.2.3プログラムの実行

..。

抽象マシンでは、すべての式がセマンティクスで指定されたとおりに評価されます。実際の実装では、値が使用されておらず、必要な副作用(関数の呼び出しや揮発性オブジェクトへのアクセスによるものを含む)が発生していないと推測できる場合は、式の一部を評価する必要はありません。

そして

3.3.2.2関数呼び出し

...。

セマンティクス

..。

関数の呼び出しの準備では、引数が評価され、各パラメーターに対応する引数の値が割り当てられます。

私の推測では、各引数は式として評価されますが、引数リスト全体は式ではないため、非SCEの動作は必須です。

C規格の深海の表面のスプラッシャーとして、私は2つの側面について適切な情報に基づいた見解をいただければ幸いです。

  • 評価1 / 0は未定義の振る舞いを生み出しますか?
  • 引数リストは式ですか?(私はそうは思わない)

PS

C ++に移行しsc_andinline関数として定義しても、SCE取得されません。@alkのように、Cマクロとして定義すると、確かにそうなります。


1
いいえ、inline関数は関数呼び出しのセマンティクスを変更しません。そして、これらのセマンティクス、正しく引用したように、すべての引数評価されることを指定します。コンパイラは、呼び出しを最適化することができたとしても、目に見える行動はすなわち、変更してはならないsc_and(f(), g())の両方かのように動作しなければならないf()g()常に呼ばれています。sc_and(0, 1/0)これ未定義の動作であり、コンパイラーが呼び出しさえする必要がないため、悪い例sc_and()です...
cmaster --reinstate monica 2014年

@cmasterありがとうございます。C ++がinline呼び出しセマンティクスを保持していることを私は単に知りませんでした。例に合うように、私はゼロ除算に固執しました。SCEは、未定義の動作を回避するために悪用されることが多いと思います。
サムネイル

4

三項演算の短絡を明確に確認するには、整数の代わりに関数ポインタを使用するようにコードを少し変更してみてください。

そしてそれをコンパイルします(最適化がほとんどない場合でも:-O0!)。falseを返すb()と、実行されないことがわかりa()ます。

ここで、生成されたアセンブリは次のようになります。

したがって、他の人が正しく指摘しているように、質問のトリックは、短絡しない呼び出し(値による呼び出し)のパラメーター評価ではなく、短絡する(「条件付き評価」と呼ぶ)三項演算子の動作に焦点を当てさせることです。


0

C三項演算子は、単一の式a(条件)のみを評価して、式bおよびcで与えられる値を決定するため、短絡することはありません。値が返される可能性がある場合にですることはありません。

次のコード:

これは、次のコードとほぼ同等です。

aは、&&や||などの他の演算子によって形成される場合があります。値を返す前に2つの式を評価する可能性があるため、短絡する可能性がありますが、短絡を行う3項演算子とは見なされず、通常のifステートメントのように条件で使用される演算子と見なされます。

更新:

三項演算子が短絡演算子であることについては、いくつかの議論があります。引数は、すべてのオペランドを評価しない演算子は、以下のコメントの@aruisdanteに従って短絡することを示しています。この定義が与えられた場合、三項演算子は短絡し、これが元の定義である場合、私は同意します。問題は、「短絡」という用語が元々この動作を可能にする特定の種類の演算子に使用されていて、それらが論理/ブール演算子であるということです。これらだけである理由は、私が説明しようとしていることです。

記事「短絡評価」に続いて、短絡評価は、最初のオペランドが2番目のオペランドを無関係にすることを知っている方法で言語に実装されたブール演算子のみを参照します。つまり、&&演算子が最初のオペランドである場合はfalse、および|| 演算子は最初のオペランドがtrueであるため、C11仕様では、6.5.13論理AND演算子および6.5.14論理OR演算子にも記載されています。

これは、短絡動作を識別するために、最初のオペランドが2番目のオペランドと無関係にならない場合は、ブール演算子と同じようにすべてのオペランドを評価する必要がある演算子でそれを識別することを期待することを意味します。短絡は論理演算子から発生するため、これは、MathWorksの「論理短絡」セクションの短絡の別の定義に記述されている内容と一致しています。

三項演算子とも呼ばれるC三項演算子を説明しようとしているので、オペランドの2つだけを評価し、最初のオペランドを評価してから、2番目の演算子を評価します。最初の1つ。これは常に実行され、どのような状況でも3つすべてを評価することは想定されていないため、どのような場合でも「短絡」は発生しません。

いつものように、何かが正しくない場合は、反対票だけでなく、これに反対する議論をコメントに書き込んでください。これは、SOエクスペリエンスを悪化させるだけです。私たちは、反対票を投じるだけのコミュニティよりもはるかに優れたコミュニティになることができると信じています。同意しません。


3
そして、なぜ反対票を投じるのですか?間違ったことを修正できるようにコメントが欲しいのですが。建設的にしてください。
アドリアン・ペレス

2
100%On Topicではないため、おそらく-1です。しかし、とにかく私は+1を与えました。なぜなら、あなたの答えは少なくともそれがステートマンであると説明していて、それは間違ったompvではないように思われるからです。
dhein 2014年

4
@AdrianPerezは、ほとんどの人が三次演算子を短絡することを100%検討する理由について、私の回答のコメントセクションを参照してください。これは、一部のオペランドの値に基づいてすべてのオペランドを評価するわけではない式です。それは短絡です。
aruisdante 2014年

3
すべてのオペランドを評価しない非ブール演算子は考えられません。しかし、それは重要なことではありません。短絡と呼ばれる理由は、すべてのオペランドを評価しない演算子(または一般的な意味ではステートメントではなく式)であるため、正確です。オペレーターのタイプは実際には重要ではありません。短絡しないブール演算子を作成することは完全に可能であり、同様に短絡しない非ブール演算子を作成することもできます(例:0の整数多重化は常に0であるため、最初のオペランドが0の場合はすぐに0を返します)。
aruisdante 2014年

2
x = a ? b : 0;意味的には(a && (x = b)) || (x = 0);
jxh 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.