NULL
読み:ヌル
外語:NULL
C/C++の標準ライブラリが定義しているマクロで、NULLポインターを表わすもの。
概要
NULLマクロは、stdio.hなどをincludeすると定義される。
NULLポインターは、実装上の制約からアドレスであり、その環境内において、決して他に使用されることのない番地を用いることになる。
したがって、その値は環境によって異なる。
Cでは、移植性に配慮し、これをNULLマクロによって定義した。
特徴
0番地
殆どのプロセッサー環境で、そのアドレスは0番地が使われる。
まれに異なる番地を用いるシステムもあるが、Cの規格上、その場合も0番地と可換でなければならない。
結果として、NULLポインターは0と書いておけば、Cコンパイラーが自動的に対応してくれることになっている。
定義
Cでは、殆どの環境で次のように定義されている。
#define NULL ((void *)0)
詳細は後述するが、例えばFreeBSDのg++(GCC)はsys/_null.hに定義があり、stdio.hほか、様々なヘッダーファイルからincludeされている。
但し、C++ではこの定義をそのまま使えない。Cは、void *を任意のポインター型に代入できるが、C++はvoid *から他のポインター型に暗黙的な変換をせず、エラーになるからである。
C++では数値の0をNULLポインターとして使うことになったため、C++では次のように定義されている可能性がある。
#define NULL 0
この場合、数値の0と、ポインターの0との区別が付かない。
実装
FreeBSD
FreeBSD 9.1ではsys/_null.hに定義があり、stdio.hほか、様々なヘッダーファイルからincludeされている。
g++(GCC)やclang/LLVMには__nullという独自拡張の予約語が定義されており、NULLは__nullで定義される。__nullは環境に応じた長さになるため、代入で問題が発生しない。このような理由により、NULLポインターは、0と書くのではなくNULLと書く方が、総じて見て安全である。
sys/_null.hでの定義部分を全文引用すると、次のようになる。
#ifndef NULL
#if !defined(__cplusplus)
#define NULL ((void *)0)
#else
#if defined(__GNUG__) && defined(__GNUC__) && __GNUC__ >= 4
#define NULL __null
#else
#if defined(__LP64__)
#define NULL (0L)
#else
#define NULL 0
#endif /* __LP64__ */
#endif /* __GNUG__ */
#endif /* !__cplusplus */
#endif
これをみれば、次の四種類の定義があるのが分かる。
- Cの場合 … ((void *)0)
- C++で、g++バージョン4以上 … __null
- C++で、g++バージョン4以上ではなく、64ビットである … (0L)
- C++で、g++バージョン4以上ではなく、64ビットでもない … 0
Linux
Linuxのg++(GCC)では、環境によって変わるが、例えば次のような場所にstddef.hと定義がある。
/usr/lib/gcc/x86_64-linux-gnu/4.4/include/stddef.h
#ifdef __GNUG__
#define NULL __null
#else /* G++ */
#ifndef __cplusplus
#define NULL ((void *)0)
#else /* C++ */
#define NULL 0
#endif /* C++ */
#endif /* G++ */
これをみれば、次の三種類の定義があるのが分かる。
- GNU C++(__GNUG__)の場合 … __null
- GNU C++でなく、Cの場合 … ((void *)0)
- GNU C++でなく、C++の場合 … 0
再検索