증감 연산자로 살펴보는 sequence point의 개념
신영진 http://www.jiniya.net [email protected]
아래 프로그램의 실행 결과는 무엇인가?
int a = 1, b = 2, c = 3;
int r = a++ + ++b + c++;
printf(“%d\n”, r);
흔히 초급 C언어 강좌에서 자주 시험 문제로 등장하는 것이다. 전위형 증감 연산자와 후위형 증감 연산자의 차이를 이해하고 있는지를 묻는 문제다. 이러한 문제를 골똘히 생각해본 독자라면 한번쯤 다음과 같은 값들의 결과는 어떻게 되는지를 두고 고민해 본적도 있을 것이다.
r = ++k + k++;
a[k] = ++k;
위 구문의 합당성을 완전히 이해하기 위해서는 C와 같은 명령형 프로그래밍 언어의 특징을 알아야 한다. 명령형 프로그래밍 언어는 변수의 값을 바꾸는 명령이 모인 것을 프로그램으로 간주한다. 따라서 값의 변경, 내지는 상태를 변경하는 순서와 규칙이 중요하다. 왜냐하면 결국은 그 순서에 의해서 프로그램의 결과가 결정되기 때문이다.
이런 이유로 명령형 언어에서는 값의 변경이 완료된 지점, 내지는 모든 사이드 이펙트가 처리된 지점을 정의하고 있다. 그것이 바로 sequence point다. 모든 값의 변경은 이러한 sequence point를 기준으로 처리된다. C언어에서는 다음과 같은 것들을 sequence point로 정의하고 있다.
1. 함수 호출
2. 논리적 AND(&&), 논리적 OR(||), 삼항연산자(?), 콤마연산자(,)
3. 선언문의 완료
4. 표현식의 완료
5. 함수 리턴
C언어 표준은 sequence point와 관련해서 아래와 같은 제약 사항을 명시하고 있다. 한 오브젝트는 두 인접한 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. Furthermore, the prior value shall be read only to determine the value to be stored.
이러한 관점에서 앞서 제기했던 두 구문은 C언어 표준에 부합하지 않는다는 것을 알 수 있다. “r = ++k + k++”의 경우에는 인접한 두 sequence point 사이에서 k 값의 변경이 두 번 발생했기 때문에 표준에 부합하지 않는 문장이고, 따라서 이 연산의 결과는 보장할 수 없다. “a[k] = ++k”의 경우에는 변경된 k값이 최종적으로 저장될 대상의 값을 결정하기 위한 목적이 아닌 배열 인덱스의 용도로 사용되었기 때문에 표준의 두 번째 요건을 충족시키지 않는다. 따라서 이 또한 정의되지 않은 행동이고, 이 연산의 결과는 보장되지 않는다.
그렇다면 다음과 같은 printf 문장은 어떨까?
printf(“%d %d %d\n”, ++j, j++, ++j);
앞서 살펴본 C언어 sequence point 정의에서 콤마 연산자는 하나의 sequence point를 형성한다고 나와있다. 따라서 이 경우에는 정상적으로 값이 계산된다. 반면에 다음과 같은 구문은 인접한 두 sequence point 사이에 값을 두 번 변경하는 것이 되므로 부당한 것이다.
printf(“%d\n”, ++j + ++j);
일반적으로 C/C++ 개발자들이 알고 있는 것처럼 값의 변경 단위는 구문 단위로 이루어지지는 않는다. 값의 변경 단위는 sequence point라는 것을 단위로 이루어지며, 표준에서는 두 인접한 sequence point 사이에 발생할 수 있는 사이드 이펙트에 관한 제약사항을 분명하게 명시하고 있다. 이러한 복잡한 내용을 모두 피하고 싶다면 가급적 증감 연산자는 분리된 구문으로 작성하는 것이 좋다.
1 0