programing

연산자 우선 순위와 평가 순서

goodsources 2022. 7. 30. 19:15
반응형

연산자 우선 순위와 평가 순서

'operator precedence'와 '평가 순서'라는 용어는 프로그래밍에서 매우 일반적으로 사용되는 용어이며 프로그래머가 알아야 할 매우 중요한 용어입니다.그리고 제가 알기론 두 가지 개념이 밀접하게 얽혀있기 때문에 표현을 말할 때 하나가 없으면 안 되는 거죠.

간단한 예를 들어 보겠습니다.

int a=1;  // Line 1
a = a++ + ++a;  // Line 2
printf("%d",a);  // Line 3

자, 분명한 것은Line 2CC++의 시퀀스 포인트는 다음과 같기 때문에 정의되지 않은 동작으로 이어집니다.

  1. &(논리 AND), ||(논리 OR) 및 쉼표 연산자의 왼쪽 피연산자와 오른쪽 피연산자의 평가 사이.예를 들어, 식에서*p++ != 0 && *q++ != 0, 서브 표현으로 인한 모든 것*p++ != 0접속을 시도하기 전에 완료됩니다.q.

  2. 3차 "물음표" 연산자의 첫 번째 피연산자와 두 번째 또는 세 번째 피연산자의 평가 사이.예를 들어, 식에서a = (*p++) ? (*p++) : 0첫 번째 뒤에 시퀀스 포인트가 있다*p++이는 두 번째 인스턴스가 실행될 때까지 이미 증가했음을 의미합니다.

  3. 완전한 표현 끝에.이 범주에는 식 문(예: 할당)이 포함됩니다.a=b;)는 명령문, if, switch, while 또는 do-while 명령문의 제어식 및 for 명령문의 3가지 표현식을 모두 반환합니다.

  4. 함수 호출에 함수를 입력하기 전에.인수의 평가 순서는 지정되어 있지 않지만, 이 시퀀스 포인트는 함수를 입력하기 전에 인수의 모든 부작용이 완료되었음을 의미합니다.표현 중에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미지정입니다.

  5. 함수 반환 시 반환값이 호출 컨텍스트에 복사된 후.(이 시퀀스 포인트는 C++ 표준에서만 지정되며 암묵적으로 C에만 존재합니다).

  6. 이니셜라이저의 끝(예를 들어 선언의 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<zLine 5다음 중 하나로 평가할 수 있습니다.

x<(y<z)또는(x<y)<z첫 번째 경우, 의 가치는result될 것이다0그리고 두 번째 경우에는result될 것이다1하지만 우리는 알고 있다,Operator PrecedenceEqual/Same-Associativity따라서, 라고 평가된다.(x<y)<z.

이 MSDN 기사에는 다음과 같은 내용이 기재되어 있습니다.

C 연산자의 우선순위와 연관성은 식 내 오퍼랜드의 그룹화와 평가에 영향을 미칩니다.연산자의 우선순위는 우선순위가 높거나 낮은 다른 연산자가 존재하는 경우에만 의미가 있습니다.우선 우선순위가 높은 연산자를 가진 식이 평가됩니다.우선순위는 "바인딩"이라는 단어로도 설명할 수 있습니다.precedence가 높은 연산자는 바인딩이 긴 것으로 알려져 있습니다.

위 기사에 대해서:

"우선 순위가 높은 연산자가 있는 식이 먼저 평가됩니다."라고 언급되어 있습니다.

잘못 들릴 수 있습니다.하지만 그 기사를 보면 틀린 말은 아닌 것 같아요.()연산자이기도 합니다.x<y<z와 같다(x<y)<z내 추론은 연관성이 작용하지 않으면 완전한 표현 평가가 모호해질 수 있다는 것이다.<시퀀스 포인트가 아닙니다.

또한 Operator PrecedenceAssociativity에서 발견된 다른 링크에는 다음과 같이 표시됩니다.

이 페이지에는 C 연산자가 우선 순위(가장 높은 연산자부터 가장 낮은 연산자까지)로 나열됩니다.이러한 연관성은 식에서 동일한 우선순위의 연산자가 적용되는 순서를 나타냅니다.

그래서 두 번째 예시는int result=x<y<z3가지 표현 모두 있는 것을 알 수 있습니다.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

반응형