59.2 포인터 연산과 역참조 사용하기
포인터 연산으로 조작한 메모리 주소도 역참조 연산을 사용하여 메모리에 접근할 수 있습니다. 다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.
pointer_dereference.c
#include <stdio.h> int main() { int numArr[5] = { 11, 22, 33, 44, 55 }; int *numPtrA; int *numPtrB; int *numPtrC; numPtrA = numArr; // 배열 첫 번째 요소의 주소를 포인터에 저장 numPtrB = numPtrA + 1; // 포인터 연산. numPtrA + 4바이트 numPtrC = numPtrA + 2; // 포인터 연산. numPtrA + 8바이트 printf("%d\n", *numPtrB); // 22: 역참조로 값을 가져옴, numArr[1]과 같음 printf("%d\n", *numPtrC); // 33: 역참조로 값을 가져옴, numArr[2]와 같음 return 0; }
실행 결과
22 33
numPtrB에는 numPtrA에 1을 더해서 4바이트만큼 순방향으로 이동한 메모리 주소를 저장했고, numPtrC에는 numPtrA에 2를 더해서 8바이트만큼 순방향으로 이동한 메모리 주소를 저장했습니다.
numPtrB = numPtrA + 1; // 포인터 연산. numPtrA + 4바이트 numPtrC = numPtrA + 2; // 포인터 연산. numPtrA + 8바이트
numPtrB와 numPtrC도 일반 포인터이므로 * (역참조 연산자)를 사용하여 메모리의 값을 가져올 수 있습니다. 여기서 포인터 연산은 결과적으로 numPtrA + 1과 numArr[1]은 같고, numPtrA + 2와 numArr[2]는 같습니다.
printf("%d\n", *numPtrB); // 22: 역참조로 값을 가져옴, numArr[1]과 같음 printf("%d\n", *numPtrC); // 33: 역참조로 값을 가져옴, numArr[2]와 같음
이번에는 포인터 연산과 동시에 역참조 연산을 사용하는 방법을 알아보겠습니다. 다음과 같이 포인터 연산을 한 부분을 ( ) 괄호로 묶어준 뒤 맨 앞에 * (역참조 연산자)를 붙이면 됩니다.
- *(포인터 + 값)
- *(포인터 - 값)
다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.
pointer_add_dereference.c
#include <stdio.h> int main() { int numArr[5] = { 11, 22, 33, 44, 55 }; int *numPtrA; numPtrA = numArr; // 배열 첫 번째 요소의 주소를 포인터에 저장 printf("%d\n", *(numPtrA + 1)); // 22: numPtrA에서 순방향으로 4바이트만큼 떨어진 // 메모리에 주소에 접근. numArr[1]과 같음 printf("%d\n", *(numPtrA + 2)); // 33: numPtrA에서 순방향으로 8바이트만큼 떨어진 // 메모리에 주소에 접근. numArr[2]와 같음 return 0; }
실행 결과
22 33
*(numPtrA + 1)와 같이 numPtrA에 1을 더한 뒤 괄호로 묶어줍니다. 그리고 맨 앞에 역참조 연산자를 붙여주면 numPtrA에서 순방향으로 4바이트만큼 떨어진 메모리 주소에서 값을 가져올 수 있습니다. 여기서 포인터 연산과 역참조 연산 *(numPtrA + 1)은 결과적으로 numArr[1]과 같습니다.
printf("%d\n", *(numPtrA + 1)); // 22: numPtrA에서 순방향으로 4바이트만큼 떨어진 // 메모리에 주소에 접근. numArr[1]과 같음 printf("%d\n", *(numPtrA + 2)); // 33: numPtrA에서 순방향으로 8바이트만큼 떨어진 // 메모리에 주소에 접근. numArr[2]와 같음
만약 포인터 연산을 괄호로 묶어주지 않으면 역참조 연산자가 먼저 실행되어 값을 가져온 뒤 덧셈(뺄셈) 연산을 하게 됩니다. 따라서 다음 코드는 numPtrA를 역참조하여 11을 가져온 뒤 1을 더하므로 12가 됩니다.
printf("%d\n", *numPtrA + 1); // 12: numPtrA를 역참조하여 나온 값에 1을 더하므로 12
즉, 역참조 연산보다 포인터 연산이 먼저 실행될 수 있도록 괄호로 묶어주는 것입니다.
이번에는 증가 연산자와 역참조 연산자를 사용해보겠습니다.
- *(++포인터)
- *(--포인터)
pointer_inc_dec_dereference.c
#include <stdio.h> int main() { int numArr[5] = { 11, 22, 33, 44, 55 }; int *numPtrA; int *numPtrB; int *numPtrC; numPtrA = &numArr[2]; // 배열 세 번째 요소의 주소를 포인터에 저장 numPtrB = numPtrA; numPtrC = numPtrA; printf("%d\n", *(++numPtrB)); // 44: numPtrA에서 순방향으로 4바이트만큼 떨어진 // 메모리 주소에 접근 printf("%d\n", *(--numPtrC)); // 22: numPtrA에서 역방향으로 4바이트만큼 떨어진 // 메모리 주소에 접근 return 0; }
실행 결과
44 22
*(++numPtrB)와 같이 변수 앞에 증가 연산자를 사용한 뒤 괄호로 묶어줍니다. 그리고 맨 앞에 역참조 연산자를 붙여주면 numPtrB에 저장된 메모리 주소가 4바이트만큼 증가하고, 해당 메모리의 값을 가져옵니다. 마찬가지로 *(--numPtrC)처럼 감소 연산자와 역참조 연산자를 사용하면 numPtrC에 저장된 메모리 주소가 4바이트만큼 감소하고, 해당 메모리의 값을 가져옵니다.
printf("%d\n", *(++numPtrB)); // 44: numPtrA에서 순방향으로 4바이트만큼 떨어진 // 메모리 주소에 접근 printf("%d\n", *(--numPtrC)); // 22: numPtrA에서 역방향으로 4바이트만큼 떨어진 // 메모리 주소에 접근
만약 증감 연산자를 변수 앞에 사용할 때 괄호로 묶어주지 않아도 동작은 같습니다(전위 증감 연산자와 역참조 연산자는 우선순위가 같고 결합방향이 오른쪽에서 왼쪽이기 때문).
printf("%d\n", *++numPtrB); // 44 printf("%d\n", *--numPtrC); // 22
증가, 감소 연산자를 변수 뒤에 붙이고 포인터 연산을 하면 현재 메모리의 값을 가져온 뒤 포인터 연산을 하므로 주의해야 합니다.
printf("%d\n", *(numPtrB++)); // 33: numPtrA의 메모리에 접근하여 값을 가져온 뒤 포인터 연산 printf("%d\n", *(numPtrC--)); // 33: numPtrA의 메모리에 접근하여 값을 가져온 뒤 포인터 연산