ヌルポインタ
ヌルポインタ(英: null pointer 英語: [nʌl pɔɪntə(ɹ)])とは、何のオブジェクトも指していないことを表す特別なポインタの値である。
プログラムではヌルポインタを、不定長のリストの終端を表したり、何らかの動作の結果が失敗であることを表したりするのに使用する。後者の用法は、nullable型やオプション型の Nothing 値(None 値)を使用することもできる。
ヌルポインタの値や型がいかなるものかという詳細は言語によって異なる。実際的にはいかなるオブジェクトも参照しないという言語もあり、参照先を求めようとするとJava(NullPointerException)のように例外が発生するものもある。
ヌルポインタはほとんどの処理系(この場合、言語処理系プログラムだけではなく、ハードウェアまでを含めて)で、内部的に数値0で表現されるが、ごく希に、0でない処理系もある。言語仕様上の意味としては普通「アドレス0(あるいは他のアドレス)を指し示すポインタ」ではなく、どこも指し示さないものとされる。
ヌルポインタを未初期化のポインタと混同してはならない。ヌルポインタは、あらゆる有効なオブジェクトとも異なることが保証されている。それに対し、言語や実装によっては、未初期化のポインタはそのような保証はなく、C言語の自動変数のようにでたらめなアドレス(不定値)を指している可能性もある。
ヌルポインタはヌル値とは意味が違う。ヌルポインタは多くのプログラミング言語において「値がない (no value)」ことを意味し、ヌル値はリレーショナルデータベースにおいて「未詳値 (unknown value)」であることを意味する。ほとんどのプログラミング言語では2つのヌルポインタは等しいが、リレーショナルデータベースエンジンは2つのヌル値を等しいとはみなさない(それらは未詳値を表しているので、それらが等しいかどうかはわからない)。
C言語
[編集]C言語では、すべての型の2つのヌルポインタは等しいことが保証されている[1]。また、マクロ NULL
が処理系定義のヌルポインタ定数 (null pointer constant) に展開されるものと規定されている[2]。C89/C90 (ISO/IEC 9899:1990) や C99 (ISO/IEC 9899:1999) では、NULL
はヌルポインタ定数を表現するマクロとして定義され、多くの実装においてヌルポインタ定数は整数定数の 0 もしくは 0 を汎用ポインタ void *
に型キャストしたものとして定義されている[3]。C23 (ISO/IEC 9899:2024) ではキーワード nullptr
がヌルポインタを表す、左辺値とならない定数値として追加され、その型も nullptr_t
として追加される予定である。規格上は、依然としてマクロNULL
は処理系定義のヌルポインタ定数に展開されるものであることには変わりない[4]が、nullptr
に展開される実装がありえることになる[3]。
C言語における汎用ポインタ型はあらゆるポインタ型に暗黙的変換可能となっているため、ポインタ型変数にNULL
を代入する際は型キャストの記述が不要となる。POSIX.1-2008 では整数定数の 0 ではなく、0 を void *
に型キャストした物、(void *) 0
でなければならないと定義していて、rationale にて、整数の 0 ではあってはならないと、その定義の差を明記している[5]。よって、ほぼ全ての標準Cライブラリは NULL
を (void *) 0
としている。そして、C89, C99 では、ヌルポインタ定数を、(void *
以外を含めた)ポイント型に型キャストした物をヌルポインタと定義している。ほとんどの実装はそのまま変換するが、C89, C99 の仕様では、整数からポインタ、ポインタから整数への型キャストがどのような結果になるかは実装依存と明記されていて、ポインタに 0 や NULL
を代入したときに、ほとんどのコンパイラは数値の 0 を代入したというコンパイル結果を生成するが、数値の 0 以外を代入するというコンパイル結果を生成しても良いという仕様になっている[6]。整数の 0 をポインタにキャストするとヌルポインタとなることは規定されているが、ヌルポインタを整数にキャストしても 0 になることは規定されていない。存在するオブジェクトや関数へのポインタはヌルポインタであってはならないと規定されている。近年[いつ?]はほとんど無いが、歴史的には、ヌルポインタを 0 以外で表現することもあった[7][8]。
ヌルポインタを参照して、そのメモリアドレスに読み書きしようとすると、それは何も指していないため、メモリ保護の仕組みが存在する実行環境では、通常セグメンテーション違反やアクセス違反が発生する。この場合、Unix系ではシグナル、Windowsでは構造化例外処理による例外が生成され、特に対処しなければ当該のプロセスは異常終了(クラッシュ)となる。一方、メモリ保護のない実行環境、例えばx86のリアルモードでは、アドレス0000:0000は読み書きが可能であり、ヌルポインタを参照したメモリアクセスは成功しクラッシュはしないものの、不定の期待しない動作になる。ただし、ヌルポインタへの参照があえて定義されている場合もある。例えば、16ビットのリアルモードx86デバイスのためにCで記述されたBIOSコードは、ヌルポインタを参照することにより、物理アドレス0にInterrupt Descriptor Table(IDT) を書く場合がある。ヌルポインタの参照をしないようにコンパイラが最適化することもできる。それにより、セグメンテーション違反を避けられるが、他の望ましくないふるまいを引き起こす。
C++
[編集]C++では、C言語よりも型チェックが厳密になり、汎用ポインタvoid *
をあらゆるポインタ型に暗黙的変換することが不可能になった。代わりに、整数リテラル 0 がヌルポインタ定数を意味するという仕様になった[9]。C言語からの移植を容易にするためなどの目的で、NULL
マクロ自体は依然として標準ヘッダーで定義され、実装定義のヌルポインタ定数に展開される仕様となっているが[10]、前述の理由から#define NULL ((void*)0)
と定義してしまうと、void*
以外のポインタに代入する際は明示的な型キャストが必要になるため、C言語とは異なり整数リテラル0
に展開される実装となっていることが多い。Cと共有されるヘッダーでは、通例__cplusplus
の定義有無でNULL
の定義が切り替わる実装となっている。
ただし、整数リテラル 0 をヌルポインタとみなすという仕様は、関数オーバーロードやテンプレート関連でしばしば問題を引き起こす結果となった。C++11では代替として、std::nullptr_t
という型を持ち、明確にヌルポインタであることを表すnullptr
リテラルが導入された[11]。C++11以降でもNULL
マクロは引き続き利用可能だが、整数リテラル0
に展開される実装とは限らず、nullptr
に展開される実装もありえる[12]。
その他の言語
[編集]Pascalでは綴り記号[13] nil
である。nil
は、空の値を表す[14]。
そのほかにも参照型の概念を持つ言語には、用語やキーワードこそ違えど同様なものが存在する。例えばJavaやC#ではnull
、C++/CLIおよびC++11ではnullptr
、Visual BasicおよびVisual Basic .NETではNothing
、PythonではNone
、Objective-CとRubyではnil
、REALbasicではNil
がヌルポインタに相当するキーワードである。ポインタではなく参照を利用する言語の場合、概念上ヌルポインタに相当するものはヌル参照 (null reference) と呼ばれる。
歴史
[編集]2009年にアントニー・ホーアは、彼が1965年にALGOL Wの一部としてヌル参照を発明したと述べている[15]。2009年のカンファレンスでホーアはこの発明を「10億ドルの誤り」と述べた。
それは10億ドルにも相当する私の誤りだ。null参照を発明したのは1965年のことだった。当時、私はオブジェクト指向言語 (ALGOL W) における参照のための包括的型システムを設計していた。目標は、コンパイラでの自動チェックで全ての参照が完全に安全であることを保証することだった。しかし、私は単にそれが容易だというだけで、無効な参照を含める誘惑に抵抗できなかった。これは、後に数え切れない過ち、脆弱性、システムクラッシュを引き起こし、過去40年間で10億ドル相当の苦痛と損害を引き起こしたとみられる。
脚注
[編集]- ^ ISO/IEC 9899, §6.3.2.3, ¶4: Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal.
- ^ ISO/IEC 9899, §7.17, ¶3: NULL... which expands to an implementation-defined null pointer constant...
- ^ a b NULL - cppreference.com (C)
- ^ ISO/IEC 9899:2024 (en) — N3220 working draft, §7.21, ¶4
- ^ stddef.h - standard type definitions - The Open Group Base Specifications Issue 7
- ^ Can a conforming C implementation #define NULL to be something wacky - Stack Overflow
- ^ comp.lang.c FAQ list - Question 5.17
- ^ “C FAQ 5.17 ヌルポインターに0以外の値を使用するマシンや、異なる型のポインターに異なる内部形式を持つマシンは本当に存在するのか。”. C言語 FAQ 日本語訳. 2021年4月12日閲覧。
- ^ Stroustrup, Bjarne (March 2001). “Chapter 5: Pointers, Arrays, and Structures: 5.1.1: Zero”. The C++ Programming Language (14th printing of 3rd ed.). United States and Canada: Addison–Wesley. p. 88. ISBN 0-201-88954-4. "In C, it has been popular to define a macro
NULL
to represent the zero pointer. Because of C++'s tighter type checking, the use of plain 0, rather than any suggestedNULL
macro, leads to fewer problems. If you feel you must defineNULL
, useconst int NULL = 0;
const
qualifier (§5.4) prevents accidental redefinition ofNULL
and ensures thatNULL
can be used where a constant is required." - ^ ISO/IEC 14882:1998, C.2.2.3 Macro NULL
- ^ nullptr - cpprefjp C++日本語リファレンス
- ^ NULL - cppreference.com (C++)
- ^ word symbol、すなわち予約語。『PASCAL 原書第4版』§1.2
- ^ 『PASCAL 原書第4版』§8.1
- ^ Tony Hoare (2009年8月25日). “Null References: The Billion Dollar Mistake”. InfoQ.com. 2016年3月9日閲覧。
外部リンク
[編集]- Joint Technical Committee ISO/IEC JTC 1, Subcommittee SC 22, Working Group WG 14 (2007-09-08) (PDF). International Standard ISO/IEC 9899.
- C言語FAQ日本語訳 ヌルポインタ