If you have ever spent time digging through the official ISO C standards, you may have encountered one of the most puzzling footnotes in C specification history. It touches on the unary indirection operator (*), address-of operator (&), and null pointers.

Specifically, the standard notes that &*E is equivalent to E "even if E is a null pointer." Yet in the very next breath, the standard confirms that dereferencing a null pointer with the unary * operator results in undefined behavior (UB). How can both statements be true?

The Apparent Contradiction in the C Standard

In ISO/IEC 9899 (from C99 all the way up through C17 and C23), section 6.5.3.2 paragraph 4 describes the unary * operator:

"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."

Attached to this section is a famous footnote:

"Thus, &*E is equivalent to E (even if E is a null pointer) [...] Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer..."

This raises two natural questions:

  1. Can a null pointer actually be the operand of unary *?
  2. Does evaluating &*E invoke undefined behavior if E is null?

The Missing Piece: The Normative Cancellation Rule

The confusion usually arises because footnotes are informative rather than normative. However, the footnote is not making up an exception out of thin air—it is referencing a very explicit normative rule found earlier in the same section.

Look at 6.5.3.2 Paragraph 3 (address-of operator &):

"The unary & operator yields the address of its operand. 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."

This single sentence changes everything. When the compiler encounters &*E, neither & nor * is evaluated at runtime. The operators essentially cancel each other out syntactically before any pointer dereference can occur.

int *p = NULL;
int *q = &*p; // Perfectly valid C99/C11/C17/C23! No UB.

Because the unary * operator is never actually evaluated, the null pointer is never dereferenced, and undefined behavior is completely avoided.

What Happens If You Use *p Alone?

If you don't precede it with the & operator, evaluating a null pointer with unary * is strictly undefined behavior in C:

int *p = NULL;
*p; // Undefined Behavior!

Even if you do not read from or write to the memory (i.e. even if there is no lvalue-to-rvalue conversion), the mere evaluation of the unary * operator on an invalid value like NULL violates 6.5.3.2p4.

The Exception: Unevaluated Contexts

Just like with &*, a null pointer can appear as an operand to * inside unevaluated expressions such as sizeof or _Generic:

int *p = NULL;
size_t s = sizeof(*p); // Valid: *p is in an unevaluated operand

Because the operand to sizeof (for non-variable-length arrays) is not evaluated at runtime, *p does not cause a crash or undefined behavior.

Important Difference: C vs. C++

Be careful if you work across both languages! C++ does not have the &* cancellation rule that C has.

In C++, *p always yields an lvalue reference. Binding an lvalue reference to a dereferenced null pointer violates the core language standard rules (an lvalue must refer to a valid object). Therefore, in C++:

int* p = nullptr;
int* q = &*p; // Undefined Behavior in C++!

Many C++ compilers might optimize it away in practice, but strictly speaking, it is undefined behavior under the ISO C++ standard.

Summary

  • Can a null pointer be an operand of unary *? Only when the expression is not evaluated (e.g., inside sizeof(*p)) or when immediately preceded by & in C (e.g., &*p).
  • Does &*E result in UB when E is null? No, not in C99 and later. The standard explicitly states that in &*E, neither the * nor the & is evaluated.
  • Does evaluating *p by itself cause UB? Yes. Evaluating *p on a null pointer is always undefined behavior.