質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

4回答

11903閲覧

Javaのfor文とカウンタ変数について

punyupunyu1

総合スコア13

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2018/09/14 19:38

前提・実現したいこと

初めまして。
オラクルの資格本を参考に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ページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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

KSwordOfHaste

総合スコア18392

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

punyupunyu1

2018/09/14 23:03

ご回答頂き、誠にありがとうございます。 バイトコードを見るという手もあるのですね。 何故結果が変わらないのかという点について、理解を深めることができました。
guest

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

opyon

総合スコア1009

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

punyupunyu1

2018/09/14 22:51

回答頂き、誠にありがとうございます。 前後のインクリメントの挙動については、記載頂いた通りかと存じます。 ただ、for文に使われているカウンタ変数のインクリメントされるタイミングが、前置か後置かで変わってくるのかが気になっておりました。 仰る通り、サンプルコードを書きながら学習した方が理解は深められそうですね。 早速試してみようと思います。
opyon

2018/09/14 22:57

それなら@spookybirdさんの回答の通りだと思います。 for文のカウンタとして「単体」で使うなら、どちらでも同じとなるでしょう。
guest

0

勘違いさせるためのひっかけ問題的なものだと思いますね。

for文の変化式は、ブロック内の処理が終わったタイミングで評価されます。
これは絶対です。
なので、そこに前置インクリメントで書いても後置インクリメントで書いても挙動は変わらないです。
変化式にもっと複雑な式を書くなら、式内で評価順序が変わってくるのでなんらかの違いが出る可能性はあります。

for文内でコンソール出力でもやってみるといいと思います。
毎周のnumがどう変化して何回出力されるのか。
どういう挙動をするかがすぐにわかります。

Oracleの問題ってそういう仕様の隅っこを突くみたいなもの多いですよね。

投稿2018/09/14 21:12

spookybird

総合スコア1803

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

punyupunyu1

2018/09/14 22:58

ご回答頂き、誠にありがとうございます。 前置・後置に関係なく、ブロックの処理が終わったタイミングなのですね。覚えておきます。 引っ掛かっていた部分がスッキリしました。
guest

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では、なぜインクリメントやデクリメントができないのか?

https://xuwei-k.hatenablog.com/entry/20110130/1296360331

投稿2018/09/15 04:24

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問