일부 Java 텍스트를 읽고 다음 코드를 받았습니다.
int[] a = {4,4};
int b = 1;
a[b] = b = 0;
본문에서 저자는 명확한 설명을하지 않았으며 마지막 줄의 효과는 다음과 같습니다. a[1] = 0;
이해가 잘 안됩니다. 평가는 어떻게 이루어 졌습니까?
일부 Java 텍스트를 읽고 다음 코드를 받았습니다.
int[] a = {4,4};
int b = 1;
a[b] = b = 0;
본문에서 저자는 명확한 설명을하지 않았으며 마지막 줄의 효과는 다음과 같습니다. a[1] = 0;
이해가 잘 안됩니다. 평가는 어떻게 이루어 졌습니까?
답변:
사람들이 항상 이것을 오해하기 때문에 이것을 매우 명확하게 말하겠습니다.
하위 표현식의 평가 순서는 연관성 및 우선 순위와 무관 합니다. 연관성과 우선 순위는 연산자 가 실행되는 순서를 결정 하지만 하위 표현식 이 평가 되는 순서는 결정 하지 않습니다 . 귀하의 질문은 하위 표현식 이 평가 되는 순서에 관한 것 입니다.
고려하십시오 A() + B() + C() * D()
. 곱셈은 덧셈보다 우선 순위가 높고 덧셈은 왼쪽 연관성이므로 이것은 (A() + B()) + (C() * D())
첫 번째 덧셈이 두 번째 덧셈 이전에 발생하고 곱셈이 두 번째 덧셈 이전에 발생한다는 것을 알려주 는 것과 동일합니다 . A (), B (), C () 및 D ()가 어떤 순서로 호출되는지 알려주지 않습니다! (또한 곱셈이 첫 번째 덧셈 전후에 발생하는지 여부를 알려주지 않습니다.) 다음과 같이 컴파일하여 우선 순위 및 연관성 규칙을 완벽하게 준수 할 수 있습니다 .
d = D() // these four computations can happen in any order
b = B()
c = C()
a = A()
sum = a + b // these two computations can happen in any order
product = c * d
result = sum + product // this has to happen last
모든 우선 순위 및 연관성 규칙이 거기에 따릅니다. 첫 번째 더하기는 두 번째 더하기 전에 발생하고 곱하기는 두 번째 더하기 전에 발생합니다. 분명히 우리는 A (), B (), C () 및 D ()에 대한 호출을 순서 에 관계없이 수행 할 수 있으며 여전히 우선 순위 및 연관성 규칙을 준수합니다!
하위 표현식이 평가되는 순서를 설명하려면 우선 순위 및 연관성 규칙 과 관련이없는 규칙 이 필요합니다 . Java (및 C #)의 관련 규칙은 "하위 표현식은 왼쪽에서 오른쪽으로 평가됩니다"입니다. A ()가 C ()의 왼쪽에 나타나기 때문에 C ()가 곱셈에 관련되고 A ()가 덧셈에만 관련된다는 사실에 관계없이 A ()가 먼저 평가 됩니다.
이제 질문에 답할 수있는 충분한 정보가 있습니다. 에서 a[b] = b = 0
연관성 말의 규칙이 있음을 a[b] = (b = 0);
하지만 그 뜻은 아닙니다 b=0
첫번째 실행을! 우선 순위 규칙은 인덱싱이 할당보다 우선 순위가 높다고 말하지만 인덱서가 가장 오른쪽 할당보다 먼저 실행된다는 것을 의미하지는 않습니다. .
(업데이트 :이 답변의 이전 버전에는 내가 수정 한 섹션에 작고 실질적으로 중요하지 않은 누락이 있습니다.이 규칙이 Java 및 C #에서 왜 합리적인지 설명하는 블로그 기사를 작성했습니다 .https : // ericlippert.com/2019/01/18/indexer-error-cases/ )
것을 우선 순위와는 단지 우리에게 영의 할당 에 b
일어나야 하기 전에 에 할당 a[b]
, 0이기 때문에 계산하는 색인 작업에 할당 된 값의 할당. 우선 순위와 여부에 대한 연관성 혼자 말 아무것도 a[b]
평가 전 또는 후에b=0
.
다시 말하지만 이것은 다음과 같습니다 A()[B()] = C()
.-우리가 아는 것은 할당 전에 인덱싱이 이루어져야한다는 것입니다. A (), B () 또는 C ()가 우선 순위와 연관성을 기반으로 먼저 실행되는지 여부는 알 수 없습니다 . 우리는 그것을 말할 또 다른 규칙이 필요합니다.
규칙은 "먼저 무엇을할지 선택할 수있을 때 항상 왼쪽에서 오른쪽으로 이동"하는 것입니다. 그러나이 특정 시나리오에는 흥미로운 주름이 있습니다. null 컬렉션 또는 범위를 벗어난 인덱스로 인해 발생한 예외의 부작용이 할당의 왼쪽 계산의 일부로 간주됩니까, 아니면 할당 자체 계산의 일부로 간주됩니까? 자바는 후자를 선택합니다. (물론 이것은 올바른 코드가 null을 역 참조하거나 처음부터 잘못된 인덱스를 전달하지 않기 때문에 코드가 이미 잘못된 경우 에만 중요한 차이입니다 .)
그래서 어떻게 되나요?
a[b]
의 왼쪽에 b=0
소위, a[b]
실행은 첫째 , 결과 a[1]
. 그러나 유효성 확인 인덱싱 작업 이 지연됩니다.b=0
발생합니다.a
유효하고 a[1]
범위 내에 있는 확인이 발생합니다.a[1]
마지막 에 발생합니다.따라서이 특정 경우에는 처음에 올바른 코드에서 발생하지 않아야하는 드문 오류 사례에 대해 고려해야 할 몇 가지 미묘한 점이 있지만 일반적으로 추론 할 수 있습니다. 왼쪽에있는 일이 오른쪽에있는 일보다 먼저 발생합니다. . 그것이 당신이 찾고있는 규칙입니다. 우선 순위와 연관성에 대한 이야기는 혼란스럽고 관련성이 없습니다.
사람들은이 문제를 항상 잘못 이해 합니다 . 더 잘 알아야하는 사람들도 마찬가지입니다. 규칙을 잘못 언급 한 프로그래밍 책을 너무 많이 편집 했으므로 많은 사람들이 우선 순위 / 연관성 및 평가 순서 사이의 관계에 대해 완전히 잘못된 믿음을 가지고 있다는 사실, 즉 실제로 그러한 관계가 없다는 것은 놀라운 일이 아닙니다. ; 그들은 독립적입니다.
이 주제가 관심이있는 경우 추가 읽기를 위해 주제에 대한 내 기사를 참조하십시오.
http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/
C #에 관한 것이지만 대부분의 내용은 Java에도 동일하게 적용됩니다.
그럼에도 불구하고 Eric Lippert의 뛰어난 답변은 다른 언어에 대해 이야기하고 있기 때문에 적절하게 도움이되지 않습니다. 이것은 Java입니다. 여기서 Java 언어 사양은 의미론에 대한 명확한 설명입니다. 특히, §15.26.1 은 =
연산자에 대한 평가 순서를 설명하기 때문에 관련이 있습니다 (우리 모두는 그것이 오른쪽 연관성이라는 것을 알고 있습니까?). 이 질문에서 우리가 관심을 갖는 부분으로 조금 줄이십시오.
왼쪽 피연산자식이 배열 액세스 식 ( §15.13 )이면 여러 단계가 필요합니다.
- 먼저, 왼쪽 피연산자 배열 액세스 표현식의 배열 참조 하위 표현식이 평가됩니다. 이 평가가 갑자기 완료되면 같은 이유로 할당 표현식이 갑자기 완료됩니다. 인덱스 하위 표현식 (왼쪽 피연산자 배열 액세스 표현식의) 및 오른쪽 피연산자는 평가되지 않으며 할당이 발생하지 않습니다.
- 그렇지 않으면 왼쪽 피연산자 배열 액세스 식의 인덱스 하위식이 평가됩니다. 이 평가가 갑자기 완료되면 같은 이유로 할당식이 갑자기 완료되고 오른쪽 피연산자가 평가되지 않고 할당이 발생하지 않습니다.
- 그렇지 않으면 오른쪽 피연산자가 평가됩니다. 이 평가가 갑자기 완료되면 할당식이 같은 이유로 갑자기 완료되고 할당이 발생하지 않습니다.
[… 그런 다음 과제 자체의 실제 의미를 설명합니다. 여기서는 간결하게 무시할 수 있습니다.…]
요컨대, Java는 연산자 또는 메소드 호출에 대한 인수 내에서 거의 정확히 왼쪽에서 오른쪽 으로 매우 밀접하게 정의 된 평가 순서를 가지고 있습니다. 배열 할당은 더 복잡한 경우 중 하나이지만 여전히 L2R입니다. (JLS는 이러한 종류의 복잡한 의미 제약 조건이 필요한 코드를 작성 하지 말 것을 권장하며 , 저도 그렇게합니다 : 문당 하나의 할당만으로도 충분한 문제를 겪을 수 있습니다!)
C 및 C ++는이 영역에서 Java와 확실히 다릅니다. 언어 정의는 더 많은 최적화를 가능하게하기 위해 의도적으로 평가 순서를 정의하지 않은 상태로 둡니다. C #은 분명히 Java와 비슷하지만 공식적인 정의를 가리킬 수있을만큼 관련 문헌을 잘 모릅니다. (이것은 언어에 따라 실제로 다릅니다. Ruby는 Tcl과 마찬가지로 엄격하게 L2R입니다. 여기에 관련되지 않은 이유로 할당 연산자 자체 가 부족하지만 Python은 L2R이지만 할당 과 관련하여 R2L입니다. .)
a[-1]=c
, c
평가, 이전에 -1
무효로 인식되고 있습니다.
a[b] = b = 0;
1) 배열 인덱싱 연산자는 할당 연산자보다 우선 순위가 높습니다 ( 이 답변 참조 ).
(a[b]) = b = 0;
2) 15.26에 따름. JLS의 할당 연산자
12 개의 할당 연산자가 있습니다. 모두 구문 상 오른쪽 연관입니다 (오른쪽에서 왼쪽으로 그룹화 됨). 따라서 a = b = c는 a = (b = c)를 의미하며, c 값을 b에 할당 한 다음 b 값을 a에 할당합니다.
(a[b]) = (b=0);
3) 15.7에 따르면. JLS 평가 순서
Java 프로그래밍 언어는 연산자의 피연산자가 특정 평가 순서, 즉 왼쪽에서 오른쪽으로 평가되는 것처럼 보이도록 보장합니다.
과
이항 연산자의 왼쪽 피연산자는 오른쪽 피연산자의 일부가 평가되기 전에 완전히 평가 된 것처럼 보입니다.
그래서:
a) (a[b])
먼저 평가a[1]
b) 다음 (b=0)
평가0
c) (a[1] = 0)
마지막으로 평가
코드는 다음과 같습니다.
int[] a = {4,4};
int b = 1;
c = b;
b = 0;
a[c] = b;
결과를 설명합니다.
아래에서 더 자세한 예를 살펴보십시오.
이러한 질문을 해결할 때 읽을 수있는 우선 순위 규칙 및 연관성 표를 갖는 것이 가장 좋습니다. http://introcs.cs.princeton.edu/java/11precedence/와
다음은 좋은 예입니다.
System.out.println(3+100/10*2-13);
질문 : 위 라인의 출력은 무엇입니까?
답변 : 우선 순위 및 연관성 규칙 적용
1 단계 : 우선 순위 규칙에 따라 : / 및 * 연산자가 +-연산자보다 우선합니다. 따라서이 방정식을 실행하는 시작점은 다음과 같이 좁혀집니다.
100/10*2
2 단계 : 규칙 및 우선 순위에 따라 : / 및 *는 우선 순위가 동일합니다.
/ 및 * 연산자는 우선 순위가 동일하므로 해당 연산자 간의 연관성을 살펴볼 필요가 있습니다.
이 두 연산자의 연관성 규칙에 따라 왼쪽에서 오른쪽으로 방정식을 실행하기 시작합니다. 즉, 100/10이 먼저 실행됩니다.
100/10*2
=100/10
=10*2
=20
3 단계 : 방정식은 이제 다음 실행 상태에 있습니다.
=3+20-13
규칙과 우선 순위에 따르면 +와-는 우선 순위가 같습니다.
이제 연산자 + 및-연산자 간의 연관성을 살펴볼 필요가 있습니다. 이 두 특정 연산자의 연관성에 따라 LEFT에서 RIGHT로 방정식 실행을 시작합니다. 즉, 3 + 20이 먼저 실행됩니다.
=3+20
=23
=23-13
=10
10은 컴파일시 올바른 출력입니다.
다시 말하지만, http://introcs.cs.princeton.edu/java/11precedence/ 와 같은 질문을 해결할 때 우선 순위 규칙 및 연관성에 대한 표를 가지고있는 것이 중요합니다.
10 - 4 - 3
.
+
단항 연산자 (오른쪽에서 왼쪽으로의 연관성이 있음)이지만 덧셈 + 및-는 곱셈 * / % 왼쪽과 같다는 사실로 인해 발생할 수 있다고 생각합니다. 오른쪽 연관성에.