HOWTO · C++
C++의 사전 증가 대 사후 증가 연산자
이 기사에서는 C++에서 사전 증가 VS 사후 증가를 사용하는 방법을 보여줍니다.
이 페이지의 내용
C++의 기본 산술형 변수에서는 ++x와 x++ 모두 x를 1 증가시킵니다. 차이는 식의 결과값입니다. 전위 증가는 증가한 뒤의 값을, 후위 증가는 증가하기 전의 값을 반환합니다. 주변 식에서 어떤 값이 필요한지에 따라 선택하세요. 사용자 정의 타입에서는 오버로드된 증가 연산자의 동작을 직접 정의하며, 관례적으로 기본 연산자와 비슷한 전위 및 후위 동작을 제공합니다.
C++ 전위 증가와 후위 증가 연산의 차이
기본 산술형에서는 ++x가 x를 변경한 뒤 식의 결과값을 만듭니다. x++는 이전 값을 결과로 내고 연산 과정에서 증가를 완료합니다. 두 경우 모두 변수는 한 번 변경됩니다. 오버로드된 연산자는 세부 동작을 다르게 정의할 수 있지만, 관례적으로 같은 전위와 후위의 차이를 따릅니다.
다음 예제를 실행해 보세요.
#include <iostream>
int main() {
int value = 4;
int previous = value++;
int current = ++value;
std::cout << previous << ' ' << current << ' ' << value << '\n';
}
4 6 6
previous에는 첫 번째 증가 전의 값이 저장되고, current에는 두 번째 증가 후의 값이 저장됩니다. 이 값은 value의 최종값이기도 합니다.
루프 본문에서 증가 연산을 어디에 두는지에 따라 본문이 출력하는 값이 달라집니다. 첫 번째 루프에서는 i++가 출력 식이므로 현재 값을 출력한 다음 i를 증가시킵니다. 두 번째 루프에서는 출력 식보다 먼저 ++j가 실행되므로 본문은 증가한 값을 출력합니다.
#include <iostream>
int main() {
int i = 0;
while (i < 5) {
std::cout << i++ << '\n';
}
std::cout << "final i = " << i << '\n';
}
#include <iostream>
int main() {
int j = 0;
while (j < 5) {
std::cout << ++j << '\n';
}
std::cout << "final j = " << j << '\n';
}
첫 번째 루프는 0, 1, 2, 3, 4를 출력한 뒤 final i = 5를 출력합니다. 두 번째 루프는 1, 2, 3, 4, 5를 출력한 뒤 final j = 5를 출력합니다. 여기서 final i와 final j라는 레이블은 코드와 출력 블록이 변경되지 않아 영어로 표시됩니다. 두 루프 모두 카운터가 0에서 시작해 본문을 다섯 번 실행하고 최종적으로 5가 됩니다. 이 예제에서는 본문 안에서 증가시킵니다. 반면 증가 연산이 for 루프의 반복 식에 있으면 본문 실행 후에 수행됩니다. 그 결과값을 사용하지 않는다면 ++k를 k++로 바꾸어도 반복 순서는 달라지지 않습니다. for (int k = 0; k < 5; ++k)와 증가 연산을 k++로 쓴 버전 모두 0, 1, 2, 3, 4를 방문한 뒤 k가 5가 됩니다.
하나의 식에서 같은 변수를 수정하고 읽지 않기
변수를 증가시키는 연산과 같은 변수를 읽는 연산을 순서가 정해지지 않은 하나의 식에 함께 넣지 마세요. 예를 들어 k++ * k는 C++에서 정의되지 않은 동작을 일으킵니다. 변수 변경과 다른 읽기 사이의 실행 순서가 정해져 있지 않기 때문입니다. 따라서 결과를 예측할 수 없으며 유효한 예제 출력으로 사용할 수도 없습니다.
이전 값과 다음 값을 곱하려는 의도라면 단계를 분명히 나누어 순서를 정하세요. k를 초기화하고, 값을 old에 저장한 다음, k를 증가시키고 old * k를 계산합니다.
각 문장은 k를 증가시키기 전에 이전 값을 저장하고, 저장된 값과 증가한 값을 곱합니다. 다음 예제를 실행해 보세요.
#include <iostream>
int main() {
int k = 10;
int old = k;
++k;
int product = old * k;
std::cout << product << '\n';
}
110
전위 증가와 후위 증가를 선택하는 경우
식에서 이전 값이 필요하면 int previous = value++;와 같이 후위 증가를 사용하세요. 증가한 값이 필요하거나 루프의 반복 식처럼 식의 결과값을 사용하지 않는 경우에는 전위 증가를 사용하면 됩니다.
반복자의 이전 값이 필요하지 않을 때는 전위 증가를 사용하는 것이 일반적인 관례입니다. 사용자 정의 반복자의 오버로드 연산자는 구현에 따라 동작하지만, 관례적으로 기본 타입의 전위와 후위 구분을 따릅니다. 후위 증가 연산은 이전 반복자의 복사본을 보존해 반환할 수 있습니다. 복사 비용이 드는 반복자 타입에서는 전위 증가가 그 작업을 피할 수 있습니다. 다만 이것만으로 특정 프로그램에서 측정 가능한 속도 차이가 난다고 단정할 수는 없습니다. 컴파일러 최적화와 반복자 구현에 따라 생성되는 작업이 달라집니다.
결론
두 형식 모두 변수를 한 번 증가시킵니다. 전위 증가는 증가한 값을 반환하고, 후위 증가는 이전 값을 반환합니다. 식에 필요한 값을 잘 드러내는 형식을 선택하세요. 반복자의 이전 값이 필요하지 않다면 전위 증가를 사용하는 편이 좋지만, 속도가 반드시 향상된다고 가정해서는 안 됩니다.
자주 묻는 질문
전위 증가와 후위 증가의 가장 큰 차이는 무엇인가요?
기본 산술형에서 ++x는 먼저 x를 변경하고 변경된 값을 반환합니다. x++는 이전 값을 반환한 뒤 증가를 완료합니다. 오버로드된 연산자는 각 구현에 따라 동작하지만, 관례적으로 이에 대응하는 전위 및 후위 동작을 제공합니다.
루프에서 두 형식 중 어느 것이든 사용할 수 있나요?
네. for 루프의 반복 식에서는 결과값을 사용하지 않는 경우가 많으므로, ++i를 i++로 바꾸어도 해당 루프의 반복 순서는 달라지지 않습니다.
C++에서는 어느 형식이 더 빠른가요?
모든 식과 타입에 적용되는 보편적인 속도 순위는 없습니다. 반복자나 사용자 정의 타입의 후위 증가는 임시 복사본을 만들 수 있지만, 최적화가 불필요한 작업을 제거할 수도 있습니다. 필요한 값과 명확한 의도에 따라 선택하고, 성능을 주장하기 전에 실제 작업 부하를 측정하세요.
후위 증가는 언제 유용한가요?
다른 변수에 이전 값을 대입하는 경우처럼 식에서 증가 전의 값이 필요할 때 사용합니다.
모든 언어에서 전위 증가와 후위 증가가 똑같이 동작하나요?
아니요. 표기가 비슷한 연산자도 언어마다 규칙이 다를 수 있습니다. 사용하는 언어의 의미를 확인하세요. 여기서 설명한 동작은 C++에 해당합니다.