I'm FanJae.

[C++] nullptr 본문

C++/Basic

[C++] nullptr

FanJae 2024. 8. 11. 00:47

1. nullptr

- '0'은 원래 정수(int)형 literal(리터럴)이다.

- 이는 포인터 변수 초기화에 사용될 수 있다.

- 0이 아닌 다른 정수형 literal은 포인터로 암시적 변환이 될 수 없다.

- 0을 가진 정수형 변수도 포인터로 암시적 형변환이 될 수 없다

int  n1 = 0;

int* p1 = 0;
int* p2 = n1; // error

 

nullptr // C++11

- null pointer를 나타내는 literal

- 모든 종류(타입)의 포인터 변수를 초기화 하는데 사용 가능

- 정수(실수) 초기화에 사용될 수는 없다.

int* p4 = nullptr;
int  n2 = nullptr; // error

 

1-1. nullptr의 등장배경

- 컴파일러마다 조금 차이는 있지만, 일반적으로 C와 C++에서 NULL의 매크로 정의하는 방법은 아래와 같다.

#ifdef __cplusplus
    #define NULL 0
#else
    #define NULL (void *) 0
#endif

- __cplusplus란 모든 C++ 컴파일러가 제공하는 매크로이다.

void foo(int n) { std::cout << "int" << std::endl; }
void foo(void *p) { std::cout << "void*" << std::endl; }

int main(void)
{
    foo(0);
    foo(NULL); // void *을 기대하였으나, int가 나옴.
}

 

- C++에서 함수 오버로딩을 지원한다. 

- C언어에서의 NULL을 기대하고 void *가 나오길 바랬지만, 기대한대로 나오지 않는다.

[출력 결과]
int
int

- int와 void *가 나오길 기대했지만, C++에서 NULL은 0으로 정의된 상수이다. 0이니까 int로 받아들인 것이다.

- 만약 제대로 된 결과를 기대한다면, 아래와 같이 호출해야 한다.

foo(0)
foo((void *)0);

- 이 상황을 잘 해결하였지만, 또 다른 문제가 존재한다.

void goo(char *p) { std::cout << "char*" << std::endl; }

goo(0);        // Ok.
goo((void *)0) // C 언어에서는 문제 없으나, C++에서는 Error

- 위와 같은 함수가 있을때, C언어에서는 void *를 char *로 암시적 형변환 해주었지만, C++에서는 해주지 않는다.

 

NULL은 포인터 0의 의미로 사용하기 위해 만든 매크로이나, 일반적으로 C 언어와 C++ 언어에서 다르게 정의된다. 이는 컴파일러마다 조금씩 차이가있다.

C 언어 : (void *) 0.

C++ 언어 :  0 또는 0L 

 

※ 따라서, 이들을 NULL이아닌 nullptr로 호출하면 원하는 결과 출력이 가능하다.

void foo(int n) { std::cout << "int" << std::endl; }
void foo(void *p) { std::cout << "void*" << std::endl; }

int main(void)
{
    foo(0);
    foo(nullptr); // void *
}
void goo(char *p) { std::cout << "char*" << std::endl; }

goo(nullptr); // char*가 나옴.

2. std::nullptr_t

- 모든 리터럴은 타입이 존재한다. (Ex. 0 -> int, 0.0 -> double, false -> bool)

- nullptr은 포인터형 리터럴은 std::nulptr_t이다.

 

std::nullptr_t

- nullptr의 타입

- 모든 종류(타입)의 포인터로 암시적 형변환이 된다.

#include <iostream>
int main()
{
    std::nullptr_t null = nullptr;
    
    int* p1 = null; // Ok.
    char* p2 = null; // Ok.
}

- 암시적으로 형변환이 되기 때문에 정상 실행이 된다.

 

nullptr과 template

template<class F, class T>
void call(F f, T arg) // int arg = 0
{                     // std::nullptr_t arg = nullptr
     f(arg);
}

void foo(int a) {}
void goo(int *p) {}

int main()
{
    foo(10); // ok
    goo(0); // ok
    
    call(foo, 10);
    call(goo, 0); // error
    
    call(goo,nullptr); // ok
}

- 이 문장에서 call(goo, 0);의 경우 에러가 발생한다.

- call에서 각각 goo,0을 받으면 이를 goo(arg)로 변환하여 넣을 것이다.

- goo(arg)로 변환하였기 때문에, 정수형 변수로 인식하면서, 이를 포인터로 형변환이 되지 않는다.

 

따라서, 0이 아닌 nullptr을 넣어서 사용해줘야 정상 작동 한다.

 

결론은 포인터 변수를 초기화 할때는 이제부터 0이 아닌 nullptr을 사용해야 한다.

 

'C++ > Basic' 카테고리의 다른 글

[C++] OOP(Object Oriented Programming) I  (0) 2024.08.12
[C++] Explicit casting  (0) 2024.08.11
[C++] Reference  (0) 2024.08.09
[C++] 연산자와 제어문  (0) 2024.08.09
[C++] Function III  (0) 2024.08.08
Comments