C ++のポインターにNULLまたは0(ゼロ)を使用していますか?


192

C ++がCの上にボルトで固定された初期の頃は、NULLはとして定義されていたため、使用できませんでした(void*)0。NULLを以外のポインタに割り当てることはできませんvoid*。当時、0nullポインタに(ゼロ)を使用することが受け入れられていました。

今日まで、私はゼロをnullポインターとして使用し続けましたが、私の周りの人たちはを使用することを強く求めていますNULL。個人的にはNULL、既存の値に名前()を付けることには何のメリットもないと思います。また、ポインタを真理値としてテストすることも好きなので、

if (p && !q)
  do_something();

次に、ゼロを使用する方が理にかなっています(を使用する場合と同様に、NULL論理的に使用することはできません。ゼロであると想定しない限り、p && !q明示的に比較する必要がありNULLます。NULLNULL

NULLよりもゼロ(またはその逆)を優先する客観的な理由はありますか、それともすべて個人的な好みですか?

編集:RAIIと例外を使用して、ゼロ/ NULLポインターを使用することはめったにありません(ただし、元々言うことを意味します)必要がありますが、まだ必要な場合があります。


9
待って、nullが内部的にゼロかどうかに関係なく、nullポインターがfalseと評価される必要はありませんか?
Mooing Duck

回答:


185

これがStroustrupの見解です:C ++スタイルとテクニックFAQ

C ++では、の定義NULLは0なので、見た目の違いだけです。マクロは避けたいので、0を使用します。別の問題としてNULL、0とは異なる、または整数ではない、と人々が誤って信じることがあります。先行標準のコードでNULLは、不適切であると定義されていた、または定義されていたため、回避する必要がありました。最近はそれほど一般的ではありません。

nullポインタに名前を付ける必要がある場合は、それを呼び出しますnullptr。それがC ++ 11で呼ばれているものです。次に、nullptrキーワードになります。

とはいえ、小さなことを気にしないでください。


7
Bjarneは、C ++ 0xが新しいnull型の作業を始める前にこれを書いた。これは、プラットフォームで使用可能な場合にNULLがこの型に使用されるケースであり、これに関する一般的なコンセンサスにはCの変更が​​あると思います。
Richard Corden、

122

いくつかの議論があります(そのうちの1つは比較的最近のものです)、これに関するBjarneの立場に矛盾すると私は信じています。

  1. 意図の文書化

を使用NULLすると、その使用に関する検索が可能になります。また、コンパイラーによってポインターが解釈されているかどうかに関係なく、開発者NULLポインターを使用したい思ったことも強調表示されますNULL

  1. ポインタと 'int'のオーバーロードは比較的まれです

みんなが引用する例は次のとおりです:

void foo(int*);
void foo (int);

void bar() {
  foo (NULL);  // Calls 'foo(int)'
}

ただし、少なくとも私の意見では、上記の問題はnullポインター定数にNULLを使用していることではなく、非常に異なる種類の引数を取る 'foo'のオーバーロードがあることです。int他のタイプではあいまいな呼び出しが発生し、有用なコンパイラの警告が生成されるため、パラメータも多すぎる必要があります。

  1. 分析ツールは今日を助けることができます!

C ++ 0xがない場合でも、NULLポインターに使用されて0いること、および整数型に使用されていることを確認するツールが今日利用可能です。

  1. C ++ 11には新しいstd::nullptr_tタイプがあります。

これは、テーブルの最新の引数です。0およびの問題はNULLC ++ 0xで積極的に対処されておりNULL、を提供するすべての実装で、最初に行うことは次のとおりです。

#define NULL  nullptr

使う人のためNULLではなく0、変更がほとんど、あるいは全くの努力で型の安全性の改善になります-それはまた、彼らが使用したいくつかのバグをキャッチもどちらかといえばNULLのために00今日を使用している人にとっては...えーと...まあうまくいけば、彼らは正規表現についての十分な知識を持っている...


1
それらはかなり良い点だと認めざるを得ません。C ++ 0xにnull型があることをうれしく思います。これにより、多くのものがよりクリーンになります。
Rob

2
@リチャード、どうして反対しないの?Meyers nullptr_tを使用すると、0xが使用可能になったときにを削除#includeして、安全な側にずっと保持できます。
fnieto-フェルナンドニエト2009年

15
#define NULL nullptr危険そうです。良くも悪くも、多くのレガシーコードは0以外のものにNULLを使用します。たとえば、ハンドルはしばしば整数型として実装され、それらをに設定することNULLは珍しくありません。をゼロターミネータNULLに設定するのcharに使用するような悪用を見たことさえあります。
エイドリアン・マッカーシー

8
@AdrianMcCarthy:コードが黙ってコンパイルされて別の意味を持つという危険があった場合にのみ危険だと私は言うでしょう。これが当てはまらないと確信しているため、実際には、NULLの不正な使用がすべて検出されます。
Richard Corden、2012年

3
@RichardCorden:ええと、それはこれらの他の使用法NULLが実際には正しくないと想定しています。多くのAPIは長い間NULLハンドルで使用されており、それは実際、多くのAPIでの使用が文書化されています。それらを突然破って、彼らがそれを間違っていると宣言することは実際的ではありません。
エイドリアン・マッカーシー

45

NULLを使用します。NULLは意図を示します。それが0であることは重要ではない実装の詳細です。


28
0は実装の詳細ではありません。規格では、0をヌルポインターを表すビットパターンと定義しています。
Ferruccio

5
かのように ..!!おい、C ++は低レベルの言語です!0を使用します。これはよく知られているイディオムです。
Hasen 2009年

8
規格の一部であることを理解しています。コードの読み取りに関する限り、これは実装の詳細です。読者は「NULLポインタ」を「0はこの場合NULLポインタを意味するのではなく、私が算術を行うことができる数ではない」と考えるべきです。
アンディレスター

2
+1。アンディに同意します。@Ferruccio、実装の詳細プログラマの考え方のは、コンパイラの実装と同じではありません定義された
ユーザー

あなたはNULLを使用する場合、複雑なヘッダなしのプレーンなコードでは、あなたが..「NULLはこのスコープで定義されていません」のエラーがあります
ArtificiallyIntelligence

37

私は常に使用します:

  • NULL ポインタ用
  • '\0' 文字用
  • 0.0 floatとdoubleの場合

0で問題ありません。それはシグナリングの意図の問題です。とは言っても、私はそれについてアナルではありません。


24
暗黙の型キャストを回避するには、おそらくフロートに0.0Fを使用する必要があります
EvilTeach

35

ずっと前にNULLを使用することをやめました(他のほとんどのマクロと同様)。これは、マクロをできるだけ避けたいだけでなく、CおよびC ++コードでNULLが過剰に使用されているように見えるためです。ポインタだけでなく、値0が必要なときに使用されるようです。

新しいプロジェクトでは、これをプロジェクトヘッダーに入れます。

static const int nullptr = 0;

今、C ++ 0x準拠のコンパイラが到着したとき、私がしなければならないことはその行を削除することだけです。これのすばらしい利点は、Visual Studioがnullptrをキーワードとして認識し、適切に強調表示することです。


4
NULLを使用すると、移植性が高くなります。「nullptr」は、一部のプラットフォームでは使用可能であり、他のプラットフォームでは使用できません。ここでのソリューションでは、宣言の前後にプリプロセッサを使用して、必要な場合にのみ存在することを確認する必要があります。NULLはこれを自動的に行います。
Richard Corden、

6
同意しません。コンパイラが追いつくまで、短期的には移植性が低下します。長期的には、移植性が高くなり、おそらくもう少し読みやすくなります。
Ferruccio

4
さらに、C ++ 0x以外のコンパイラでは、常に#define nullptr NULLを使用できます。
Anteru

20
    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おそらくポータブルではなく、安全ではありません。まだプラットフォームがある可能性があります#define NULL 0StroustrupのFAQによると、一番上の質問で引用されているNULLまたは0を使用する必要があります。これは最初の検索結果の1つです)。少なくとも古いC ++では、0ポインターのコンテキストで特別な概念的な意味があります。ビットについて具体的に考えるべきではありません。また、異なる整数コンテキストで(ことに注意してくださいshortintlong long)「sizeof(0)」異なるものになります。この答えは少し見当違いです。
FooF 2014年

(個人的に日常生活の中でCプログラマとして、私は人々が利用したい理由を理解するためにこの質問を訪れるようになったNULLの代わりに(char *)0(const char *)0または(struct Boo *)0または(void *)0または何より明確に意図を表現するために- 。あまりにも面倒()私の意見ではせずに)
FooF

投票してください。msvc2013 Cコンパイラで発生しています。64ビットでは、ポインターへの変換時の0はNULLポインターであるとは限りません。
pengMiao

16

正しく思い出せば、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代わりに単純に使用できNULLnullptr_tそれがのタイプでもあると言えば十分nullptrです。


15

歴史が話されており、0(ゼロ)の使用を支持して主張した人々は間違っていました(Bjarne Stroustrupを含む)。0を支持する議論は、主に美学と「個人的な好み」でした。

C ++ 11の作成後、新しいnullptr型を使用して、0はポインターではないため、一部のコンパイラーは(デフォルトのパラメーターを使用して)ポインター引数を持つ関数に0を渡すことについて不平を言い始めました。

コードがNULLを使用して記述されている場合、単純な検索と置換をコードベースで実行して、代わりにnullptrにすることができます。ポインタとして0の選択を使用して記述されたコードで行き詰まっている場合、それを更新するのははるかに面倒です。

また、C ++ 03標準に今すぐ新しいコードを記述する必要がある(そしてnullptrを使用できない)場合は、本当にNULLを使用する必要があります。今後の更新がより簡単になります。


11

私は通常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」。


9
NULLを0以外に定義し(void*)0ている(またはCコードとしてコンパイルされている場合)サードパーティのライブラリは、問題を引き起こすだけなので、使用しないでください。
Adam Rosenfield、

2
NULLを再定義するライブラリを実際に見たことがありますか?今まで?そのようなライブラリが存在する場合は、再定義されたNULLよりも大きな問題が発生します。たとえば、NULLを再定義するのに十分な能力のないライブラリを使用している場合などです。
アンディレスター2013

1
10年以上前に、NULLを定義したサードパーティのヘッダー(OrbixまたはObjectStore)を処理する必要があったことを漠然と覚えています。windows.hでさまざまなサードパーティのヘッダーを機能させるために数日と夜を無駄にした後、私はマクロの病的な憎しみを持っていると思います。
jon-hanson 2013

2
「マクロが好きではない」は、オブジェクトのような#defineの奇妙な批評です。たぶん、あなたはあなたがCプリプロセッサを好きではないと言うつもりですか?
Andrew Prock 14

@Andrew- NULLオーバーの唯一の利点(type *)0は検索可能性であると私には思われます。それ以外の場合、Cイディオムでなければ、不必要な難読化のように見えます。個人的には、NULL場所全体に広がるという慣用句は死ぬに値すると思います。NULL私の意見では役に立たないマクロです。Occamのかみそりは、ここで行うためにいくつかの仕事を得ました...
FooF 14年

11

私はかつて、0が有効なアドレスであり、NULLが特別な8進値として定義されているマシンで作業しました。そのマシン(0!= NULL)では、次のようなコード

char *p;

...

if (p) { ... }

期待どおりに動作しません。あなたは書く必要があった

if (p != NULL) { ... }

私はほとんどのコンパイラーがNULLを0と定義していると私は信じていますが、私はそれらの年前の教訓を覚えています:NULLは必ずしも0ではありません。


26
準拠コンパイラを使用していませんでした。標準では、NULL 0であり、コンパイラーはポインターコンテキストの0をアーチの適切な真のNULL値に変換する必要があるとしています。
エヴァンテラン

17
はい、そうです。これは、ANSIがC標準を作成する前の80年代半ばでした。当時はコンプライアンスなどのことはなく、コンパイラの作成者は言語を自由に解釈できました。そのため、標準が必要でした。
mxg 2008年

9

標準ではNULL == 0が保証されているので、どちらでもかまいません。それはあなたの意図を文書化するので、私はNULLを好みます。


ネストされた構造を持っている場合、私は言うfoo.bar_ptr = (Bar *) 0ことは意図をより明確に表していると思いますfoo.bar_ptr = NULL。この習慣はまた、コンパイラーが誤解エラーをキャッチできるようにします。私にとっては、それが指針であることがわかっている場合にfoo.bar_ptr = 0使用NULLするだけでなく、意図も表現してfoo.bar_ptrいます。
FooF 2014年

9

0またはNULLを使用しても同じ効果があります。

ただし、これらが両方とも優れたプログラミング手法であるとは限りません。パフォーマンスに違いがないことを考えると、不可知論的/抽象的代替案よりも低レベル対応オプションを選択することは、不適切なプログラミング手法です。コードの読者が思考プロセスを理解できるようにしてください

NULL、0、0.0、 '\ 0'、0x00、その他はすべて同じものに変換されますが、プログラム内の異なる論理エンティティです。これらはそのまま使用する必要があります。NULLはポインタ、0は数量、0x0はビットが興味深い値などです。コンパイルするかどうかに関係なく、ポインタに '\ 0'を割り当てません。

一部のコミュニティは、環境の契約を破ることによって、環境についての深い知識を示すことを奨励していることを知っています。ただし、責任のあるプログラマは、保守可能なコードを作成し、そのような慣行をコードから除外します。


5

奇妙なことですが、Stroustroupを含む誰もそれについて言及していません。あることに気づいた基準や美学誰について多くのことを話しながら、危険な使用する0にはNULLどこアーキテクチャ上の変数引数リストで、たとえば、の代わりsizeof(int) != sizeof(void*)。Stroustroupのように、私0は美的理由から好みますが、その型があいまいになる可能性があるところでは使用しないように注意する必要があります。


そして、これらの危険な場所であなたはまだ使用でき0ますが、その指定提供0あなたが意味する-例えば(int *)0(char *)0(const char *)0または(void *)0または(unsigned long long) 0またはものは何でも。私の意見では、これは、よりも明確な意図を表していNULLます。
FooF 2014年

1
もちろん、何のNULL略かわからない場合は。
マイケルクレリン-ハッカー、2014年

個人的には、正確な型を使用できるときに不必要に何かをキャストするのは少し不愉快だと(void *)思います。ポインタの場合に類似しているため、リストの(通常は)64ビット整数の例を意図的に示しました。さらに、古いC ++ NULLとして定義された私の記憶0が正確である場合(C ++でプログラミングしてから数年後)、プログラムの正確性は向上しません。幸い、新しいC ++標準にはnullptrキーワードが用意されている ため、NULL新しいC ++を作成するときに、この醜さや論争全体を取り除くことができます。
FooF 2014年

まあ、それがキャスト先(void*)がに抽象化された理由NULLです。そしてNULL実際には、ほとんどの場合、意図が非常に明確に表現されています。そして、あなたの記憶は間違っていると思います。標準についてはわかりませんが、実際にはそうだと思います(void*)0。そして、はい、nullptrそれは同じNULLことになりますが、タイプを指定せずにnullポインターを指定することはできますが、素晴らしいプリティファイアです。
マイケルクレリン-ハッカー、2014年

1
@FooF、いくつかのプラットフォームでは—多分。私の現実ではそれは機能したので、それはポインタとして定義されたのではないかと思います。堅牢性に関しては、そうです。私が使おうとしてnullptrいたのは、と同じメッセージを持ってNULLいるということです。それは、冒頭で述べた意図を表現することだけに関するものでした。(それが何であれNULL、現代のgcc利回りの前処理__null)。
マイケルクレリン-ハッカー、2014年

4

可能な限りC ++リファレンスを使用することで、質問全体を回避しようとしています。のではなく

void foo(const Bar* pBar) { ... }

あなたはしばしば書くことができるかもしれません

void foo(const Bar& bar) { ... }

もちろん、これは常に機能するとは限りません。ただし、nullポインタは過剰に使用される可能性があります。


3

私はこれにStroustrupを使っています:-) NULLは言語の一部ではないため、0を使用することを好みます。


3

主に個人的な好みですが、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に対する明示的なチェックを使用するかですが、読みやすさにも影響します。


3

あなたの意図は値が算術値ではなくポインタを表すことであることを明らかにしているので、私はNULLを使用することを好みます。それがマクロであるという事実は残念ですが、非常に広く浸透しているので、危険はほとんどありません(誰かが本当に骨の折れることをしない限り)。最初からキーワードだったらいいのにと思いますが、何ができますか?

とはいえ、ポインタ自体を真理値として使用しても問題はありません。NULLと同様に、それは根底にあるイディオムです。

C ++ 09は、私が長い間遅れていると思うnullptrコンストラクトを追加します。


1

私は常に0を使用します。実際に考え抜かれた理由ではありません。C++を最初に学習したときに、0の使用を推奨するものを読んだので、常にそうしたのです。理論的には可読性に混乱の問題がある可能性がありますが、実際には、このような問題に数千時間と数百万行のコードで一度も出会ったことはありません。Stroustrupが言うように、標準がnullptrになるまで、それは本当に個人的な美的問題です。


1

誰かが一度私に言った...私は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コメントを参照)。


5
NULLC ++標準の明確に定義された部分です。標準マクロを再定義したい人がプロジェクトのコードを編集できるようにすると、コードが「脆弱」になります。使用NULLしません。
CBベイリー

1

まあ、可能な限り0またはNULLポインタをまったく使用しないことを主張します。

それらを使用すると、遅かれ早かれ、コードでセグメンテーション違反が発生します。私の経験では、これと、一般的なポインタはC ++のバグの最大の原因の1つです

また、コード全体で「if-not-null」ステートメントが発生します。常に有効な状態を信頼できる場合は、さらに便利です。

ほとんどの場合、より良い代替案があります。


2
保証されたセグメンテーションフォールト(および最近のシステムでは逆参照する保証されます0)はデバッグに役立ちます。ランダムなゴミを逆参照し、誰がどの結果を知っているかを取得するよりもはるかに優れています。
2013

-4

ポインタを0に設定しても、それほど明確ではありません。特にC ++以外の言語を使用する場合。これには、CとJavascriptが含まれます。

私は最近、次のようなコードを削除しました:

virtual void DrawTo(BITMAP *buffer) =0;

初めての純粋仮想関数。私はそれが一週間の魔法のジベルジャシュだと思った。基本的には関数ポインターをaに設定しているだけだと気づいたときnull(仮想関数はほとんどの場合C ++の関数ポインターにすぎないため)、自分で蹴りました。

virtual void DrawTo(BITMAP *buffer) =null;

私の新しい目との間に適切な間隔がなければ、その野蛮人よりも混乱が少ないでしょう。実際、C ++がnull小文字のfalseとtrueを採用しているように、なぜ小文字を採用していないのかと不思議に思っています。


一般に、ポインタにはNULlを0よりも優先します。ただし、「= 0;」C ++で純粋な仮想関数を宣言する慣用的な方法です。「= NULL」を使用しないことを強くお勧めします。この特定のケースでは。
danio 2012年

これはStackOverflowで最も面白いコメントです。おそらくあなたは、あなたが与えた例が純粋な仮想関数の構文であり、ポインターではないことをすでに知っているでしょう。はい、@ danioは正しいです。純粋な仮想関数にはNULLを使用しないでください。
sgowd 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.