C와 C++은 문법이 매우 비슷해 보이지만, NULL 포인터 상수(null pointer constant)를 정의하는 방식에는 미묘하면서도 중요한 차이가 있습니다. 이 차이는 두 언어의 타입 시스템 설계 철학에서 비롯된 것입니다.
C++에서의 NULL 포인터
C++에서 널 포인터 상수는 값이 0인 정수 상수 표현식(integer constant expression)으로 정의됩니다. 예를 들어 다음과 같이 작성할 수 있습니다.
int* p = 0;
여기서 주목할 점은 C++에서는 (void*)0 형태의 캐스팅을 널 포인터 상수로 인정하지 않는다는 것입니다. 그 이유는 C++이 타입 안전성(type safety)을 중시하기 때문입니다. C++에서는 void* 포인터를 다른 포인터 타입으로 암시적으로 변환할 수 없으므로, 명시적인 캐스팅 없이는 대입 자체가 허용되지 않습니다.
C에서의 NULL 포인터
반면 C 언어에서는 널 포인터 상수가 값이 0인 정수 상수 표현식이거나, 그러한 표현식을 void*로 캐스팅한 값 모두 가능합니다.
int* p = 0;
또는 다음과 같이 작성할 수도 있습니다.
int* p = (void*)0;
C에서는 void*에서 임의의 객체 포인터 타입으로의 암시적 변환이 허용되기 때문에 두 방식 모두 유효합니다.
C++11의 nullptr 키워드
C++11부터는 숫자 0 대신 사용할 수 있는 전용 키워드 nullptr가 도입되었습니다. nullptr은 std::nullptr_t 타입을 가지며, 어떤 포인터 타입으로든 변환될 수 있지만 정수로는 해석되지 않습니다. 이를 통해 함수 오버로딩 시 발생할 수 있는 모호함(예: f(0) 호출 시 f(int)와 f(int*) 중 무엇이 선택될지 불분명한 문제)을 해결할 수 있습니다.
int* ptr = nullptr;
C 예제 코드
#include <stdio.h>
int main() {
int *p = NULL; // 포인터를 NULL로 초기화
printf("포인터의 값은 %u\n", p);
return 0;
}
실행 결과
포인터의 값은 0
C++ 예제 코드
#include <iostream>
using namespace std;
int main() {
int *p = NULL; // 포인터를 NULL로 초기화
cout << "포인터의 값은 ";
cout << p;
return 0;
}
실행 결과
포인터의 값은 0
정리
- C: 널 포인터 상수는
0또는(void*)0모두 허용됩니다. - C++: 널 포인터 상수는
0만 허용되며,(void*)0은 널 포인터 상수로 간주되지 않습니다. - C++11 이후: 타입 안전성을 보장하는
nullptr키워드 사용이 권장됩니다.
결국 이 차이는 C++이 C보다 엄격한 타입 검사를 채택하면서 생긴 것이며, 현대 C++ 코드에서는 가독성과 안전성을 위해 NULL이나 0 대신 nullptr을 사용하는 것이 가장 좋은 습관입니다.