• ベストアンサー

NULLポインタは0と書かなければだめ?

C++言語を使っています。 C++言語でNULLポインタを表す場合、0やNULLを使用しますが、 「NULLは単なるマクロなので本来は0を使わなければだめ」という話を聞きました。 つまり、 int *p = 0; が本当の正しいNULLポインタであり、 int *p = NULL; は推奨されていない(?)NULLポインタというようです。 今まであまり疑うことなく、NULLポインタを表すのにNULLを使用していましたが、 上記の内容は本当でしょうか。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.4

(1) NULL を使った方が良いという意見と 0 を使った方が良いという意見の両方がある 規格にはどちらかを使ってはいけないなどという記述はありません。また、C++ では NULL は #define NULL 0 とされるので、実質的にはどちらも同じです。従って、どちらかが推奨されるとしたら、可読性やミスを減らすという観点から議論される事になると思います。因みに、No.3 の #define NULL ((void*)0) は C 言語の場合です。 先ず、NULL を使うと「意味的にそれがポインタを意図している」という事がプログラムの読み手に伝わりやすく、良いとする人があります。(これが、そもそもマクロとして NULL を定義した切っ掛けだと思います。) しかし、問題点は NULL は単なる 0 に展開されるので、それが「ポインタを意図した 0 である」という事がコンパイラに伝わらないという所にあります。例えば、 void f(int*); void f(int); の前者を呼び出そうと思って f(NULL); と書くと f(0); に展開され、実際には後者が呼び出されます。null ポインタを 0 と書いていれば f(0); まで書いた時点で「おや、これはまずいぞ」と気付く可能性が高いですが、NULL と書いていると認識として「NULL はポインタ」という先入観がある為に f(int*) の方が呼び出される様に錯覚してしまいます。因みに、前者の f を正しく呼び出す為には f((int*)NULL) または f((int*)0) とする必要があります (別に 0 と表記していれば何も注意が要らないという訳ではなくやはりキャスト (int*) が必要になりますが、その事に気付きやすいという事です)。 これに対して、そもそもポインタを受け取る関数と整数を受け取る関数を同名で多重定義する事自体が問題なのではないかという反論もあります。つまり、f(int*) f(int) の呼出の問題は、引数に NULL を指定した事に問題があるのではなくその様な多重定義をしてしまった事に問題があるのであって、これは NULL を敬遠する理由にはならないという主張です。 何れにしても、 > 「NULLは単なるマクロなので本来は0を使わなければだめ」 の記述は、違います。0 が推奨される理由は NULL がマクロだからだとかではありません。マクロは偽物で即値が本物なので、即値の方が良いなどという議論は成立しません。マクロの方が読みやすければ、即値ではなくマクロを使うべきです。例えば EXIT_SUCCESS, EXIT_FAILURE, SEEK_SET などのマクロ定数は使った方が分かりやすいプログラムになります。 NULL に関して賛否両論があるのは、C言語からC++への発展の際の矛盾 (ここでは説明しませんでしたが) などの複雑な背景があるからなのです。 (2) C++11 では nullptr 先に述べたように NULL と書くのも 0 と書くのも欠点があるので、それを補う為に C++ の 2011 の規格で nullptr という新しいキーワードが追加されました。今後は、C++11 に対応している新しいコンパイラでは nullptr を使っていくと良いでしょう。(勿論、古いコンパイラでコンパイルできなくなるという問題点はありますが)。 (3) null ポインタを表す整数リテラルは常に 0 です No.1 補足。 確かに NULL ポインタの「内部表現」(機械の中でどう表されるか) は処理系依存なので、null ポインタのビットパターンをそのまま整数として解釈 (reinterpret_cast) した時に 0 になるとは限りません。しかし、ソースコードの上では常に 0 と表します。なぜならば、整数を普通にポインタに変換 (static_cast) する場合は、整数の 0 と null ポインタが対応する様に変換されるという事が保証されているからです。

nanako_04
質問者

お礼

回答ありがとうございます。 大変参考になりました。

その他の回答 (8)

  • wormhole
  • ベストアンサー率28% (1619/5652)
回答No.9

>http://www.curiocube.com/mikata/oop/p1_ch09_nonull.php Func(0); これを後で読んだ場合、ポインタのつもりだったのか数値の0のつもりだったのかわからないと思いますが。 C++で Func(NULL) を「Func(int *)が呼び出されることを意図したコード」というのが、そもそもの間違いですし。 NULLを使うにしろ0を使うにしろFunc(int *)が呼び出されることを意図したコードは Func((int *)NULL); Func((int *)0); です。 >http://www.geocities.jp/bleis_tift/cpp/null.html NULLの定義は確かに処理系依存ですがNULLポインタの内部表現自体処理系依存です。 NULLが思った通りの動作をしない事があるというのもNULLの使い方を間違えてるくらいしか私には思いつかないです。 3番目については「NULLは単なるマクロなので本来は0を使わなければだめ」といった内容には思えないですけど。

nanako_04
質問者

お礼

3つ目の参考URLについて、 「@5mingame2 @torotiti 「NULL」はあくまでもC言語用マクロであって、C++では「0」と比較するのが正しいと教わりましたが、(以下略」 とか 「@5mingame2 逆だと思いますよ。ポインタに参照無しは0と書くのが本来で、NULLは単なるマクロです。」 とかあったので、そういう話だと思いました。

  • mk48a
  • ベストアンサー率56% (1133/2007)
回答No.8

補足です。 Microsoftのフォーラムのリンクです。 http://social.msdn.microsoft.com/Forums/ja-JP/8096f9d1-a4a7-44a8-8baf-476aa4868fab/0null?forum=vcgeneralja EffectiveC++からの引用があります。 要は0でも弊害が出るのでnullptrを使うべきだが、使えない環境もある。 ソースから検索して置換する場合0だと難しいが、NULLだとやり易い。 という感じかと。 http://msdn.microsoft.com/ja-jp/library/vstudio/4ex65770%28v=vs.110%29.aspx

nanako_04
質問者

お礼

回答ありがとうございます。 > ソースから検索して置換する場合0だと難しいが、NULLだとやり易い。 そういえば、そうですね。 NULL にしておくと、後のメンテナンスがしやすいのかもしれません。

回答No.7

#6 の方へ 申し訳御座いません。原稿を書き終わった後に改めて回答がついていないかを見たのですが、適当に見てしまったせいで見落としておりました。

nanako_04
質問者

お礼

解決しました。 ありがとうございます。

  • wormhole
  • ベストアンサー率28% (1619/5652)
回答No.6

#4の方へ >因みに、No.3 の #define NULL ((void*)0) は C 言語の場合です。 #3では「C言語だと」と前置きしていますが。 C言語でも、そういう話はないですということ書きたかったわけなんだけど。

nanako_04
質問者

お礼

理解しております。

  • mk48a
  • ベストアンサー率56% (1133/2007)
回答No.5

>「NULLは単なるマクロなので本来は0を使わなければだめ」という話を聞きました。 その発想であれば、マクロは全部使うなということになりますが、マクロは機種やコンパイラ依存の部分を吸収する目的もあるので、その主張には無理があるかと思います。 一応現在は言語使用上NULLポインタは0となっているので、0を使用するのは間違いではありませんが。 参考 http://www7b.biglobe.ne.jp/~robe/cpphtml/html01/cpp01063.html 現在は可読性を重視するプログラムが主流なので、どちらが読みやすいかですが、個人的にはNULLの方が意味をとらえやすいと思います。 0だと数値と間違えやすい。 その話をした人に何でマクロを使用してはいけないのか聞いて見ましょう。

nanako_04
質問者

お礼

回答ありがとうございます。 No.4の回答で理解できました。

  • wormhole
  • ベストアンサー率28% (1619/5652)
回答No.3

その話が本当という事になるとC言語でも 「NULLは単なるマクロなので本来は((void*)0)を使わなければだめ」 という事になりますけど。 それにそれだとNULLと同様にマクロで定義された定数は、即値で書くべきという事になりそうな。

nanako_04
質問者

お礼

回答ありがとうございます。 No.4 の回答で理解できました。

nanako_04
質問者

補足

No.1 の回答の補足にいくつかページのURLをつけたので、 そちらも参照していただけませんか。

回答No.2

使ったらダメということでは無いですが、話の背景は間違っていないと思います。 C言語のNULLマクロとC++のNULLマクロは全く異質の物であり C++におけるNULLマクロはただ単に0と展開されるだけのようです。 また、これは整数のゼロとNULLポインタの区別がつかないことを意味し、 場合によっては多重定義された関数が正しく選択されないという問題が発生します。 この問題を解消するため、新しい標準では NULLポインタ専用に nullptr というキーワードがあるようです。 【参考】 More C++ Idioms/nullptr http://ja.wikibooks.org/wiki/More_C%2B%2B_Idioms/nullptr C++11 https://ja.wikipedia.org/wiki/C%2B%2B11

nanako_04
質問者

お礼

回答ありがとうございます。 話の背景は間違っていない、というのはよくわかりませんでしたが、 全体としては、No.4の回答で理解できました。

  • BuriBuri4
  • ベストアンサー率28% (150/525)
回答No.1

そんな嘘っぱち誰に聞いたの? 処理系依存だからNULLが0じゃない場合もありますよん。

nanako_04
質問者

お礼

No.4の回答で理解できました。

nanako_04
質問者

補足

このあたりでしょうか。 http://www.curiocube.com/mikata/oop/p1_ch09_nonull.php ページ最後 「このような失敗を防ぐためには、NULL マクロを使わないことです。いままでこのマクロに親しんできた人には少し辛い別れかもしれませんが、これからは NULL と書くかわりに 0 と書きましょう。」 http://www.geocities.jp/bleis_tift/cpp/null.html ページ真ん中あたり 「NULLの代用 そもそもNULLはC言語との互換性のために残されているマクロ定義であり、 C++では不必要な存在なのです。 C++で(C言語で言う)NULLを表現したい場合、 0を使用します。 0は任意の型のヌルポインタに変換される事が保証されているので、 どんなNULLマクロよりも安全です。 」 http://togetter.com/li/110100 全体的に 3つ目のページはよくわからない記述もありますが。。。 > 処理系依存だからNULLが0じゃない場合もありますよん。 これはどういう意味(意図)か、ちょっと私にはわかりませんでした。 すいません。

関連するQ&A

  • 「NULLポインタ」と「演算の結果としてのアドレス0」との比較

    ものすごく基本的な疑問です。。。 「C言語FAQ日本語訳」http://www.kouno.jp/home/c_faq/ ここの「05.ヌルポインター」を見ると以下のような意味の記述があります。 「NULLポインタは他のどんなポインタの値とも区別可能で、有効なポインタと比較しても等しくなる事はない」 「ポインタを書くべき場所に書かれた定数0はコンパイル時にNULLポインタに変換される」 そこで以下のプログラムを Borland C++ 5.5 for Win32 でコンパイル・実行してみたところ、 p1 == p2 とりました。これって変ですよね? p1 は明示的に定数0で初期化しているのでNULLポインタですが、p2 は演算の結果としてアドレス0番地を指しているので、NULLポインタでは無いですよね? これはコンパイラが間違っていると思って良いのでしょうか? #include <stdio.h> int main() { char *p1,*p2; p1 = 0; p2 = (char *)1; p2--; if(p1 == p2){ printf("p1 == p2"); } else{ printf("p1 != p2"); } return 0; }

  • 警告をださずにポインタにnullを渡す方法は?

    こんにちは。 ポインタ型の引数にNULLを渡してやりたいのですが 以下のような警告がでてしまいます。 test.c: In function 'test_': test.c:14: 警告: passing argument 2 of 'test' makes pointer from integer without a cast 問題の箇所は以下のように書いてます。 【test.c】 ~省略~ /** プロトタイプ **/ 7 void aaa(int,int*,int*); ~省略~ 14 aaa(0,NULL,&tv); NULLを渡したい場合、警告をださないように スマートに記述するにはどうしたらいいでしょうか? よろしくお願いします。

  • NULLポインタが0でない処理系とは?

    http://okwave.jp/qa/q7458540.html にて、 > C言語の規格上、0はNULLポインタとして扱われますが > NULLポインタは0とは限らないので(0でない処理系もあります)。 と他の回答者が答えていたのが妙に引っかかりました。 後学のために知っておきたいのですが、 どの処理系だとNULLポインタが0ではないのかどなたかご存じないですか? 気になって、C99の仕様書らしいものを検索で見つけたのですが、 http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf これによると、6.3.2.3 Pointers の 3節 にて、 > An integer constant expression with the value 0, > or such an expression cast to type void *, is called a null pointer constant. とあり、7.17 Common definitions <stddef.h> の 3節 でも、 > The macros are > NULL > which expands to an implementation-defined null pointer constant; (略) とあるので、NULLポインタを表す定数は0か(void*)0だと思っていたのですが。 言い換えると、 struct foo { char* bar; }; と構造体が定義されている時、 struct foo baz; bzero(&baz, sizeof(baz)); すればbaz.barはNULLポインタ定数に初期化されると思っていたのですが、 これだとダメな処理系はどういう処理系で、どんな用途で使うのでしょうか? おそらく、0でない値にした目的や歴史的経緯があるのではないかと推測するのですが。

  • NULL

    C言語で、0の代入とNULLの代入はバイナリレベルでは同じ事だと聞いたのですが本当なんでしょうか? とすると int i = 0; と int i= NULL; は、同じ事なんでしょうか。 また、 if(!aaaa){ と if(aaaa == NULL){ は同じ条件文なんでしょうか?

  • C言語のポインタについて教えてください。

    C言語のポインタについて教えてください。 ・pointer1.c  int main(){   int a;   int *p;   p = &a;     a = 123;   printf("%d", *p);   return 0;  } ・pointer2.c   int main(){ int a[100]; int *p; p = &a[0]; int i; for(i = 0; i < 100; i++) a[i] = i; for(i = 0; i < 100; i++) printf("%d", *p++); return 0; } と二つのソースコードがあるとき、pointer2.cの「p = &a[0]」をpointer1.cのように「p = &a」と書けないのはなぜですか?  また、「&a」は動かすことのできなく、「aを指し示す*p」は動かすことができる変数のようなもの、という認識に誤りはないでしょうか?  宜しくお願いします。

  • C言語のポインタのどのあたりが難しいと思いますか?

    C言語のポインタは初心者にとって大きな壁であるとよく言われます。 具体的にポインタのどのあたりが難しいと思いますか? 経験者の方、初心者の方などいろんな意見をお聞かせください。 ちなみに私もポインタでつまづいた人間の一人です。 つまづいた箇所は一箇所ではないのですが、例を挙げるなら int *p; p = &a; *p = a; で pと *pの違いがわからなかったですね…。\0とNULLの違いもごっちゃになっていましたし。 似たような表記だけど意味は違う、という箇所にひっかかっていました。 ポインタがどのようなものなのかというイメージは理解しやすかったのですが…。

  • ポインタのポインタ

    こんにちは。 C言語の「ポインタのポインタ」の学習中なのですが、以下のプラグラムがエラーが出てしまします。 if文が間違っていると思うのですが、具体的に何がどう間違っているのがわかりません。 ご教示お願いいたします! #include<stdio.h> int main(void) { int date = 300; int *pdate = NULL; int **ppdate = NULL; printf("dateの値は%d<\n", date); pdate = &date; printf("*pdateの値は%d<\n", *pdate); if(**ppdate = NULL){ printf("**ppdateには何も与えられていません。\n"); } else{ return 0; } ppdate = &pdate; if(**ppdate == 300){ printf("**ppdateの値は%dになりました。正常です。\n", **ppdate); } /*強制終了を避けるためのプログラム*/ int i; scanf("%d", &i); return 0; }

  • C++言語のポインタについて

    現在、C++言語を学習しているのですが、 ポインタを使わない、 int main() { Human human; human.Introduction(); } と、ポインタを使った、 int main() { Human *p; p = new Human; p->Introduction(); delete p; } があります。どちらも同じ動作をしますが、ポインタを使ったものがよく使われているのは、 メモリの節約?動作速度?のためなのでしょうか? なぜ使われるのか教えてください。

  • error C2105 CとC++の違い関して

    C++で動いていたプログラムをCに移行したらerror C2105が出力されました。プログラムは以下の通りです。   :  int *p;  int i;   : i = *(++p)++; //pを1つ進めて、内容を取り出し、さらにpを進めたい   :  C++では++Pの評価結果がポインタ変数となり、Cでは単なるアドレスになってしまっているようですが、  これは言語仕様の違いですか?  それとも、私のC++ではたまたま動いただけで、本来このような使用方法はしてはいけないのでしょうか?  私はVisualC++6.0を使用しています。また、上記でC++とCといっているのは、ファイルの拡張子をcppをcにしただけのことを言っています。どなたかご教授お願いします。

  • for(s=p; *p; p++)の*p(ポインタ)の意味

    for(s=p; *p; p++)の*p(ポインタ)の意味 C言語初心者です。 今ポインタを勉強しているのですが、 for文で上記のようなものが出てきて、意味が分からず困っています。 *pで*p != NULL と同じような意味になるみたいなのですが…。 どうしてそのような意味になるのでしょうか? ちなみにsとpはポインタで、 sには配列(入力した文字列)の先頭アドレスが入っています。 pは文字列を指していて○○○○○NULL ←になるから上記のような条件で 回るんだろうなぁとはなんとなく考えているのですが。