B
Ben Bacarisse
Keith Thompson said:No, '*p' is an lvalue, regardless of the value of p. ('p' is also an
lvalue, but that's beside the point). But using '*p' in a context
that requires an lvalue invokes undefined behavior.
The lvalue-ness of an expression cannot depend on the run-time value
of any object. There are contexts that require lvalues, and the
determination of whether something is an lvalue must be made at
compilation time, since the failure to provide an lvalue is a
constraint violation, requiring a compile-time diagnostic.
For example:
int *p;
/* ... */
*p = 42;
The assignment is legal (not a constraint violation) because '*p' is
an lvalue (specifically a modifiable lvalue) regardless of what the
value of 'p' happens to be. If '*p' does not designate an object when
the assignment is evaluated (e.g., if p == NULL), the behavior is
undefined.
I can't provide chapter and verse, because the standard gets this
wrong. (This isn't just a disgreement; the wording in the standard
leads to logical contradictions.)
The confusion is understandable. As you say, the compiler must be
able to determine if the left operand of an assignment is a modifiable
lvalue:
6.5.16
...
Constraints
An assignment operator shall have a modifiable lvalue as its left
operand.
but in 6.5.3.2, * is defined like this:
... 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.)
In my opinion, it takes some linguistic hoop-jumping to read that to
mean that *p is also an lvalue when p does *not* point to an object
(when it is == NULL, for example). It could have been written:
... If the operand points to a function, the result is a function
designator; otherwise the result is an lvalue. If the pointer
points to an object, the resulting lvalue designates that 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.)
but its was not.
The C90 standard got this wrong by implying that lvalue-ness can
depend on run-time values. The C99 standard corrected that problem,
but went too far in the other direction, saying that any expression of
object type is an lvalue.
I see the current definition of * as a remnant of the old run-time
dependent meaning.