Posted at

NULL と nullptr って何が違う?

More than 5 years have passed since last update.

C++11 では、NULL に代わるものとして nullptr キーワードが追加されています。

この話を聞くと「NULL じゃだめなの?」と一度は思う人は少なく無いと思います。

そこで、今回は NULL と nullptr の違いについて書いてみたいと思います。


NULL じゃ出来ないことがある

今更言うことでもないですが、NULL は以下のようにマクロで定義されています。

#define NULL    0

マクロということは、NULL なんて大層な扱いになっていても 0 以外の何者でもないということです。

そこで、以下のコードと実行結果を見てみます。


test.cpp

#include <iostream>


using namespace std;

void Func(void * p) {
cout << __FUNCSIG__ << endl;
}
void Func(int v) {
cout << __FUNCSIG__ << endl;
}
int main()
{
Func(10);
Func(NULL);

return 0;
}



output

void __cdecl Func(int)

void __cdecl Func(int)

NULL を指定しても Func(void * v) ではなく、Func(int v) が呼び出されてます。

そりゃそうですよね。

コンパイラから見たら NULL はポインタではなく、ただの 0 なんですから。

もし、Func(void * v) を呼びたいのであれば

Func((void*)NULL);

のように明示的にキャストしてやる必要があります。

が、NULL ポインタを明示的にキャストするのはなんともおかしな話ですし、

何より「それくらい勝手にやってくれよ!」って思いますよね。


nullptr だからこそできる事がある

C++11 で追加された nullptr キーワードは、まさしくそのために用意されたものです。

以下が NULL を nullptr に置き換えたコードと実行結果です。


test2.cpp

#include <iostream>


using namespace std;

void Func(void * p) {
cout << __FUNCSIG__ << endl;
}
void Func(int v) {
cout << __FUNCSIG__ << endl;
}
int main()
{
Func(10);
Func(nullptr);

return 0;
}



output

void __cdecl Func(int)

void __cdecl Func(void *)

nullptr を指定したら、見事に Func(void *) が呼び出されています。

このように、nullptr を指定すると、きちんとポインタとして扱ってくれます。


nullptr は nullptr_t 型

また、nullptr はポインタとして扱われますが、 型としては std::nullptr_t 型 という扱いになっています。

どういう事かというと、それは以下のコードと実行結果を見てもらえればわかるかと思います。


test3.cpp

#include <iostream>


using namespace std;

void Func(void * p) {
cout << __FUNCSIG__ << endl;
}

void Func(int v) {
cout << __FUNCSIG__ << endl;
}

void Func(nullptr_t p) {
cout << __FUNCSIG__ << endl;
}

int main()
{
Func(10);
Func(nullptr);

return 0;
}



output

void __cdecl Func(int)

void __cdecl Func(nullptr)

このように、ポインタを引数として取る関数と nullptr_t を引数として取る関数がオーバーロードされている場合は、

nullptr の本来の型である nullptr_t が優先して呼び出されることになります。