char 型には '\0' や 'A' など,int 型には10進数表記で 0 や16進数表記で 0x00 などの「定数」がありました(付録4.A)。ポインタ(char* 型や int* 型など)には,つぎの「定数」が用意されています。
0 や 0x00 など(void *)0 など,値0の整数定数を void * にキャストした式NULLstddef.h stdio.h stdlib.h string.h time.h wchar.h
[ノート]ISO規格C11(旧C99)の原文は,つぎの通りです。
[§6.3.2.3, 3] An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.
[§6.3.2.3, fn.66] The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant.
[§7.19, 3] NULL ... expands to an implementation-defined null pointer constant[.]
"a" や "an" とあるように,空ポインタ定数は,一つだけではありません。
その中でも,定数 NULL の定義は,処理系依存になっています。試しにヘッダ stddef.h を開いて,マクロ NULL を確認してみましょう。
[参考]奥山研究室のコンパイラ gcc では マクロ NULL が (void*)0 と定義されていましたが,他のコンパイラで同じように定義されているとは限りません。
これらの「定数」を使って,つぎの「空ポインタ」が出来上がります。
上記の空ポインタ定数がポインタ型(void* 型や char* 型,int* 型など)に変換されたとき,そのポインタを空ポインタ,あるいは,NULLポインタといいます。
[ノート]ISO規格C11(旧C99)の原文は,つぎの通りです。
[§6.3.2.3, 3] If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.
[§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.
/* Example 10.11 */
#include <stdio.h>
int main(void)
{
void *p = 0x0; // 右辺は空ポインタ定数なので,p は空ポインタ
int *q = 0x0; // 整数定数式 0x0 が空ポインタ定数か否かを(1)や(8)(9)で確認
int n = 0;
/* A */
printf("(A) p [%p]\n", p); // %p の出力は処理系依存;後出 13.1 参照
if(p == NULL) // (1) NULL は処理系依存の空ポインタ定数
// if文については,後出 14.3 参照
printf("OK, integer constant expression 0x0 "
"is a null pointer constant in your computer.\n");
else
printf("* WARNING\n"
"* Integer constant expression 0x0 may not be "
"with \"the value 0\" in your computer.\n"
"* Or, NULL may be incompatible with ISO:C11.\n");
/* B */
p = (void *)0; // (2) 右辺は空ポインタ定数なので,p は空ポインタ
// (void *)0 が空ポインタ定数か否かを(3)で確認
printf("(B) p [%p]\n", p);
if(p == NULL) // (3)
printf("OK, (void *)0 is a null pointer constant in your computer.\n");
else
printf("* WARNING\n"
"* Integer constant expression 0 may not be "
"with \"the value 0\" in your computer.\n"
"* Or, NULL may be incompatible with ISO:C11.\n");
/* C */
printf("(C) p [%p]\n"
" q [%p]\n", p, q);
if(q == (int *)p) // (4)
printf("OK, any two null pointers shall compare equal "
"[C11 \u00A76.3.2.3, 4; \u00A76.5.9, 6].\n");
else
printf("* WARNING\n"
"* Either integer constant expression 0x0 or 0 may not be "
"with \"the value 0\" in your computer.\n");
/* D */
q = &n; // (5) 右辺は空ポインタ定数でもなければ,空ポインタでもないので,
// q は空ポインタではない
printf("(D) q [%p]\n", q);
if(q != NULL) // (6)
printf("OK, q is not a null pointer.\n");
else
printf("* WARNING\n"
"* NULL may be broken.\n");
/* E */
p = 0x0; // (7)
printf("(E) p [%p]\n"
" q [%p]\n", p, q);
if(q != p) // (8)
printf("OK, q is not a null pointer.\n");
else
printf("* WARNING\n"
"* Integer constant expression 0x0 may not be "
"with \"the value 0\" in your computer.\n");
/* F */
printf("(F) q [%p]\n", q);
if(q != 0x0) // (9)
printf("OK, q is not a null pointer.\n");
else
printf("* WARNING\n"
"* Integer constant expression 0x0 may not be "
"with \"the value 0\" in your computer.\n");
return 0;
}
奥山研究室では gcc -Wall で no errors & no warnings,つぎの実行結果を得ます。
* UTF未対応のターミナルでの実行の場合には,§ が \u00A7 と出力されたり,文字化けするかもしれません(その場合は,上記プログラムの中の \u00A7 を削除してからコンパイルしてください)。Macユーザの方は,Terminal.app にて実行してみてください。
(A) p [0x0]
OK, integer constant expression 0x0 is a null pointer constant in your computer.
(B) p [0x0]
OK, (void *)0 is a null pointer constant in your computer.
(C) p [0x0]
q [0x0]
OK, any two null pointers shall compare equal [C11 §6.3.2.3, 4; §6.5.9, 6].
(D) q [0x7fff5142bb8c]
OK, q is not a null pointer.
(E) p [0x0]
q [0x7fff5142bb8c]
OK, q is not a null pointer.
(F) q [0x7fff5142bb8c]
OK, q is not a null pointer.
Example 10.11の(2)(5)(7)には,単純代入演算子 = が使われています。ポインタに対する文法は,つぎの通りです。
= 上のポインタの使用制限[C11(旧C99)§6.5.16.1, 1]void へのポインタ_Bool型,右辺がポインタなお,void へのポインタには,つぎの変換法則があります。
void へのポインタは,いかなるオブジェクトへのポインタにも変換でき,逆も可能[C11(旧C99)§6.3.2.3, 1]
上記(1)(3)(4)(6)(8)(9)の if文内の等価演算子 == や != に対するポインタの使用については,つぎの文法があります。
== と != 上のポインタの使用制限[C11(旧C99)§6.5.9, 2]void へのポインタ
void へのポインタの場合,もう一方のオブジェクトへのポインタは void* 型に変換されます[C11(旧C99)§6.5.9, 5]
3つ目の文法での空ポインタ定数(NULLポインタ定数)は,もう一方のオペランドのポインタ型に変換されます[C11(旧C99)§6.5.9, 5]。よって,空ポインタとして,もう一方のオペランドのポインタと比較されます。
等価演算子 == においてポインタ同士が「等しい」と判定される,すなわち,演算式が 1 を返すのは,つぎのケースです。
[ノート]ISO規格C11(旧C99)の原文は,つぎの通りです。
[§6.5.9, 6] Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.
最後に,関係演算子上の文法を再述 (7.4) しておきます。
< <= >= > 上のポインタの使用制限[C11(旧C99)§6.5.8, 2]