85.17 시퀀스 포인트
C 언어에서 모든 식의 계산(평가)이 완료되는 지점을 시퀀스 포인트라고 합니다. 그리고 시퀀스 포인트 사이에서 식의 값은 한 번만 수정될 수 있습니다. 내용이 좀 어렵죠? 먼저 대표적인 시퀀스 포인트는 ; (세미콜론)인데 다음 코드는 세미콜론 사이에서 값이 두 번 수정된 잘못된 코드입니다.
// ↓ 시퀀스 포인트 int i = 0; i = i++; // ↑ 시퀀스 포인트
코드가 여러 줄로 되어 있지만 중간에 빈 줄은 무시하고 세미콜론 사이의 식만 보면 됩니다. i = i++ 식을 보면 i는 i = i에서 값이 한 번 수정되고 i++에서 다시 수정됩니다(사실 i = i의 결과는 바뀐게 없지만 일단 값을 할당했으므로 값이 수정된 것입니다).
int i = 0; //↓ 값이 수정됨 i = i++; // ↑ 값이 수정됨
이 코드는 Visual Studio에서는 경고 없이 컴파일되지만 GCC에서는 -Wsequence-point 또는 -Wall옵션을 사용하여 경고를 출력할 수 있습니다.
$ gcc main.c -Wsequence-point main.c: In function ‘main’: main.c:7:7: warning: operation on ‘i’ may be undefined [-Wsequence-point] i = i++; ^
i = i++과 같이 의도가 명확하지 않은 코드를 정의되지 않은 행동(undefined behavior)라 부르는데 컴파일러에 따라 결과가 달라질 수 있으므로 이런 코드의 작성은 피해야 합니다.
물론 다음과 같이 ++ 연산자를 사용한 뒤 다른 변수에 저장하는 것은 올바른 코드입니다. 왜냐하면 j와 i 모두 한 번씩 수정되기 때문입니다.
int i = 0; int j; j = i++;
다음은 C 언어의 모든 시퀀스 포인트입니다.
이름 | 구문 | 설명 |
---|---|---|
논리 AND 연산자 | && | && 연산자 앞에서 모든 식의 계산이 완료되며 첫 번째 식의 결과에 따라 두 번째 식은 평가하지 않고 넘어가는 단락 평가를 수행합니다. |
논리 OR 연산자 | || | || 연산자 앞에서 모든 식의 계산이 완료되며 첫 번째 식의 결과에 따라 두 번째 식은 평가하지 않고 넘어가는 단락 평가를 수행합니다. |
쉼표 연산자 | , | , 연산자 앞에서 모든 식의 계산이 완료됩니다. 따라서 다음과 같이 i++과 i++을 콤마로 구분한 코드는 올바른 코드입니다.
// 시퀀스 포인트 사이에서 값이 두 번 수정됨. 정의되지 않은 행동 x = (i++ + i++, i += 2); // 시퀀스 포인트 사이에서 값이 한 번 수정됨. 올바른 코드 x = (i++, i++, i += 2); |
함수 호출 연산자 | ( ) | 함수 호출 직전까지 모든 식의 계산이 완료됩니다. 다음과 같이 함수 앞에서 i++가 수행되고, 함수의 인수에서 i++가 수행되어 값이 두 번 수정되었습니다. 즉, 함수 호출 연산자 ( )의 안쪽뿐만 아니라 함수 바깥의 식도 함께 평가됩니다.
#include <stdio.h> int function(int n) { return n; } int main() { int i = 0; int x; x = i++ + function(i++); // 함수 호출 전에 값이 두 번 수정됨 // 정의되지 않은 행동 return 0; } |
삼항 연산자 | ? | ?로 조건을 판단하기 전까지 모든 식의 계산이 완료됩니다. |
세미콜론 | ; | ; 직전에 모든 식의 계산이 완료됩니다. |
if, switch의 조건식 | if ( ) switch ( ) |
if, switch에 연결된 코드가 실행되기 직전에 모든 식의 계산이 완료됩니다. |
while, do while의 조건식 | while ( ) do while ( ) |
반복될 코드가 실행되기 직전에 모든 식의 계산이 완료됩니다. |
for 제어문의 세미콜론 | for ( ; ; ) | 각 세미콜론 직전에 모든 식의 계산이 완료됩니다(반복될 코드가 실행되기 직전에도 모든 식의 계산이 완료됩니다). |
함수의 반환 | return | return으로 값을 반환하기 직전에 모든 식의 계산이 완료됩니다. |