char
型には '\0'
や 'A'
など,int
型には10進数表記で 0
や16進数表記で 0x00
などの「定数」がありました(付録4.A)。ポインタ(char*
型や int*
型など)には,つぎの「定数」が用意されています。
0
や 0x00
など(void *)0
など,値0の整数定数を void *
にキャストした式NULL
stddef.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]