이것이 왜 무한 루프에 빠 집니까? args) throws Exception {

다음 코드가 있습니다.

public class Tests {
    public static void main(String[] args) throws Exception {
        int x = 0;
        while(x<3) {
            x = x++;
            System.out.println(x);
        }
    }
}

우리는 그가 just x++또는 을 작성 했어야한다는 것을 알고 x=x+1있지만, x = x++우선 x그 자체에 귀속 하고 나중에 증가 시켜야 합니다. 왜 가치를 x계속 유지 0합니까?

–최신 정보

바이트 코드는 다음과 같습니다.

public class Tests extends java.lang.Object{
public Tests();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[])   throws java.lang.Exception;
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   iconst_3
   4:   if_icmpge   22
   7:   iload_1
   8:   iinc    1, 1
   11:  istore_1
   12:  getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   15:  iload_1
   16:  invokevirtual   #3; //Method java/io/PrintStream.println:(I)V
   19:  goto    2
   22:  return

}

이해하려고 하는 지침 에 대해 읽겠습니다 …



답변

참고 : 원래 C #을 사용하면 키워드 int를 참조하여 매개 변수 를 전달할 수 있으므로 설명을 위해이 답변에 C # 코드를 게시했습니다 ref. C #에서 수행 MutableInt하는 것과 비슷한 것을 찾기 위해 Google에서 찾은 첫 번째 클래스를 사용하여 실제 법적 Java 코드로 업데이트하기로 결정했습니다 ref. 그게 답을 돕는 지 또는 아프게하는지 알 수 없습니다. 필자는 개인적으로 그렇게 많은 Java 개발을하지 않았다고 말할 것이다. 내가 아는 한,이 점을 설명하는 훨씬 더 관용적 인 방법이있을 수 있습니다.


아마도 우리가하는 것과 동등한 것을 수행하는 방법을 x++작성하면 이것이 더 명확해질 것입니다.

public MutableInt postIncrement(MutableInt x) {
    int valueBeforeIncrement = x.intValue();
    x.add(1);
    return new MutableInt(valueBeforeIncrement);
}

권리? 전달 된 값을 증가시키고 원래 값을 리턴하십시오. 이것이 증분 후 연산자의 정의입니다.

이제 예제 코드에서이 동작이 어떻게 수행되는지 봅시다 :

MutableInt x = new MutableInt();
x = postIncrement(x);

postIncrement(x)무엇을합니까? 증가 x, 예. 그리고 무엇을 반환 x 했다 증가하기 전에 . 그런 다음이 반환 값이에 할당됩니다 x.

따라서 할당 된 값의 순서 x는 0, 1, 0입니다.

위의 내용을 다시 쓰면 더 명확해질 수 있습니다.

MutableInt x = new MutableInt();    // x is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
x = temp;                           // Now x is 0 again.

x위 과제의 왼쪽에서을로 바꾸면 y“먼저 x를 증가시키고 나중에 속성을 y로 표시하는 것을 볼 수 있습니다” 라는 사실에 대한 당신의 고정 은 혼란스러워합니다. 그것은 그것에 x할당 되지 않습니다 y; 그것에는 이전에 할당 된 값x . 실제로 주입 y은 위의 시나리오와 다르지 않습니다. 우리는 단순히 다음을 얻었습니다.

MutableInt x = new MutableInt();    // x is 0.
MutableInt y = new MutableInt();    // y is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
y = temp;                           // y is still 0.

따라서 분명합니다. x = x++x의 값을 효과적으로 변경하지 않습니다. 항상 x는 값 X가 원인이 0 이면, x 0 + 1을 입력 한 다음 X 0 다시.


업데이트 : 우연히, x위의 예제에서 증가 연산과 할당 사이에 1 “할당”이 할당되는 것을 의심하지 않도록 이 중간 값이 실제로 “존재”한다는 것을 설명하기 위해 빠른 데모를 함께 작성했습니다. 실행중인 스레드에서 “보이지”마십시오.

x = x++;별도의 스레드가 지속적으로 x콘솔에 값을 인쇄하는 동안 데모 는 루프를 호출합니다 .

public class Main {
    public static volatile int x = 0;

    public static void main(String[] args) {
        LoopingThread t = new LoopingThread();
        System.out.println("Starting background thread...");
        t.start();

        while (true) {
            x = x++;
        }
    }
}

class LoopingThread extends Thread {
    public @Override void run() {
        while (true) {
            System.out.println(Main.x);
        }
    }
}

아래는 위 프로그램 출력의 일부입니다. 1과 0이 불규칙적으로 나타납니다.

백그라운드 스레드 시작 중 ...
0
0
1
1
0
0
0
0
0
0
0
0
0
0
1
0
1

답변

x = x++ 다음과 같은 방식으로 작동합니다.

  • 먼저 expression을 평가합니다 x++. 이 표현식의 평가는 표현식 값 ( x증분 전의 값)과 증가를 생성 x합니다.
  • 나중에 표현식 값을에 할당하여 x증분 값을 덮어 씁니다.

따라서 이벤트 시퀀스는 다음과 같습니다 ( javap -c내 의견과 함께 생성 된 실제 디 컴파일 된 바이트 코드입니다 ).

   8 : iload_1 // 스택에서 x의 현재 값 기억
   9 : iinc 1, 1 // 증가 x (스택을 변경하지 않음)
   12 : istore_1 // 스택에서 x로 기억 된 값을 씁니다.

비교를 위해 x = ++x:

   8 : iinc 1, 1 // 증가 x
   11 : iload_1 // x의 값을 스택에 푸시
   12 : istore_1 // 스택에서 x로 값 팝

답변

이는 값 x이 전혀 증가하지 않기 때문에 발생 합니다.

x = x++;

에 해당

int temp = x;
x++;
x = temp;

설명:

이 작업을위한 바이트 코드를 보자. 샘플 클래스를 고려하십시오.

class test {
    public static void main(String[] args) {
        int i=0;
        i=i++;
    }
}

이제 클래스 디스어셈블러를 실행하면 다음과 같은 결과를 얻습니다.

$ javap -c test
Compiled from "test.java"
class test extends java.lang.Object{
test();
  Code:
   0:    aload_0
   1:    invokespecial    #1; //Method java/lang/Object."<init>":()V
   4:    return

public static void main(java.lang.String[]);
  Code:
   0:    iconst_0
   1:    istore_1
   2:    iload_1
   3:    iinc    1, 1
   6:    istore_1
   7:    return
}

이제 Java VM 은 스택을 기반으로하므로 각 작업마다 데이터가 스택으로 푸시되고 스택에서 데이터가 팝업되어 작업을 수행합니다. 또 다른 데이터 구조, 일반적으로 지역 변수를 저장하는 배열이 있습니다. 지역 변수에는 배열에 대한 색인 인 id가 제공됩니다.

방법론 의 니모닉 을 살펴 보자 main().

  • iconst_0: 상수 값 0
    이 스택으로 푸시됩니다.
  • istore_1: 스택의 최상위 요소가 튀어 나와 인덱스가 1
    있는 로컬 변수에 저장됩니다 x.
  • iload_1: 위치의 값 1의 값 x
    이는이 0스택으로 푸시된다.
  • iinc 1, 1: 메모리 위치의 값 1이 씩 증가합니다 1. 그래서 x지금이된다
    1.
  • istore_1: 스택 상단의 값이 메모리 위치에 저장됩니다 1. 즉됩니다 0에 할당 x 덮어 쓰기 의 증분 값입니다.

따라서 값이 x변경되지 않아 무한 루프가 발생합니다.


답변

  1. 접두사 표기법은 표현식이 평가되기 전에 변수를 증가시킵니다.
  2. 접미사 표기법은 표현식 평가 후에 증가합니다.

그러나 ” =“는 ” “보다 연산자 우선 순위가 낮습니다 ++.

따라서 x=x++;다음과 같이 평가해야합니다

  1. x 과제 준비 (평가)
  2. x 증분
  3. 에 이전 값이 x할당되었습니다 x.

답변

꽤 주목되는 답변은 없으므로 다음과 같이하십시오.

당신이 글을 쓸 때 int x = x++, 당신은 x그 자체로 새로운 가치를 부여받지 않고 x, x++표현 의 반환 가치를 부여 받습니다. Colin Cochrane의 답변x 에서 암시 된 것처럼 원래 값은 입니다.

재미있게 다음 코드를 테스트하십시오.

public class Autoincrement {
        public static void main(String[] args) {
                int x = 0;
                System.out.println(x++);
                System.out.println(x);
        }
}

결과는

0
1

식의 반환 값은 초기 값 인 x0입니다. 그러나 나중에의 값을 읽을 때 x업데이트 된 값을받습니다.


답변

이미 다른 사람들에 의해 잘 설명되어 있습니다. 관련 Java 사양 섹션에 대한 링크 만 포함합니다.

x = x ++는 표현식입니다. Java는 평가 순서 를 따릅니다 . 먼저 x ++ 표현식을 평가하여 x 를 증가시키고 결과 값을 이전 값 x로 설정 합니다. 그런 다음 표현식 결과 를 변수 x에 할당합니다 . 결국 x는 이전 값으로 돌아갑니다.


답변

이 진술 :

x = x++;

다음과 같이 평가됩니다.

  1. x스택에 밀어 넣 습니다.
  2. 증가 x;
  3. x스택에서 튀어 나옵니다.

따라서 값은 변경되지 않습니다. 이것을 다음과 비교하십시오.

x = ++x;

다음과 같이 평가됩니다.

  1. 증가 x;
  2. x스택에 밀어 넣 습니다.
  3. x스택에서 튀어 나옵니다.

당신이 원하는 것은 :

while (x < 3) {
  x++;
  System.out.println(x);
}