C ++ 98 및 C ++ 03
이 답변은 이전 버전의 C ++ 표준에 대한 것입니다. 표준의 C ++ 11 및 C ++ 14 버전은 공식적으로 '시퀀스 포인트'를 포함하지 않습니다. 대신에 '시퀀스 이전'또는 '시퀀스되지 않은'또는 '불확실하게 시퀀싱'됩니다. 순 효과는 본질적으로 동일하지만 용어는 다릅니다.
면책 조항 : 알겠습니다. 이 답변은 약간 깁니다. 읽는 동안 인내심을 가지십시오. 이미 알고 있다면 다시 읽어도 미치지 않습니다.
전제 조건 : C ++ 표준에 대한 기초 지식
시퀀스 포인트는 무엇입니까?
표준은 말한다
실행 순서에서 시퀀스 포인트 라고하는 특정 특정 지점에서 이전 평가의 모든 부작용 이 완료되고 후속 평가의 부작용 이 발생 하지 않아야합니다 . (§1.9 / 7)
부작용? 부작용은 무엇입니까?
식의 평가는 무언가를 생성하며, 실행 환경의 상태에 변화가 있다면, 그 식 (평가)은 부작용이 있다고합니다.
예를 들면 다음과 같습니다.
int x = y++; //where y is also an int
초기화 작업 외에도 작업자 y
의 부작용으로 인해 값 이 변경 ++
됩니다.
여태까지는 그런대로 잘됐다. 시퀀스 포인트로 넘어갑니다. comp.lang.c 저자가 제공 한 seq-points의 대체 정의 Steve Summit
:
시퀀스 포인트는 먼지가 정착 된 시점이며 지금까지 본 모든 부작용이 완벽하게 보장됩니다.
C ++ 표준에 나열된 공통 시퀀스 포인트는 무엇입니까?
사람들은:
전체 표현식의 평가가 끝날 때 ( §1.9/16
) (전체 표현식은 다른 표현식의 하위 표현식이 아닌 표현식입니다.) 1
예 :
int a = 5; // ; is a sequence point here
첫 번째 식 ( §1.9/18
) 2 의 평가 후 다음 각 식의 평가에서
a && b (§5.14)
a || b (§5.15)
a ? b : c (§5.16)
a , b (§5.18)
(여기서, B는 콤마 연산자이다에서 func(a,a++)
,
콤마 연산자 아니라, 단지 인자 사이의 구분자 a
와 a++
. 따라서, 동작이 (이 경우에 정의되어 있지 않으면 a
기본 유형으로 간주된다))
함수 호출 (함수가 인라인인지 여부)에서 함수 본문 ( §1.9/17
) 에서 표현식 또는 명령문을 실행하기 전에 발생하는 모든 함수 인수 (있는 경우)를 평가 한 후
1 : 참고 : 전체 표현의 평가에는 전체 표현의 어휘 부분이 아닌 하위 표현의 평가가 포함될 수 있습니다. 예를 들어, 기본 인수 표현식 (8.3.6)을 평가하는 데 포함 된 하위 표현식은 기본 인수를 정의하는 표현식이 아니라 함수를 호출하는 표현식에서 작성된 것으로 간주됩니다.
2 : 표시된 연산자는 5 절에 설명 된 내장 연산자입니다. 이러한 연산자 중 하나가 유효한 컨텍스트에서 오버로드되어 (13 절) 사용자 정의 연산자 함수를 지정하면 표현식은 함수 호출을 지정합니다. 피연산자는 그들 사이에 내재 된 순서 지점없이 인수 목록을 형성합니다.
정의되지 않은 행동이란 무엇입니까?
표준은 섹션에서 정의되지 않은 동작을 다음 §1.3.12
과 같이 정의합니다.
잘못된 프로그램 구조 또는 잘못된 데이터를 사용할 때 발생할 수있는 행동 (이 국제 표준에 요구 사항 없음) 3 .
이 국제 표준이 행동의 명시 적 정의에 대한 설명을 생략 할 때 정의되지 않은 행동이 예상 될 수도 있습니다.
3 : 허용되지 않는 정의 된 동작은 예측할 수없는 결과로 상황을 완전히 무시하는 것부터, 환경의 문서화 된 방식으로 진단 또는 프로그램 실행 중 (진단 메시지 발행 여부에 관계없이), 번역 또는 실행 종료까지 (진단 메시지 발행).
즉, 정의되지 않은 동작은 코에서 날아 오는 데몬에서 여자 친구가 임신하는 것에 이르기까지 모든 일이 발생할 수 있음을 의미 합니다.
정의되지 않은 동작과 시퀀스 포인트의 관계는 무엇입니까?
들어가기 전에 Undefined Behaviour, Unspecified Behavior 및 Implementation Defined Behavior 의 차이점을 알아야합니다 .
또한 그 사실을 알아야합니다 the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified
.
예를 들면 다음과 같습니다.
int x = 5, y = 6;
int z = x++ + y++; //it is unspecified whether x++ or y++ will be evaluated first.
여기에 또 다른 예가 있습니다 .
이제 표준은 §5/4
말합니다
- 1) 이전 시퀀스 포인트와 다음 시퀀스 포인트 사이에서 스칼라 객체는 표현식의 평가에 의해 저장된 값을 최대 한 번 수정해야합니다.
무슨 뜻이에요?
공식적으로 두 시퀀스 포인트 사이에서 변수를 두 번 이상 수정해서는 안됩니다. 표현 문에서는 next sequence point
일반적으로 종료 세미콜론에 있고 previous sequence point
이전 명령문의 끝에 있습니다. 표현식은 중간을 포함 할 수도 있습니다 sequence points
.
위 문장에서 다음 표현식은 정의되지 않은 동작을 호출합니다.
i++ * ++i; // UB, i is modified more than once btw two SPs
i = ++i; // UB, same as above
++i = 2; // UB, same as above
i = ++i + 1; // UB, same as above
++++++i; // UB, parsed as (++(++(++i)))
i = (i, ++i, ++i); // UB, there's no SP between `++i` (right most) and assignment to `i` (`i` is modified more than once btw two SPs)
그러나 다음 표현은 괜찮습니다.
i = (i, ++i, 1) + 1; // well defined (AFAIK)
i = (++i, i++, i); // well defined
int j = i;
j = (++i, i++, j*i); // well defined
- 2) 또한, 이전 값은 저장 될 값을 결정하기 위해서만 액세스되어야한다.
무슨 뜻이에요? 이는 전체 표현식 내에서 오브젝트를 작성하는 경우 동일한 표현식 내에서 오브젝트에 대한 모든 액세스는 작성 될 값 계산에 직접 포함되어야 함을 의미합니다 .
예를 들어 (LHS 및 RHS에서의) i = i + 1
모든 접근 i
에서 기록 될 값의 계산 에 직접 관여 합니다. 그래서 괜찮습니다.
이 규칙은 법적 표현이 접근이 수정 전에 명백하게 적용되는 표현으로 효과적으로 제한합니다.
예 1 :
std::printf("%d %d", i,++i); // invokes Undefined Behaviour because of Rule no 2
예 2 :
a[i] = i++ // or a[++i] = i or a[i++] = ++i etc
의 액세스 i
중 하나가 a[i]
i에 저장된 값과 관련이 없기 때문에 허용되지 않으므로 (또는에서 발생 함 i++
) 우리의 이해 또는 컴파일러-증가 된 값이 저장되기 전후에 액세스가 수행되어야하는지 여부. 따라서 동작은 정의되지 않습니다.
예 3 :
int x = i + i++ ;// Similar to above
여기 에서 C ++ 11에 대한 답변을 따르십시오 .
*p++ = 4
정의되지 않은 동작이 아닙니다.*p++
로 해석됩니다*(p++)
. (사본)과 이전 주소에 저장된 값을p++
반환p
합니다. 왜 UB를 호출합니까? 완벽하게 괜찮습니다.