C / C ++でのNULLポインターのチェック[終了]


153

最近のコードレビューでは、寄稿者がNULLポインターのすべてのチェックが次の方法で実行されるように強制しようとしています:

int * some_ptr;
// ...
if (some_ptr == NULL)
{
    // Handle null-pointer error
}
else
{
    // Proceed
}

の代わりに

int * some_ptr;
// ...
if (some_ptr)
{
    // Proceed
}
else
{
    // Handle null-pointer error
}

「このポインターがNULLではないことを確認してください」と明示的に言っているという意味で彼のやり方はもう少し明確であることに同意しますが、このコードに取り組んでいる誰もがポインター変数をifステートメントは暗黙的にをチェックしていNULLます。また、2番目の方法では、ilkのバグが発生する可能性が低くなります。

if (some_ptr = NULL)

これは、見つけてデバッグするための絶対的な苦痛です。

あなたはどちらの方法が好きですか、そしてなぜですか?


32
一貫したスタイルを持つことは、選択するスタイルよりも重要な場合があります。
Mark Ransom

15
@マーク:一貫性は常にあなたのスタイルの最も重要な要素です。
sbi

13
「また、2番目の方法では、ilkのバグが発生する可能性が少ないように感じます。コンパイルエラー。
user122302

14
最近のほとんどのコンパイラーは、条件付きの割り当てについて警告しています-残念ながら、あまりにも多くの開発者がコンパイラーの警告を無視しています。
Mike Ellery

10
一貫性が重要であるという上記の説明に同意しません。これは一貫性とは関係ありません。とにかくグッドソートではありません。これは、プログラマーに独自のスタイルを表現させる領域です。どちらの形式もすぐに理解できない人がいる場合は、すぐにコードを読むのをやめて、キャリアの変更を検討する必要があります。それ以上のことを言っておきます。表面的な一貫性がある場合は、スクリプトで自動的に適用できないため、削除します。人間の道徳的を排出するコストは、WAY人が会議で行ったもの愚かな決断よりも重要。
ヴィルヘルムテル

回答:


203

私の経験では、フォームif (ptr)またはのテストif (!ptr)が推奨されます。シンボルの定義には依存しませんNULL。彼らは偶然の割り当ての機会を公開していません。そして、それらは明確で簡潔です。

編集: SoapBoxがコメントで指摘しているように、それらはauto_ptr、ポインターとして機能し、boolこのイディオムを正確に有効にするための変換を提供するオブジェクトなどのC ++クラスと互換性があります。これらのオブジェクトの場合、への明示的な比較NULLは、他の意味上の副作用があるか、bool変換が意味する単純な存在チェックよりもコストがかかる可能性があるポインターへの変換を呼び出す必要があります。

私は、不要なテキストなしでそれが何を意味するかを示すコードを好みます。if (ptr != NULL)と同じ意味if (ptr)がありますが、冗長な特異性が犠牲になります。次の論理的なことは書くif ((ptr != NULL) == TRUE)ことであり、その方法は狂気です。C言語はifwhileなどによってテストされたブール値がゼロ以外の値がtrueであり、ゼロがfalseであるという特定の意味を持つことは明らかです。冗長性はそれを明確にしません。


23
さらに、これらは通常、演算子bool(またはsafe_bool)をオーバーライドするポインターラッパークラス(shared_ptr、auto_ptr、scoped_trなど)と互換性があります。
SoapBox 2010

2
auto_ptrへの参照がディスカッションに追加されたため、これを「回答」として投票します。質問を書き込んだ後、これは実際には他のものより主観的であり、状況によってはこれらのいずれかが「正しい」可能性があるため、スタックオーバーフローの「回答」には適していないことが明らかです。
ブライアンマーブル

2
@ブライアン、私はそれを尊重し、「より良い」答えが得られた場合は、必要に応じてチェックマークを移動してください。主観に関しては、はい、それは主観的です。しかし、それは少なくとも、主観的な議論であり、問​​題が提起されたときに必ずしも明白ではない客観的な問題があります。線がぼやけています。そして主観... ;-)
RBerteig

1
注意すべき重要なことの1つ:最近まで、私は "if(ptr!= NULL)"または "if(ptr!= nullptr)"ではなく "if(ptr)"と書く方がより簡潔なので、より読みやすいコード。しかし、最近のコンパイラでNULL / nullptrと比較すると、比較によって(それぞれ)警告/エラーが発生することがわかりました。したがって、ポインタをチェックする場合は、「if(ptr)」の代わりに「if(ptr!= NULL)」を実行することをお勧めします。誤ってptrをintとして宣言すると、最初のチェックで警告/エラーが発生し、後者では警告なしでコンパイルされます。
Arnaud 2014年

1
@David天宇Wongいいえ、それは意図されたものではありません。そのページの例int y = *x; if (!x) {...}は、オプティマイザがNULLの*x場合xは未定義であるため、NULLであってはならず、したがって!xFALSE でなければならないことを推論できる2行のシーケンスです。式!ptrは未定義の動作ではありません。この例では、テストがを使用するに行われた場合、*xオプティマイザはテストを排除するのが間違っています。
RBerteig 2017


25

私はこれから始めます:一貫性は王様です、決定はあなたのコードベースの一貫性より重要ではありません。

C ++では

NULLは、C ++では0または0Lとして定義されます。

C ++プログラミング言語 Bjarne Stroustrup0NULLを読んだ場合、割り当てを行うときマクロを回避するために明示的に使用することをお勧めします。比較で同じことをしたかどうかはわかりませんが、本を読んでからしばらく経っていますif(some_ptr)明確な比較なしではありますが、私はそれについて曖昧です。

この理由は、NULLマクロが(ほとんどすべてのマクロと同じように)欺瞞的0であるためです。その名前が示すように、マクロは実際にはリテラルであり、一意の型ではありません。マクロの回避は、C ++の一般的なガイドラインの1つです。一方、0整数のように見え、ポインターと比較したり、ポインターに割り当てられたりしたときとは異なります。個人的にはどちらの方法でもかまいませんが、通常は明示的な比較をスキップします(一部の人々はこれを嫌いますが、おそらく貢献者が変更を提案している理由です)。

個人的な感情に関係なく、正しい方法は1つではないため、これは主に最も害の少ない選択肢です。

これは明確で一般的なイディオムであり、私はそれを好みます。比較中に誤って値を割り当ててしまう可能性はなく、明確に読み取れます。

if(some_ptr){;}

これがsome_ptrポインター型であることがわかっている場合はこれは明らかですが、整数比較のように見える場合もあります。

if(some_ptr != 0){;}

これは明快で、一般的なケースでは理にかなっています...しかし、それは漏れやすい抽象化でNULLあり、実際には0文字通りのものであり、簡単に誤用される可能性があります。

if(some_ptr != NULL){;}

C ++ 0xにはnullptrがあります。これは明示的かつ正確であるため、現在推奨される方法です。偶発的な割り当てに注意してください。

if(some_ptr != nullptr){;}

C ++ 0xに移行できるまでは、これらのメソッドのどれを使用するかを心配するのは時間の無駄だと主張しますが、それらはすべて不十分であり、nullptrが発明された理由です(完璧な転送を伴う一般的なプログラミングの問題とともに) 。)最も重要なことは、一貫性を維持することです。

Cで

Cは別の獣です。

Cでは、NULLは0または((void *)0)として定義できます。C99では、実装で定義されたnullポインター定数を使用できます。そのため、実際には実装のNULLの定義が原因であり、標準ライブラリで検査する必要があります。

マクロは非常に一般的であり、一般に、言語やその他の一般的なプログラミングサポートの欠陥を補うためによく使用されます。言語ははるかに単純で、プリプロセッサへの依存がより一般的です。

この観点からNULL、Cでマクロ定義を使用することをお勧めします。


tl; drそして、あなたは0C ++で正しいのですが、C:でも意味があります(void *)00好ましいNULLタイプのエラーはPITAことができるので、C ++です。
マットジョイナー2010

@マット:NULLとにかくゼロです。
GManNickG 2010

@GMan:私のシステムではありません:#define NULL ((void *)0)からlinux/stddef.h
Matt Joiner

@マット:C ++。0が望ましいとおっしゃいましたが、NULLゼロでなければなりません。
GManNickG 2010

これは良い点です、私はそれを言及するのを怠りました、そして私はそれを提案することによって私の答えを改善しています。ANSI Cは((void *)0)として定義されたNULLを持つことができ、C ++はNULLを0として定義します。これの標準を直接トロールしていませんが、私の理解では、C ++ではNULLは0または0Lです。
M2tM 2010

19

私はを使用していますif (ptr)が、これについて議論する価値はまったくありません。

簡潔なので、自分のやり方が好きですが、他の人== NULLは読みやすく、より明示的にすると言っています。私は彼らがどこから来ているのかを知っています、私は余分なものがそれをもっと簡単にするので同意しません。(私はマクロが嫌いなので偏見があります。)あなた次第です。

私はあなたの主張に同意しません。条件付きの割り当ての警告が表示されない場合は、警告レベルを上げる必要があります。そのような単純な。(そして、すべての愛が良いので、それらを入れ替えないでください。)

C ++ 0xの中に注意、我々は行うことができますif (ptr == nullptr)私にはこれ、ないの読み取りよりよいが。(繰り返しますが、私はマクロが嫌いnullptrです。でもいいです。)if (ptr)慣れているからといって、今でも私はそうしています。


追加の5年の経験があなたの心を変えたことを願っています:) C ++ / Cがif_exist()のような演算子を持っている場合、それは理にかなっています。
magulla

10

率直に言って、なぜそれが重要なのかわかりません。どちらかは非常に明確であり、CまたはC ++をある程度経験した人は両方を理解する必要があります。ただし、1つのコメント:

エラーを認識して関数の実行を続行しない場合(つまり、例外をスローするか、エラーコードをすぐに返す場合)、ガード句にする必要があります。

int f(void* p)
{
    if (!p) { return -1; }

    // p is not null
    return 0;
}

これにより、「矢印コード」を回避できます。


誰かがここでkukuruku.co/hub/programming/i-do-not-know-cを指摘しましif(!p)が、これは未定義の動作です。それはうまくいくかもしれません。
David天宇Wong

@David天宇Wongそのリンクで例2について話している場合if(!p)、これは未定義の動作ではなく、その前の行です。そしてif(p==NULL)、まったく同じ問題があります。
Mark Ransom

8

個人的にif (ptr == NULL)は、意図を明確にするためにいつも使用してきましたが、現時点では単なる習慣にすぎません。

=代わりに==を使用すると、適切な警告設定を持つ有能なコンパイラによってキャッチされます。

重要なポイントは、グループに一貫したスタイルを選び、それを守ることです。どちらの方法を使用しても、最終的にはそれに慣れ、他の人のコードで作業するときに摩擦がなくなるのを歓迎します。


7

foo == NULLプラクティスを支持するもう1つのポイント:fooが、int *またはであるbool *場合、if (foo)小切手は誤って、指示先の値をテストするもの、つまりとしてリーダーによって解釈される可能性がありますif (*foo)NULLここでの比較は、ポインターについて話していることを思い出させます。

しかし、私は良い命名規則がこの議論の意味をなくしていると思います。


4

実際には、両方のバリアントを使用しています。

最初にポインタの有効性を確認し、それがNULLの場合は、関数から戻る/終了するだけの状況があります。(これは「関数に出口点が1つだけあるべきか」という議論につながる可能性があることを知っています)

ほとんどの場合、ポインタを確認し、必要な操作を行ってから、エラーのケースを解決します。結果は、複数のifがある醜いx回インデントされたコードになる可能性があります。


1
私は個人的に、1つの出口点を使用することで生じる矢印コードが嫌いです。答えがわかっているのに終了しないのはなぜですか?
ruslik

1
@ruslik:C(またはC-with-classesスタイルのC ++)では、複数のリターンがあるとクリーンアップが難しくなります。「実際の」C ++では、クリーンアップはRAIIによって処理されるため、明らかに問題ありませんが、一部のプログラマーは(多かれ少なかれ有効な理由で)過去に行き詰まり、RAIIに依存するか、または許可されません。 。
2010

このような場合の@jalfはgoto非常に便利です。また、多くの場合、malloc()クリーンアップが必要なものは、より高速なものに置き換えることができますalloca()。私はそれらが推奨されないことを知っていますが、それらは理由のために存在します(私は、名前の付いたラベルgotoは難読化されたif/elseツリーよりもはるかにクリーンです)。
ruslik

1
alloca非標準で完全に安全ではありません。それが失敗した場合、プログラムは爆発します。回復の機会はなく、スタックは比較的小さいため、障害が発生する可能性があります。失敗した場合は、ヒープを破壊し、特権を侵害する脆弱性を導入する可能性があります。割り当てられるサイズに小さなalloca制限がない限り、またはvlasを使用しないでください(通常の配列を使用することもできます)。
R .. GitHub ICE HELPING ICEの停止

4

Cプログラミング言語(K&R)では、偶発的な割り当てを回避するためにnull == ptrをチェックする必要があります。


23
K&Rは、「スマートポインターとは何ですか?」C ++の世界では状況が変化しています。
Alex Emelianov、2009

2
というか、C ++の世界ではすでに状況が変わっています」;)
jalf

3
K&Rはそれをどこに述べていますか(参照)?彼ら自身はこのスタイルを決して使用しません。K&R2の第2章では、if (!valid)上記も推奨されていますif (valid == 0)。
撮影

6
落ち着いて、みんな。彼はK&Rではなくk&rと言った。彼らのイニシャルも大文字にされていないので、彼らは明らかにあまり有名ではありません。
jmucchiello 2010

1
大文字にすると遅くなる
Derek

3

スタイルとフォーマットがレビューの一部になる場合は、測定するための合意されたスタイルガイドが必要です。ある場合は、スタイルガイドの指示に従ってください。ない場合、このような詳細は記述されたままにする必要があります。これは時間とエネルギーの浪費であり、コードレビューが実際に明らかにすべきであるものから注意をそらします。真剣に、スタイルガイドがなければ、私が好む規則を使用していない場合でも、原則としてこのようなコードを変更しないようにプッシュします。

それは重要ではありませんが、私の個人的な好みはif (ptr)です。意味は私よりもすぐにわかりif (ptr == NULL)ます。

たぶん彼は、ハッピーパスの前にエラー状態を処理する方が良いと言っているのでしょうか?その場合、私はまだ査読者に同意しません。これについて認められた慣例があるかどうかはわかりませんが、私の意見では、最も「正常な」状態がifステートメントの最初に来るはずです。そうすれば、関数の概要と機能を理解するために掘り下げる必要が少なくなります。

これの例外は、エラーによって関数から保釈される場合、または次に進む前にエラーから回復できる場合です。それらの場合、私は最初にエラーを処理します:

if (error_condition)
  bail_or_fix();
  return if not fixed;

// If I'm still here, I'm on the happy path

異常な状態に前もって対処することで、私はそれを処理し、その後それを忘れることができます。しかし、それを前もって処理してもハッピーパスに戻れない場合は、コードを理解しやすくするため、メインケースので処理する必要があります。私の考えでは。

しかし、それがスタイルガイドにない場合、それは私の意見であり、あなたの意見も同様です。標準化するかしないか。レビューアが意見を持っているからといって、レビュアーを疑似標準化させないでください。


1

これは、ポインターboolがC ++およびCで制御式として使用できる型と値に評価される両方の言語の基本の1つですint。使用するだけです。


1
  • ポインタはブール値ではありません
  • 最近のC / C ++コンパイラはif (foo = bar)、誤って書いた場合に警告を発します。

したがって、私は好む

if (foo == NULL)
{
    // null case
}
else
{
    // non null case
}

または

if (foo != NULL)
{
    // non null case
}
else
{
    // null case
}

ただし、一連のスタイルガイドラインを作成している場合は、このようなものは入れず、次のようなものを入れます。

ポインタに対して必ずnullチェックを行ってください。


2
ポインターがブール値ではないことは事実ですが、Cでは、ifステートメントはブール値をとらず、整数式をとります。
Ken

@ケン:Cがその点で壊れているからです。概念的には、これはブール式であり、(私の意見では)そのように扱われるべきです。
JeremyP 2010年

1
一部の言語には、null / not-nullのみをテストするifステートメントがあります。一部の言語には、整数の符号のみをテストするifステートメントがあります(3ウェイテスト)。Cが「壊れている」と考える理由はないと思います。彼らがあなたの好きな別のコンセプトを選んだからです。私がCについて嫌いなことはたくさんありますが、それはCプログラムモデルが私のメンタルモデルと同じではないことを意味します。
ケン

@ケン:ブール値は概念的には数値でもポインタでもありません。どの言語でもかまいません。
JeremyP 2010年

1
ブール値が数値やポインタであるとは言いませんでした。私は、ifステートメントがブール式を取る必要があるか、またはブール式しか取ることができないと主張する理由はないと述べ、手元に加えて反例を提供しました。多くの言語は、Cの「ゼロ/非ゼロ」の方法だけでなく、ブール値以外のものを取ります。コンピューティングでは、ブール値(のみ)を受け入れるifステートメントの作成は比較的最近の開発です。
ケン

1

C / C ++がifforおよびwhileステートメントのブール条件の型をチェックしないという事実の大ファンです。私は常に以下を使用します:

if (ptr)

if (!ptr)

整数またはブールに変換される他のタイプでも:

while(i--)
{
    // Something to do i times
}

while(cin >> a >> b)
{
    // Do something while you've input
}

このスタイルでコーディングすると、私にとって読みやすく、明確になります。私の個人的な意見です。

最近、OKI 431マイクロコントローラーで作業しているときに、次のことに気付きました。

unsigned char chx;

if (chx) // ...

より効率的です

if (chx == 1) // ...

なぜなら、後者の場合、コンパイラはchxの値を1と比較する必要があるためです。ここで、chxはtrue / falseフラグです。


1

私が使用したほとんどのコンパイラーはif、それ以上の構文シュガーなしで少なくとも割り当てに警告するので、私はその引数を購入しません。とは言っても、私は両方を専門的に使用しており、どちらも好みはありません。== NULL私の意見では、これは間違いなく明確です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.