C++에서 포인터는 메모리를 직접 다룰 수 있게 해주는 강력한 도구이지만, 잘못 사용하면 심각한 오류를 일으킬 수 있습니다. 이 글에서는 댕글링 포인터(Dangling Pointer), void 포인터(Void Pointer), NULL 포인터(Null Pointer), 와일드 포인터(Wild Pointer) 네 가지 주요 포인터 유형을 개념과 예제 코드와 함께 살펴봅니다.
1. 댕글링 포인터(Dangling Pointer)
댕글링 포인터란 이미 해제되었거나 삭제된 메모리 위치를 가리키는 포인터를 말합니다. 유효하지 않은 메모리에 접근하게 되므로 프로그램 충돌이나 예측 불가능한 동작의 원인이 됩니다. 댕글링 포인터가 발생하는 대표적인 상황은 다음과 같습니다.
① 함수 호출 시 (지역 변수 반환)
포인터가 static이 아닌 지역 변수를 가리키고 있을 때, 함수가 종료되면 해당 지역 변수는 소멸하므로 포인터는 댕글링 포인터가 됩니다.
int *show(void) {
int n = 76; /* ... */
return &n;
}
출력 결과: 이 프로그램은 쓰레기 값(garbage address)을 반환합니다.
② 메모리 해제 후
free()로 동적 할당된 메모리를 해제한 뒤에도 포인터가 그 주소를 계속 가지고 있다면 댕글링 포인터가 됩니다. 이를 방지하려면 해제 후 반드시 NULL을 대입해야 합니다.
int main() {
float *p = (float *)malloc(sizeof(float));
// 동적 메모리 할당
free(p);
// free() 호출 후 p는 댕글링 포인터가 됨
p = NULL;
// NULL 대입 후 p는 더 이상 댕글링 포인터가 아님
}
③ 변수가 스코프(scope)를 벗어날 때
블록 내부에서 선언된 변수의 주소를 외부 포인터가 가리키고 있으면, 블록이 끝나는 순간 그 변수는 소멸하고 포인터는 댕글링 상태가 됩니다.
int main() {
int *p;
// some code
{
int c;
p = &c;
}
// some code
// 이 시점부터 p는 댕글링 포인터임
}
2. void 포인터(Void Pointer)
void 포인터는 특정 데이터 타입과 연관되지 않은 포인터입니다. 저장소의 어떤 데이터 위치든 가리킬 수 있으며, 변수의 주소를 담을 수 있습니다. 타입에 구애받지 않기 때문에 범용 포인터(general purpose pointer)라고도 부릅니다.
다만 void 포인터에는 몇 가지 제약이 있습니다.
- 크기가 고정되어 있지 않아 포인터 연산(pointer arithmetic)이 불가능합니다.
- 직접 역참조(dereference)할 수 없으며, 적절한 타입으로 형 변환(casting)해야 합니다.
예제
#include<stdlib.h>
#include<iostream>
using namespace std;
int main() {
int a = 7;
float b = 7.6;
void *p;
p = &a;
cout << *((int*)p) << endl; // int* 로 형 변환 후 역참조
p = &b;
cout << *((float*)p); // float* 로 형 변환 후 역참조
return 0;
}
출력 결과:
7 7.600000
동작 원리(알고리즘)
Begin
정수형 변수 a와 실수형 변수 b를 각각 초기화한다.
void 포인터 p를 선언한다.
(int*)p → void 포인터를 int* 로 형 변환하여 값을 출력한다.
p = &b → void 포인터 p가 이제 float 변수를 가리킨다.
(float*)p → void 포인터를 float* 로 형 변환하여 값을 출력한다.
End.
3. NULL 포인터(Null Pointer)
NULL 포인터는 아무것도 가리키지 않는 포인터입니다. 유효하지 않은 메모리 접근을 사전에 차단하기 위한 안전장치로 활용됩니다.
NULL 포인터의 주요 용도는 다음과 같습니다.
포인터 변수에 아직 유효한 메모리 주소가 할당되지 않았을 때 초기값으로 사용합니다.
유효한 메모리 주소를 전달할 필요가 없을 때 함수 인자로 NULL을 전달합니다.
포인터 변수에 접근하기 전 NULL 여부를 검사하여, 포인터 관련 코드에서 오류 처리를 수행할 수 있습니다. 예를 들어 NULL이 아닌 경우에만 역참조하도록 작성하는 방식입니다.
예제
#include <iostream>
using namespace std;
int main() {
int *p = NULL; // 포인터를 NULL로 초기화
cout << "The value of pointer is ";
cout << p;
return 0;
}
출력 결과:
The value of pointer is 0.
4. 와일드 포인터(Wild Pointer)
와일드 포인터는 초기화되지 않은 채 임의의 메모리 위치를 가리키는 포인터입니다. NULL조차 가리키지 않기 때문에 더욱 위험하며, 역참조 시 정의되지 않은 동작(undefined behavior)을 유발할 수 있습니다.
int main() {
int *ptr; // 와일드 포인터 (초기화되지 않음)
*ptr = 5; // 위험! 임의의 메모리에 값을 쓰게 됨
}
와일드 포인터를 피하려면 포인터 선언 즉시 유효한 주소 또는 nullptr(C++11 이상)로 초기화하는 습관이 중요합니다.