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

StackOverflow https://stackoverflow.com/questions/176989

  •  05-07-2019
  •  | 
  •  

質問

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

今日まで、ゼロポインターをヌルポインターとして使用し続けていますが、周囲の人々はNULLの使用を強く求めています。個人的には、既存の値に名前(p && !q)を付けることには何のメリットもありません-ポインターを真理値としてテストすることも好きなので:

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

次にゼロを使用する方が理にかなっています(<=>を使用する場合のように、論理的に<=>を使用することはできません-<=>がゼロであると仮定しない限り、明示的に<=>と比較する必要があります) <=>)を使用します。

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

編集:RAIIと例外を使用して、ゼロ/ NULLポインターを使用することはめったにないことを追加する必要があります(もともと言うつもりでした)。

役に立ちましたか?

解決

これに関するStroustrupの見解: C ++スタイルとテクニックに関するよくある質問

  

C ++では、NULLの定義は0であるため、美的な違いのみがあります。マクロを避けることを好むので、0を使用します。nullptrの別の問題は、人々がそれが0や整数ではない、あるいはその両方と異なると時々誤解することです。先行標準コードでは、<=>は不適切なものに定義されていたため、避ける必要がありました。最近ではそれほど一般的ではありません。

     

ヌルポインターに名前を付ける必要がある場合は、<=>と呼びます。それがC ++ 11で呼ばれていることです。次に、<=>がキーワードになります。

とはいえ、小さなものに汗をかかないでください。

他のヒント

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

  1. 意図の文書

NULLを使用すると、その使用を検索できます。また、コンパイラによってintとして解釈されているかどうかに関係なく、開発者がポインターを使用する を使用したかどうか。

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

誰もが引用する例は次のとおりです。

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

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

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

  1. 分析ツールが今日に役立つ!

C ++ 0xがなくても、ポインターにstd::nullptr_tが使用されていること、および整数型に<=>が使用されていることを確認するツールが現在利用可能です。

  1. C ++ 11 に新しい<=>タイプが追加されます。

これはテーブルの最新の引数です。 <=>と<=>の問題はC ++ 0xで積極的に対処されており、<=>を提供するすべての実装について、最初に行うことを保証できます:

#define NULL  nullptr

<=>ではなく<=>を使用する場合、変更はほとんどまたはまったく労力なしで型安全性が向上します-何かあれば、<=>を使用したいくつかのバグをキャッチすることもできます<=>。今日<=>を使用している人にとって......えーと...うまくいけば、彼らは正規表現についての十分な知識を持っている...

NULLを使用します。 NULLは意図を示します。 0であることは実装の詳細であり、重要ではありません。

(以前のほとんどのマクロと同様に)0を優先してNULLの使用を停止しました。マクロをできる限り避けたいだけでなく、CおよびC ++コードでNULLが過剰に使用されるようになったため、これを行いました。ポインタだけでなく、0の値が必要なときにいつでも使用されるようです。

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

static const int nullptr = 0;

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

常に使用:

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

0は問題ありません。それは信号の意図の問題です。とは言うものの、私はそれについてアナルではありません。

    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_tのタイプである<=>を使用できるようになったと言えば十分です。

歴史は語られていて、0(ゼロ)を使用することに賛成した人は間違っていたと思います(Bjarne Stroustrupを含む)。 0を支持する議論は、主に美学と<!> quot;個人的な好み<!> quot;でした。

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

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

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

通常は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 <!> quot; nullptr <!> quot;詳細については。

私はかつて、0が有効なアドレスで、NULLが特別な8進数値として定義されているマシンで働いていました。そのマシン(0!= NULL)では、

などのコード
char *p;

...

if (p) { ... }

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

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

最近ではほとんどのコンパイラがNULLを0として定義していると思いますが、私は当時の教訓を今でも覚えています。NULLは必ずしも0ではありません。

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

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

しかし、だからと言って、両者が優れたプログラミング手法であることを意味するわけではありません。パフォーマンスに違いがないことを考えると、不可知論者/抽象の選択肢よりも低レベルを意識したオプションを選択することは、プログラミングの悪い習慣です。 コードの読者があなたの思考プロセスを理解するのを助ける

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

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

奇妙な、誰も、Stroustroupを含めてそれを言及しました。標準や美学について多くのことを話している間、0の代わりにNULLを使用することは危険であることに誰も気付きませんでした。たとえば、sizeof(int) != sizeof(void*)のアーキテクチャの可変引数リストで。 Stroustroupと同様に、美的理由から<=>を好みますが、タイプがあいまいな場合は使用しないように注意する必要があります。

可能な場合は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にする必要がないため、<!> lt; stddef.h <!> gt;で定義されているものを使用します。おそらくコンパイラに最適です。

引数のもう1つの側面は、論理比較(boolへの暗黙的なキャスト)を使用するか、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つです

また、<!> quot; if-not-null <!> quot;につながります。コード全体のステートメント。常に有効な状態に依存できる場合は、はるかに優れています。

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

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

最近、次のようなコードを使用しています:

virtual void DrawTo(BITMAP *buffer) =0;

初めて純粋な仮想機能用。私はそれが一週間魔法のジベルジャッシュだと思った。基本的には、関数ポインタをnullに設定するだけであることに気付いた(C ++の場合、ほとんどの場合、仮想関数は関数ポインタにすぎないため)。

virtual void DrawTo(BITMAP *buffer) =null;

新しい目との間隔が適切に設定されていなければ、そのバスタード化よりも混乱が少なくなります。実際、なぜC ++が小文字のfalseとtrueを使用するのと同じように、C ++が小文字の<=>を使用しないのか疑問に思っています。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top