소년코딩

03.03 - 증감 연산자와 사이드 이펙트 (Increment/decrement operators, and side effects)

변수를 증가(+1)하고 감소(-1)하는 것은 흔한 연산이기 때문에 C++ 에는 전위(prefix) 버전과 후위(postfix) 버전 두 가지 연산이 있다.

Operator Symbol Form Operation
Prefix increment (pre-increment) ++ ++x Increment x, then evaluate x
Prefix decrement (pre-decrement) –– ––x Decrement x, then evaluate x
Postfix increment (post-increment) ++ x++ Evaluate x, then increment x
Postfix decrement (post-decrement) –– x–– Evaluate x, then decrement x

전위 증감 연산자(prefix increment/decrement operator)는 매우 간단하다. 증가 또는 감소한 다음 x값을 평가한다.

int x = 5;
int y = ++x; // x는 이제 6과 같고, 6은 y에 할당된다.

후위 증감 연산자(postfix increment/decrement operator)는 조금 더 까다롭다. 컴파일러는 x의 임시 복사본을 만들고, 원본 x를 증가시키거나 감소시킨 다음 x의 임시 복사본을 평가한다. 그다음 x의 임시 복사본이 삭제된다.

int x = 5;
int y = x++; // 5는 y에 할당되고, x는 이제 6과 같다.

위 코드가 어떻게 수행되는지 알아보자. 먼저, 컴파일러는 x와 같은 값을 가진 임시 복사본 x(5)를 만든다. 그런 다음 원본 x를 5에서 6으로 증가시킨다. 그런 다음 컴파일러가 5로 평가되는 임시 복사본 x값을 y에 할당한다. 그런 다음 임시 복사본이 삭제된다.

결과적으로 x는 값이 5이고, y는 6이다.

다음은 전위 연산자와 후위 연산자의 차이를 보여주는 다른 예이다:
int x = 5, y = 5;
std::cout << x << " " << y << '\n';
std::cout << ++x << " " << --y << '\n'; // prefix
std::cout << x << " " << y << '\n';
std::cout << x++ << " " << y-- << '\n'; // postfix
std::cout << x << " " << y << '\n';

This produces the output:

5 5
6 4
6 4
6 4
7 3

세 번째 줄 std::cout << ++x << " " << --y << '\n'; 에서 xy는 평가되기 전에 증가하고 감소하므로 새로운 값은 std::cout으로 출력된다.

다섯 번째 줄 std::cout << x++ << " " << y-- << '\n'; 에서는 원래 값의 임시 복사본(x=6, y=4)이 std::cout으로 전송된 다음 원래 xy가 증가한다. 그래서 다음 줄까지 후위 연산자의 결과가 보이지 않는다.

전위 증감 연산자를 사용하는 것이 후위 증감 연산자를 사용하는 것보다 성능이 더 좋을뿐더러 이상한 문제에 부딪힐 가능성이 더 작다.


사이드 이펙트 (Side effects)

'사이드 이펙트(side effect)'란 함수 또는 표현식이 특정 상태(Ex. 메모리에 저장된 정보)를 수정하거나 입력 또는 출력하거나 다른 함수를 호출하는 것을 말한다.

사이드 이펙트는 대부분 유용하다:
x = 5;
++x;
std::cout << x;

위 예제에서 할당 연산자(=)는 x의 값을 변경하는 사이드 이펙트를 가진다. 명령문이 실행 완료된 후에도 x는 값이 5이다. 연산자 ++x값을 증가시키는 사이드 이펙트가 있다. x의 출력은 콘솔을 수정하는 사이드 이펙트를 가진다.

그러나 사이드 이펙트는 예상치 못한 문제를 일으키기도 한다:
int add(int x, int y)
{
    return x + y;
}

int main()
{
    int x = 5;
    int value = add(x, ++x); // is this 5 + 6, or 6 + 6?  It depends on what order your compiler evaluates the function arguments in

    std::cout << value; // value could be 11 or 12, depending on how the above line evaluates!
    return 0;
}

C++은 함수 인수를 평가하는 순서를 정의하지 않았다. 만약 왼쪽 인수를 먼저 평가하면 add(5, 6)을 호출하여 11을 반환하고, 오른쪽 인수를 먼저 평가하면 add(6, 6,)이 되어 12를 반환한다.

add() 함수에 대한 인수 중 하나가 사이드 이펙트를 가지고 있기므로 이런 문제가 생긴다.

undefined behavior:
int main()
{
    int x = 1;
    x = x++;
    std::cout << x;

    return 0;
}

정의되지 않은 동작(undefined behavior)이란 프로그래밍 언어에서 동작이 제대로 정의하지 않은 코드를 실행한 결과를 말한다.

할당 전에 ++ 연산자가 먼저 적용되면 1이 출력된다. (후위 연산자 ++x를 1에서 2로 증가시키지만 1로 평가되므로 표현식은 x = 1이 된다. )

할당 후에 ++ 연산자가 적용되면 2가 출력된다. (x = x로 평가한 다음 ++를 적용하여 1에서 2로 x가 증가한다.)

위와 같은 문제들은 사이드 이펙트가 적용된 변수를 한 명령문(statement)에서 두 번 이상 사용하지 않음으로써 모두 피할 수 있다.


cpp 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/33-incrementdecrement-operators-and-side-effects/ 입니다.

댓글 로드 중…

블로그 정보

소년코딩 - 소년코딩

소년코딩, 자바스크립트, C++, 물리, 게임 코딩 이야기

최근에 게시된 이야기