최근 인터뷰에서 다음과 같은 객관적인 질문이있었습니다.
int a = 0;
cout << a++ << a;
답변:
ㅏ. 10
b. 01
c. 정의되지 않은 동작
나는 선택 b에 대답했다. 즉 출력은 “01”이 될 것이다.
그러나 놀랍게도 면접관은 정답이 옵션 c : 정의되지 않음이라고 들었습니다.
이제 C ++의 시퀀스 포인트 개념을 알고 있습니다. 다음 문에 대해서는 동작이 정의되지 않았습니다.
int i = 0;
i += i++ + i++;
하지만 내가 문에 대한 이해에 따라 cout << a++ << a
는이 ostream.operator<<()
첫 번째와 두 번 호출됩니다 ostream.operator<<(a++)
나중에 ostream.operator<<(a)
.
또한 VS2010 컴파일러에서 결과를 확인했으며 출력도 ’01’입니다.
답변
다음을 생각할 수 있습니다.
cout << a++ << a;
같이:
std::operator<<(std::operator<<(std::cout, a++), a);
C ++는 이전 평가의 모든 부작용이 시퀀스 지점 에서 수행되었음을 보장합니다 . 함수 인수 평가 사이에는 시퀀스 포인트가 없습니다. 이는 인수가 인수 a
이전 std::operator<<(std::cout, a++)
또는 이후에 평가 될 수 있음 을 의미합니다 . 따라서 위의 결과는 정의되지 않았습니다.
C ++ 17 업데이트
C ++ 17에서는 규칙이 업데이트되었습니다. 특히:
시프트 연산자 식
E1<<E2
및E1>>E2
에서의 모든 값 계산 및 부작용E1
은의 모든 값 계산 및 부작용 이전에 시퀀싱됩니다E2
.
어느 것이 생산 결과의 코드를 필요로한다는 의미 b
있는 출력을 01
.
자세한 내용은 P0145R3 관용적 C ++에 대한 표현식 평가 순서 구체화 를 참조하십시오.
답변
기술적으로 전반적으로 이것은 Undefined Behavior 입니다.
그러나 대답에는 두 가지 중요한 측면이 있습니다.
코드 설명 :
std::cout << a++ << a;
다음과 같이 평가됩니다.
std::operator<<(std::operator<<(std::cout, a++), a);
표준은 함수에 대한 인수 평가 순서를 정의하지 않습니다.
따라서 다음 중 하나 :
std::operator<<(std::cout, a++)
먼저 평가되거나a
먼저 평가되거나- 구현 정의 순서 일 수 있습니다.
이 순서는 표준에 따라 미지정 [Ref 1] 입니다.
[참고 1] C ++ 03 5.2.2 함수 호출
8 항
인수 평가 순서는 지정되지 않습니다 . 인수 표현식 평가의 모든 부작용은 함수가 입력되기 전에 적용됩니다. 접미사 식 및 인수 식 목록의 평가 순서는 지정되지 않습니다.
또한 함수에 대한 인수 평가 사이에는 시퀀스 포인트가 없지만 모든 인수를 평가 한 후에 만 시퀀스 포인트가 존재합니다 [Ref 2] .
[참고 2] C ++ 03 1.9 프로그램 실행 [intro.execution] :
17 항 :
함수를 호출 할 때 (함수가 인라인인지 여부에 관계없이) 모든 함수 인수 (있는 경우)를 평가 한 후 함수 본문의 표현식 또는 명령문을 실행하기 전에 발생하는 시퀀스 지점이 있습니다.
여기에서의 값은 c
중간 시퀀스 지점없이 두 번 이상 액세스되고 있으며 이에 대해 표준은 다음과 같이 말합니다.
[참고 3] C ++ 03 5 표현식 [expr] :
4 항 :
….
이전 시퀀스 포인트와 다음 시퀀스 포인트 사이에서 스칼라 객체는 표현식 평가에 의해 최대 한 번 수정 된 저장된 값을 가져야합니다. 또한 이전 값은 저장할 값을 결정하기 위해서만 액세스해야합니다 . 이 단락의 요구 사항은 전체 표현의 하위 표현의 허용 가능한 각 순서에 대해 충족되어야합니다. 그렇지 않으면 동작이 정의되지 않습니다 .
코드는 c
시퀀스 포인트를 간섭하지 않고 두 번 이상 수정 하며 저장된 객체의 값을 결정하기 위해 액세스하지 않습니다. 이는 위의 조항에 대한 명백한 위반이므로 표준에서 요구하는 결과는 Undefined Behavior [Ref 3] 입니다.
답변
시퀀스 포인트는 부분 순서 만 정의합니다 . 귀하의 경우 (과부하 해결이 완료되면) :
std::cout.operator<<( a++ ).operator<<( a );
에 a++
대한 첫 번째 호출과의
std::ostream::operator<<
사이에 시퀀스 포인트가 a
있고에 대한 두 번째 호출과 두 번째 호출 std::ostream::operator<<
사이에 시퀀스 포인트가 있지만 a++
과 사이에는 시퀀스 포인트가 없습니다 a
. 유일한 순서 제약은에 a++
대한 첫 번째 호출 전에 완전히 평가 (부작용 포함) operator<<
하고 두 번째 a
는에 대한 두 번째 호출 전에 완전히 평가된다는 것 operator<<
입니다. (인과적인 순서 제약도 있습니다. 두 번째 호출 operator<<
은 첫 번째 결과가 인수로 필요하기 때문에 첫 번째 호출 보다 우선 할 수 없습니다.) §5 / 4 (C ++ 03)은 다음과 같이 설명합니다.
언급 된 경우를 제외하고 개별 연산자의 피연산자 및 개별 식의 하위 식에 대한 평가 순서와 부작용이 발생하는 순서는 지정되지 않습니다. 이전 시퀀스 포인트와 다음 시퀀스 포인트 사이에서 스칼라 객체는 표현식 평가에 의해 최대 한 번 수정 된 저장된 값을 가져야합니다. 또한 이전 값은 저장할 값을 결정하기 위해서만 액세스해야합니다. 이 단락의 요구 사항은 전체 표현의 하위 표현의 허용 가능한 각 순서에 대해 충족되어야합니다. 그렇지 않으면 동작이 정의되지 않습니다.
식의 허용되는 순서 중 하나는 a++
,, a
에 대한 첫 번째 호출 operator<<
,에 대한 두 번째 호출입니다 operator<<
. 이것은 a
( a++
) 의 저장된 값을 수정하고 새 값 (두 번째 a
) 을 결정하는 것 외에 액세스 하면 동작이 정의되지 않습니다.
답변
정답은 질문하는 것입니다. 독자가 명확한 답을 볼 수 없기 때문에 그 진술은 받아 들일 수 없습니다. 그것을 보는 또 다른 방법은 우리가 문장을 해석하기 훨씬 더 어렵게 만드는 부작용 (c ++)을 도입했다는 것입니다. 간결한 코드는 의미가 명확하면 훌륭합니다.