みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

後置インクリメントの挙動が不明で困ってます

 Eclipse 4.7 Java 8でプログラミングをやっています。
いまスレッドのプログラミングをやっているのですが、後置インクリメント
の挙動がC言語の時と違っていて戸惑っています。
問題は、変数 idx でこの変数の値が加算されていきません。
条件演算子がある箇所で加算しているのですが、後置インクリメントでも
加算されていくはずですよね?
しかしそうなりません。値は0のままです。
なぜそうなるのか分からないので質問しました。

答えられたらよろしくお願いします。

import static java.lang.System.out;

import java.applet.Applet;
import java.awt.Graphics;

public class JavaThread4 extends Applet implements Runnable
{
// TODO 自動生成されたメソッド・スタブ
final String HelloWorld = "Hello World";
volatile int idx;

Thread helloThread = null;

public void init()
{
idx = 0;
out.println("init");
}

public void paint(Graphics g)
{
g.drawString(HelloWorld.substring(0, idx), 30, 30);
}

public void start() {
out.println("start");
if(helloThread == null) {
helloThread = new Thread(this);
helloThread.start();
}
}

public void run() {
out.println("run");
for(;;) {
try {
Thread.sleep(400);
}catch(InterruptedException e) {

}
out.println(" idx = " + idx);
idx = (idx < HelloWorld.length()) ? idx++ : 0;//この部分が問題の部分です
repaint();
}
}
}

投稿日時 - 2017-08-04 00:48:38

QNo.9359104

困ってます

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

idx = (idx < HelloWorld.length()) ? idx++ : 0;

if ( idx < HelloWorld.length() )
 idx = idx++;
else
 idx = 0;
と同等かと思いますが
普通は、idx=idx++;なんて書かずに、idx++;と書くので気にすることはないですが
たしかにC言語だと、環境かコンパイラによって挙動が違うようです。

idx=0;
idx = idx++;
printf("idx = %d\n" , idx);
でテストしたら
gcc4.1(32bitOS)だと、idx = 1
gcc4.8(64bitOS)だと、idx = 0
となりました。


なんとなくですが、
idx = idx++;

右辺の計算結果の0を左辺のidxに代入したあとでidxインクリメントするのか
右辺の計算後に、idxをインクリメントしてから右辺の計算結果の0を左辺のidxへの代入をしているか
の違いのような感じです。
(コンパイラの最適化のせいなのか、アーキテクチャの違いなのかわかりませんが)

ともかく、
idx = idx++;
のような挙動が不安定なコーディングは避けたほうがよいと思います。

投稿日時 - 2017-08-04 18:06:29

お礼

インクリメントが内部でどうなっているのか理解する必要がありそうです。
>右辺の計算後に、idxをインクリメントしてから右辺の計算結果の0を左辺のi>dxへの代入をしているか
そういった挙動をする可能性もあるんですね。
勉強になりました。

投稿日時 - 2017-08-04 20:02:42

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(6)

ANo.6

> No.5
説明がわかりにくかったですかね。

C言語でも、No.3で言われているような 前置インクリメント、後置インクリメントの
仕様は、当然のことながら決定されていて、同様の挙動になります。

しかし、今回のような
同じ変数への 後置インクリメントと代入が同時に行われるようなコーディングをしてしまうと、コンパイラの最適化やアーキテクチャによっては
質問者様が、
> 後置インクリメントの挙動がC言語の時と違っていて戸惑っています。
と言われているような、idxがインクリメントしてしまうこともあれば
No1でご回答されているように
> その後 idxの評価された値(インクリメント前の値)が設定されるのでidxの値は変わりません。
といこともあるようなので、

「そもそもC言語の時点で、同じ変数にインクリと代入を同時に行って
どちらが先に実行されるかわからないような、不安定なコーディングを
していたのが問題では」という点を 強調したかったのです。

投稿日時 - 2017-08-04 20:15:36

ANo.5

>回答No.4 superside0

>C言語だと、環境かコンパイラによって挙動が違うようです。

Javaの場合は言語仕様として挙動は決定されています、詳細は「回答No.3 amanojaku1」参照。
それは さておき

>idx = (idx < HelloWorld.length()) ? idx++ : 0;

↑これを具体的に どうすれば良いのかと言えば、下記のように修正すれば良いと思われます。

idx = (idx < HelloWorld.length()) ? idx+1 : 0;

投稿日時 - 2017-08-04 18:53:10

お礼

ありがとうございます。
じつは参考書に書いてあるソースを
idx = (idx < HelloWorld.length()) ? idx++ : 0;
のようにしたら、この結果になったんです。

参考書には
idx = (idx < HelloWorld.length()) ? idx+1 : 0;
と書いてありました。 もしかしたらこのことを考慮して、こうしたの
かもしれません。
これなら問題なく1加算されています。

投稿日時 - 2017-08-04 20:06:13

ANo.3

処理の説明は[回答No.1 wormhole]さんの方を参照して下さい。

前置と後置で処理が違うようです。

int a = 7;
int b;
b = a++;

↑この場合、内部的には 下記ような処理になり

b = a;
a = a + 1;

↑結果は「a=8」、「b=7」になります。


int a = 7;
int b;
b = ++a;

↑この場合、内部的には 下記ような処理になり

a = a + 1;
b = a;

↑結果は「a=8」、「b=8」になります。

投稿日時 - 2017-08-04 16:24:36

ANo.2

#1です。
ちなみにCの次のソースを試してみましたが

#include <stdio.h>

int
main(int argc, char **argv)
{
int idx = 0;
int length = 10;
for (;;) {
printf("%d\n", idx);
idx = (idx < length) ? idx++ : 0;
}
return 0;
}

延々と0が出力されます。
環境はFreeBSD-10.3で試したCコンパイラは
FreeBSD clang version 3.4.1 (tags/RELEASE_34/dot1-final 208032) 20140512
gcc6 (FreeBSD Ports Collection) 6.4.0
の2つです。

投稿日時 - 2017-08-04 01:53:50

お礼

なぜなんでしょう?
ちなみにC言語の環境はVS2012Express DeskTopです。
環境によって動作が違うだけなんでしょうか?

投稿日時 - 2017-08-04 19:57:45

ANo.1

>idx = (idx < HelloWorld.length()) ? idx++ : 0;//この部分が問題の部分です

元々、問題あるコーディングかと。
idx < HelloWorld.length() が真の時には、
idxの値が評価後、idxの値がインクリメントされます。
その後 idxの評価された値(インクリメント前の値)が設定されるのでidxの値は変わりません。

投稿日時 - 2017-08-04 01:11:38

お礼

つまりインクリメントしているがidxには1が代入されないという理解で
いいでしょうか?
1加算されず0が代入され、その後にインクリメントされるというわけでは
ないという解釈でいいのでしょうか?

投稿日時 - 2017-08-04 19:59:52

あなたにオススメの質問