• ベストアンサー

c programming language

#include<stdio.h> int main(void) { int i =2, j=1, k; k=(i+=2,j--)&&(i+=2,j--)&&(i+=2,j--); printf("i=%d,j=%d,k=%d\n",i,j,k); } この時のoutputは i=6,j=-1,k=0です 何でこんな結果が出たのでしょ? kはなぜ0ですか? (i+2,j--)は+-*/の中で何もしなかったのに&&したのでfalseの0が出たんですか?

質問者が選んだベストアンサー

  • ベストアンサー
回答No.3

>kはなぜ0ですか? && 演算子は、左辺と右辺の式を評価して両辺が非ゼロであれば、 1 を片辺または両辺がゼロであれば 0 を返します。 下のコードを実行してみてください。 printf("1 && 1 --> %d\n", 1 && 1); printf("1 && 0 --> %d\n", 1 && 0); printf("0 && 1 --> %d\n", 0 && 1); printf("0 && 0 --> %d\n", 0 && 0); >(i+2,j--)は+-*/の中で何もしなかったのに&&したのでfalseの0が出たんですか? 誤植でしょうか? こちら、(i+=2,j--) としてお答えします。 (i+=2, j--) というのは、 , (comma)演算子が働きます。左辺を評価(実行)し、右辺の評価結果が式の値になります。つまり、 i+=2 と j-- が実行され、式の値は 0 になります。 下のコードを実行してみてください。 int a = 1, b = 2, c = 0; printf("%d:c --> %d\n", __LINE__, c); c = a , b; // c = b printf("%d:c --> %d\n", __LINE__, c); c = b , a; // c = a printf("%d:c --> %d\n", __LINE__, c); >何でこんな結果が出たのでしょ? この行(k=(i+=2,j--)&&(i+=2,j--)&&(i+=2,j--);)の実行結果について整理すると k= (i+=2,j--) // i+=2 実行されます。 j-- 実行されます。 && (i+=2,j--) // i+=2 実行されます。 j-- 実行されます。 && (i+=2,j--); // 実行されません。省略されます。 && 演算子は、実行効率を考慮して、式の評価を省略することがあります。 上記の文は、下のようになります。 式1 && 式2 && 式3 このとき、 && 演算子は左から順番に評価されるため、まず、式1が評価されますが、式1と式2が括弧演算子で囲まれているため括弧内の演算を先に行います。つまり、式1と式2を評価します。 式1 は、(i+=2,j--) とあるので、i+=2 が実行され i が 4 になります。 , (comma)演算子で続く、 j-- が実行され j が 0 になります。 次に式2が評価されます。式1と同じように (i+=2,j--) とあるので i が 6 になります。 j は -1 になります。 ここで、式1と式2の評価が終わったので、 && 演算が行われます。 式1 の評価は 0 式2 の評価は -1 となるので && 演算の結果は、式3を評価せずとも偽 (false) と確定するため、式3は評価されずに k に 0 (false) が設定されます。 このような式3の評価が省略される論理演算を行うことから、&&はショートカット演算子と呼ばれます。 実行効率がよくなる特徴がありますが、式3が評価されないという性質は一般に混乱をきたすため論理演算と算術演算を混ぜることは好ましくありません。 正確な理解をするために、次の括弧演算子付きと括弧演算子なしの実行結果を見比べてみてください。 int a = 0, b = 0, c = 0; a && b++ && c++; printf("a --> %d b --> %d c --> %d\n", a, b, c); int a = 0, b = 0, c = 0; (a) && (b++) && (c++); printf("a --> %d b --> %d c --> %d\n", a, b, c); あと、 +-*/ は、算術演算(+-*/)と書いた方が伝わりやすいと思います。

その他の回答 (5)

回答No.6

ちょっとだけ補足。 && と || は「左から右に向かって演算し、結果が確定したところでそれ以降の演算を行わない」というのが、「仕様」です。 これは、必ずしも効率を狙ったというだけではありません。 たとえば、 const int N = 10; int data[N]; int i; for(i = 0; i < N && data[i] != 0; i++); // セミコロンの位置に注意 などというソースが存在します。 このループを抜けた時点で、i は、data[] の「最初に見つかったゼロのインデックス」になります。 また、data[] にゼロが含まれない場合、i == N となってループを抜けます。 このときに、&& の評価順序が仕様上決定しているので、必ず、 ・i < N をチェック、これが偽の場合この時点で評価終了 ・i < N が真なら、data[i] の値を確認 という動作をします。 つまり、i == 10 のとき、data[i] (つまり、data[10])をアクセスすることはないわけです。 これが、「必ず全部を評価してから、最終結果を決める」としたら、i == 10 のとき、 ・ i < N を評価しておいて、 ・ data[i] (つまり、data[10])にアクセスして、 ・ その結果から、真偽を決定 となります。data[10] をアクセスしてしまうわけですね。 || の場合は、たとえば、探索の優先順序を決めたりする処理に使えます。 たとえば、include "foo.h" を探索する順序は、多くの処理系では ・ソースの存在するディレクトリ ・システムが指定しているディレクトリ です。 これを探すのに、 bool findInclude(char *folder, char *fileName, char *fullPath); という関数(folder で、fileName を探して、fullPath にフルパスの形でセットする。ファイルがあれば、true を返す) を使って、 char fullPath[1024]; if (findInclude(sourecePath, "test.h", fullPath) || findInclude(systemPath, "test.h", fullPath)) { // inlude は、fullPath にセットされた } else { // include file が見つからない } というロジックで(探索順序を含めて)include ファイルの探索を行うことができます。

  • titokani
  • ベストアンサー率19% (341/1726)
回答No.5

&&演算子は、まず左辺を評価します。 左辺が0であった場合は、式の値はそのまま0です。右辺の評価は行いません。 左辺が0以外であった場合は、右辺の値を評価します。 ここで右辺が0であった場合は式の値は0です。 右辺が0以外であった場合は、式の値は1です。 カンマ演算子は、左辺と右辺を両方評価した後に、左辺を式の値とします。 +=演算子は、左辺の変数の値に右辺の値を加えたあと、その加えた値を変数に代入します。 式の値は、加えた後の変数の値となります。 後置--演算子は、変数の値から1を引いて、その引いた値を変数に代入します。 式の値は、1を引く前の変数の値となります。 これをふまえて、 (i+=2,j--)&&(i+=2,j--)&&(i+=2,j--); この式を考えます。 &&演算子の動作より、まず一番左にある (i+=2,j--) が評価されます。 カンマ演算子の動作より、 i+=2と j-- とが評価されます。 i+=2 は、iの値に2を加えて、その結果を値とします。 iの値は2に初期化されていますから、iの値は4となって、式の値も4です。 j--はjから1を引きますが、値は引く前の値です。 jの値は0となりますが、式の値は1です。 したがって、 (i+=2,j--) の値はカンマ演算子の動作より、左辺の値となりますので、j--の値である1となります。 これは0以外ですから、一つ目の&&の右辺、すなわち2つ目の(i+=2,j--)の評価を行います。 2つ目の(i+=2,j--)の評価も同様に行うことができます。 結果、iの値は6となり、jの値は-1,式の値は0となります。 従って、 (i+=2,j--)&&(i+=2,j--) の値は0となります。 2つめの&&の左辺が0となりましたので、右辺、つまり3つ目の(i+=2,j--)の評価は行われません。 したがって、 (i+=2,j--)&&(i+=2,j--)&&(i+=2,j--) この式を評価した最終結果としては、 iの値は6、jの値は-1、式の値は0となります。 式の値が0なので、kには0が代入されます。 したがって、printfで表示される値は、 i=6,j=-1,k=0 となります。 以上です。

yawara18
質問者

お礼

そうですね 最初にjが0になったけどj--だからまだj=1 それで&&をするんですね

  • okormazd
  • ベストアンサー率50% (1224/2412)
回答No.4

#2です。 「もう、k=0なので、ANDしても0とわかっているので、コンパイラは「もうやめた」」 不正確な書き方なので、削除・訂正。 K=0とのANDは、相手がなんであっても0なので、効率化のためにk=0のときはANDをしないようなコードをコンパイラが生成する。 実行系はそれにしたがって、実行するので、k=0でのANDは実行されない。したがって、j=8などとなることはない。 なお、コンパイラの設定によっては、無条件でANDをするようなコードを生成できるのかもしれないが、私は、やったことはありません。

  • okormazd
  • ベストアンサー率50% (1224/2412)
回答No.2

「kはなぜ0ですか?」 k=(i+=2,j--) で、i=4、k=1、j=0です。 k=(i+=2,j--)&&(i+=2,j--) で、kはj=0とのANDで0、i=6、j=-1です。 k=(i+=2,j--)&&(i+=2,j--)&&(i+=2,j--) もう、k=0なので、ANDしても0とわかっているので、コンパイラは「もうやめた」 で、 i=6,j=-1,k=0です。

回答No.1

>kはなぜ0ですか? 最初の j-- でjが0になり、2つ目の j-- が 0 を返すから。