시퀀스 포인트
시퀀스 포인트는 C언어 계열의 언어에서 매우 중요한 개념이므로, 두가지 주요 문서를 번역해서 정리하고자 한다.
다음은 C FAQ 3.8번 질문을 번역한 것이다.
A sequence point is a point in time at which the dust has settled and all side effects which have been seen so far are guaranteed to be complete.
(시퀀스 포인트는 모든 연산에 의한 결과가 완료되는 지점을 의미합니다.)
Sequence point는 크게 세가지가 있다.
- at the end of the evaluation of a full expression (a full expression is an expression statement, or any other expression which is not a subexpression within any larger expression);
(full expression의 끝) - at the ||, &&, ?:, and comma operators; and
(||, &&, ?:, , 등의 연산자) - at a function call (after the evaluation of all the arguments, and just before the actual call).
(argument 평가 후 함수 호출 직전)
Sequence point는 다음의 규칙을 따라야한다.
- Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression.
(이전 시퀀스 포인트와 다음 시퀀스 포인트 사이에서 객체(변수 등)는 표현식 평가에 의해 최대 한 번 수정될 수 있습니다.) - the prior value shall be accessed only to determine the value to be stored.
(또한 수정하기의 이전 값의 액세스는 새로이 저장할 값을 결정하기 위해서만 발생해야합니다.)
대표적인 시퀀스 포인트 예시 1
i++ * i++;
와
i = i++;
i는 하나의 full expression(seqeunce point)에서 두번 값을 변경했다. 즉 시퀀스 포인트의 첫번째 규칙을 위반한 것이다.
대표적인 시퀀스 포인트 예시 2
a[i] = i++;
와 같은 문장은 i가 subscript로 쓰이기 전에 증가할지, 쓰인 후 증가할지 알 수 없다.
이는 두번째 규칙과 관련이 있는데, 번역하자면,
And that's what the second sentence says: 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.
(그리고 그것이 두 번째 문장이 말하는 것입니다. 객체가 full expression 내에서 쓰여진다면, 같은 표현식 내에서 객체에 대한 모든 접근은 쓰여질 값의 계산에 직접적으로 관련되어야합니다.)
This rule effectively constrains legal expressions to those in which the accesses demonstrably precede the modification.
(이 규칙은 올바른 표현을 '수정 이전에 명백하게 액세스하는 표현'으로 제한합니다.)
For example, the old standby i = i + 1
is allowed, because the access of i is used to determine i's final value. The example a[i] = i++
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++),
(i = i + 1
는 i의 최종값을 저장하기 위해 i에 액세스하기에 legal하다. a[i] = i++
는 i의 액세스 중 하나(여기서는 a[i]와 a[i++])가 증가한 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.
(그리고 액세스가 increment 이후 혹은 이전에 발생했는지 정의할 방법이 없다.)
이번엔 위키 문서를 보자.
A sequence point defines any point in a computer program's execution at which it is guaranteed that all side effects of previous evaluations will have been performed, and no side effects from subsequent evaluations have yet been performed. They are often mentioned in reference to C and C++, because they are a core concept for determining the validity and, if valid, the possible results of expressions. Adding more sequence points is sometimes necessary to make an expression defined and to ensure a single valid order of evaluation.
(시퀀스 포인트는 이전 평가의 효과가 수행되고 후속 평가의 효과가 아직 수행되지 않았음을 보장하는 컴퓨터 프로그램 실행의 모든 지점을 의미합니다. C 및 C ++와 관련하여 자주 언급됩니다. 타당성을 결정하기위한 핵심 개념이고 유효한 경우 표현식의 결과를 결정하기 때문이다. 표현식을 정의하고 유효한 단일 평가 순서를 보장하기 위해 더 많은 시퀀스 포인트를 추가해야하는 경우가 있습니다.)
C11 표준문서부터 시퀀스포인트를 시퀀싱으로 표현한다. 시퀀싱에는 세가지 경우가 있다.
- 표현식의 평가는 다른 표현식보다 먼저 시퀀스되거나, 다른 표현식이 평가된 후 시퀀싱된다.
- 표현식의 평가는 불확실하게 시퀀스된다. 이는 다른 지정되지 않은 것(시퀀싱이 지정되지 않은 것을 의미)보다 시퀀스되는 것을 의미한다.
- 표현식의 평가는 시퀀스되지 않는다.
대표적인 시퀀스 포인트 예시
Consider two functions f() and g(). In C and C++, the + operator is not associated with a sequence point, and therefore in the expression f()+g() it is possible that either f() or g() will be executed first.
(두 함수 f(), g()에 대해서 f() + g()라는 표현식을 작성한다고 하면, +는 시퀀스 포인트와 관계없는 연산자이기에, f()가 먼저 실행될지, g()가 먼저 실행될지 알 수가 없다.)
The comma operator introduces a sequence point, and therefore in the code f(),g() the order of evaluation is defined: first f() is called, and then g() is called.
(, 연산자의 경우 시퀀스 포인트를 의미하기에, f(),g()연산은 f()이후 g()가 호출된다.)
Sequence points also come into play when the same variable is modified more than once within a single expression.
(시퀀스 포인트는 한 표현식에서 한 번 이상 값이 바뀌는 변수에 대해서 자주 언급된다.)
An often-cited example is the C expression i=i++, which apparently both assigns i its previous value and increments i. The final value of i is ambiguous, because, depending on the order of expression evaluation, the increment may occur before, after, or interleaved with the assignment. The definition of a particular language might specify one of the possible behaviors or simply say the behavior is undefined.
(자주 인용되는 예는 C 표현식 i = i ++이며, 이는 분명히 i에 이전 값을 할당하고 i를 증가시킵니다. i의 최종 값은 식 평가 순서에 따라 증가가 할당 전, 후 또는 인터리브 될 수 있기 때문에 모호합니다. 언어에 따라서 가능한 동작 결과 중 하나를 지정하거나 단순히 동작이 정의되지 않았다(Undefined Behavior)고 말할 수 있습니다.)
C와 C++에서의 시퀀스 포인트 정리
In C and C++, sequence points occur in the following places.
(C와 C++에서, 시퀀스 포인트는 아래와 같은 경우에 발생한다.)
- && , ||, , 연산자는 왼쪽에서 오른쪽으로 평가를 실시한다. 왼쪽의 평가만으로 결과값이 결정되면 오른쪽의 평가를 스킵한다.
*p++ != 0 && *q++ != 0
의 경우*p++ != 0
의 효과가 모두 마친 후*q++ != 0
이 평가된다. - 삼항 연산자의 첫 번째 피연산자와 두 번째 또는 세 번째 피연산자의 평가 사이. 예를 들어,
a = (* p ++)? (* p ++) : 0
첫 번째* p ++
뒤에 시퀀스 포인트가 있습니다. 즉, 두 번째 피연산자가 실행될 때 이미 증가했음을 의미합니다. - full expression의 마지막. 표현식 한 문장 (예 : 대입 a = b;), return 문, if, switch, while 또는 do-while 문의 조건문 및 for 문에있는 조건문이 모두 포함됩니다.
- argument의 evaluation이 모두 끝나고 함수 호출 직전. argument가 평가되는 순서는 지정되지 않지만, 이 시퀀스 포인트는 함수가 호출되기 전에 모든 부작용이 완료됨을 의미합니다. 식
f (i ++) + g (j ++) + h (k ++)
에서 f는 i의 원래 값 형태로 매개 변수로 호출되지만, i는 f의 본문에 들어가기 전에 증가합니다. 마찬가지로 j와 k는 각각 g와 h를 입력하기 전에 업데이트됩니다. 그러나 f (), g (), h ()가 실행되는 순서나 i, j, k가 증가하는 순서는 지정되지 않습니다. f의 본문이 변수 j와 k에 액세스하면 둘 다 찾거나 둘 다 찾지 못하거나 둘 중 하나만 증가 된 것을 찾을 수 있습니다. (함수 호출f (a, b, c)
에서의 ,는 연산자의 의미가 아닙니다. a, b, c에 대한 평가 순서는 지정되지 않습니다.) - 함수 반환에서 반환 값이 호출 컨텍스트에 복사된 후. (이 시퀀스 포인트는 C ++ 표준에서만 지정되며 C에서는 암시적으로만 존재합니다.)
- 초기화 이후. 예를 들어
int a = 5;
에서 5가 대입된 이후. - declarator sequence(변수 선언을 한 번에 모아서 하는 것을 의미)에서 각각의 declarator마다. 예를 들어
int x = a++, y = a++;
에서a++
평가 이후. - 입출력 함수의 escape character(여기서는 format specifier라고 표현했다.)에 argument가 대입된 후. 예를 들어,
printf ( "foo % n % d", & a, 42)
표현식에서%n
이 평가된 후(%n
에&a
이 대입된 후) 42를 인쇄하기 전에 시퀀스 포인트가 있습니다.
In C++, overloaded operators act like functions, and thus operators that have been overloaded introduce sequence points in the same way as function calls.
(C++에서 연산자 오버로드는 함수처럼 동작한다. 그렇기에 오버로드된 연산자는 함수와 같은 방법으로 시퀀스 포인트가 도입된다.)
참고할만한 사이트
http://c-faq.com/expr/seqpoints.html
https://en.wikipedia.org/wiki/Sequence_point