C++がCの上に固定された初期のC++では、(void*)0
として定義されていたため、NULLは使用できませんでした。 void*
以外のポインターにNULLを割り当てることはできませんでした。当時、NULLポインターに0
(ゼロ)を使用していたことが受け入れられました。
今日まで、ゼロポインターをヌルポインターとして使用し続けていますが、周りの人はNULL
を使用することを主張しています。個人的には、既存の値に名前(NULL
)を付けることには何のメリットもありません。また、ポインターを真理値としてテストすることも好きです。
if (p && !q)
do_something();
その後、ゼロを使用するほうが理にかなっています(NULL
を使用する場合、p && !q
を論理的に使用することはできません-NULL
がゼロであると仮定しない限り、NULL
、その場合はNULL
を使用する理由。
NULLよりもゼロを好む(またはその逆)客観的な理由はありますか、それとも個人的な好みだけですか?
編集:RAIIと例外では、ゼロ/ NULLポインタを使用することはめったにありませんが、それでも必要な場合があることを追加する必要があります。
これに関するStroustrupの見解は次のとおりです。 C++スタイルとテクニックに関するFAQ
C++では、
NULL
の定義は0であるため、外観上の違いのみがあります。私はマクロを避けることを好むので、0を使用します。NULL
の別の問題は、人々がときどきそれが0や整数ではない、あるいはその両方と異なると誤解することです。先行標準コードでは、NULL
は不適切なものに定義されていたため、避ける必要がありました。最近ではそれほど一般的ではありません。Nullポインターに名前を付ける必要がある場合は、
nullptr
と呼びます。それがC++ 11で呼ばれていることです。次に、nullptr
がキーワードになります。
そうは言っても、小さなものに汗をかかないでください。
いくつかの議論がありますが(そのうちの1つは比較的最近のものです)、これに関するBjarneの立場に反すると思います。
NULL
を使用すると、その使用に関する検索が可能になり、開発者wantedがNULL
ポインターを使用することに関係なく、コンパイラによってNULL
として解釈されているかどうか。
みんなが引用する例は次のとおりです。
void foo(int*);
void foo (int);
void bar() {
foo (NULL); // Calls 'foo(int)'
}
ただし、少なくとも私の意見では、上記の問題は、nullポインター定数にNULLを使用しているということではなく、非常に異なる種類の引数を取る 'foo'のオーバーロードがあることです。パラメーターもint
である必要があります。他のタイプはあいまいな呼び出しになり、有用なコンパイラー警告を生成するためです。
C++ 0xがない場合でも、NULL
がポインターに使用されていること、および0
が整数型に使用されていることを確認するツールが現在利用可能です。
std::nullptr_t
タイプが追加されます。これは、テーブルへの最新の引数です。 0
とNULL
の問題はC++ 0xで積極的に対処されており、NULL
を提供するすべての実装で、最初に行うことを保証できます。
#define NULL nullptr
0
ではなくNULL
を使用する場合、変更はほとんどまたはまったく労力なしで型安全性が改善されます-使用すると、NULL
for 0
。今日0
を使用している人は誰でも...えーと...彼らが正規表現について十分な知識を持っていることを願っています...
NULLを使用します。 NULLは意図を示します。 0であることは実装の詳細であり、重要ではありません。
私はいつも使用します:
NULL
ポインター用'\0'
文字0.0
floatおよびdoubleの場合ここで、0で問題ありません。それは信号の意図の問題です。とは言うものの、私はそれについてアナルではありません。
ずっと前に(他のほとんどのマクロと同様に)NULLの使用をやめました。マクロをできる限り避けたいだけでなく、CおよびC++コードでNULLが過剰に使用されているように思われるため、これを行いました。ポインタだけでなく、0の値が必要な場合に使用されるようです。
新しいプロジェクトでは、これをプロジェクトヘッダーに入れます。
static const int nullptr = 0;
さて、C++ 0x準拠のコンパイラが到着したら、その行を削除するだけです。これの素晴らしい利点は、Visual Studioがすでにnullptrをキーワードとして認識し、適切に強調表示することです。
cerr << sizeof(0) << endl;
cerr << sizeof(NULL) << endl;
cerr << sizeof(void*) << endl;
============
On a 64-bit gcc RHEL platform you get:
4
8
8
================
物語の教訓。ポインターを処理する場合は、NULLを使用する必要があります。
1)あなたの意図を宣言します(変数がポインターか数値型かを把握しようとしてすべてのコードを検索させないでください)。
2)変数の引数を予期する特定のAPI呼び出しでは、NULLポインターを使用して引数リストの終わりを示します。この場合、NULLの代わりに「0」を使用すると問題が発生する可能性があります。 64ビットプラットフォームでは、va_arg呼び出しには64ビットポインターが必要ですが、32ビット整数のみを渡します。他の32ビットに依存しているように思えますか?特定のコンパイラー(Intelのicpcなど)はあまり優雅ではありませんが、これによりランタイムエラーが発生しました。
正しく思い出せば、使用したヘッダーでNULLの定義が異なります。 Cの場合は(void *)0として定義され、C++の場合は0として定義されます。コードは次のようになります。
#ifndef __cplusplus
#define NULL (void*)0
#else
#define NULL 0
#endif
個人的には、NULL値を使用してNULLポインターを表します。これにより、整数型ではなくポインターを使用していることが明示されます。はい、内部的にはNULL値はまだ0ですが、そのように表されていません。
さらに、整数のブール値への自動変換に依存せず、明示的に比較します。
たとえば、次の使用を好む:
if (pointer_value != NULL || integer_value == 0)
のではなく:
if (pointer_value || !integer_value)
これはC++ 11ですべて修正され、nullptr
の代わりにNULL
を使用でき、nullptr
のタイプであるnullptr_t
も使用できると言えば十分です。
私は歴史が話したと言うだろうと0(ゼロ)を使用することを支持して主張した人々は間違っていた(Bjarne Stroustrupを含む)。 0を支持する議論は、主に美学と「個人的な好み」でした。
新しいnullptr型を使用したC++ 11の作成後、一部のコンパイラは、0はポインターではないため、ポインター引数を持つ関数に0を渡すことについて(デフォルトのパラメーターを使用して)文句を言い始めました。
コードがNULLを使用して記述されている場合、代わりにnullptrにするためにコードベースを介して単純な検索と置換を実行できます。 0の選択をポインターとして使用して記述されたコードで立ち往生している場合、それを更新するのははるかに面倒です。
そして、今すぐC++ 03標準に合わせて新しいコードを書く必要がある(そしてnullptrを使用できない)場合は、実際には単にNULLを使用する必要があります。これにより、将来の更新がはるかに簡単になります。
私はかつて、0が有効なアドレスであり、NULLが特別な8進数値として定義されているマシンで働いていました。そのマシン(0!= NULL)では、次のようなコード
char *p;
...
if (p) { ... }
期待どおりに動作しません。あなたは書くことをしなければならなかった
if (p != NULL) { ... }
最近ではほとんどのコンパイラがNULLを0として定義していると思いますが、私はまだ数年前の教訓を覚えています。NULLは必ずしも0ではありません。
私は通常0を使用します。マクロは好きではありません。また、使用しているサードパーティのヘッダーがNULLを奇妙なものに再定義しないという保証はありません。
C++がnullptrキーワードを取得するまで、Scott Meyersなどが提案したnullptrオブジェクトを使用できます。
const // It is a const object...
class nullptr_t
{
public:
template<class T>
operator T*() const // convertible to any type of null non-member pointer...
{ return 0; }
template<class C, class T>
operator T C::*() const // or any type of null member pointer...
{ return 0; }
private:
void operator&() const; // Can't take address of nullptr
} nullptr = {};
詳細については、Google「nullptr」。
0またはNULLを使用しても同じ効果があります。
ただし、それは両方が良いプログラミング手法であることを意味するものではありません。パフォーマンスに違いがないことを考えると、不可知論者/抽象の選択肢よりも低レベルを意識したオプションを選択することは、プログラミングの悪い習慣です。 コードの読者があなたの思考プロセスを理解するのを助ける。
NULL、0、0.0、 '\ 0'、0x00、およびwhatelseはすべて同じものに変換されますが、プログラム内の異なる論理エンティティです。それらはそのまま使用する必要があります。 NULLはポインター、0は数量、0x0は興味深いビットなどの値です。コンパイルするかどうかにかかわらず、ポインターに「\ 0」を割り当てません。
一部のコミュニティでは、環境の契約を破ることにより、環境に関する詳細な知識を実証することを奨励しています。ただし、責任あるプログラマーは、保守可能なコードを作成し、そのような慣行をコードから排除します。
標準ではNULL == 0が保証されているため、どちらでも可能です。あなたの意図を記録するので、私はNULLを好みます。
Stroustroupを含め、奇妙なことに誰も言及していません。標準や美学について多くのことを話している間、例えば変数引数リストでNULL
の代わりに0
を使用することはdangerousであることに誰も気づきませんでしたsizeof(int) != sizeof(void*)
のアーキテクチャ上。 Stroustroupのように、私は審美的な理由から0
を好みますが、その型があいまいな場合は使用しないように注意する必要があります。
可能な場合はC++リファレンスを使用して、質問全体を避けようとします。のではなく
void foo(const Bar* pBar) { ... }
あなたはしばしば書くことができるかもしれません
void foo(const Bar& bar) { ... }
もちろん、これは常に機能するとは限りません。ただし、nullポインターは使いすぎる可能性があります。
私はこれについてStroustrupと一緒にいます:-) NULLは言語の一部ではないため、0を使用することを好みます。
ほとんどが個人的な好みですが、オブジェクトが現在何も指し示していないポインタであることがNULLであるとはっきりと主張することもできます。
void *ptr = &something;
/* lots o' code */
ptr = NULL; // more obvious that it's a pointer and not being used
IIRCでは、標準ではNULLを0にする必要がないため、<stddef.h>で定義されているものを使用するのがおそらくコンパイラに最適です。
引数のもう1つの側面は、論理比較(ブールへの暗黙のキャスト)を使用するか、NULLに対する明示的なチェックを使用するかということですが、それは読みやすさにもなります。
NULLは、値が算術値ではなくポインターを表すことを意図していることが明確になるため、NULLを使用することを好みます。マクロであるという事実は残念ですが、非常に広く浸透しているため、危険はほとんどありません(誰かが本当に骨の折れる何かをしない限り)。最初からキーワードだったらよかったのですが、何ができますか?
そうは言っても、ポインター自体を真理値として使用しても問題はありません。 NULLと同じように、それは染み込んだイディオムです。
C++ 09はnullptrコンストラクトを追加します。
私は常に0を使用します。C++を初めて学習したときに、0を使用することをお勧めするものを読んだので、実際に考えたわけではありません。理論的には、読みやすさに混乱の問題があるかもしれませんが、実際には、数千時間と数百万行のコードでそのような問題に遭遇したことは一度もありません。 Stroustrupが言うように、標準がnullptrになるまで、それは本当に個人的な美学の問題です。
誰かが一度私に言った...私はNULLを69に再定義するつもりです。それ以来、私はそれを使用しません:P
コードが非常に脆弱になります。
編集:
標準のすべてが完璧ではありません。マクロNULLは、C NULLマクロと完全に互換性のない、実装定義のC++ NULLポインター定数です。タイプ隠蔽以外に、役に立たずエラーが発生しやすいツールで変換するものです。
NULLはNULLポインターとしてではなく、O/OLリテラルとして動作します。
次の例は混乱しないことを教えてください:
void foo(char *);
void foo(int);
foo(NULL); // calls int version instead of pointer version!
すべてのため、新しい標準ではstd :: nullptr_tが表示されます
新しい標準を待ちたくなく、nullptrを使用したい場合は、少なくともMeyersが提案したようなまともなものを使用してください(jon.hコメントを参照)。
可能であれば、0またはNULLポインターをまったく使用しないと主張します。
それらを使用すると、遅かれ早かれ、コードのセグメンテーション違反が発生します。私の経験では、これと、gereralのポインターは、C++のバグの最大の原因の1つです。
また、コード全体に「if-not-null」ステートメントが発生します。常に有効な状態に依存できる場合は、はるかに優れています。
ほとんどの場合、より良い代替手段があります。