Chris Torek said:
Richard Heathfield wrote:
[Array-to-pointer] decay happens only in value contexts,
Well, I must admit I was getting it from Chris Torek's constant incantations
of "The Rule", and perhaps I was over-interpreting it. The fact remains
that the conversion you expect is not guaranteed by the Standard for the
simple reason that the code itself violates a constraint.
Indeed, "The Rule" and the C standards (both of them) are not
literally the same -- but they *are* isomorphic, i.e., both give
the same result. I think my formulation is easier to understand,
as well, because we need the idea of "value context" vs "object
context" in the first place, so that we can figure why:
int x, b;
...
x = b;
copies b's *value* to the *object* named x.
If x used to be 3 before the assignment, and b is 7, why does this
set x to 7, instead of setting b to 3, or setting 3 to 7, or setting
7 to 3? (Of course, only two of those four possibilities even make
sense.) The answer is that the "=" operator demands an object on
its left, and a value on its right. The context on the left of
the "=" is an "object context", and the context on the right is a
"value context". Put down a value when something needs a value
and there is no problem:
x = 3;
Here x is an object and 3 is a value (of the correct type), so this
sets x to 3. Name an object on the right, though, and the operation
automatically fetches the object's value:
x = b;
so if b was 7, x becomes 7 too.
Once you understand the idea of "object context" and "value context",
you simply have to memorize which operators have which context(s):
&foo - object context
sizeof foo - object (or maybe even "sizeof") context
foo + bar - two value contexts
foo = bar - one object context, one value context
++foo - object context
and so on.
A difficulty with the above is that the operand of sizeof
isn't an object context: any expression can be used as the
operand of sizeof [1], not just an lvalue expression. Also,
note a difference in behavior: usually,
sizeof &*p == sizeof p
if 'p' is a pointer (eg, to int), but (again, usually)
sizeof &*array != sizeof array
if 'array' is an array, even though both equalities hold if
the 'sizeof's are taken out.
Of course, it's important to understand the distinction
between "object context" and "value context", but trying to
use that distinction to explain what happens with arrays in
conjunction with sizeof seems like a strain. An expression
of array type simply behaves differently than expressions of
other types. It isn't that hard to remember the exception
cases for when an array expression doesn't convert to a
pointer; there are after all only three of them.
Incidentally, note that the distinction between object
context and value context also doesn't work very well for
the third exceptional case for array-type non-conversion,
which is that of a string literal used to initialize an
array:
char *p = "blah"; /* conversion */
char s[] = "blah"; /* no conversion */
On the face of it both instances of "blah" look like they
ought to be object contexts.
=====
[1] Subject to the usual need for parentheses around
operators of lower precedence, and the need to avoid
function types and incomplete types.
Given all of that, applying The Rule becomes easy, and whenever
the result is not "this makes no sense and a diagnostic is required",
it gives the same result as the more complicated way the C standards
put it.
It seems easier (at least to me) to remember that array
expressions always convert to pointers, except when they are
the operand of sizeof or address-of operators (or string
literals used to initialize an array).
That the conversion yields a value, rather than being an
object designator, suffices to explain (and without having
to remember anything extra) why an array expression can't be
assigned to or incremented, any more than the value of a
function call
f() = 3; /* WRONG! */
f() ++; /* WRONG! */
can be assigned to or incremented. Just knowing that
the array conversion is happening is enough to explain
and understand the behavior here.
Certainly there are places where the C standards documents
give explanations that are more complicated than they need
to be. IMO however the rule for when arrays are or aren't
converted to pointers isn't one of them.