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를 호출합니까? 완벽하게 괜찮습니다.