- ベストアンサー
C++のNULLは意味があるの?
タイトルの通りです。 0で全て済むのに、わざわざNULLを使う必要が分かりませんし、私は全く使った事がありません。 ただ0と書くと数値かポインターかすぐには分からない からというのは、あまり対した理由にはなりそうも無いし。 ほかの人がNULLを使っているのをそのまま参考にしたと言うのが多いかもしれないですね。
- みんなの回答 (11)
- 専門家の回答
質問者が選んだベストアンサー
#4です。 C++で考えられる唯一の例外は可変引数の関数を呼び出すときです。この場合、"0"をintの0として扱うべきかdoubleの0.0として扱うべきか無効ポインターとして扱うべきか、コンパイラーが判断できない為、明示的にキャストして型を教える必要があります。この場合、単に0と書くよりはNULLの方が少しマシといった程度で、移殖性を考えると危険であることに変わりはありません。 #8他について、無効ポインターの値(ビットパターン)は処理系定義ですが、上に示した特殊な状況を除き、無効ポインターの値が何であろうとコンパイラーが定数0を無効ポインターに変換してくれます。ちなみに、mallocが失敗したときの値は、厳密にいうとNULLではなくて無効ポインター(a null pointer)です。 #10について、この場合、0と書いてもNULLと書いても同じです。"C *c = (C *)0;"の時点でcの値は無効ポインターになります。そして、無効ポインターを他の型のポインターにキャストすると必ず無効ポインターになります。
その他の回答 (10)
- jmh
- ベストアンサー率23% (71/304)
自信ないですが、C++でキャストした0は危険だと思います。 無効なポインタの"値"が0でない場合に、 struct A { ... }; struct B { ... }; struct C: A, B { ... }; C *c = (C *)0; B *b = c; の b が無効なポインタになるのでしょうか? (C *)0 は定"整"数の0ではないので、無効なポインタには変換されないと思います。 素朴に考えると最後の行は B *b = (c == (無効なC *) ? (無効なB *) : (B *)((char *)c + sizeof(A)); と同じコトになる気がします。 今、(無効なC *) != (C *)0 なので b == (B *)sizeof(A) になりませんか?
- jmh
- ベストアンサー率23% (71/304)
no.5 jmh です。 g++ には、__null というキーワードがあって、 int n = NULL; // #define NULL __null には警告できるみたいです。でも、より明らかに間違えた感じの int *n = 1 + 2 - 3; // == 0 は素通りみたいです。
- toysmith
- ベストアンサー率37% (570/1525)
自分で読み返して「何を言ってるのかよくわからないかも?」と感じたので、もう一度。 ・インターフェース上NULLと記述することを要求している部分に関しては素直にNULLを使うべき malloc()など「失敗したときにはNULLが返される」というインターフェースの場合、わざわざ0と比較するよりもNULLで比較するべきです。 16bit版のMS-CやVC++では0は16bitなのvoid far *malloc()の戻り値(32bitアドレス値)と比較することは推奨されません。 この場合は#define NULL 0Lと定義されたNULLと比較するべきです。 ・0が(char *)0や(void *)0と同じ形式である保証はない アドレス値のフォーマットは処理系依存です。 NULLは「インターフェース上NULLが使われることが明らかな場合」に使うべきで、「NULL」と「正しくキャストされた0」を便宜使い分けるべきです。 と、言いたかったわけです。 可読性/移植性の問題からキャストを伴わない0をアドレス値として利用することは勧められません。
ヌルポインタをNULLと表現するのは、やはり可読性の為だと思います。 graphaffine さんが、変数名に対しては可読性を向上させる手法を認めているにもかかわらず、定数に対して窮屈な考え方をするのは、客観的にみて不自然です。 #define、const int、enum などで定義された名前のうち、0と同じものをわざわざ定義する必要はないと言っているように思えます。 0は式において「偽」であり、特別だと言えますが、NULLを0の中でもさらに特別なものと位置づけるコーディングスタイルだという事です。
- toysmith
- ベストアンサー率37% (570/1525)
まず理解しなくてはいけないのは「C,C++には組み込み定数が存在せず、NULLはプリプロセッサシンボルでしかない」ということです。 プリプロセッサがプリプロセッサシンボルを値に変換するときに文法を意識することを期待できません。 ANSI以前のCではNULL=0というのはUNIXにおける慣例にすぎず、NULLで示される無効ポインタは0という保証はありませんでした。 またint16ビット、アドレス32bitのコンパイラではNULLは0Lである必要がありました。 移植性の観点から無効ポインタを0と記述することは問題がありました。 しかし、32bitアドレスと16ビットアドレスを使い分ける必要のある場合などは「何が何でも向こうポインタはNULL」と考えると問題が発生する場合があります。 C/C++ではアドレス値の形式は処理系依存なので(char *)0とか(long *)0のように明示的なキャストを伴う値を使った方が移植性は向上します。
- jmh
- ベストアンサー率23% (71/304)
私は逆に次のはどちらも警告かエラーであってもいいのになぁと思います。 char *p = 0; int n = NULL;
- furlong
- ベストアンサー率50% (17/34)
C++でNULLと書くのは昔のCの名残だと思います。プロトタイプ宣言をせずに関数を呼ぶとき、コンパイラーが引数の型を知らないので、整数0なのかポインターNULLなのか、教えてあげる必要があります。C++では可変引数の関数を呼ぶ場合ぐらいにしか意味がありません。(整数とポインターの取り扱いが異なるCPUを使う場合) 但し、本気で移殖性を考えるとNULLは中途半端です。神経質に考えるとintへのポインターのNULLとcharへのポインターのNULLは取り扱いが違う可能性がある為、万全を期すには個別にキャストを付けるしかないようです。 追伸:プロトタイプ宣言がある場合は、仮にNULLポインタの値が物理的に0以外の特殊な値であっても、コンパイラが0をNULLに自動的に変換してくれます。これは保証されています。
補足
furlongさん解答有り難う御座います。 >C++でNULLと書くのは昔のCの名残だと思います。 ということは、現在は実質的な意味は無いということですね。C++はプロトタイプ宣言が必須ですからね。 あと、移植性を考えた場合は0を指定した方が良いということですね。
- taka_tetsu
- ベストアンサー率65% (1020/1553)
NULLって0x00とは限らないはずですよ。 コンパイラ依存だったはずです。 もしそういうコンパイラを使ったら、graphaffineのソースはすべてアウトですね。
補足
#4追伸を御参照下さい。
- mrumesuke
- ベストアンサー率45% (254/557)
NULLの値は0であるけれど、0はNULLと等しいのでしょうか。 ある日突然、あるいは環境によってNULLの定義が変わってしまったらどうするのでしょう。 また、ソースの品質と言うことを考えてください。 多人数でプロジェクトを組んだとき、あるいは何らかの形でソースを公開したとき、他人はあなたのコードを見てどれだけ理解できるのでしょうか。場合によっては誰かに引き継がなくてはならないこともあるでしょう。マジックナンバーだらけのコードの可読性は低く、その結果品質は低下します。適切にNULLとしておけば、引き継いだ相手を「この0の意味はなんだろう」と悩ませることもないでしょう。 個人で完結するものならどんなコーディングスタイルでも構わないでしょうけど。
補足
前半については#4追伸を御参照下さい。 また、後半は0は特別であり一般的なマジックナンバーと 同列には扱えないと思っています。 実際一般的な場合は変更があり得ますが、0は変更の必要はありませんし。
NULLを使うとポインタだと分かるのに わざわざ0を使う理由が分かりません。 結局同じだからといっても NULLと書くと可読性がいいと思うのですが。
補足
arrowrootさん、解答有り難う御座います。 可読性については変数の命名法等でカバーできるので あまり気にしていません。ので、他に何か理由があるのか知りたかったのですが、どうやら無いようですね。 >NULLを使うとポインタだと分かるのに >わざわざ0を使う理由が分かりません。 万が一、NULLポインターの内部形式が変わった場合でも ヘッダーを切り替える必要はありません。 #4の追伸参照。私の質問はこの追伸を踏まえたものです。
補足
toysmithさん、今晩は。それから他の方にも纏めて 返信です。 まず、大前提はタイトルにも有りますように、C++ です。私はC固有の話は詳しくありません。 質問の趣旨は可読性以外にNULLを使う必要性があるか?ということですがどうやら無いようですね。この点に関しては了解しました。ということで可読性の件は終わりにします。 派生事項として、NULLと移植性との関係ですが、ヘッダーファイルを見ましたがVCではNULLは0と 定義されていました。従って、NULLでも0でも処理的には同じであるということが分かりました。 >NULLは「インターフェース上NULLが使われることが明らかな場合」に使うべきで、 >「NULL」と「正しくキャストされた0」を便宜使い分けるべきです。 #4の追伸のように単独の0をポインター値として使う場合、コンパイラーにより適切に変換される。従って、0を キャストする必要が無いと取ったのですがこの理解は間違っているのでしょうか。