1. ホーム
  2. c++

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

2023-09-22 18:09:49

疑問点

C言語では NULL は次のように定義されます。 (void *)0 であるのに対し,C++では 0 . なぜ、そうなるのでしょうか? C言語では、もし NULL がタイプキャストされないと (void *) にタイプキャストされていない場合、コンパイラは警告を生成するかもしれませんし、生成しないかもしれません。これ以外に何か理由があるのでしょうか?

どのように解決するのですか?

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

ヌルポインタ定数は、ゼロに評価される整数型の積分定数式(5.19)rvalueである。

このため、C++では、次のように書くことができます。

int* ptr = 0;

C言語では、このルールは似ていますが、少し違います(§6.3.2.3/3)。

値0を持つ整数定数式、または型にキャストされたそのような式は void * 55) ヌルポインタ定数をポインタ型に変換すると、ヌルポインタと呼ばれる結果のポインタは、不等間隔で比較されることが保証される。 55) ヌルポインタ定数をポインタ型に変換した場合,結果として得られるポインタはヌルポインタと呼ばれ,あらゆるオブジェクトやポインタと不等間隔で比較されることが保証されている。 は、任意のオブジェクトまたは関数へのポインタと比較することが保証されています。

その結果、両方の

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 は、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*> があり、各要素をヌルポインタに設定したいとします。 このとき std::fill アルゴリズムを使って、私は

std::fill(v.begin(), v.end(), NULL);

しかし、これはコンパイルできません。なぜなら、テンプレートシステムは NULLint であり、ポインタではありません。 これを修正するには、次のように記述する必要があります。

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

これは醜いもので、テンプレート・システムの目的をやや破っています。 これを修正するために、私は nullptr :

std::fill(v.begin(), v.end(), nullptr);

また nullptr はヌルポインタに対応する型を持っていることが知られています(具体的には std::nullptr_t のようなもの)、これは正しくコンパイルされます。

これが役に立つことを願っています。