“시퀀스 포인트”란 무엇입니까?
정의되지 않은 동작과 시퀀스 포인트의 관계는 무엇입니까?
나는 종종 a[++i] = i;
기분이 나아지도록 재미 있고 복잡한 표현을 사용 합니다. 왜 사용을 중단해야합니까?
이 내용을 읽은 경우 후속 질문 Undefined behavior and sequence points reloaded 를 방문하십시오 .
(참고 : 이것은 Stack Overflow의 C ++ FAQ에 대한 항목 입니다.이 양식으로 FAQ를 제공한다는 아이디어를 비판하려면이 모든 것을 시작한 메타에 게시 하면됩니다. 이 질문은 C ++ 대화방 에서 모니터링되며 여기서 FAQ 아이디어는 처음부터 시작되었으므로 아이디어를 얻은 사람들이 대답을 읽을 가능성이 큽니다.)
답변
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에 대한 답변을 따르십시오 .
답변
이것은 이전 답변에 대한 후속 조치 이며 C ++ 11 관련 자료를 포함합니다. .
선수 과목 : 관계 (수학)에 대한 기초 지식.
C ++ 11에 시퀀스 포인트가 없다는 것이 사실입니까?
예! 이것은 매우 사실입니다.
C ++ 11에서 Sequence Points 는 Sequenced Before 및 Sequenced After (및 Unsequenced and Indeterminately Sequenced ) 관계 로 대체되었습니다 .
이 ‘시퀀스 이전’은 정확히 무엇입니까?
Sequenced Before (§1.9 / 13) 는 다음과 같은 관계입니다.
단일 스레드에 의해 실행되는 평가 사이 에 엄격한 부분 순서를 유도합니다 1
공식적으로 그것은 두 가지 평가 (아래 참조)가 주어진다는 것을 의미 A
하며 B
, 만약 이전 에 순서A
가 정해져 있다면 B
,의 실행이 의 실행 A
보다 우선해야한다B
. 경우 A
이전 서열되지 B
및 B
이전 순서가되지 않고 A
, 다음 A
과 B
이다 unsequenced 2 .
평가 A
하고 B
있다 불확실 서열 하나가 될 때 A
전에 서열화 B
또는 B
전에 서열화되어 A
있지만, 어느 불특정 인 3 .
[주]
1 : 엄격한 순서 부분은 인 이진 관계 "<"
집합을 통해 P
인 asymmetric
및 transitive
모두, 즉 a
, b
및 c
에 P
: 우리가 가지고
…….. (I). a <b이면 ¬ (b <a) ( asymmetry
);
…….. (ii). a <b 및 b <c이면 a <c ( transitivity
)입니다.
2 : 순서없는 평가 실행이 겹칠 수 있습니다 .
3 : 불확실한 시퀀스 평가 는 중복 될 수 없지만 둘 중 하나를 먼저 실행할 수 있습니다.
C ++ 11과 관련하여 ‘평가’라는 단어의 의미는 무엇입니까?
C ++ 11에서 일반적으로 표현식 (또는 하위 표현식)의 평가에는 다음이 포함됩니다.
-
계산 값 (위한 개체의 아이덴티티를 결정하는 단계를 더 포함하는 glvalue 평가 이전의 오브젝트에 할당 된 값을 인출 prvalue 평가 ) 및
-
부작용의 시작 .
이제 (§1.9 / 14)는 다음과 같이 말합니다.
전체 표현식과 관련된 모든 값 계산 및 부작용 은 평가할 다음 전체 표현식 과 관련된 모든 값 계산 및 부작용 보다 먼저 시퀀싱 됩니다 .
-
사소한 예 :
int x;
x = 10;
++x;
값 계산 및 부작용
++x
은 다음의 값 계산 및 부작용 후에 순서화됩니다.x = 10;
정의되지 않은 행동과 위에 언급 된 것들 사이에는 어떤 관계가 있어야합니다.
예! 권리.
(§1.9 / 15)에서 다음과 같이 언급되었습니다.
언급 된 경우를 제외하고, 각각의 통신 사업자의 개별 식 표현식의 피연산자의 평가는 unsequenced 4 .
예를 들면 다음과 같습니다.
int main()
{
int num = 19 ;
num = (num << 3) + (num >> 3);
}
+
연산자 의 피연산자 평가는 서로에 대해 순서가 없습니다.- 피연산자
<<
와>>
연산자 의 평가는 서로에 대해 순서가 없습니다.
4 : 프로그램 실행 중 두 번 이상 평가되는 표현식에서 하위 표현식에 대해 순서가 지정 되지 않고 불확실하게 시퀀스 된 평가를 다른 평가에서 일관되게 수행 할 필요는 없습니다.
(§1.9 / 15) 연산자 결과의 값 계산 전에 연산자 피연산자의 값 계산이 시퀀싱됩니다.
수단에서 x + y
의 계산 값 x
과 y
의 값을 계산하기 전에 서열화된다 (x + y)
.
더 중요한 것은
(§1.9 / 15) 스칼라 객체에 대한 부작용이 다음 중 하나와 관련이없는 경우
(a) 동일한 스칼라 객체에 대한 다른 부작용
또는
(b) 동일한 스칼라 객체의 값을 사용한 값 계산.
동작은 정의되어 있지 않습니다 .
예 :
int i = 5, v[10] = { };
void f(int, int);
i = i++ * ++i; // Undefined Behaviour
i = ++i + i++; // Undefined Behaviour
i = ++i + ++i; // Undefined Behaviour
i = v[i++]; // Undefined Behaviour
i = v[++i]: // Well-defined Behavior
i = i++ + 1; // Undefined Behaviour
i = ++i + 1; // Well-defined Behaviour
++++i; // Well-defined Behaviour
f(i = -1, i = -1); // Undefined Behaviour (see below)
함수가 인라인인지 여부에 관계없이 함수를 호출하면 인수 표현식 또는 호출 된 함수를 지정하는 접미사 표현식과 관련된 모든 값 계산 및 부작용이 본문의 모든 표현식 또는 명령문을 실행하기 전에 순서화됩니다. 함수를 호출했습니다. [ 참고 : 다른 인수 표현식과 관련된 값 계산 및 부작용은 순서가 없습니다 . — 끝 참고 ]
표현식 (5)
, (7)
그리고 (8)
정의되지 않은 동작을 호출하지 않습니다. 자세한 설명은 다음 답변을 확인하십시오.
최종 메모 :
게시물에 결함이 있으면 의견을 남겨주세요. 고급 사용자 (rep> 20000)는 오타 및 기타 실수를 수정하기 위해 게시물을 편집하는 것을 망설이지 마십시오.
답변
C ++ 17 ( N4659
)에는
보다 엄격한 식 평가 순서를 정의하는 관용적 C ++에 대한 Refining Expression Evaluation Order 제안이 포함되어 있습니다.
특히 다음 문장
8.18 대입 및 복합 대입 연산자 :
….모든 경우에, 할당은 오른쪽과 왼쪽 피연산자의 값 계산 후와 할당 식의 값 계산 전에 순서화됩니다.
오른쪽 피연산자는 왼쪽 피연산자보다 먼저 시퀀싱됩니다.
다음 설명과 함께
식 X 와 관련된 모든 값 계산 및 모든 부작용이 식 Y 와 관련된 모든 값 계산 및 모든 부작용 전에 시퀀싱된다면 , 식 X 는 식 Y 전에 시퀀싱된다고한다 .
해당 사건을 포함하여 이전에 정의되지 않은 행동의 여러 사례를 유효하게 만드십시오.
a[++i] = i;
그러나 몇 가지 다른 유사한 사례는 여전히 정의되지 않은 동작으로 이어집니다.
에서 N4140
:
i = i++ + 1; // the behavior is undefined
그러나 N4659
i = i++ + 1; // the value of i is incremented
i = i++ + i; // the behavior is undefined
물론 C ++ 17 호환 컴파일러를 사용한다고해서 반드시 그러한 표현식을 작성해야한다는 의미는 아닙니다.
답변
나는 변화의 근본적인 이유가 있다고 추측하고 있습니다. 오래된 해석을 더 명확하게 만드는 것은 단지 화장품이 아닙니다. 그 이유는 동시성입니다. 지정되지 않은 정교화 순서는 가능한 여러 일련의 순서 중 하나를 선택하는 것입니다. 이는 지정된 순서가 없으면 동시 평가가 가능하기 때문에 순서 전후와는 상당히 다릅니다. 이전 규칙은 그렇지 않습니다. 예를 들면 다음과 같습니다.
f (a,b)
이전에는 a 다음 b 또는 b 다음에 a. 이제 a와 b는 인터리브 된 명령어 또는 다른 코어에서 평가할 수 있습니다.
답변
에 C99(ISO/IEC 9899:TC3)
있는 지금까지 다음 steteents이 evaluaiton의 순서에 관한 만들어진이 토론에없는 것 같다.
[…] 부 표현식 평가 순서와 부작용 발생 순서는 모두 지정되어 있지 않습니다. (섹션 6.5 pp 67)
피연산자의 평가 순서는 지정되어 있지 않습니다. 할당 연산자의 결과를 수정하거나 다음 시퀀스 포인트 이후에 액세스하려고하면 동작 [sic]이 정의되지 않습니다 (6.5.16 pp 91 절).