質問: インクリメント演算子をprintf文で使うことについて ◎1-------------------------------------- #include int main(void) { int i=0; while(i<=20){ printf("%d\t%d\t%d\t%d\t%d\n",++i,++i,++i,++i,++i); } return 0; } ------------------------------------------ ◎2-------------------------------------- #include int main(void) { int i=0; while(i<=20){ printf("%d\t%d\t%d\t%d\t%d\n",i++,i++,i++,i++,i++); } return 0; } ------------------------------------------ 以上2つのプログラムについて疑問があるのですが、まず◎1についてですが、実行結果は、 5 4 3 2 1 10 9 8 7 6 15 14 13 12 11 20 19 18 17 16 25 24 23 22 21 以上のようになるのですが、自分の考えでは、「printf("%d\t%d\t%d\t%d\t%d\n",i++,i++,i++,i++,i++);」部分の、一番最初の「++i」から処理が始まり、 1 2 3 4 5 ・ ・ ・ のようになる事を期待したのですが、一番最後の「++i」から処理が始まってしまいました。 次に、◎2についてですが実行結果は、 0 0 0 0 0 5 5 5 5 5 10 10 10 10 10 15 15 15 15 15 20 20 20 20 20 以上のようになりました。「printf("%d\t%d\t%d\t%d\t%d\n",i++,i++,i++,i++,i++);」部分で、iをいったん表示してから、iに1を加算するということで、次のiは1になっており、 4 3 2 1 0 ・ ・ ・ のようにまた一番最後の「i++」から処理され以上のようになると思ったのですが、そうはなりませんでした。 以上、◎1と◎2について何故こうなるのかご回答いただければ嬉しいです。
回答: 正解は既に回答があるとおり、「何が 起こるかわからない」「何が起こって も文句は言えない」です。 ただ、具体的な動作の例は、以下のよ うなものがあります。 ※これは、あくまでも、「あるコンパ イラはこういう動きをする」というだ けで、そうでなければ、ならないとか、 そのような動きを期待してもいいとか 言う意味ではありません。 c言語では、多くの場合、関数への引 数渡しは「スタック」が使われます。 結論から言えば、多くのコンパイラが、 引数を、「左から(先頭から)順に積み 上げ、反対側(後ろから)順に処理す る」という挙動をします。 引数の評価は、関数の呼び出し前(引 数をスタックに積み上げるとき)に行 われるので、多くの場合、引数は左 → 右の順に評価されます。 ただ、printf() のような「可変引数」 の関数は事情が異なってきます。 この場合、最初の引数を理解しないと、 引数の数すら把握できません。 従って、「可変引数」の関数は、逆に 「右から(先頭から)順に積み上げ、 反対側(前から)順に処理する」 ということになります。このため、引 数は通常とは逆に、後ろから(右 → 左)の順序で評価されることになります。 あと、++ を前置したときと後置したと きの挙動ですが、入門書には、時々 前置:インクリメントしてから、評価 後置:評価してからインクリメント と書かれていますが、これは、厳密に は間違いです。 正解は、 前置:評価結果はインクリメント後のもの 副作用として、インクリメント実施 後置:評価結果はインクリメントする前の もの、副作用としてインクリメント 実施 になります。 つまり、評価とインクリメントの時間的な 関係までは規定されていないのです。 ですから、 a = (i++) + (j++); の場合、 ・i を評価して、インクリメント ・j を評価して、インクリメント でもいいですし、 ・i を評価 ・j を評価 ・i をインクリメント ・j をインクリメント の順序で動作してもokです。 今回の例では、 ・i をそれぞれ評価して、 ・あとで、i をそれぞれインクリメント という動作にしているのでしょう。 (ただ、次の式が評価される「前」には終 わっている必要はあります。これが、回答 にある「副作用完了点」というものです) 以上は、あくまでも、「そういう例もある」 「そういう例なら、今回のような結果になる」 というだけで、「これが正しい動作」では ありません。