Search code examples
c++language-lawyerdereferencenull-pointer

Is member access on a null pointer defined in C++?


Is address computation on a null pointer defined behavior in C++? Here's a simple example program.

struct A { int x; };
int main() {
  A* p = nullptr;
  &(p->x);  // is this undefined behavior?
  return 0;
}

Thanks.

EDIT Subscripting is covered in this other question.


Solution

  • &(p->x);  // is this undefined behavior?
    

    Standard is a bit vague regarding this:

    [expr.ref] ... The expression E1->E2 is converted to the equivalent form (*(E1)).E2;

    [expr.unary.op] The unary * operator ... the result is an lvalue referring to the object ... to which the expression points.

    There is no explicit mention of UB in the section. The quoted rule does appear to conflict with the fact that the null pointer doesn't point to any object. This could be interpreted that yes, behaviour is undefined.

    [expr.unary.op] The result of the unary & operator is a pointer to its operand. ... if the operand is an lvalue of type T, the resulting expression is a prvalue of type “pointer to T” whose result is a pointer to the designated object ([intro.memory]).

    Again, no designated object exists. Note that at no point is the operand lvalue converted to an rvalue, which would definitely have been UB.

    Back in 2000 there was CWG issue to clarify whether indirection through null is undefined. The proposed resolution (2004), that would clarify that indirection through null is not UB, appears to not have been added to the standard so far.

    However whether it is or isn't UB doesn't matter much since you don't need to do this. At the very least, the resulting pointer will be invalid and thus useless.

    If you were planning to convert the pointer to an integer to get the offset of the member, there is no need to do this because you can instead us the offsetof macro from the standard library, which doesn't have UB.


    &(p[1]); // undefined?
    

    Here, behaviour is quite clearly undefined:

    [expr.sub] ... The expression E1[E2] is identical (by definition) to *((E1)+(E2)), except that in the case of an array operand, the result is an lvalue if that operand is an lvalue and an xvalue otherwise.

    [expr.add] When an expression J that has integral type is added to or subtracted from an expression P of pointer type, the result has the type of P.

    • If P evaluates to a null pointer value and J evaluates to 0 (does not apply)

    • Otherwise, if P points to an array element (does not apply)

    • Otherwise, the behavior is undefined.


    &(p[0]); // undefined?
    

    As per previous rules, the first option applies:

    If P evaluates to a null pointer value and J evaluates to 0, the result is a null pointer value.

    And now we are back to the question of whether indirection through this null is UB. See the beginning of the answer.

    Still, doesn't really matter. There is no need to write this, since this is simply unnecessarily complicated way to write sizeof(int) * i (with i being 1 and 0 respectively).