Programing

정의되지 않은 동작 및 시퀀스 포인트

lottogame 2020. 9. 28. 07:51
반응형

정의되지 않은 동작 및 시퀀스 포인트


"시퀀스 포인트"란 무엇입니까?

정의되지 않은 동작과 시퀀스 포인트 사이의 관계는 무엇입니까?

나는 a[++i] = i;기분을 좋게 만들기 위해 종종 , 같은 재미 있고 복잡한 표현을 사용 합니다. 왜 사용을 중지해야합니까?

이 내용을 읽은 경우 후속 질문 Undefined behavior and sequence points reloaded 를 방문하십시오 .

(참고 : 이것은 Stack Overflow의 C ++ FAQ에 대한 항목 입니다.이 양식으로 FAQ를 제공하는 아이디어를 비판하고 싶다면이 모든 것을 시작한 메타에 게시 할 수 있습니다. 이 질문은 FAQ 아이디어가 처음 시작된 C ++ 채팅룸 에서 모니터링 되므로 아이디어를 제안한 사람들이 답변을 읽을 가능성이 매우 높습니다.)


C ++ 98 및 C ++ 03

이 대답은 이전 버전의 C ++ 표준에 대한 것입니다. 표준의 C ++ 11 및 C ++ 14 버전은 공식적으로 '시퀀스 포인트'를 포함하지 않습니다. 작업은 '이전에 순서가 지정됨'또는 '순차 화되지 않음'또는 '결정되지 않은 순서로 지정'됩니다. 순 효과는 본질적으로 동일하지만 용어는 다릅니다.


면책 조항 : 좋습니다. 이 답변은 약간 깁니다. 그러므로 그것을 읽는 동안 인내심을 가지십시오. 이미 이것을 알고 있다면 다시 읽는다고 당신을 미치게하지 않습니다.

선수 과목 : C ++ Standard 의 기초 지식


시퀀스 포인트는 무엇입니까?

표준 말한다

시퀀스 포인트 라고하는 실행 순서의 특정 지정된 지점에서 이전 평가의 모든 부작용 이 완료되고 후속 평가의 부작용 이 발생 하지 않아야합니다 . (§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)(여기서 a, b는 쉼표 연산자입니다. in 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.

Another example here.


Now the Standard in §5/4 says

  • 1) Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression.

What does it mean?

Informally it means that between two sequence points a variable must not be modified more than once. In an expression statement, the next sequence point is usually at the terminating semicolon, and the previous sequence point is at the end of the previous statement. An expression may also contain intermediate sequence points.

From the above sentence the following expressions invoke Undefined Behaviour:

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)

But the following expressions are fine:

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) Furthermore, the prior value shall be accessed only to determine the value to be stored.

What does it mean? It means if an object is written to within a full expression, any and all accesses to it within the same expression must be directly involved in the computation of the value to be written.

For example in i = i + 1 all the access of i (in L.H.S and in R.H.S) are directly involved in computation of the value to be written. So it is fine.

This rule effectively constrains legal expressions to those in which the accesses demonstrably precede the modification.

Example 1:

std::printf("%d %d", i,++i); // invokes Undefined Behaviour because of Rule no 2

Example 2:

a[i] = i++ // or a[++i] = i or a[i++] = ++i etc

is disallowed because one of the accesses of i (the one in a[i]) has nothing to do with the value which ends up being stored in i (which happens over in i++), and so there's no good way to define--either for our understanding or the compiler's--whether the access should take place before or after the incremented value is stored. So the behaviour is undefined.

Example 3 :

int x = i + i++ ;// Similar to above

Follow up answer for C++11 here.


This is a follow up to my previous answer and contains C++11 related material..


Pre-requisites : An elementary knowledge of Relations (Mathematics).


Is it true that there are no Sequence Points in C++11?

Yes! This is very true.

Sequence Points have been replaced by Sequenced Before and Sequenced After (and Unsequenced and Indeterminately Sequenced) relations in C++11.


What exactly is this 'Sequenced before' thing?

Sequenced Before(§1.9/13) is a relation which is:

between evaluations executed by a single thread and induces a strict partial order1

Formally it means given any two evaluations(See below) A and B, if A is sequenced before B, then the execution of A shall precede the execution of B. If A is not sequenced before B and B is not sequenced before A, then A and B are unsequenced 2.

Evaluations A and B are indeterminately sequenced when either A is sequenced before B or B is sequenced before A, but it is unspecified which3.

[NOTES]
1 : A strict partial order is a binary relation "<" over a set P which is asymmetric, and transitive, i.e., for all a, b, and c in P, we have that:
........(i). if a < b then ¬ (b < a) (asymmetry);
........(ii). if a < b and b < c then a < c (transitivity).
2 : The execution of unsequenced evaluations can overlap.
3 : Indeterminately sequenced evaluations cannot overlap, but either could be executed first.


What is the meaning of the word 'evaluation' in context of C++11?

In C++11, evaluation of an expression (or a sub-expression) in general includes:

  • value computations (including determining the identity of an object for glvalue evaluation and fetching a value previously assigned to an object for prvalue evaluation) and

  • initiation of side effects.

Now (§1.9/14) says:

Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.

  • Trivial example:

    int x; x = 10; ++x;

    Value computation and side effect associated with ++x is sequenced after the value computation and side effect of x = 10;


So there must be some relation between Undefined Behaviour and the above-mentioned things, right?

Yes! Right.

In (§1.9/15) it has been mentioned that

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced4.

For example :

int main()
{
     int num = 19 ;
     num = (num << 3) + (num >> 3);
} 
  1. Evaluation of operands of + operator are unsequenced relative to each other.
  2. Evaluation of operands of << and >> operators are unsequenced relative to each other.

4: In an expression that is evaluated more than once during the execution of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be performed consistently in different evaluations.

(§1.9/15) The value computations of the operands of an operator are sequenced before the value computation of the result of the operator.

That means in x + y the value computation of x and y are sequenced before the value computation of (x + y).

More importantly

(§1.9/15) If a side effect on a scalar object is unsequenced relative to either

(a) another side effect on the same scalar object

or

(b) a value computation using the value of the same scalar object.

the behaviour is undefined.

Examples:

int i = 5, v[10] = { };
void  f(int,  int);
  1. i = i++ * ++i; // Undefined Behaviour
  2. i = ++i + i++; // Undefined Behaviour
  3. i = ++i + ++i; // Undefined Behaviour
  4. i = v[i++]; // Undefined Behaviour
  5. i = v[++i]: // Well-defined Behavior
  6. i = i++ + 1; // Undefined Behaviour
  7. i = ++i + 1; // Well-defined Behaviour
  8. ++++i; // Well-defined Behaviour
  9. f(i = -1, i = -1); // Undefined Behaviour (see below)

When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function. [Note: Value computations and side effects associated with different argument expressions are unsequenced. — end note]

Expressions (5), (7) and (8) do not invoke undefined behaviour. Check out the following answers for a more detailed explanation.


Final Note :

If you find any flaw in the post please leave a comment. Power-users (With rep >20000) please do not hesitate to edit the post for correcting typos and other mistakes.


C++17 (N4659) includes a proposal Refining Expression Evaluation Order for Idiomatic C++ which defines a stricter order of expression evaluation.

In particular, the following sentence was added:

8.18 Assignment and compound assignment operators:
....

In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression. The right operand is sequenced before the left operand.

It makes several cases of previously undefined behavior valid, including the one in question:

a[++i] = i;

However several other similar cases still lead to undefined behavior.

In N4140:

i = i++ + 1; // the behavior is undefined

But in N4659

i = i++ + 1; // the value of i is incremented
i = i++ + i; // the behavior is undefined

Of course, using a C++17 compliant compiler does not necessarily mean that one should start writing such expressions.


I am guessing there is a fundamental reason for the change, it isn't merely cosmetic to make the old interpretation clearer: that reason is concurrency. Unspecified order of elaboration is merely selection of one of several possible serial orderings, this is quite different to before and after orderings, because if there is no specified ordering, concurrent evaluation is possible: not so with the old rules. For example in:

f (a,b)

previously either a then b, or, b then a. Now, a and b can be evaluated with instructions interleaved or even on different cores.


In C99(ISO/IEC 9899:TC3) which seems absent from this discussion thus far the following steteents are made regarding order of evaluaiton.

[...]the order of evaluation of subexpressions and the order in which side effects take place are both unspecified. (Section 6.5 pp 67)

The order of evaluation of the operands is unspecified. If an attempt is made to modify the result of an assignment operator or to access it after the next sequence point, the behavior[sic] is undefined.(Section 6.5.16 pp 91)

참고URL : https://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points

반응형