NULLは、ポインターのコンテキストでよく使用され、複数の標準ライブラリ(<iostream>
など)のマクロを介して整数0
に定義されます。 '\0'
はヌル文字であり、8ビットのゼロです。ちなみに、8ビットのゼロは整数0
と同等です。
場合によっては、それは恐ろしいスタイルと見なされますが、これら2つは交換可能です。
int *p='\0';
if (p==NULL) //evaluates to true
cout << "equal\n";
または
char a=NULL;
char b='\0';
if (a==b) //evaluates to true
cout << "equal again\n";
SO単独;たとえば、この質問のトップアンサー( NULL、 '\ 0'と0の違いは何ですか ) 「彼らは実際には同じものではない」と言います。
誰かがNULL
と\0
を交換できない例を提供できますか(できれば実際のアプリケーションであり、病理学的なケースではありません)?
NULLと\ 0を交換できないという例を提供できますか?
NULL
と'\0'
の違いは、オーバーロードの解決に影響する場合があります。
例( Coliruで確認 ):
#include <iostream>
// The overloaded function under question can be a constructor or
// an overloaded operator, which would make this example less silly
void foo(char) { std::cout << "foo(char)" << std::endl; }
void foo(int) { std::cout << "foo(int)" << std::endl; }
void foo(long) { std::cout << "foo(long)" << std::endl; }
void foo(void*) { std::cout << "foo(void*)" << std::endl; }
int main()
{
foo('\0'); // this will definitely call foo(char)
foo(NULL); // this, most probably, will not call foo(char)
}
Coliruで使用されているgccコンパイラはNULL
を0L
として定義していることに注意してください。この例では、foo(NULL)
はfoo(long)
ではなくfoo(void*)
に解決されます。 この回答 は、その側面について詳しく説明しています。
レオンが正しい 同じ関数に複数のオーバーロードがある場合、_\0
_はchar
型のパラメーターを取るものを優先します。ただし、典型的なコンパイラでは、NULL
は_void*
_型ではなく、int
型のパラメーターを取るオーバーロードを好むことに注意することが重要です。
おそらくこの混乱の原因は、C言語でNULL
を_(void*)0
_として定義できることです。 C++標準は明示的に述べています(ドラフトN3936、ページ444):
[マクロ
NULL
]の可能な定義には_0
_および_0L
_が含まれますが、_(void*)0
_は含まれません。
この制限は必要です。 char *p = (void*)0
は有効なCですが、C++は無効です。一方、_char *p = 0
_は両方で有効です。
C++ 11以降では、ポインターとして動作するNULL定数が必要な場合は、nullptr
を使用する必要があります。
このコードは、単一の関数のいくつかのオーバーロードを定義します。各オーバーロードは、パラメーターのタイプを出力します。
_#include <iostream>
void f(int) {
std::cout << "int" << std::endl;
}
void f(long) {
std::cout << "long" << std::endl;
}
void f(char) {
std::cout << "char" << std::endl;
}
void f(void*) {
std::cout << "void*" << std::endl;
}
int main() {
f(0);
f(NULL);
f('\0');
f(nullptr);
}
_
On Ideone この出力
_int
int
char
void*
_
したがって、オーバーロードの問題は実際のアプリケーションではなく、病理学的なケースであると主張します。とにかくNULL
定数は間違った動作をするので、C++ 11ではnullptr
に置き換える必要があります。
別の病理学的なケース Andrew Keetonによって提案された 別の質問で:
C言語でのNULLポインターとは何かに注意してください。基盤となるアーキテクチャには関係ありません。基盤となるアーキテクチャにアドレス0xDEADBEEFとして定義されたnullポインタ値がある場合、この混乱を整理するのはコンパイラ次第です。
そのため、この面白いアーキテクチャでも、次の方法はヌルポインターをチェックする有効な方法です。
_if (!pointer) if (pointer == NULL) if (pointer == 0)
_以下は、nullポインターをチェックする無効な方法です。
_#define MYNULL (void *) 0xDEADBEEF if (pointer == MYNULL) if (pointer == 0xDEADBEEF)
_これらはコンパイラによって通常の比較と見なされるためです。
全体として、私は違いが主に文体的であると言うでしょう。 int
を取る関数とchar
を取るオーバーロードがあり、それらの機能が異なる場合、_\0
_定数とNULL
定数を使用してそれらを呼び出すと違いがわかります。ただし、これらの定数を変数に配置するとすぐに、呼び出される関数が変数の型から差し引かれるので、違いがなくなります。
正しい定数を使用すると、コードの保守性が向上し、意味がわかりやすくなります。数値を意味する場合は_0
_を、文字を意味する場合は_\0
_を、ポインターを意味する場合はnullptr
を使用する必要があります。 Matthieu M。 はコメントで指摘しています GCCにバグがありました では、_char*
_が_\0
_と比較されましたが、意図はポインターを逆参照し、char
を_\0
_と比較します。コードベース全体で適切なスタイルが使用されている場合、このようなエラーは簡単に検出できます。
あなたの質問に答えるために、実際に_\0
_とNULL
を交互に使用することを妨げる実際のユースケースはありません。単なる文体上の理由といくつかのエッジのケース。
これをしないでください。これはアンチパターンであり、実際は間違っています。 NULLはNULLポインター用で、'\0'
はNULL文字です。それらは論理的に異なるものです。
私はこれを見たことがないと思う:
int* pVal='\0';
しかし、これはかなり一般的です:
char a=NULL;
しかし、それは良い形ではありません。コードの移植性が低くなり、私の意見では読みにくくなります。また、混合C/C++環境で問題が発生する可能性があります。
特定の実装がどのようにNULLを定義するかに関する仮定に依存しています。たとえば、一部の実装では単純な
#define NULL 0
他のものは使用するかもしれません:
#define NULL ((void*) 0)
そして、私は他の人が整数として定義し、あらゆる種類の奇妙な扱いを見てきました。
NULL
は、私の意見では、無効なアドレスを示すためだけに使用されるべきです。ヌル文字が必要な場合は、'\0'
を使用します。または、これをNULLCHR
として定義します。しかし、それはそれほどきれいではありません。
これにより、コードの移植性が向上します。コンパイラ/環境/コンパイラの設定を変更しても、タイプなどに関する警告が表示されなくなります。これは、C環境またはC/C++混合環境ではより重要になる場合があります。
発生する可能性のある警告の例:次のコードを検討してください。
#define NULL 0
char str[8];
str[0]=NULL;
これは次と同等です:
#define NULL 0
char str[8];
str[0]=0;
そして、charに整数値を割り当てています。これにより、コンパイラの警告が発生する可能性があります。これが十分に発生している場合は、すぐにimportant警告が表示されなくなります。そして私にとって、これは本当の問題です。コードに警告があると、次の2つの副作用があります。
どちらの場合でも、実際のバグがすり抜けることがあります。これは、警告を読むことに煩わされていた場合(または-Werrorをオンにした場合)コンパイラによってキャッチされます。
はい、オーバーロードされた関数の解決中に異なる動作を示す場合があります。
func('\0')
はfunc(char)
を呼び出し、
ながら
func(NULL)
はfunc(integer_type)
を呼び出します。
nullptr を使用して混乱を取り除くことができます。これは常にポインター型であり、あいまいさを表示しませんが、値または関数のオーバーロード解像度。
char a = nullptr; //error : cannot convert 'std::nullptr_t' to 'char' in initialization
int x = nullptr; //error : nullptr is a pointer not an integer
NULLと互換性があることに注意してください。
int *p=nullptr;
if (p==NULL) //evaluates to true
C++プログラミングStroustrup 4th Editionブックからの抜粋:
古いコードでは、nullptrの代わりに通常0またはNULLが使用されます(§7.2.2)。ただし、nullptrを使用すると、整数(0やNULLなど)とポインター(nullptrなど)の間の潜在的な混乱を排除できます。
コンピュータプログラムには2種類のリーダーがあります。
最初のタイプは、コンパイラーのようなコンピュータープログラムです。
2番目のタイプは、あなたや同僚のような人間です。
一般に、プログラムは、1つのタイプのゼロを別のタイプの代わりに取得しても問題ありません。他の回答が指摘したように、例外がありますが、それは本当に重要ではありません。
重要なのは、human読者をいじっていることです。
人間の読者はveryコンテキストに敏感です。間違ったゼロを使用することにより、あなたは人間の読者にとってlyingになります。彼らはあなたをのろいます。
嘘をついている人間は、バグを見逃しやすくなります。
嘘をついた人間は、そこにいない「バグ」を見ることができます。これらの仮のバグを「修正」すると、実際のバグが発生します。
あなたの人間に嘘をつかないでください。あなたが嘘をついている人間の一人はあなたの将来の自己です。あなたもあなたをのろいます。
C++ 14ドラフトN3936からの抜粋:
18.2タイプ[support.types]
3マクロ
NULL
は、この国際標準(4.10)の実装定義のC++ nullポインター定数です。4.10ポインター変換[conv.ptr]
1 A ヌルポインター定数は、値がゼロの整数リテラル(2.14.2)またはタイプ
std::nullptr_t
のprvalueです。
nullポインター定数は、ポインター型に変換できます。結果は、そのタイプのnullポインター値であり、オブジェクトポインターまたは関数ポインタータイプの他のすべての値と区別できます。
したがって、NULL
は、値がゼロの任意の整数リテラル、またはnullptr
のようなstd::nullptr_t
型の値になりますが、'\0'
は常にゼロのナロー文字リテラルです。
したがって、一般的に互換性はありませんが、ポインターコンテキストでは文体の違い以外は見られません。
例は次のとおりです。
#include <iostream>
#include <typeinfo>
int main() {
std::cout << typeid('\0').name << '\n'
<< typeid(NULL).name << '\n'
<< typeid(nullptr).name << '\n';
}
C/C++の参照によると、NULLはNULLポインター定数に展開されるマクロとして定義されています。次に、ヌルポインター定数を任意のポインター型(またはポインターからメンバーへの型)に変換し、ヌルポインター値を取得できることがわかります。これは、ポインターがオブジェクトを指していないことを示す特別な値です。
Cを参照する定義:
NULLポインター定数は、0(0または0Lなど)に評価される整数定数式、またはvoid *型((void *)0など)へのそのような値のキャストです。
C++ 98を参照する定義:
NULLポインター定数は、ゼロ(0や0Lなど)に評価される整数定数式です。
C++ 11を参照する定義:
NULLポインター定数は、ゼロ(0または0Lなど)に評価される整数定数式、またはnullptr_t型の値(nullptrなど)のいずれかです。
次のメソッドがあると仮定しましょう。
_class Test {
public:
method1(char arg0);
method1(int arg0);
method1(void* arg0);
method1(bool arg0);
}
_
引数NULL
またはnullptr
でmethod1を呼び出すには、method1(void* arg0);
を呼び出す必要があります。ただし、引数_'\0'
_または_0
_でmethod1を呼び出す場合、method1(char arg0);
およびmethod1(int arg0);
を実行する必要があります。