2010年09月01日

nullptrに対する素朴な疑問

数日前Twitterでもtweetしたけど。

nullptrってなんぞ?って話は、まぁ、C++0x - nullptr - Faith and Brave - C++で遊ぼうあたり参照。

『ヌルポインタを表すキーワード』の必要性は分かる。でもそれのために、わざわざnullptrなんて新キーワードを作る必要性があったんだろうか。既に在るNULLをキーワードにするんじゃ駄目なんだろうか。

標準ライブラリ内で定義されている#defineマクロをキーワードに昇格(?)させるんじゃ駄目なのかなぁ。C言語ではただのtypedefだったwchar_tをC++ではキーワードにしたみたいに。

そりゃ、マクロっぽい名前でキモイ、という欠点はあるけど。

既存のソースコードとの衝突を忌避したとしても、標準ライブラリで用意されている物と同じ名前の物を定義しているプログラムなんて在るのかなぁ。仮に在ったとしても、『ヌルポインタ』以外の意味で定義しているプログラムとか存在するのか?

……という、素朴な疑問。
多分、何らかの理由が有っての事だろうけど、その理由が分からない。誰か知ってる人教えて。

ラベル:C++ C++0x
posted by 天井冴太 at 03:14| Comment(10) | TrackBack(0) | Question | 更新情報をチェックする
この記事へのコメント
nullptrの元の提案の文書に書いてあります(5ページ目)。
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf
英語あんまり得意ではないですが、1. 昔のコードでアウト 2. NULLが0であることに依存したコード 3. #ifdefなんかで使えなくなるなどと書いてあるようです。

昔のコードってのは自前で#define NULL 0などと書いているもののことだと思います。
0であることに依存ってのは、char c = NULL; ('\0'でしょ)なんてやっちゃっているような類のもののことだと思います。
Posted by egtra at 2011年01月19日 18:42
おお、egtraさん有り難う御座います。
N2431読んでみた感じでは、標準化委は結構神経質といえる程既存コードとの衝突を危惧しているように感じました。
(この記事で書いたように)『標準にあるのと同名でかつ中身の違うマクロ定義しないだろJK』じゃなく、『いや、もしかしたら……』と。
Posted by 天井冴太 (AmaiSaeta) at 2011年01月20日 15:24
大昔、C言語を独学した者です。で、64ビットの現在ではNULLや文字コードの扱いはどうなるのだろう?と、好奇心で調べてみましたら、びっくりしたことに、文字列の終端の'\0'をセットするのにNULLを代入するプロが結構いるのです。私なりに推論しました。
(1)C言語ではNULLは0または((void*)0)に展開されるのがほとんど。
(2)c++言語では後者の展開はできない。何故なら、(void *)型の値を通常のポインタに代入するためには、キャストが必要だから。つまり、
char *p = NULL;
はコンパイルエラーになる( (char*)NULL としないといけなくなる)。現在はC++優勢なので、C言語のつもりでコードを書いても、拡張子がcppなどだとC++ソースと見なされるので、NULLはほとんど0に展開される。
(3) 誰がやり始めたのか分からないが、文字列の終端の'\0' を「NULL文字」と呼ぶことが多くなった。これは、「C言語」+「NULL文字」でググればぞろぞろ出てきます。また、本屋でもよく見かけます。そう書いてあれば、文字列を生成するときに、最後に'\0'を代入する代わりにNULLを代入する人は沢山出てくる。

ということで、char型の左辺値にNULLを代入したり、ポインタでない値とNULLを比較するコードは現在溢れているので、NULLをキーワードにすることはおろか、NULLをnullptrに展開することも、うかつにできなくなった。

・・・と思われます。
Posted by 通りすがり at 2012年08月08日 03:49
通りすがりさん、コメント有り難う御座います。

> char型の左辺値にNULLを代入したり、ポインタでない値とNULLを比較するコードは現在溢れているので
成る程、それもありそうですね……
勿論、'\0'が0と同値であるとは規定されていないので、そんなコード書く方が悪いのですが、これもまた標準化委の神経質さに拠るものなのでしょうかね。

因みに、
> (3) 誰がやり始めたのか分からないが、文字列の終端の'\0' を「NULL文字」と呼ぶことが多くなった。
'\0'を「NULL文字」と呼ぶのは正式な物のようです。JIS C、及びJIS C++規格(JIS X3014:2003の8頁、JIS X3010:2003の12頁。C++11相当の規格文書は未確認ですが)上に、「ナル文字 (null character)」という記述が見られますし。
Posted by 天井冴太 at 2012年08月10日 02:37
どうも、レスありがとうございます。
いや、何しろMS-DOS時代に勉強したので、現在は事情が違うのかと、ANSI規格書を取り寄せているところです。(そもそも、現在は64ビット環境が優勢になってきているのだから、0Lとか0LLとかになってもおかしくないけど。)
大文字の「NULL文字」という言葉が正式名称になっているのでしょうか? ナル文字とかヌル文字とかnull文字ならいいのですが、大文字のNULL文字と書かれると、当然NULLマクロを使っていい、と思う人は沢山出ると思います。実際、プロでそういうコードを堂々と出してるのを見ました。酷いケースとしてmainで return NULL; とかやってるのも見たことがあります。お気に入りに残ってればいいのですが。
書籍でも汎用OSで使うことを前提とした本にはNULLと'\0'を区別している傾向がありますが、組み込みソフト関係の本では区別をしてなかったりあいまいな傾向があるようです。プロは後者の方を買いやすいですよね。

もしかして、VC++に問題がないか?と、DLしてみましたが、ソースのデフォルトがcppファイルなのが問題かもしれません。拡張子をcにしてソースを書けば、NULLは((void*)0)に展開されるようですが、「c++はCの上位言語だから、問題ない」と、拡張子がcppのままC言語ソースを書いて・・・しかし、C++としてコンパイルするから、NULLは0になる。で「要するに、NULLは0なんだ」という前提でコードを書くと。

一応、char *p=0; とやると、pはナルポインタになることは保障されるが、pの内部表現が0とは保障されない、というところまでは分かってます。しかし、これだと、コンパイル時点で「ポインタのあるべき場所に0がある」と分かる場合でないと可搬性がなくなりますし、「0番地」が意味ある場所(例えば、restartコードがそこに書かれてるマシンとか)で、そこをあえてアクセスしたい場合に困ります。何でK&Rはこんな仕様にしたのでしょう?
基本的に、私はナルポインタが0で表されるとは、「0番地」というより、0はC言語でナルポインタを表す「マジックナンバー」と思ってます。
Posted by 通りすがり at 2012年08月13日 02:06
> 大文字の「NULL文字」という言葉が正式名称になっているのでしょうか?
ああ、そうですね。確かにJISの規格文書のそれは"null"ですね。"NULL文字"或いは"NULL character"という表記が"ナル文字"以外の用法として使われているかですが、C言語(JIS X3010:2003)には規格書の索引を見た限りでは無さそうです(C++(JIS X3014:2003)には索引がないので流石に探しきれませんでした……)。"NULL"と大文字で表記されているのはNULLマクロぐらいのようですね。

> 酷いケースとしてmainで return NULL; とかやってるのも見たことがあります。
確かにC++においては、NULL==0なので通ってしまいますね……恐ろしい……

> 「0番地」が意味ある場所(例えば、restartコードがそこに書かれてるマシンとか)で、そこをあえてアクセスしたい場合に困ります。何でK&Rはこんな仕様にしたのでしょう?
ポインタ指すアドレス値は、実際のメモリのアドレス値とは規定されてなかった筈です。なので、(C/C++プログラム上の)有意なアドレス値を1以上から割り当てれば、仮にメモリアドレス0番を指す必要が有るとしても問題は起こらないでしょう。
Posted by 天井冴太 at 2012年08月15日 03:17
ご丁寧なレスをありがとうございます。それも、いろいろ調べてくださったようで。すみません。
>有意なアドレス値を1以上から割り当てれば、
なるほど、それなら大丈夫ですね。
昔何かの本で、
void(*ReStart)() = NULL;
とやれば、リスタートのとき(*Restart)();すればよい、と嘯くプログラマもいて困る、と読んだことがあるので。
それにしても、そもそも最初からNULLをマクロにせずにキーワードにしておけば、こんな混乱はなかったのに。C++で((void*)0)が使えなくなったため、混乱が酷くなってしまいましたね。
文字列の終端をセットするためにもマクロNULLを使うプロの方に「このNULLの使い方はおかしいのでは?」と訊いたところ「NULLはヌルポインタとヌル文字の2つの意味がある。NULLが0でない場合など見たこともない。これが業界の常識だ」と言われたので、不安になって、初めてお会いする方にぶしつけなコメントを書いてしまいました。申し訳ありません。
天井様の記事を見て、私のNULLの理解で大丈夫そうだ、と分かり、ほっといたしました。
とはいえ、業界の常識とかいうプロがいる以上、NULLを空ポインタ以外の意味で使ってるプロが沢山いることは、多分間違いないと思います。
Posted by 通りすがり at 2012年08月15日 13:46
> それも、いろいろ調べてくださったようで。すみません。
いえいえ、手元には最新のC++規格文書は無いもので、正直、「最新規格で変更されていたらどうしよう」とドキドキしながら書いている所もありますが ;-P

> そもそも最初からNULLをマクロにせずにキーワードにしておけば、こんな混乱はなかったのに。
そういえばそうですね。何故キーワードじゃないんだろう……Dennis Ritchie氏に聞いてみたいところです(既に他界されていますが)。

> 文字列の終端をセットするためにもマクロNULLを使うプロの方に(略)
"NULLはNULLポインタとNULL文字の双方の意味が有る"は完全に間違いですね。ただし、現在出回っている大部分のコンピュータにおいて'\0'==0が成り立つのも事実ではあります。勿論、そんなコードは「現在偶々動いている環境」以外での動作を保証出来ず、また、そのように放言なさる方や、ひいてはその所属する会社の技術に対する信用度を落とす事になるのですが……
悲しい事ですが、
> NULLを空ポインタ以外の意味で使ってるプロが沢山いることは、多分間違いないと思います。
というのが実際の所なんでしょうね……
Posted by 天井冴太 at 2012年08月16日 02:31
どうも、長文のコメを連続投稿してしまい、申し訳ありませんでした。
私も「もしかして、現在では規格が変わったのか?」とパニックになっていたもので。VC++2010でのC++0Xに関する説明
http://msdn.microsoft.com/ja-jp/magazine/ee336130.aspx
で、
>ただし、NULL はコンパイラで引き続きサポートされていて、まだ nullptr に置き換わっていません。これは主に、NULL が広範囲にわたって使用され、また不適切に使用されていることが多いので、既存のコードが機能しなくなることを避けるためです。しかし、将来的には、NULL を使用する場所では nullptr を使用し、下位互換性をサポートする機能として NULL を扱うことになります。

という部分を見つけました。たぶん、これからは、ナルポインタはnullptrを使う方向に進み、NULLは過去の互換性を保つために残される、と言う形のようです。
Posted by 通りすがり at 2012年08月16日 16:38
> 長文のコメを連続投稿してしまい、申し訳ありませんでした。
いいえー、むしろ当blogを盛り上げて頂き此方がお礼を言いたい程です。
# 実際、コメント貰えるとblogのモチベーション上がるので大歓迎ですよ。
Posted by 天井冴太 at 2012年08月16日 20:47
コメントを書く
コチラをクリックしてください

この記事へのトラックバック