쉼표가있는 삼항 연산자가 실제 경우에 하나의 표현식 만 평가하는 이유는 무엇입니까?


119

저는 현재 C ++ Primer 책으로 C ++를 배우고 있으며이 책의 연습 중 하나는 다음과 같습니다.

다음 표현이 무엇을하는지 설명하십시오. someValue ? ++x, ++y : --x, --y

우리는 무엇을 압니까? 삼항 연산자가 쉼표 연산자보다 우선 순위가 높다는 것을 알고 있습니다. 이항 연산자를 사용하면 이해하기 쉬웠지만 삼항 연산자를 사용하면 약간 어려움을 겪고 있습니다. 이항 연산자를 사용하면 "더 높은 우선 순위를 갖는다"는 것은 우리가 더 높은 우선 순위로 표현식 주위에 괄호를 사용할 수 있으며 실행을 변경하지 않을 것임을 의미합니다.

삼항 연산자의 경우 다음을 수행합니다.

(someValue ? ++x, ++y : --x, --y)

컴파일러가 코드를 그룹화하는 방법을 이해하는 데 도움이되지 않는 동일한 코드를 효과적으로 생성합니다.

그러나 C ++ 컴파일러를 사용한 테스트를 통해 표현식이 컴파일된다는 것을 알고 있으며 :연산자가 그 자체로 무엇을 의미 할 수 있는지 알 수 없습니다 . 따라서 컴파일러는 삼항 연산자를 올바르게 해석하는 것 같습니다.

그런 다음 두 가지 방법으로 프로그램을 실행했습니다.

#include <iostream>

int main()
{
    bool someValue = true;
    int x = 10, y = 10;

    someValue ? ++x, ++y : --x, --y;

    std::cout << x << " " << y << std::endl;
    return 0;
}

결과 :

11 10

반면에 다음과 someValue = false같이 인쇄됩니다.

9 9

왜 C ++ 컴파일러는 삼항 연산자의 참 분기에 대해 증가하는 코드를 생성하고 삼항 x의 거짓 분기에 대해 x및 둘 다 감소시키는 이유는 y무엇입니까?

나는 심지어 다음과 같이 진정한 가지 주위에 괄호를두기까지했다.

someValue ? (++x, ++y) : --x, --y;

그러나 여전히 11 10.


5
"우선 순위"는 C ++에서 나타나는 현상 일뿐입니다. 언어 문법을 직접보고 표현이 어떻게 작동하는지 보는 것이 더 간단 할 수 있습니다.
Kerrek SB

26
우리는 상관 없어 원칙에 대해 많이. :-) 여기서 물어봐야한다는 사실은 코드가 동료 프로그래머의 코드 검토를 통과하지 않을 것임을 나타냅니다. 그것은 이것이 실제로 어떻게 작동 하는지에 대한 지식을 유용하지 않게 만듭니다. 물론 난독 화 된 C 콘테스트 에 참여하고 싶지 않다면 말입니다 .
Bo Persson

5
@BoPersson에서 배울 수있는 예제가 없으면 미래의 리뷰어는 왜 이것을 프로덕션에서 거부해야하는지 결코 알 수 없습니다.
Leushenko 2017

8
@Leushenko-어쨌든 경고 벨이 울릴 것입니다. 동일한 명령문에서 여러 증가 감소 (ding, ding, ding!). if-else (ding, ding, ding!)를 사용할 수있는 경우 삼항 연산자. 잠깐, 그 쉼표가 무서운 쉼표 연산자입니까? (ding, DING, DING!) 그 모든 연산자들에게 우선 순위가있을 수 있습니까? (ding, ding, ding!) 그래서 우리는 결코 그것을 사용할 수 없을 것입니다. 그렇다면 그것이 무엇을하는지 알아내는 데 시간을 낭비하는 이유는 무엇입니까?
Bo Persson

4
Minor nit :의 이름 ?조건부 연산자 입니다. 삼항 연산자 라는 용어는 단순히 세 개의 피연산자가있는 연산자를 의미합니다. 조건부 연산자는 삼항 연산자 의 한 예 이지만 언어는 (이론적으로) 여러 삼항 연산자를 가질 수 있습니다.
bta

답변:


122

으로 @Rakete이 우수한 대답했다,이 까다 롭습니다. 나는 그것에 조금 더하고 싶습니다.

삼항 연산자는 다음과 같은 형식이어야합니다.

논리 또는 표현식 ? 표현식 : 할당 표현식

따라서 다음과 같은 매핑이 있습니다.

  • someValue: 논리 또는 표현
  • ++x, ++y: 표현
  • ??? 입니다 할당 표현은 --x, --y 또는 유일한 --x?

실제로 할당 표현식 은 쉼표로 구분 된 두 표현식으로 구문 분석 할 수 --x없기 때문에 (C ++의 문법 규칙에 따라) 할당 표현식 으로 취급 할 수 없습니다 .--x, --y

결과적으로 삼항 (조건부) 표현식 부분은 다음과 같습니다.

someValue?++x,++y:--x

가독성을 위해 괄호 안에있는 것처럼++x,++y 계산 되는 것으로 간주 하는 것이 도움이 될 수 있습니다 (++x,++y). ?와 사이에 포함 된 모든 항목 은 조건부 뒤에: 순서 가 지정됩니다. (나머지 게시물에서는 괄호로 묶습니다).

다음 순서로 평가됩니다.

  1. someValue?
  2. (++x,++y)또는 --x( bool1의 결과에 따라 )

이 표현식은 쉼표 연산자에 대한 왼쪽 하위 표현식으로 처리되며 오른쪽 하위 표현식은 다음 --y과 같습니다.

(someValue?(++x,++y):--x), --y;

즉, 왼쪽이 폐기 된 값 표현식 이므로 확실히 평가되지만 오른쪽을 평가하여 반환합니다.

그래서 무슨 일이 someValue있다 true?

  1. (someValue?(++x,++y):--x)실행하고 증가 x하고 y1111
  2. 왼쪽 표현식은 삭제됩니다 (증가의 부작용은 남아 있음).
  3. : 우리는 쉼표 연산자의 오른쪽 평가 --y후 감소 y가기10

행동 "수정"에, 당신은 그룹 수 --x, --y괄호는로 변환하는 주요 표현 이다 에 대한 유효한 항목을 지정 표현 * :

someValue?++x,++y:(--x, --y);

* 할당 식을 다시 기본 식으로 연결하는 다소 재미있는 긴 체인입니다 .

할당 표현식 --- (다음으로 구성 될 수 있음)-> 조건식 -> 논리 또는 표현식 -> 논리 및 표현식 -> 포함 또는 표현식 -> 배타적 또는 표현식 - -> and-expression- > 같음-표현식 -> 관계형-표현식 -> shift-expression- > 가산 식 -> 곱셈-표현식 -> pm- 표현식 -> 캐스트-표현식 -> 단항 식 -> 접미사 식 -> 기본 식


10
문법 규칙을 풀어 주셔서 감사합니다. 이렇게하면 대부분의 교과서에서 찾을 수있는 것보다 C ++의 문법에 더 많은 것이 있음을 보여줍니다.
sdenham

4
@sdenham : 사람들이 "표현 지향 언어"가 좋은 이유 (즉 언제 { ... }표현으로 취급 될 수 있는지)를 물었을 때 , 저는 이제 답을 얻었습니다 => 그렇게 까다로운 방식으로 작동하는 쉼표 연산자를 도입하지 않아도되는 것입니다.
Matthieu M.

assignment-expression체인 에 대해 읽을 수있는 링크를 제공해 주 시겠습니까?
MiP

@MiP : 나는 당신이 아래를 찾을 수 있습니다, 표준 자체를 해제 gram.expr
AndyG

88

와, 까다 롭 네요.

컴파일러는 표현식을 다음과 같이 간주합니다.

(someValue ? (++x, ++y) : --x), --y;

삼항 연산자는를 필요로하고 :, 그 문맥에서 그 자체로 서있을 수는 없지만, 그 뒤에 쉼표가 거짓 케이스에 속해야하는 이유가 없습니다.

이제 그 결과를 얻는 이유가 더 이해할 수 있습니다. 경우 someValue사실, 다음 ++x, ++y그리고 --y효율적으로 변경하지 않는 실행받을 y만에 1을 추가합니다 x.

경우 someValue거짓, 다음 --x--y하나 둘을 감소시키는, 실행됩니다.


42

C ++ 컴파일러가 삼항 연산자의 실제 분기에 대해서만 증가하는 코드를 생성하는 이유 x

당신은 무슨 일이 일어 났는지 잘못 해석했습니다. 참 분기는 xy. 그러나 y그 직후 무조건 감소합니다.

이것이 발생하는 방법은 다음과 같습니다 . 조건부 연산자가 C ++의 쉼표 연산자보다 우선 순위가 높기 때문에 컴파일러는 다음과 같이 표현식을 구문 분석합니다.

   (someValue ? ++x, ++y : --x), (--y);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^  ^^^^^

--y쉼표 뒤 의 "분리 된"에 유의하십시오 . 이것이 y초기에 증가 된 감소로 이어지는 것 입니다.

나는 심지어 다음과 같이 진정한 가지 주위에 괄호를두기까지했다.

someValue ? (++x, ++y) : --x, --y;

올바른 경로에 있었지만 잘못된 분기를 괄호로 묶었습니다. 다음과 같이 else-branch를 괄호로 묶어이 문제를 해결할 수 있습니다.

someValue ? ++x, ++y : (--x, --y);

데모 (11 11 인쇄)


5

문제는 삼항 표현식이 실제로 쉼표보다 우선 순위가 높지 않다는 것입니다. 사실, C ++는 단순히 우선 순위로 정확하게 설명 할 수 없으며, 정확히 3 항 연산자와 쉼표 사이의 상호 작용으로 분해됩니다.

a ? b++, c++ : d++

다음과 같이 취급됩니다.

a ? (b++, c++) : d++

(쉼표는 우선 순위가 더 높은 것처럼 작동합니다). 반면에

a ? b++ : c++, d++

다음과 같이 취급됩니다.

(a ? b++ : c++), d++

삼항 연산자가 더 높은 우선 순위입니다.


중간 줄에 유효한 구문 분석이 하나뿐이므로 이것이 여전히 우선 순위 영역 내에 있다고 생각합니다. 그래도 유용한 예
sudo rm -rf 슬래시

2

답변에서 간과 된 요점은 (주석에 언급되었지만) 조건부 연산자는 변수에 두 값 중 하나를 할당하는 지름길로 실제 코드에서 변함없이 사용된다는 것입니다 (설계에 의해 의도 되었습니까?).

따라서 더 큰 컨텍스트는 다음과 같습니다.

whatIreallyWanted = someValue ? ++x, ++y : --x, --y;

얼굴에 터무니없는 일이므로 범죄는 다양합니다.

  • 언어는 과제에서 어리석은 부작용을 허용합니다.
  • 컴파일러는 당신이 이상한 일을하고 있다는 경고를하지 않았습니다.
  • 이 책은 '속임수'질문에 초점을 맞추고있는 것으로 보입니다. 뒤에있는 대답이 "이 표현이하는 일은 아무도 예상하지 못한 부작용을 생성하기 위해 고안된 예에서 이상한 가장자리 사례에 의존하는 것입니다. 절대 이렇게하지 마십시오."라는 대답을 바랄 수 있습니다.

1
두 변수 중 하나를 할당하는 것은 운영자의 원 통상의 경우이지만, 거기에 있는 그것의 발현 형태 가질 때 유용 경우는 if(루프에 대한 예를 들면, 증가 식). 큰 맥락을 잘 수 있습니다 for (x = 0, y=0; x+y < 100; someValue?(++x, ++y) :( --x, --y))수정할 수있는 루프 xy독립적으로.
마틴 보너 모니카 지원

@MartinBonner 나는 확신하지 못한다. 그리고이 예는 그가 Tony Hoare를 인용했듯이 bo-perrson의 주장을 꽤 잘 만드는 것 같다.
Taryn
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.