web-dev-qa-db-ja.com

CとC ++でNULLポインターの定義が異なるのはなぜですか?

Cでは、NULL(void *)0として定義されますが、C++では0です。なぜそうなのですか? Cでは、NULL(void *)に型キャストされていない場合、コンパイラは警告を生成する場合としない場合があることを理解できます。これ以外に理由はありますか?

70
mohit

C++ 03に戻ると、NULLポインターはISO仕様(§4.10/ 1)で次のように定義されていました。

NULLポインター定数は、整数型の整数定数式(5.19)で、ゼロと評価される右辺値です。

これが、C++で次のように記述できる理由です。

_int* ptr = 0;
_

Cでは、この規則は似ていますが、少し異なります(§6.3.2.3/ 3):

値0の整数定数式、または_void *_型にキャストされる式は、nullポインター定数と呼ばれます。55)nullポインター定数がポインター型に変換される場合、結果のポインターはnullと呼ばれますポインター、任意のオブジェクトまたは関数へのポインターと等しくない比較が保証されます。

その結果、両方

_int* ptr = 0;
_

そして

_int* ptr = (void *)0
_

合法です。しかし、私の推測では_void*_キャストがここにあるので、

_int x = NULL;
_

ほとんどのシステムでコンパイラ警告を生成します。 C++では、キャストなしで暗黙的に_void*_を暗黙的に別のポインター型に変換できないため、これは正当ではありません。たとえば、これは違法です:

_int* ptr = (void*)0; // Legal C, illegal C++
_

ただし、コードは

_int x = NULL;
_

正当なC++です。これとその後の混乱のため(および後で説明する別のケース)、C++ 11以降、ヌルポインターを表すキーワードnullptrがあります。

_int* ptr = nullptr;
_

これには上記の問題はありません。

nullptrの0を超えるもう1つの利点は、C++型システムでより適切に動作することです。たとえば、次の2つの関数があるとします。

_void DoSomething(int x);
void DoSomething(char* x);
_

電話したら

_DoSomething(NULL);
_

それは同等です

_DoSomething(0);
_

予想されるDoSomething(int)の代わりにDoSomething(char*)を呼び出します。ただし、nullptrを使用すると、次のように記述できます。

_DoSomething(nullptr);
_

そして、DoSomething(char*)関数を期待どおりに呼び出します。

同様に、_vector<Object*>_があり、各要素をNULLポインターに設定したいとします。 _std::fill_アルゴリズムを使用して、次のように書きます。

_std::fill(v.begin(), v.end(), NULL);
_

ただし、テンプレートシステムはNULLをポインターではなくintとして処理するため、これはコンパイルされません。これを修正するには、私が書く必要があります

_std::fill(v.begin(), v.end(), (Object*)NULL);
_

これは見苦しく、テンプレートシステムの目的をやや損ねます。これを修正するには、nullptrを使用できます。

_std::fill(v.begin(), v.end(), nullptr);
_

nullptrはnullポインター(具体的には_std::nullptr_t_)に対応する型を持つことがわかっているため、これは正しくコンパイルされます。

お役に立てれば!

101
templatetypedef

Cでは、NULLは実装定義の「ヌルポインター定数」に展開されます。 NULLポインター定数は、値が0の整数定数式、またはvoid*にキャストされるそのような式のいずれかです。そのため、C実装ではNULL0または((void*)0)として定義できます。

C++では、NULLポインター定数の規則が異なります。特に、((void*)0)はC++のNULLポインター定数ではないため、C++実装ではNULLをそのように定義できません。

16
Keith Thompson