C 언어에서 NULL 포인터와 void 포인터는 자주 혼동되는 개념입니다. 두 가지의 핵심적인 차이는 다음과 같습니다. NULL 포인터는 '값(value)'이고, void 포인터는 '타입(type)'이라는 점입니다.
즉, NULL 포인터는 포인터가 가질 수 있는 특수한 값(아무것도 가리키지 않음)을 의미하며, void 포인터는 가리키는 대상의 자료형이 정해지지 않은 포인터 타입을 뜻합니다.
NULL 포인터란?
NULL 포인터는 아무것도 가리키고 있지 않은 포인터를 의미합니다. 포인터 변수를 선언했지만 아직 유효한 메모리 주소를 할당하지 않았다면, 반드시 NULL로 초기화하는 것이 좋습니다. 그렇지 않으면 쓰레기 값(garbage value)을 가진 댕글링 포인터(dangling pointer)가 되어 예기치 않은 오류를 일으킬 수 있습니다.
int *, char * 등 모든 포인터 타입에는 각각 대응되는 널 포인터 값이 존재합니다.
NULL 포인터 선언 문법
<데이터 타입> *<변수명> = NULL;
예시:
int *p = NULL; char *c = '\0';
포인터를 NULL로 초기화한 후에는 역참조(dereference) 전에 반드시 NULL 여부를 검사하는 습관을 들이면 안전성을 크게 높일 수 있습니다.
NULL 포인터 예제 프로그램
다음은 NULL 포인터의 동작을 확인하는 C 프로그램입니다.
#include<stdio.h>
int main(){
printf("TutorialPoint C Programming");
int *p = NULL; // p는 NULL 포인터
printf("\n The value of pointer is: %x ", p);
return 0;
}실행 결과
TutorialPoint C Programming The value of pointer is: 0
NULL 포인터의 값은 0으로 출력됩니다. 즉, 어떤 유효한 메모리 주소도 가리키지 않음을 나타냅니다.
Void 포인터란?
void 포인터는 연결된 데이터 타입이 없는 포인터입니다. 범용 포인터(general purpose pointer)라고도 불리며, int, char, float 등 어떤 자료형의 주소든 저장할 수 있습니다.
void 포인터 선언 문법
void *<변수명>;
예시:
void *p; int a; char c; p = &a; // 정수형 주소가 대입되므로 p는 정수형 포인터처럼 동작 p = &c; // 문자형 주소가 대입되므로 p는 문자형 포인터처럼 동작
단, void 포인터는 자료형 정보가 없기 때문에 역참조하기 전에 반드시 적절한 타입으로 명시적 형 변환(casting)을 해주어야 합니다. 또한 void 포인터에는 포인터 산술 연산(pointer arithmetic)을 직접 수행할 수 없다는 점도 기억하세요.
Void 포인터 예제 프로그램
다음은 void 포인터를 활용하는 C 프로그램입니다.
#include<stdio.h>
int main(){
int a = 10;
void *ptr = &a;
printf("%d", *(int *)ptr); // int* 로 형 변환 후 역참조
return 0;
}실행 결과
10
*(int *)ptr처럼 void 포인터를 int *로 형 변환한 뒤 역참조하면, 원래 저장되어 있던 정수값 10이 정상적으로 출력됩니다.
정리: NULL 포인터 vs Void 포인터 비교표
| 구분 | NULL 포인터 | Void 포인터 |
|---|---|---|
| 본질 | 특수한 '값' | 자료형 미지정 '타입' |
| 의미 | 아무것도 가리키지 않음 | 모든 자료형의 주소를 저장 가능 |
| 선언 예 | int *p = NULL; | void *p; |
| 역참조 | NULL 검사 후 사용 권장 | 반드시 형 변환 필요 |
| 활용 | 포인터 안전한 초기화 | 범용 함수 매개변수 등 |
두 개념은 서로 배타적인 것이 아니라 함께 사용할 수도 있습니다. 예를 들어 void *p = NULL;처럼 선언하면, 자료형이 정해지지 않으면서 아무것도 가리키지 않는 포인터가 됩니다. 상황에 맞게 올바르게 활용하면 더욱 안전하고 유연한 C 코드를 작성할 수 있습니다.