最近のコードレビューで、貢献者は、ポインターに対するすべての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)
見つけてデバッグするのは絶対に苦痛です。
あなたはどちらの方法を好むのですか?
私の経験では、フォームif (ptr)
またはif (!ptr)
のテストが推奨されます。これらは、シンボルNULL
の定義に依存しません。彼らは偶然の割り当ての機会を公開しません。そして、それらは明確で簡潔です。
編集: SoapBoxはコメントで指摘しているように、ポインターとして機能し、bool
への変換を提供するauto_ptr
などのC++クラスと互換性があります。これらのオブジェクトの場合、NULL
との明示的な比較では、ポインターへの変換を呼び出す必要があります。これは、他のセマンティック副作用があるか、bool
変換が示す単純な存在チェックよりも高価になる可能性があります。
不要なテキストなしでそれが何を意味するかを示すコードを好みます。 if (ptr != NULL)
の意味はif (ptr)
と同じですが、冗長性が犠牲になります。次の論理的なことはif ((ptr != NULL) == TRUE)
を書くことであり、その方法は狂気にあります。 C言語では、if
、while
などによってテストされるブール値が、ゼロ以外の値がtrueであり、ゼロがfalseであるという特定の意味を持つことは明らかです。冗長性はそれを明確にしません。
if (foo)
は十分に明確です。これを使って。
私はこれから始めます:一貫性は王様です、決定はコードベースの一貫性よりも重要ではありません。
NULLは、C++では0または0Lとして定義されます。
The C++ Programming Languageを読んだ場合 Bjarne Stroustrupは、割り当てを行うときにNULL
マクロを避けるために0
を明示的に使用することを推奨しています 、I彼が比較で同じことをしたかどうかはわかりません。本を読んでからしばらく経ちました。明示的な比較をせずにif(some_ptr)
をやっただけだと思いますが、それについてはあいまいです。
その理由は、NULL
マクロは(ほとんどすべてのマクロがそうであるように)欺de的であり、実際には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では、NULLを0または((void *)0)として定義できます。C99では、実装で定義されたNULLポインター定数を使用できます。したがって、実際には実装のNULLの定義に依存するため、標準ライブラリで検査する必要があります。
マクロは非常に一般的であり、一般的に、言語などでの汎用プログラミングサポートの不足を補うために多く使用されます。この言語ははるかに単純であり、より一般的なプリプロセッサに依存しています。
この観点から、CでNULL
マクロ定義を使用することをお勧めします。
if (ptr)
を使用しますが、これについて議論する価値はまったくありません。
他の人は== NULL
が読みやすく、より明確にすると言っていますが、私はそれが簡潔だから私のやり方が好きです。彼らがどこから来たのかわかりますが、余分なものがそれをより簡単にすることに同意しません。 (マクロが嫌いなので、偏見があります。)あなた次第です。
私はあなたの議論に同意しません。条件式の割り当てに対する警告が表示されない場合は、警告レベルを上げる必要があります。そのような単純な。 (そして、良いものすべてへの愛のために、それらを変えないでください。)
C++ 0xでは、if (ptr == nullptr)
を実行できます。これは、私にとってdoesより読みやすいです。 (繰り返しますが、私はマクロが嫌いです。しかし、nullptr
はいいです。)しかし、私はまだif (ptr)
をしています。
率直に言って、なぜそれが重要なのかわかりません。どちらか一方は非常に明確であり、CまたはC++をある程度経験した人は両方を理解する必要があります。ただし、1つのコメント:
エラーを認識し、関数の実行を継続しない場合(つまり、例外をスローするか、すぐにエラーコードを返す場合)、ガード句にする必要があります。
int f(void* p)
{
if (!p) { return -1; }
// p is not null
return 0;
}
これにより、「矢印コード」を回避できます。
foo == NULL
プラクティスを支持するもう1つのポイント:foo
が、たとえばint *
またはbool *
である場合、if (foo)
チェックは、ポインティッドの値をテストしている、つまりif (*foo)
として読者が誤って解釈する可能性があります。ここでのNULL
の比較は、ポインターについて話していることを思い出させるものです。
しかし、適切な命名規則がこの議論を無意味にしていると思います。
個人的には、if (ptr == NULL)
を使用しています。これは、意図を明確にするためです。しかし、この時点では習慣にすぎません。
=
の代わりに==
を使用すると、適切な警告設定を持つ有能なコンパイラーによってキャッチされます。
重要なポイントは、グループに一貫したスタイルを選択し、それに固執することです。どの方法を使用しても、最終的には慣れるでしょう。他の人のコードで作業する際の摩擦の損失は歓迎されます。
実際、両方のバリエーションを使用しています。
ポインターの有効性を最初に確認し、それがNULLの場合は、関数から単に戻る/終了する状況があります。 (これにより、「関数には出口点が1つしかありません」という議論につながることがあります)
ほとんどの場合、ポインターをチェックしてから、必要なことを実行してからエラーのケースを解決します。結果は、複数のifを持つwithいx回インデントされたコードになります。
Cプログラミング言語(K&R)は、偶発的な割り当てを避けるためにnull == ptrをチェックする必要があります。
スタイルと形式がレビューの一部となる場合は、評価するスタイルガイドに同意する必要があります。ある場合は、スタイルガイドの説明に従ってください。存在しない場合、このような詳細は記述されたままにしておく必要があります。これは時間とエネルギーの無駄であり、コードレビューが実際に明らかにすべきことから注意をそらします。真剣に、スタイルガイドがなければ、原則としてこのようなコードを変更しないようにプッシュします。たとえそれが好みの規則を使用していなくてもです。
それは重要ではありませんが、私の個人的な好みは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
前もって異常な状態に対処することで、私はそれを処理し、それを忘れることができます。しかし、それを前もって処理することで幸せな道に戻ることができない場合は、コードをより理解しやすくするため、afterメインケースで処理する必要があります。私の考えでは。
しかし、それがスタイルガイドにない場合、それは単なる私の意見であり、あなたの意見は同じように有効です。標準化するかしないか。レビュアーが意見を持っているからといって、レビュアーに疑似標準化させないでください。
私が使用したほとんどのコンパイラーは、少なくとも構文シュガーなしでif
割り当てについて警告するので、その引数を購入しません。とはいえ、私は両方を専門的に使用しており、どちらにも好みはありません。私の意見では、== NULL
は明確です。
if (foo = bar)
を記述すると警告を発します。したがって、私は好む
if (foo == NULL)
{
// null case
}
else
{
// non null case
}
または
if (foo != NULL)
{
// non null case
}
else
{
// null case
}
ただし、一連のスタイルガイドラインを書いている場合、このようなものは含めません。次のようなものを入れます。
ポインタでnullチェックを実行してください。
これは非常に明確だと思います:
if( !some_ptr )
{
// handle null-pointer error
}
else
{
// proceed
}
"ポインタがない場合..."
さらに、簡潔であり、偶発的な割り当ての危険がありません。
私は、C/C++がif
、for
および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フラグです。
これは、ポインターが制御式として使用できる型と値、C++のbool
およびCのint
を評価する両方の言語の基本の1つです。使用するだけです。