nullptr(널포인터)?
- nullptr은 C++11부터 추가된 새로운 키워드이다.
- 기존 C, C++은 NULL이나 0을 사용해 포인터를 초기화 하였다.
- nullptr은 널 포인터 값(Null Pointer Value)를 나타내는 포인터 리터럴(Pointer Literal)이다.
- 즉, 포인터를 표현하는 값 중 널을 표현한 값이다.
int* ptr1 = 0;
int* ptr2 = nullptr;
- 기존의 방식과 동일하게 0으로 초기화할 수 있지만 nullptr을 사용하는 것이 직관적
- 이전에 0을 사용해서 초기화하는 방식은 실제 0을 저장시 모호한 상황이 발생할 수 있음
- nullptr 사용 시 모호한 상황이 아닌 포인터만 체크할 수 있다는 장점이 있음
nullptr_t 타입
- C++11부터 만들어진 nullptr 타입은 <cstddef> 헤더에 있는 std::nullptr_t 타입이다.
- 0이 int 타입, 0.0이 double 타입, 0.0f가 float 타입인듯 모든 변수에는 데이터 타입이 존재
- nullptr은 std::nullptr_t라는 타입
- std::nullptr_t 타입은 모든 타입의 포인터로 암시적 형 변환이 가능
int* ptr1 = nullptr; // int 포인터
char* ptr2 = nullptr; // char 포인터
double* ptr3 = nullptr; // double 포인터
void (*func1) (int a, int b) = nullptr; // 함수 포인터1
void (*func2) () = nullptr; // 함수 포인터2
- 위와 같이 모든 타입의 포인터를 초기화할 수 있다.
장점
- nullptr 사용 시 가독성이 좋아지는 장점이 있다.
- C++에서 nullptr 사용 시 NULL을 체크하는 것이 뚜렸해진다.
int main(void) {
int* a = 0;
// a가 int 값의 0인지 포인터 타입인지 모호함
if (a == 0)
{
cout << "a == 0" << endl;
}
// a가 포인터 타입인지 명확해짐
if (a == nullptr)
{
cout << "a == nullptr" << endl;
}
}
- 첫번째 if문에서 0으로 널체크 시 값이 0인지 포인터 타입인지 알기 어렵다
- 2번째 if문에서 nullptr을 사용 시 a 변수가 포인터인지 명확하게 알 수 있다.
- 따라서 코드의 안정성이 올라가고 가독성이 높아진다.
NULL과 nullptr의 차이점
- 컴파일러의 종류에 따라 다르지만 NULL은 아래와 같이 정의되어 있다.
// stddef.h
#if defined(__need_NULL)
#undef NULL
#ifdef __cplusplus
# if !defined(__MINGW32__) && !defined(_MSC_VER)
# define NULL __null
# else
# define NULL 0
# endif
#else
# define NULL ((void*)0)
#endif
- 이것은 쉽게 설명하면 C++에서는 NULL을 0으로 사용하고 C에서는 NULL을 ((void *) 0)으로 치환해서 사용한다는 것
- NULL은 진짜 NULL을 가리키는 포인터가 아닌 숫자 0인 것이다.
- 그러니 가짜가 아닌 진짜 널을 가리키는 nullptr을 사용해야하는 것이다.
- NULL과 nullptr의 차이점은 아래와 같다.
#include<iostream>
using namespace std;
void func(int a){ cout<<"int 호출"<<'\n'; }
void func(int* b){ cout<<"int* 호출"<<'\n'; }
int main(){
cout<<"\n NULL, nullptr 비교1\n";
func(0);
// func(NULL); #define NULL __null이므로 error
func((int*)0);
func(nullptr);
cout<<"\n NULL, nullptr 비교2\n";
int* ptr1 = NULL;
int* ptr2 = nullptr;
if(ptr1 == NULL){ cout<<"2.1 NULL == NULL\n"; }
if(ptr2 == NULL){ cout<<"2.2 nullptr == NULL\n"; }
if(ptr1 == nullptr){ cout<<"2.3 NULL == nullptr\n"; }
if(ptr2 == nullptr){ cout<<"2.4 nullptr == nullptr\n"; }
if(ptr1 == ptr2){ cout<<"2.5 NULL == nullptr\n"; }
cout<<"\n NULL, nullptr 비교3\n";
int a = 0;
if(a==NULL) cout<<"3.1 int 타입 0 == NULL\n";
// if(a==nullptr) cout<<"3.2 int 타입 0 == nullptr\n";
}
// 출력
NULL, nullptr 비교1
int 호출
int* 호출
int* 호출
NULL, nullptr 비교2
2.1 NULL == NULL
2.2 nullptr == NULL
2.3 NULL == nullptr
2.4 nullptr == nullptr
2.5 NULL == nullptr
NULL, nullptr 비교3
3.1 int 타입 0 == NULL
- 예제 1
- 인자로 0과 NULL을 넣게 된다면 컴파일러의 종류에 따라 func(int a) 함수가 호출될 수 있다.
- 표준 C++11에 의하면 NULL은 #define NULL __null이며 __null은 long 타입이므로 위 함수로 호출될 수 없다.
- 중요한 점은 NULL은 정수 형태의 값이며 nullptr은 포인터 값을 뜻한다는 것이다.
- 예제 2
- 2번째 예제에서 NULL과 nullptr은 완전히 다르다고 생각할 수 없다.
- NULL이 0이긴 하지만 nullptr과 비교 시 true가 나오는 것을 알 수 있다.
- 예제 2번의 0은 int의 0이 아닌 int*의 0이므로 널을 가리키는 것이 맞기 때문에 nullptr과 비교 가능한 것이다.
- 예제 3
- int 타입의 0과 NULL, nullptr 비교 시 차이를 명확하게 알 수 있다.
- int 타입의 0은 NULL과 비교가 가능하다.
- nullptr과 비교 시 비교 자체가 불가능하다.
출처
'C++' 카테고리의 다른 글
[C++] 람다 함수 (1) | 2024.12.13 |
---|---|
<C++> enum class (0) | 2024.04.04 |
<C++> static_cast (0) | 2024.04.03 |
<STL> Array (0) | 2024.02.01 |
<STL> List (0) | 2024.02.01 |