개요
C++에서 포인터를 다루다 보면 *ptr++, *++ptr, ++*ptr처럼 비슷해 보이지만 전혀 다른 의미를 가진 표현식들을 만나게 됩니다. 이 세 표현식의 차이는 연산자 우선순위(precedence)에 의해 결정되는데, 이를 제대로 이해하지 못하면 예상치 못한 버그를 만들 수 있습니다.
연산자 우선순위 이해하기
핵심은 증감 연산자(++/--)와 역참조 연산자(*) 사이의 우선순위 관계입니다.
- 후위 증감 연산자(ptr++): 전위 증감과 역참조 연산자(*)보다 우선순위가 높습니다.
- 전위 증감 연산자(++ptr): 역참조 연산자(*)보다 우선순위가 높습니다.
따라서 컴파일러는 아래와 같이 해석합니다.
*ptr++→*(ptr++): 포인터 자체를 증가시킨 후 해당 위치의 값을 참조++*ptr→++(*ptr): 포인터가 가리키는 '값'을 1 증가*++ptr→ 포인터를 먼저 증가시킨 뒤 새로운 위치의 값을 참조
예제 1: ++*ptr — 값 자체를 증가
#include<iostream>
using namespace std;
int main() {
char arr[] = "Hello World";
char *ptr = arr;
++*ptr;
cout << *ptr;
return 0;
}출력 결과
I
처음에 ptr은 배열의 첫 번째 문자 'H'를 가리키고 있습니다. 여기서 ++*ptr을 사용하면 포인터가 아니라 포인터가 가리키는 값인 'H'가 1 증가하여 'I'가 됩니다. 즉, 문자열의 내용 자체가 변경된 것입니다.
예제 2: *ptr++ — 포인터를 증가
#include<iostream>
using namespace std;
int main() {
char arr[] = "Hello World";
char *ptr = arr;
*ptr++;
cout << *ptr;
return 0;
}출력 결과
e
이번에는 처음에 ptr이 'H'를 가리키고 있습니다. *ptr++은 후위 증감 연산자의 우선순위가 높기 때문에 포인터 자체가 증가하여 ptr은 다음 요소를 가리키게 됩니다. 그 결과 두 번째 문자인 'e'가 출력됩니다.
예제 3: *++ptr — 먼저 증가한 후 참조
#include<iostream>
using namespace std;
int main() {
char arr[] = "Hello World";
char *ptr = arr;
*++ptr;
cout << *ptr;
return 0;
}출력 결과
e
여기서도 전위 증감 연산자(++)가 역참조 연산자(*)보다 우선순위가 높으므로, 포인터가 먼저 증가한 후 그 위치의 값을 참조합니다. 결과적으로 ptr은 두 번째 문자를 가리키며 'e'가 출력됩니다.
정리
| 표현식 | 해석 | 동작 |
|---|---|---|
*ptr++ | *(ptr++) | 포인터를 증가시킨 후 해당 위치의 값 참조 |
*++ptr | *(++ptr) | 포인터를 먼저 증가시킨 후 값 참조 |
++*ptr | ++(*ptr) | 포인터가 가리키는 값을 1 증가 |
결론적으로 *ptr++과 *++ptr은 포인터의 위치를 이동시키고, ++*ptr은 포인터가 가리키는 데이터 자체를 변경한다는 점이 핵심 차이입니다. 코드 가독성을 위해 이런 표현식이 필요하다면 괄호를 명시적으로 사용하는 것이 좋습니다.