NULLだけチェックしても意味ないじゃん

おっしゃるとおり!

 

 

C/C++のコードでNULLチェックしない派が存在します。

 

構造体やクラスオブジェクトのポインタを受け取る関数すべてに引数チェックとしてNULLチェックするのは意味がないという主張です。

 

主張の例:

 

   

NULLポインタがアドレス0x0へのポインタだとしたら、

   

0x1へのポインタはチェックしなくてもいいのかよ。

   

「不正なポインタかどうか」なんてチェックのしようがない。

   

NULLチェックなんて、無意味なんだよ。

 

 

 

この手の主張に技術的に応えるためには、

 

   

アドレスがヒープかどうかチェックする機構

 

を実装するとか、

 

   

独自メモリアロケータのアドレス範囲でチェックする機構

 

を実装すればよいだけであることは、ペレの皆様ならご存知かと思います。

 

 

 

一方、コーディングルールなどで、

 

   

ポインタの初期値はNULL。

   

使用済みポインタにはNULLを格納。

 

というような面倒なルールを徹底しておれば、メモリ破壊やスタック崩壊でもしない限り、NULLチェックには

 

   

ロジックのバグを検出する

 

という正当な役割が沸いてきます。

 

 

 

さて、ポインタを他のパラメータと区別して特別に扱うのはなぜでしょうか。

 

たとえば、intの引数のレンジチェックと何がちがうでしょうか。

 

それはポインタアクセスでソフトウェア割り込み(例外)が発生するからですね。

 

ここで忘れないでほしいのは、

 

   

あるアドレスにアクセスしようとして

   

例外が発生するのは、

   

“そうなるように”設定されているから

 

という事実です。

 

たとえばカーネル側のプログラムやデバイスドライバのようなものは

 

   

ゼロアドレスも含めたすべてのアクセス空間できないと困る

 

わけです。ユーザプログラムにおいて、”そうなるように”設定されているということは、

 

   

アクセスしたら例外が発生するアドレスはどこら辺か

 

という情報はシステム設計レベルでは既知であるということです。

 

# 調べりゃわかる。わからん状態でどうやってデバッグできるでしょうか。

 

 

 

さて、”どこから呼ばれるか分からないライブラリ”、たとえばlibcに含まれる関数は、内部でNULLチェックしませんね。ユーザプログラムからNULLポインタをprintfに渡したらズドンと墜ちます。それに対して、

 

   

printf()を呼び出して例外でシステムが落ちるのは、

   

libc内部でNULLチェックしないクソ仕様のせいです。

 

などと主張するのは、「私は馬鹿です」と書いた名刺を差し出すようなものですから、やめましょう。

 

 

 

ポインタを引数として受け取る関数で、NULLチェックしたとして、”NULLが来たらどうするの?”という問題もあります。これは他の非ポインタの引数と同じように扱えばよいと思います。たとえばルールで

 

   

引数チェックをして、異常値が検出されたら、

   

(1)エラーログを出力して処理せずreturn

   

(2)返り値でエラーを返して処理は続行しない

 

というようなものです。ログだけ吐いて処理続行という選択肢もあるでしょうが、

 

   

NULLポインタアクセスでシステムが落ちるのと、

   

NULLポインタアクセスでエラーログが大量に出るのと、

 

どちらを選択するかは皆さんの勝手です。

 

 

 

以上のようなことを踏まえて強引にまとめると以下のようになります。

 

 

 

NULLチェックをするなら~♪

 

・ポインタの初期値と仕様済みポインタはNULL代入を徹底する

 

・処理を続行しないようにしてログ出力(”ふつうの”異常処理)

 

・やろうと思えば、”不正ポインタチェック”も可能

 

 

 

というわけで今後はしたり顔で、

 

   

NULLチェックなんて無意味なことをルールに設定するやつは

   

本物のプログラマじゃねぇ

 

などと嘯くことはできなくなるわけです。

 

   

つか、ヒープアドレスのチェックしてねぇの?

   

NULLチェックすらやってねぇの?

   

はぁ?おめぇシロウト?

 

と明るく元気に反論しましょう。