44.1 포인터 연산으로 메모리 주소 조작하기
포인터 연산은 포인터 변수에 +, - 연산자를 사용하여 값을 더하거나 뺍니다. 또는, ++, -- 연산자를 사용하여 값을 증가, 감소시킵니다. 단, *, / 연산자와 실수 값은 사용할 수 없습니다.
- 포인터 + 값
- 포인터 - 값
다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.
pointer_add.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; // 포인터 연산 numPtrC = numPtrA + 2; // 포인터 연산 printf("%p\n", numPtrA); // 00A3FC00: 메모리 주소. 컴퓨터마다, 실행할 때마다 달라짐 printf("%p\n", numPtrB); // 00A3FC04: sizeof(int) * 1이므로 numPtrA에서 4가 증가함 printf("%p\n", numPtrC); // 00A3FC08: sizeof(int) * 2이므로 numPtrB에서 8이 증가함 return 0; }
실행 결과
00A3FC00 (메모리 주소. 컴퓨터마다, 실행할 때마다 달라짐) 00A3FC04 00A3FC08
먼저 정수형 배열을 선언하고 값을 초기화 했습니다. 그리고 배열을 포인터에 할당했습니다(배열 이름 자체는 배열의 첫 번째 요소에 대한 포인터라 할 수 있습니다).
포인터 연산은 특별한 것이 없습니다. 포인터 변수에 그냥 정수 값을 더하거나 빼면 됩니다. 단, 연산하는 값이 메모리 주소이므로 곱하거나 나누는 연산은 의미가 없습니다(컴파일 에러 발생).
numPtrB = numPtrA + 1; // 포인터 연산 numPtrC = numPtrA + 2; // 포인터 연산
그런데 연산한 결과가 조금 이상하죠? numPtrA에 저장된 메모리 주소 00A3FC00에 1을 더하면 1이 증가한 00A3FC01이 나와야 할 것 같은데 00A3FC04가 나왔습니다. 또한, 00A3FC00에 2를 더하면 2가 증가한 00A3FC02가 나와야 할 것 같지만 00A3FC08이 나옵니다(주솟값은 컴퓨터마다, 실행할 때마다 달라짐). 즉, 포인터 연산은 포인터 자료형의 크기만큼 더하거나 뺍니다.
printf("%p\n", numPtrA); // 00A3FC00: 메모리 주소. 컴퓨터마다, 실행할 때마다 달라짐 printf("%p\n", numPtrB); // 00A3FC04: sizeof(int) * 1이므로 numPtrA에서 4가 증가함 printf("%p\n", numPtrC); // 00A3FC08: sizeof(int) * 2이므로 numPtrB에서 8이 증가함
여기서는 numPtrA가 4바이트 크기의 int형입니다. 따라서 numPtrA + 1은 메모리 주소에서 4바이트만큼 1번 순방향으로 이동한다는 뜻(포인터 전진, forward)이고, numPtrA + 2는 메모리 주소에서 4바이트만큼 2번 순방향으로 이동한다는 뜻입니다. 즉, 계산식은 "sizeof(자료형) * 더하거나 빼는 값"이 됩니다.
이번에는 포인터에서 뺄셈을 해보겠습니다.
pointer_sub.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 - 1; // 포인터 연산 numPtrC = numPtrA - 2; // 포인터 연산 printf("%p\n", numPtrA); // 00A3FC08: 메모리 주소. 컴퓨터마다, 실행할 때마다 달라짐 printf("%p\n", numPtrB); // 00A3FC04: sizeof(int) * -1이므로 numPtrA에서 4가 감소함 printf("%p\n", numPtrC); // 00A3FC00: sizeof(int) * -2이므로 numPtrB에서 8이 감소함 return 0; }
실행 결과
00A3FC08 (메모리 주소. 컴퓨터마다, 실행할 때마다 달라짐) 00A3FC04 00A3FC00
numPtrA = &numArr[2];와 같이 배열에 [ ](대괄호)를 사용하여 요소에 접근한 뒤 &(주소 연산자)를 사용하면 해당 요소의 메모리 주소를 구할 수 있습니다. 여기서는 numArr에서 세 번째 요소의 메모리 주소를 구해서 numPtrA에 저장했습니다.
포인터 연산으로 numPtrA에서 1, 2를 뺍니다.
numPtrB = numPtrA - 1; // 포인터 연산 numPtrC = numPtrA - 2; // 포인터 연산
numPtrB는 numPtrA에서 4가 감소한 값이 나오고, numPtrC는 numPtrA에서 8이 감소한 값이 나옵니다. 즉, sizeof(int) * -1이므로 메모리 주소에서 4바이트만큼 역방향으로 이동(포인터 후진, backward)하고, sizeof(int) * -2이므로 메모리 주소에서 8바이트만큼 역방향으로 이동합니다.
printf("%p\n", numPtrA); // 00A3FC08: 메모리 주소. 컴퓨터마다, 실행할 때마다 달라짐 printf("%p\n", numPtrB); // 00A3FC04: sizeof(int) * -1이므로 numPtrA에서 4가 감소함 printf("%p\n", numPtrC); // 00A3FC00: sizeof(int) * -2이므로 numPtrB에서 8이 감소함
포인터 연산은 "sizeof(자료형) * 더하거나 빼는 값"이므로 char는 1바이트, short는 2바이트, long long은 8바이트만큼 메모리 주소에서 순방향, 역방향으로 이동합니다.
pointer_add_char_short_long_long.c
#include <stdio.h> int main() { char *cPtr1 = NULL; short *numPtr1 = NULL; long long *numPtr2 = NULL; printf("%p\n", cPtr1 + 1); // 00000001: 0x000000에서 1바이트만큼 순방향으로 이동 printf("%p\n", numPtr1 + 1); // 00000002: 0x000000에서 2바이트만큼 순방향으로 이동 printf("%p\n", numPtr2 + 1); // 00000008: 0x000000에서 8바이트만큼 순방향으로 이동 return 0; }
실행 결과
00000001 00000002 00000008
이번에는 증가, 감소 연산자를 사용해보겠습니다.
- 포인터++
- 포인터--
- ++포인터
- --포인터
pointer_increment_decrement.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; numPtrB++; // 포인터 연산 numPtrC--; // 포인터 연산 printf("%p\n", numPtrA); // 00A3FC08: 메모리 주소. 컴퓨터마다, 실행할 때마다 달라짐 printf("%p\n", numPtrB); // 00A3FC0C: sizeof(int) * 1이므로 numPtrA에서 4가 증가함 printf("%p\n", numPtrC); // 00A3FC04: sizeof(int) * -1이므로 numPtrB에서 4가 감소함 return 0; }
실행 결과
00A3FC08 00A3FC0C 00A3FC04
먼저 numPtrB와 numPtrC에 numPtrA의 메모리 주소를 저장했습니다. 그리고 numPtrB에는 증가 연산자를 사용하고, numPtrC에는 감소 연산자를 사용했습니다. 메모리 주소는 어떻게 될까요?
증가 연산자는 + 1과 같습니다. 따라서 sizeof(int) * 1이 되므로 numPtrA에서 4가 증가합니다. 마찬가지로 감소 연산자는 - 1과 같으므로 sizeof(int) * -1이고 numPtrB에서 4가 감소합니다.
numPtrB++; // 포인터 연산 numPtrC--; // 포인터 연산 printf("%p\n", numPtrA); // 00A3FC08: 메모리 주소. 컴퓨터마다, 실행할 때마다 달라짐 printf("%p\n", numPtrB); // 00A3FC0C: sizeof(int) * 1이므로 numPtrA에서 4가 증가함 printf("%p\n", numPtrC); // 00A3FC04: sizeof(int) * -1이므로 numPtrB에서 4가 감소함