前提・実現したいこと
初めまして。
オラクルの資格本を参考にJavaの学習を行っております。
繰り返し文の章末問題の中で判らない箇所があったので、お教えいただきたく存じます。
該当のソースコード
class Test{
public static void main(String[] args){
int num = 1;
for(num = 0; num < 3; ++num ){
num *= 2;
}
System.out.println("num = " + (num++));
}
}
試したこと
上記のような繰り返し文が例題として挙がっています。
3つ目のカウンタ変数が先頭でインクリメントされていますが、
これは"num++"の場合と同様に、for文内の処理が完了したタイミングで+1されているのでしょうか。
実際に"num++"と"++num"の両方の場合で出力してみたところ、両者とも同じ結果が返ってはきたのですが、本当に同じ挙動として捉えて問題ないものか不安です。
初歩的な質問で恐縮ですが、よろしくお願いいたします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答4件
0
ベストアンサー
結論としては「問題ない」です。
理由は
for (初期化; 終了判定; 更新) { 文 }
において「更新」部分はそこに記述した「変数の値の変化」つまり副作用だけが重要だからです。
java
1int i = 0; 2int a = ++i; // (A) iはインクリメントされる。aはインクリメントされた後の値となる。 3int b = i++; // (B) iはインクリメントされる。bはインクリメントされる前の値となる。
インクリメント演算子++
が前置か後置であるかの違いは「変数の値の変化の違い」ではなく、「その式の結果の値の違い」なのです。「その式」ってどこを指すかといえば(A),(B)の代入演算子の右辺の式のことであり、それが影響するのはその式の値に影響される代入先の変数a, bの値です。
for文の「更新」の個所の記述は「式の結果の値は単に無視される」ので
for (num = ...; ...; ++num) { ... }
と書こうが
for (num = ...; ...; num++) { ... }
と書こうが違いはなく、「numがインクリメントされて1増える」という点だけが重要なのです。
蛇足:
細かいことを気にするなら「どちらの書き方がより効率のよいプログラムなのか」を気にする人はいるかもしれません。C/C++のプロの方だとコンパイルした結果の機械語を比べてみるなんて経験があるのではないでしょうか。Javaでもそうしたければコンパイル結果のバイトコードを調べることができるので実際やってみますと・・・
java
1public class For { 2 public static void main(String[] args) { 3 int i; 4 for (i = 0; i < 3; i++) { 5 } 6 for (i = 0; i < 3; ++i) { 7 } 8 } 9}
bash
1$ javap -v For.class 2... 3 public static void main(java.lang.String[]); 4 descriptor: ([Ljava/lang/String;)V 5 flags: (0x0009) ACC_PUBLIC, ACC_STATIC 6 Code: 7 stack=2, locals=2, args_size=1 8 0: iconst_0 9 1: istore_1 10 2: iload_1 11 3: iconst_3 12 4: if_icmpge 13 13 7: iinc 1, 1 # (A) 14 10: goto 2 15 13: iconst_0 16 14: istore_1 17 15: iload_1 18 16: iconst_3 19 17: if_icmpge 26 20 20: iinc 1, 1 # (B) 21 23: goto 15 22 26: return 23...
さてjavapの結果をみると(A),(B)の個所でわかるとおり「なんら違いがない」ことがわかると思います。さらに細かいことを考えるとJVMはJITコンパイラがバイトコードをネイティブな機械語に変換して実行するため本当に最終的にどうなるかは自分にはわかりません。JITのコンパイル結果を調べる方法を自分は知らないのです...(^^;
投稿2018/09/14 21:05
総合スコア18392
0
演算前にインクリメントするか、
演算後にインクリメントするかの違いです。
気になった挙動があればサンプルコード書いて試す癖をつけるといいですよ。
java
1public class memo_qa146694 { 2 3 public static void main(String[] args) { 4 5 int num1 = 1; 6 int ret1 = ++num1; 7 System.out.println("ret1:" + ret1); 8 9 int num2 = 1; 10 int ret2 = num2++; 11 System.out.println("ret2:" + ret2); 12 13 } 14} 15
ret1:2 ret2:1
投稿2018/09/14 21:04
総合スコア1009
0
勘違いさせるためのひっかけ問題的なものだと思いますね。
for文の変化式は、ブロック内の処理が終わったタイミングで評価されます。
これは絶対です。
なので、そこに前置インクリメントで書いても後置インクリメントで書いても挙動は変わらないです。
変化式にもっと複雑な式を書くなら、式内で評価順序が変わってくるのでなんらかの違いが出る可能性はあります。
for文内でコンソール出力でもやってみるといいと思います。
毎周のnum
がどう変化して何回出力されるのか。
どういう挙動をするかがすぐにわかります。
Oracleの問題ってそういう仕様の隅っこを突くみたいなもの多いですよね。
投稿2018/09/14 21:12
総合スコア1803
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/09/14 22:58
0
for の最後の num++, ++num の差については他の回答の通りです。
でも質問文にあるコードは クイズ的ですね。
実際のプログラム、教科書のサンプルとしても、こんなの書いたら駄目です。
理由:
num を for の外と中の両方で変更している。
説明:
for の前の num = 1 は全く無駄になっています。
int num; だけのほうがまだマシです。
for の body 中で num を変更してしまっているので、この for が実際には何回実行されるのかが判断しにくくなっている。
このばあいは for より while をつかう方が良いかもしれません。
for の終了条件部の num < 3 が num < 1000 に書き換えた時 for 抜けた後の System.println で何が表示されるかをすぐに答えることができますか?
このループを次のように書き換えれば、上の問題に答えやすくなると思います。
java
1 num = 0; 2 while (num < 1000) { 3 num = num * 2 + 1; 4 }
num は 2進数でかけば 0, 1, 11, 111, ... と変化していくのが num = num * 2 + 1;
の部分を読むだけで判明します。
for で書いていたときは *2 は for の body 部に、 +1 は for の 3 番目の部分に別れてしまっています。
つまり n の変化を追うのに 2 箇所を認識しないと正しく追えなくなっています。
実際に書いて実行してみます。
java
1public class TeraXXX { 2 public static void main(String[] args) { 3 int num; 4 for (num = 0; num < 1000; ++num) { 5 System.out.println(" num = " + num); 6 num *= 2; 7 } 8 System.out.println("num = " + (num++)); 9 10 num = 0; 11 while (num < 1000) { 12 System.out.println(" num = " + num); 13 num = num * 2 + 1; 14 } 15 System.out.println("num = " + (num++)); 16 } 17}
さらに言えば、 System.out.println の中身もダメです。
num の値を表示したのなら、素直に
"num = " + num
とすべきです。
ここを LLnum と num++ の差を理解していない人が
"num = " + (++num)
と変更してしまったら表示結果が変化してしまいます。
debug のための print 文のなかで変数を変化させてしまっていると、この print 文をカットしたらプログラムの動作も変わってしまいます。
関連情報
- scalaでは、なぜインクリメントやデクリメントができないのか?
投稿2018/09/15 04:24
総合スコア22324
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/09/14 23:03