포인터는 주소를 저장한다 — 그렇다면 왜 타입이 있나요?
Source: Dev.to
포인터에 타입이 있는 이유
우리는 종종 포인터는 메모리 주소를 저장하는 변수일 뿐이다 라는 말을 듣습니다.
이 말은 기술적으로 맞지만 완전하지는 않습니다. 포인터가 단순히 원시 주소만이라면, 모든 포인터는 동일할 것입니다(보통 4바이트 또는 8바이트). C/C++에서는 char*, int*, double*, struct* 등 다양한 포인터 타입이 존재합니다.
주소가 단지 숫자라면, 타입이 왜 중요한 걸까요?
포인터 타입의 역할
물리 메모리는 정수, 문자, 부동소수점 숫자를 구분하지 않습니다; 단순히 바이트들의 연속일 뿐입니다. 포인터 타입은 컴파일러에게 두 가지 중요한 정보를 제공하는 템플릿 역할을 합니다:
- 폭 – 해당 주소에서 시작해 몇 바이트를 읽을지.
- 해석 – 읽어들인 바이트들을 어떻게 취급할지(예: 부호 있는 정수, IEEE‑754 부동소수점, ASCII 문자 등).
예시 시각화
주소 0x20000000부터 시작하는 네 바이트 메모리를 살펴보겠습니다:
| 주소 | 값 |
|---|---|
0x20000000 | 0x01 |
0x20000001 | 0x02 |
0x20000002 | 0x03 |
0x20000003 | 0x04 |
char* 역참조
char *pc = (char *)0x20000000;
char c = *pc; // 1 바이트를 읽음
컴파일러는 정확히 한 바이트(0x01)를 읽고 이를 문자로 해석합니다.
int* 역참조
int *pi = (int *)0x20000000;
int i = *pi; // 4 바이트를 읽음
컴파일러는 다음 네 바이트를 읽어 작은 엔디안 시스템에서는 0x04030201이라는 정수값으로 결합합니다.
주소와 그 아래에 있는 데이터 자체는 변하지 않으며, 해석만 달라집니다.
메모리와 포인터에 관한 기본 진리
- 메모리는 바이트 단위 저장소로 이루어진 선형 주소 공간이다.
- 주소는 단지 시작점일 뿐이다.
- 포인터 타입은 컴파일러에게 그 주소에 있는 바이트들을 어떻게 해석할지 알려준다.
메모리 자체는 타입 개념이 없기 때문에, 포인터 타입은 매우 중요해집니다. 컴파일러에 다음을 알려줍니다:
- 읽거나 쓸 바이트 수.
- 그 바이트들을 어떻게 해석할지.
- 포인터 연산이 어떻게 동작해야 하는지.
포인터 연산과 타입 스케일링
포인터 연산은 가리키는 대상 타입의 크기에 따라 스케일됩니다. 예를 들어:
int *p;
p = /* some address */;
p + 1; // sizeof(int) == 4 이므로 4 바이트 앞으로 이동
만약 p가 char*라면 p + 1은 한 바이트만 이동합니다.
“포인터는 주소를 저장한다”는 사실은 첫 번째 단계일 뿐입니다. 포인터 타입을 이해하면 소프트웨어가 메모리와 실제로 어떻게 소통하는지 알 수 있습니다.