comp.lang.c FAQ にあるように、nullポインターがすべてのビットがゼロではないアーキテクチャがあります。したがって、問題は実際に次の構成をチェックするものです。
void* p = get_some_pointer();
if (!p)
return;
p
をマシン依存のNULLポインターと比較していますか、それともp
を算術ゼロと比較していますか?
書かなければならない
void* p = get_some_pointer();
if (NULL == p)
return;
代わりに、そのようなアーキテクチャの準備ができているのか、それとも単なる私の妄想ですか?
C仕様によると:
値が0の整数定数式、またはvoid *型にキャストされる式は、nullポインター定数と呼ばれます。 55)NULLポインター定数がポインター型に変換される場合、NULLポインターと呼ばれる結果のポインターは、オブジェクトまたは関数へのポインターと等しくないものと比較されることが保証されます。
したがって、_0
_はNULLポインター定数です。そして、それをポインター型に変換すると、一部のアーキテクチャでは非オールビットゼロになる可能性のあるヌルポインターを取得します。次に、ポインターとNULLポインター定数の比較に関する仕様の内容を見てみましょう。
一方のオペランドがポインターで、もう一方がNULLポインター定数である場合、NULLポインター定数はポインターのタイプに変換されます。
_(p == 0)
_を考えてみましょう。最初の_0
_がヌルポインターに変換され、次にp
が実際のビット値がアーキテクチャに依存するヌルポインター定数と比較されます。
次に、否定演算子に関する仕様の説明を参照してください。
論理否定演算子の結果!オペランドの値が0と等しくない場合は0、オペランドの値が0と等しい場合は1です。結果の型はintです。式!Eは(0 == E)と同等です。
これは、_(!p)
_が_(p == 0)
_と同等であることを意味します。これは、仕様によれば、マシン定義のNULLポインター定数に対してp
をテストします。
したがって、nullポインター定数がall-bits-zeroでないアーキテクチャでも、if (!p)
を安全に記述できます。
C++の場合、ヌルポインター定数は次のように定義されます。
NULLポインター定数は、整数型の整数定数式(5.19)のprvalueで、ゼロまたはstd :: nullptr_t型のprvalueに評価されます。 NULLポインター定数は、ポインター型に変換できます。結果はそのタイプのNULLポインター値であり、オブジェクトポインターまたは関数ポインタータイプの他のすべての値と区別できます。
これは、Cの場合とnullptr
構文シュガーに近いものです。演算子_==
_の動作は、次によって定義されます。
さらに、メンバーへのポインター、またはメンバーへのポインターとNULLポインター定数を比較できます。メンバーへのポインター変換(4.11)および資格変換(4.4)は、それらを共通の型にするために実行されます。一方のオペランドがNULLポインター定数の場合、共通の型はもう一方のオペランドの型です。それ以外の場合、共通型は、オペランド型のcv修飾シグネチャの和集合であるcv修飾シグネチャ(4.4)を持つ、オペランドの1つの型に類似した(4.4)メンバー型へのポインタです。 [注:これは、メンバーへのポインターをNULLポインター定数と比較できることを意味します。 —終了ノート]
それは_0
_のポインター型への変換につながります(Cの場合)。否定演算子の場合:
論理否定演算子のオペランド!文脈的にブールに変換されます(4節)。その値は、変換されたオペランドがtrueの場合はtrue、それ以外の場合はfalseです。結果の型はブールです。
つまり、_!p
_の結果は、ポインターからbool
への変換の実行方法に依存します。標準は言う:
ゼロ値、nullポインター値、またはnullメンバーポインター値はfalseに変換されます。
したがって、if (p==NULL)
とif (!p)
はC++でも同じことを行います。
実際のマシンでは、nullポインターが全ビット0であるかどうかは関係ありません。 p
がポインターであると仮定すると:
if (!p)
p
がNULLポインターであるかどうかをテストするための常に有効な方法であり、常に次と同等です。
if (p == NULL)
別のC-FAQ記事に興味があるかもしれません: これは奇妙です。NULLは0であることが保証されていますが、NULLポインターはそうではありませんか?
上記はCとC++の両方に当てはまります。 C++(11)では、nullポインターリテラルにnullptr
を使用することが推奨されます。
この回答はCに適用されます。
NULL
をNULLポインターと混同しないでください。 NULL
は、nullポインター定数であることが保証された単なるマクロです。 NULLポインター定数は、_0
_または_(void*)0
_のいずれかであることが保証されています。
C11 6.3.2.3から:
値が0の整数定数式、またはそのような式をvoid *型にキャストしたものは、nullポインター定数と呼ばれます66)。 NULLポインター定数がポインター型に変換される場合、NULLポインターと呼ばれる結果のポインターは、オブジェクトまたは関数へのポインターと等しくないことを保証されます。
66)マクロNULLは、<stddef.h>(および他のヘッダー)でNULLポインター定数として定義されています。 7.19を参照してください。
7.19:
マクロは
ヌル
実装定義のヌルポインター定数に展開されます。
NULL
の場合の実装定義は、_0
_または_(void*)0
_です。 NULL
は他のものにはできません。
ただし、nullポインター定数がポインターに割り当てられると、nullポインターが返されます。これは、nullポインター定数と比較しても値がゼロにならない場合があります。コードif (!p)
はNULL
マクロとは関係ありません。nullポインターを算術値ゼロと比較しています。
したがって、理論的には、_int* p = NULL
_のようなコードは、ゼロとは異なるヌルポインターp
になる可能性があります。
かつて、STRATUSコンピュータにはすべての言語でNULLポインターが1でした。
これによりCに問題が発生したため、Cコンパイラは0と1のポインター比較でtrueを返すことができました
これにより、次のことが可能になります。
void * ptr=some_func();
if (!ptr)
{
return;
}
デバッガーでreturn
の値が1であることがわかりましたが、null ptrでptr
に
if ((void *)0 == (void *)1)
{
printf("Welcome to STRATUS\n");
}
実際に「STRATUSへようこそ」を印刷しますか
コンパイラーが優れている場合、注意すべき点が2つ(そして2つだけ)あります。
1:静的なデフォルトで初期化された(つまり、割り当てられていない)ポインターにはNULLが含まれません。
2:構造体または配列のmemset()、または拡張機能によってcalloc()はポインターをNULLに設定しません。