연산자 우선 순위와 평가 순서
'operator precedence'와 '평가 순서'라는 용어는 프로그래밍에서 매우 일반적으로 사용되는 용어이며 프로그래머가 알아야 할 매우 중요한 용어입니다.그리고 제가 알기론 두 가지 개념이 밀접하게 얽혀있기 때문에 표현을 말할 때 하나가 없으면 안 되는 거죠.
간단한 예를 들어 보겠습니다.
int a=1; // Line 1
a = a++ + ++a; // Line 2
printf("%d",a); // Line 3
자, 분명한 것은Line 2
C 및 C++의 시퀀스 포인트는 다음과 같기 때문에 정의되지 않은 동작으로 이어집니다.
&(논리 AND), ||(논리 OR) 및 쉼표 연산자의 왼쪽 피연산자와 오른쪽 피연산자의 평가 사이.예를 들어, 식에서
*p++ != 0 && *q++ != 0
, 서브 표현으로 인한 모든 것*p++ != 0
접속을 시도하기 전에 완료됩니다.q
.3차 "물음표" 연산자의 첫 번째 피연산자와 두 번째 또는 세 번째 피연산자의 평가 사이.예를 들어, 식에서
a = (*p++) ? (*p++) : 0
첫 번째 뒤에 시퀀스 포인트가 있다*p++
이는 두 번째 인스턴스가 실행될 때까지 이미 증가했음을 의미합니다.완전한 표현 끝에.이 범주에는 식 문(예: 할당)이 포함됩니다.
a=b;
)는 명령문, if, switch, while 또는 do-while 명령문의 제어식 및 for 명령문의 3가지 표현식을 모두 반환합니다.함수 호출에 함수를 입력하기 전에.인수의 평가 순서는 지정되어 있지 않지만, 이 시퀀스 포인트는 함수를 입력하기 전에 인수의 모든 부작용이 완료되었음을 의미합니다.표현 중에
f(i++) + g(j++) + h(k++)
,f
원래 값의 파라미터를 사용하여 호출됩니다.i
,그렇지만i
체내에 들어가기 전에 증가하다f
.유사하게,j
그리고.k
를 입력하기 전에 갱신됩니다.g
그리고.h
각각 다음과 같다.그러나 어떤 순서로 지정되지는 않았습니다.f()
,g()
,h()
실행되며, 또한 어떤 순서로 실행되는지i
,j
,k
증가합니다.의 가치j
그리고.k
의 몸으로f
정의되어 있지 않습니다.3함수가 호출되는 것에 주의해 주세요.f(a,b,c)
는 콤마 연산자와 평가 순서를 사용하지 않습니다.a
,b
,그리고.c
미지정입니다.함수 반환 시 반환값이 호출 컨텍스트에 복사된 후.(이 시퀀스 포인트는 C++ 표준에서만 지정되며 암묵적으로 C에만 존재합니다).
이니셜라이저의 끝(예를 들어 선언의 5 평가 후)
int a = 5;
.
따라서 3번 포인트로 넘어가면:
완전한 표현 끝에.이 범주에는 표현식 문(예: 할당 a=b;), 반환문, if, switch, while 또는 do-while 문의 제어식 및 for 문의 세 가지 표현식이 모두 포함됩니다.
Line 2
분명히 정의되지 않은 동작으로 이어집니다.이는 정의되지 않은 동작이 시퀀스 포인트와 어떻게 밀접하게 결합되어 있는지를 보여줍니다.
이제 다른 예를 들어 보겠습니다.
int x=10,y=1,z=2; // Line 4
int result = x<y<z; // Line 5
이제 알 수 있다Line 5
변수를 만듭니다.result
가게1
.
이제 그 표현은x<y<z
에Line 5
다음 중 하나로 평가할 수 있습니다.
x<(y<z)
또는(x<y)<z
첫 번째 경우, 의 가치는result
될 것이다0
그리고 두 번째 경우에는result
될 것이다1
하지만 우리는 알고 있다,Operator Precedence
이Equal/Same
-Associativity
따라서, 라고 평가된다.(x<y)<z
.
이 MSDN 기사에는 다음과 같은 내용이 기재되어 있습니다.
C 연산자의 우선순위와 연관성은 식 내 오퍼랜드의 그룹화와 평가에 영향을 미칩니다.연산자의 우선순위는 우선순위가 높거나 낮은 다른 연산자가 존재하는 경우에만 의미가 있습니다.우선 우선순위가 높은 연산자를 가진 식이 평가됩니다.우선순위는 "바인딩"이라는 단어로도 설명할 수 있습니다.precedence가 높은 연산자는 바인딩이 긴 것으로 알려져 있습니다.
위 기사에 대해서:
"우선 순위가 높은 연산자가 있는 식이 먼저 평가됩니다."라고 언급되어 있습니다.
잘못 들릴 수 있습니다.하지만 그 기사를 보면 틀린 말은 아닌 것 같아요.()
연산자이기도 합니다.x<y<z
와 같다(x<y)<z
내 추론은 연관성이 작용하지 않으면 완전한 표현 평가가 모호해질 수 있다는 것이다.<
시퀀스 포인트가 아닙니다.
또한 Operator Precedence 및 Associativity에서 발견된 다른 링크에는 다음과 같이 표시됩니다.
이 페이지에는 C 연산자가 우선 순위(가장 높은 연산자부터 가장 낮은 연산자까지)로 나열됩니다.이러한 연관성은 식에서 동일한 우선순위의 연산자가 적용되는 순서를 나타냅니다.
그래서 두 번째 예시는int result=x<y<z
3가지 표현 모두 있는 것을 알 수 있습니다.x
,y
그리고.z
표현식의 가장 단순한 형식은 단일 리터럴 상수 또는 객체로 구성됩니다.그래서 그 표현들의 결과는x
,y
,z
가치가 있을 것이다, 즉,10
,1
그리고.2
각각 다음과 같다.따라서, 이제 우리는 해석할 수 있다.x<y<z
~하듯이10<1<2
.
이제 평가해야 할 두 가지 표현도 있기 때문에 연관성이 작용하지 않습니까?10<1
또는1<2
연산자의 우선순위가 같기 때문에 왼쪽에서 오른쪽으로 평가합니까?
이 마지막 예를 제 주장으로 들어 보겠습니다.
int myval = ( printf("Operator\n"), printf("Precedence\n"), printf("vs\n"),
printf("Order of Evaluation\n") );
위의 예에서는 다음 예에서comma
연산자가 같은 우선순위를 가지며 식이 평가됩니다.left-to-right
그리고 마지막 반환값printf()
저장 장소myval
.
SO/IEC 9899:201x의 J.1 Unspecified behavior(J.1 지정되지 않음 동작)에서 다음과 같이 언급됩니다.
function-call() &&, ||, ?: 및 쉼표 연산자(6.5)에 대해 지정된 경우를 제외하고 서브 표현이 평가되는 순서와 부작용이 발생하는 순서.
이제 궁금한 게 있는데, 이렇게 말하는 게 잘못된 걸까요?
평가 순서는 연산자의 우선순위에 따라 달라지며, 지정되지 않은 동작의 사례가 남습니다.
질문에서 틀린 부분이 있으면 정정해 주셨으면 합니다.제가 이 질문을 올린 이유는 MSDN 기사로 인해 제 마음이 혼란스러웠기 때문입니다.에러인가 아닌가?
네, MSDN 문서는 적어도 표준 C 및 C++1에 관해 오류가 있습니다.
그 후, 용어에 관한 주의사항부터 설명하겠습니다.C++ 표준에서는 (대부분의 실수는) 오퍼랜드를 평가하기 위해 "평가"를 사용하고, 연산을 실행하기 위해 "값 계산"을 사용합니다.따라서 (예를 들어) 할 때a + b
, 각a
그리고.b
평가한 후 값을 계산하여 결과를 결정합니다.
값 계산의 순서는 (대부분) 우선순위 및 연관성에 의해 제어됩니다.제어 값 계산은 기본적으로 우선순위 및 연관성에 대한 정의입니다.이 답변의 나머지 부분에서는 "평가"를 사용하여 값 연산이 아닌 오퍼랜드의 평가를 참조합니다.
평가 순서는 우선순위에 따라 결정됩니다만, 아닙니다!그것은 저것만큼 간단합니다.예를 들어, 예를 들어, 다음 예시를 생각해 봅시다.x<y<z
연관성 규칙에 따르면 이는 다음과 같이 해석됩니다.(x<y)<z
스택 머신에서 이 식을 평가해 보겠습니다.다음과 같은 작업을 수행할 수 있습니다.
push(z); // Evaluates its argument and pushes value on stack
push(y);
push(x);
test_less(); // compares TOS to TOS(1), pushes result on stack
test_less();
이것은 평가합니다.z
전에x
또는y
, 그러나 여전히 평가합니다.(x<y)
다음으로 비교 결과를 비교합니다.z
원래대로야.
요약: 평가 순서는 연관성과 무관합니다.
우선순위는 동일합니다.표현은 다음과 같이 바꿀 수 있습니다.x*y+z
, 그리고 계속 평가합니다.z
전에x
또는y
:
push(z);
push(y);
push(x);
mul();
add();
요약: 평가 순서는 우선순위와 무관합니다.
부작용을 추가해도 이는 그대로 유지됩니다.부작용은 별도의 실행의 줄에 의해 수행된다고 생각하는 것이 교육적이라고 생각합니다.join
다음 시퀀스 포인트(예: 표현의 끝)에서.그래서 뭐랄까...a=b++ + ++c;
다음과 같이 실행할 수 있습니다.
push(a);
push(b);
push(c+1);
side_effects_thread.queue(inc, b);
side_effects_thread.queue(inc, c);
add();
assign();
join(side_effects_thread);
이것은 또한 왜 명백한 의존성이 평가 순서에도 영향을 주지 않는지를 보여준다.그럼에도 불구하고.a
과제의 대상이며, 이것은 여전히 평가됩니다.a
어느쪽인가를 평가하기 전에b
또는c
또한 위의 "스레드"라고 썼지만, 이 또한 스레드 풀과 마찬가지로 모두 병렬로 실행되므로 증분 순서와 다른 증분 순서 모두 보장되지 않습니다.
하드웨어가 스레드 세이프 큐잉을 직접(저렴하게) 지원하지 않는 한 실제 구현에서는 사용되지 않을 수 있습니다(그렇다고 해도 그다지 가능성이 높지 않습니다).스레드 세이프 큐에 넣는 것은 보통 한 번의 증가보다 훨씬 많은 오버헤드가 발생하기 때문에 실제로 이러한 작업을 수행하는 사람은 상상하기 어렵습니다.그러나 개념적으로 이 아이디어는 표준의 요건에 들어맞습니다.즉, 사전/사후 증분/감소 연산을 사용할 경우 표현의 해당 부분이 평가된 후 잠시 후에 수행되며 다음 시퀀스 포인트에서 완료되는 연산을 지정합니다.
편집: 스레드화는 아니지만 일부 아키텍처에서는 이러한 병렬 실행을 허용합니다.예를 들어 일부 DSP 등의 인텔 Itanium 및 VLIW 프로세서를 사용하면 컴파일러는 다수의 명령을 병렬로 실행할 수 있습니다.대부분의 VLIW 머신에는 병렬로 실행되는 명령의 수를 제한하는 특정 명령의 "패킷" 크기가 있습니다.Itanium은 명령 패킷도 사용하지만 명령 패킷의 비트를 지정하여 현재 패킷의 명령이 다음 패킷의 명령과 병렬로 실행될 수 있음을 나타냅니다.이와 같은 메커니즘을 사용하면 병렬로 실행되는 명령을 얻을 수 있습니다. 이는 우리 대부분이 익숙한 아키텍처에서 여러 스레드를 사용하는 경우와 같습니다.
요약: 평가 순서는 명백한 종속성과 무관합니다.
다음 시퀀스 포인트 전에 값을 사용하려고 하면 정의되지 않은 동작이 나타납니다.특히, "기타 스레드"는 그 사이에 (잠재적으로) 그 데이터를 수정하기 때문에 다른 스레드와 액세스를 동기화할 수 없습니다.이것을 사용하려고 하면 정의되지 않은 동작이 발생합니다.
예를 들어 64비트 가상 머신에서 코드가 실행되고 있지만 실제 하드웨어는 8비트 프로세서입니다.64비트 변수를 증가시키면 다음과 같은 시퀀스가 실행됩니다.
load variable[0]
increment
store variable[0]
for (int i=1; i<8; i++) {
load variable[i]
add_with_carry 0
store variable[i]
}
이 시퀀스 중간 어딘가에서 값을 읽으면 일부 바이트만 수정된 값을 얻을 수 있으므로 이전 값도 새 값도 얻을 수 없습니다.
이 정확한 예시는 다소 억지일 수 있지만, 실제로는 그다지 극단적이지 않은 버전(32비트 머신의 64비트 변수 등)은 매우 일반적입니다.
결론
평가 순서는 우선순위, 연관성 또는 (필요에 따라) 명백한 종속성에 의존하지 않습니다.표현식의 다른 부분에서 사전/사후 증가/감소가 적용된 변수를 사용하려고 하면 완전히 정의되지 않은 동작이 발생합니다.실제 크래시는 거의 발생하지 않지만 이전 값이나 새 값을 얻을 수 있다는 보장은 없습니다. 완전히 다른 값을 얻을 수 있습니다.
1 이 기사는 확인하지 않았습니다만, Microsoft 의 Managed C++ 및/또는 C++/CLI(또는 C++ 의 실장 전용)에 대해서는 기술하고 있습니다만, 표준 C 또는 C++ 에 적용되지 않는 것에 대해서는, 거의 또는 전혀 지적하고 있지 않습니다.이것은 그들이 자신들의 언어에 적용하기로 결정한 규칙이 표준 언어에 실제로 적용된다고 주장하는 것처럼 보일 수 있습니다.이 경우 기사는 기술적으로 잘못된 것이 아닙니다.표준 C나 C++와는 전혀 관계가 없습니다.이러한 문을 표준 C 또는 C++에 적용하려고 하면 결과는 false가 됩니다.
우선순위는 평가 순서나 그 반대와는 무관하다.
precedence 규칙은 식이 다른 종류의 연산자를 혼재시킬 때 괄호로 묶는 방법을 설명합니다.예를 들어 곱셈은 덧셈보다 우선도가 높기 때문에2 + 3 x 4
와 동등하다2 + (3 x 4)
,것은 아니다.(2 + 3) x 4
.
평가규칙의 순서는 식 내의 각 피연산자가 평가되는 순서를 나타냅니다.
예를 들다
y = ++x || --y;
연산자 우선 순위 규칙에 따라 ( )로 괄호 안에 넣습니다.++/--
보다 우선도가 높다||
우선 순위보다 높은 값을 매겨져 있습니다.=
):
y = ( (++x) || (--y) )
논리 OR의 평가 순서||
(C11 6.5.14)라고 기재되어 있습니다.
|| 연산자는 왼쪽에서 오른쪽으로의 평가를 보증합니다.
즉, 왼쪽 피연산자, 즉 하위 표현식이(x++)
먼저 평가하겠습니다.단락 동작으로 인해 첫 번째 피연산자가 와 동등하지 않은 경우 두 번째 피연산자는 평가되지 않습니다(오른쪽 피연산자).--y
다음보다 앞에 괄호 안에 넣어도 평가되지 않습니다.(++x) || (--y)
.
우선순위가 평가 순서에 영향을 미치는 유일한 방법은 종속성을 생성하는 것입니다. 그렇지 않으면 두 개의 우선순위가 직교합니다.우선 순위에 따라 생성된 종속성이 평가 순서를 완전히 정의하는 사소한 예를 신중하게 선택했지만, 일반적으로는 그렇지 않습니다.그리고 많은 표현들이 두 가지 효과를 가지고 있다는 것도 잊지 마세요. 그것들은 가치를 낳는다는 것과 부작용을 낳는다는 것입니다.이 두 가지가 함께 발생할 필요는 없기 때문에 의존성이 특정 평가 순서를 강요하는 경우에도 이는 값의 평가 순서일 뿐 부작용에는 영향을 미치지 않는다.
이것을 보는 좋은 방법은 표현 트리를 사용하는 것입니다.
표정이 있으면 이렇게 합시다.x+y*z
그것을 식 트리로 고쳐 쓸 수 있습니다.
priority 및 관련성 규칙 적용:
x + ( y * z )
priority 및 관련성 규칙을 적용하면 이러한 규칙을 잊어버릴 수 있습니다.
트리 형식:
x
+
y
*
z
이제 이 표현의 잎은x
,y
그리고.z
이게 무슨 뜻인가 하면, 당신이 평가해 볼 수 있다는 것이다.x
,y
그리고.z
당신이 원하는 어떤 순서로든, 그리고 그것은 또한 당신이 그 결과를 평가할 수 있다는 것을 의미합니다.*
그리고.x
순서를 불문하고
이런 표현들은 부작용이 없기 때문에 별로 신경 쓰지 않습니다.단, 이 경우 순서에 따라 결과가 변경될 수 있으며, 컴파일러가 결정하는 모든 순서가 될 수 있기 때문에 문제가 있습니다.
시퀀스 포인트는 이 혼돈에 약간의 질서를 가져다 줍니다.그들은 효과적으로 나무를 여러 부분으로 잘랐다.
x + y * z, z = 10, x + y * z
우선성과 연관성 후에
x + ( y * z ) , z = 10, x + ( y * z)
트리:
x
+
y
*
z
, ------------
z
=
10
, ------------
x
+
y
*
z
트리의 윗부분은 중간 전에, 중간은 아래쪽 전에 평가됩니다.
내 생각엔 이건 단지...
a++ + ++a
epxpression에 문제가 있습니다.왜냐하면
a = a++ + ++a;
3번에서 먼저 맞지만 6번 규칙: 과제를 하기 전에 평가를 완료하십시오.
그렇게,
a++ + ++a
a=1에 대해 다음을 완전히 평가합니다.
1 + 3 // left to right, or
2 + 2 // right to left
결과는 = 4와 같습니다.
안
a++ * ++a // or
a++ == ++a
정의되지 않은 결과를 얻을 수 있습니다.아니니?
"우선 순위가 높은 연산자가 있는 식이 먼저 평가됩니다."라고 언급되어 있습니다.
여기서 한 말을 다시 한 번 말씀드리겠습니다.표준 C와 C++에 관한 한 그 물품은 결함이 있다.우선순위는 각 연산자의 오퍼랜드로 간주되는 토큰에만 영향을 미치지만 평가 순서에는 영향을 주지 않습니다.
이 링크에서는 Microsoft의 구현 방법만 설명되며 언어 자체의 작동 방식은 설명되지 않습니다.
언급URL : https://stackoverflow.com/questions/5473107/operator-precedence-vs-order-of-evaluation
'programing' 카테고리의 다른 글
Java에서 int 어레이를 되돌리려면 어떻게 해야 하나요? (0) | 2022.07.30 |
---|---|
V-for에서 여러 기능을 연결할 수 있습니까? (0) | 2022.07.30 |
호스트 "를 확인할 수 없습니다. 호스트 이름과 연결된 주소가 없습니다. (0) | 2022.07.30 |
마운트된 속성 변경 사항이 VueJ에서 트리거되지 않음s (0) | 2022.07.30 |
Vuetify js에서 비활성 필드의 기본 색상을 변경하거나 재정의하는 방법 (0) | 2022.07.30 |