以下のコード例では、NULLポインタの間接参照は発生しないことが規格C99で保証されている。
&*(int *)NULL; /* &演算子も*演算子も評価されない */
&((int *)NULL)[0]; /* &演算子と、[]演算子が暗に含む*演算子は評価されない */
ただし、C89においては、上記を担保する明示的な記述は存在しない(gcc 4.8.2で、-std=c89オプションを指定してコンパイルしたプログラムを実行してみたところ、コンパイル時エラー、実行時エラーともに発生しなかった)。
ちなみに、以下は&演算子のオペランドが関数指示子でも左辺値でもないため、当然、文法的に誤りである。
*&NULL
実行中に評価されない式として、他に以下がある。
- sizeof演算子(オペランドがC99の可変長配列である場合を除く)。
- 論理AND演算子において、第1オペランドの評価結果を0と比較して等しい場合の第2オペランド。
- 論理OR演算子において、第1オペランドの評価結果を0と比較して等しくない場合の第2オペランド。
- 条件演算子において、第1オペランドの評価結果を0と比較して等しい場合の第2オペランド。および、第1オペランドの評価結果を0と比較して等しくない場合の第3オペランド。
JIS X3010:2003 (ISO/IEC 9899:1999)
6.5.3.2 アドレス及び間接演算子
意味規則 単項&演算子は,そのオペランドのアドレスを返す。オペランドが型“∼型”をもっている場合,結果は,型“∼型へのポインタ”をもつ。オペランドが,単項*演算子の結果の場合,*演算子も&演算子も評価せず,両演算子とも取り除いた場合と同じ結果となる。ただし,その場合でも演算子に対する制約を適用し,結果は左辺値とならない。同様に,オペランドが[]演算子の結果の場合,単項&演算子と,[]演算子が暗黙に意味する単項*演算子は評価されず,&演算子を削除し[]演算子を+演算子に変更した場合と同じ結果となる。これら以外の場合,結果はそのオペランドが指し示すオブジェクト又は関数へのポインタとなる。
単項*演算子は,間接参照を表す。オペランドが関数を指している場合,その結果は関数指示子とする。オペランドがオブジェクトを指している場合,その結果はそのオブジェクトを指し示す左辺値とする。オペランドが型“∼型へのポインタ”をもつ場合,その結果は型“∼型”をもつ。正しくない値がポインタに代入されている場合,単項*演算子の動作は,未定義とする(83)。
(83) &*EはEと等価であり(E が空ポインタであっても),&(E1[E2])は((E1)+(E2))と等価である。Eが単項&演算子の正しいオペランドとなる関数指示子又は左辺値の場合,*&E はEに等しい関数指示子又は左辺値である。
単項*演算子によるポインタ参照をする場合の正しくない値には,空ポインタ,指されるオブジェクトの型に対して正しく境界調整されていないアドレス,そしてその生存期間が終了してしまっているオブジェクトのアドレスなどがある。
ISO/IEC 9899:TC3 (WG14/N1256)
6.5.3.2 Address and indirection operators
Semantics
3
The unary & operator yields the address of its operand. If the operand has type ‘‘type’’, the result has type ‘‘pointer to type’’. If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue. Similarly, if the operand is the result of a [] operator, neither the & operator nor the unary * that is implied by the [] is evaluated and the result is as if the & operator were removed and the [] operator were changed to a + operator. Otherwise, the result is a pointer to the object or function designated by its operand.4
The unary * operator denotes indirection. If the operand points to a function, the result is a function designator; if it points to an object, the result is an lvalue designating the object. If the operand has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined. 87)
87)
Thus, &*E is equivalent to E (even if E is a null pointer), and &(E1[E2]) to ((E1)+(E2)). It is always true that if E is a function designator or an lvalue that is a valid operand of the unary & operator, *&E is a function designator or an lvalue equal to E. If *P is an lvalue and T is the name of an object pointer type, *(T)P is an lvalue that has a type compatible with that to which T points.Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer, an address inappropriately aligned for the type of object pointed to, and the address of an object after the end of its lifetime.