+(+ k--)Cの式


9

この質問は、次のコードの出力を通知する必要があるテストで見ました。

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
    k=k++;
    printf("%d\n", k);  
    return 0;
}

出力は-1です。なぜこれが答えなのかはわかりません。

+(+k--)Cでの表現の意味は?


4
このようにフォーマットされていましたか?それは意味です;-)
ピーター-モニカを復活させる

3
ヒント:このようなBSコードを実際に書くことはありません。
Jabberwocky

5
これはUBではありません。私の答えを見てください。
dbush

@ Peter-ReinstateMonicaはい、このようにフォーマットされました。
Ankur Gautam

2
その不自然な性質と奇妙なねじれのために- k=k++未定義ですが、難読化された状態のために実行されることはないため、未定義ではありません-私はこの質問の複製として閉じることに投票します。
スティーブサミット

回答:


10

[念のため、この回答は承認されて投票されたため、かなり大幅に編集しました。ただし、基本的には同じことを言っています。]

このコードは深く、おそらく故意に、混乱しています。これには、恐怖の未定義の動作の狭く平均化されたインスタンスが含まれています。この質問を作成した人物が非常に賢いか、非常に愚かかを判断することは基本的に不可能です。そして、このコードがあなたに教えるかクイズすることを意図しているかもしれない-つまり、単項プラス演算子はあまり機能しない-確かに、この種の破壊的な誤解に値するほど重要なものではない。

奇妙な条件であるコードには、2つの混乱する側面があります。

while(+(+k--)!=0)

そしてそれが制御する認知症の声明:

k=k++;

最初に2番目の部分をカバーします。

このような変数をk1つずつ増やしたい場合、Cは1つではなく、2つではなく、3つではなく、4つの異なる方法でそれを行います。

  1. k = k + 1
  2. k += 1
  3. ++k
  4. k++

この報奨金にもかかわらず(またはおそらくそのため)、一部のプログラマーは混乱し、次のようなゆがみを吐き出します

k = k++;

これが何をするべきか理解できなくても心配しないでください。誰もできません。この式にはk、の値を変更する2つの異なる試み(k =部分とk++部分)が含まれています。Cには、試行された変更のどれが「勝つ」かを示す規則がないため、このような式は正式には未定義であり、それだけでなく、それは明確な意味はありませんが、それを含むプログラム全体が疑わしいということです。

あなたが見れば今、非常に慎重に、あなたは、この特定のプログラムでは、ラインがいることがわかりますk = k++制御条件が最初に偽であるので、ループは0回を実行します(私たちが見るしようとしているとして)ので、実際に、実行されません。 。したがって、この特定のプログラムは実際には未定義ではない可能性がありますが、それでも病理学的に混乱します。

この種の未定義の動作に関するすべての質問に対するこれらの正規のSOの回答も参照してください。

しかし、そのk=k++部分については尋ねませんでした。最初の紛らわしい部分、+(+k--)!=0状態について尋ねました。それはので、これは、奇妙に見えている奇妙な。誰もそのようなコードを実際のプログラムで書くことは決してありません。それを理解する方法を学ぶ理由はありません。(はい、そのとおりです。システムの境界を探索することは、その細かい点について学ぶのに役立ちますが、私の本には、想像力に富む示唆に富む探索と、ぶつかり合った虐待的な探索との間にかなり明確な境界があり、この表現は非常に明確ですその行の反対側。)

とにかく調べてみましょう+(+k--)!=0。(そして、そうした後は、そのすべてを忘れましょう。)このような表現は、内側から理解する必要があります。あなたは何を知っていると思います

k--

します。それはkの現在の値を取り、それを式の残りの部分に「戻し」、多かれ少なかれ同時に減少しますk。つまり、量をにk-1戻しkます。

しかし、それでは何をし+ますか?これは単項プラスであり、バイナリプラスではありません。単項マイナスのようなものです。あなたは、バイナリマイナスが減算を行うことを知っています:式

a - b

aからbを減算します。そして、あなたは単項マイナスが物事を否定することを知っています:式

-a

aのネガを与える。単項関数+が行うことは...基本的には何もしません。 正の値を正の値に、負の値を負の値に変更した後のの値+aを示しますa。だから式

+k--

与えられたものk--、つまりkの古い値を与えます。

しかし、私たちは完了していません。

+(+k--)

これ+k--はあなたに与えられたものを取り、単項+を再び適用します。だから、それはあなたに+k--与えたものk--、あなたに与えたもの、それはk古い価値でした。

結局、状態

while(+(+k--)!=0)

ずっと普通の状態とまったく同じことをする

while(k-- != 0)

行っているだろう。(これは、さらに複雑な外観の条件while(+(+(+(+k--)))!=0)と同じことを行います。また、これらの括弧は実際には必要ありません。同じことを行いwhile(+ + + +k--!=0)ます。)

「通常の」状態を把握する

while(k-- != 0)

ちょっとトリッキーです。このループでは、次の2つの処理が行われます。ループが複数回実行される可能性があるため、次のようにします。

  1. やり続けるk--にするために、kますます小さくするだけでなく、
  2. ループの本体を実行し続けます。

ただしk--、ループをもう一度実行するかどうかを決定する前(または処理中)に、この部分をすぐに実行します。また、デクリメントする前にk--、の古い値を「返す」ことを覚えておいてkください。このプログラムでは、の初期値kは0です。したがってk--、古い値0を「返し」、次にk-1に更新します。しかし、残りの条件は!= 0-ですが、今見たように、最初に条件をテストしたときに、0が得られました。ループを実行しないため、実行を試みません。問題のあるステートメントk=k++です。

つまり、この特定のループでは、「2種類のことが起こっている」と言いましたが、1が1回発生し、2が0回発生していることがわかります。

とにかく、プログラムのこのような言い訳が不十分で、最終的に-1と表示される理由が十分に明らかになったと思いますk。通常、私はこのようなクイズの質問に答えるのは好きではありません-不正行為のように感じます-この場合、私は練習のポイント全体に激しく同意しないので、私は気にしません。


1
考案された例は、どの基本クラスでも典型的です。他にどのように試験でのオペレーターの優先順位について簡潔な質問を書くでしょうか 私は数学を教えており、生徒が「実生活でこれをやるのはいつですか?」と尋ねるたびにニッケルがあったら...
スコット

4
@スコット工夫があり、工夫がされている。あなたがドライバートレーニングのインストラクターだとします。学生に、野球のバットで頭を殴打しながら、繁華街の渋滞をドライブするように依頼するとします。おそらく、学生は潜在的に有用なスキルを学びます。しかし、私はこれを虐待と呼んでいます。そして、私はこの質問のコードは虐待的であり、否定的な教育的価値があるという私の意見を支持します。
Steve Summit

1
「私は自分の意見を支持する」これは公平ですが、ここのキーワードは「意見」です。StackOverflowの回答は、おそらく自分の意見を述べるのに最適な場所ではありません。:)
スコット

1
念のため、この回答は承認されて投票されたので、かなり大幅に編集しました。それでも基本的には同じことを言っています。
スティーブサミット

11

一見すると、このコードは未定義の動作を呼び出すように見えますが、そうではありません。

まず、コードを正しくフォーマットします。

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
        k=k++;
    printf("%d\n", k);  
    return 0;
}

これで、ステートメントk=k++;がループ内にあることがわかります。

次に、プログラムをトレースしてみましょう。

ループ条件が最初に評価されたときのk値は0です。式k--現在kは0でありk、副作用として減少します。したがって、このステートメントの後の値kは-1です。

+この式の先頭は値に影響しないため+k--、0と+(+k--)評価され、同様に0と評価されます。

次に、!=演算子が評価されます。以来0!=0falseで、ループの本体が入力されていません。本文が入力されている場合、シーケンスポイントなしでk=k++読み取りと書き込みの両方がk行われるため、未定義の動作が発生します。しかし、ループに入っていないため、UBはありません。

最後にk-1 の値が出力されます。


1
実行されない未定義の動作が未定義でないかどうかは、オープンで実存的、哲学的、不可解な質問です。未定義ではないですか?私はそうは思いません。
Steve Summit

2
@SteveSummitそれについて存在、哲学的、不可解なことは絶対にありません。if (x != NULL) *x = 42;これは未定義x == NULLですか?もちろん違います。未定義の動作は、実行されないコードの部分では発生しません。行動という言葉はヒントです。実行されないコードには、未定義な​​どの動作はありません。
n。「代名詞」m。

1
@SteveSummit実行されない未定義の動作がプログラム全体を無効にする場合、Cプログラムは動作を定義していません。Cプログラムは常にUBの実行から離れたループ/ if条件だからです。例として単純な配列反復を考えてみます。バインドされたチェックが失敗するまで反復します。次のループ反復の実行は未定義の動作になりますが、それが実行されることはないため、問題はありません。
cmaster

3
私は言語弁護士をしていないので、私はそれについて議論するつもりはありません。しかし、もしあなたがこの質問をしてそれを言語弁護士にタグ付けしたなら、きっと活発な議論が始まるでしょう。k=k++は定性的にとは異なることに注意してください*x=42xが有効なポインターである場合、後者は明確に定義されていますが、前者は何があっても未定義です。(私はあなたが正しいかもしれないと認めますが、私はそれについて断言するつもりはありません、そして私たちが巧妙にトロールされてきたことをますます心配しています。)
Steve Summit

1
「xが有効なポインターである場合、後者は明確に定義されていますが、前者は何であっても未定義です。」プログラム全体だけが未定義の動作をする可能性があります。この概念は、単独で取得されたコードフラグメントには適用されません。
n。「代名詞」m。

3

これは、演算子の優先順位を示すバージョンです。

+(+(k--))

2つの単項+演算子は何も実行しないため、この式はとまったく同じk--です。これを書いた人はおそらくあなたの心を台無しにしようとしていました。

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