(Note: the quick answer to the question in the subject is "no".)
The following code gives error C2105: '++' needs l-value.
char buf[20];
buf[n]++ = 'A';//buf[n]++ isn't a lvalue
Why buf[n]++ isn't a lvalue?
The term "lvalue" has a somewhat bizarre set of definitions depending
on which C standard you use, but in any case, the expression:
buf[n]++
parses the same way as:
(buf[n])++
which means "find the object named buf[n], obtain its current value,
add 1 to that value, arrange to store the new sum back into buf[n]
by the next sequence point, and produce as the value of the expression
the value buf[n] had before any update occurs". The resulting value
is purely a value -- it does not name an object.
Note that buf[n] in turn is defined as *(buf + n); this will matter
in a moment...
If buf[n]++ isn't a lvalue, then why the following code works?
void func(char *a, char *b)
{
while(*a++=*b++){}
}
The expression:
*a++
is parsed the same way as:
*(a++)
Note: I am avoiding the term "precedence" as it leads people into
the trap of thinking: "precedence, that means the same as precede,
so that must mean that the whole `preceding' thing is done first,
then the whole `following' thing, in exactly that order". This is
not the case: "operator precedence" merely determines a "parse" at
compile time. In effect, it says where the parentheses would be
if there were parentheses. The actual *runtime* order of evaluation
is much less constrained.
Now, *(a++) is made up of two main pieces, one being "a++" and the
other being "*(expression)". The first piece -- "a++" -- works
pretty much the same as "buf[n]++": it locates the object named
"a", finds a's current value, computes a+1, schedules a+1 to be
stuffed back into the object by the next sequence point, and
produces the old value of "a". This is the value (not an object,
not an lvalue) handed to the unary "*" operator. The unary "*"
operator requires a pointer value -- which is the kind of value
"a++" produced -- and is defined to mean "follow that pointer to
the object to which it points".
It is this last step -- the "follow the pointer" step -- that
locates an object, or in the Standard's terms, gives you an "lvalue".
In other words, the unary * operator only needs a value, and produces
an object (or "lvalue"). This is just the opposite of "++", which
needs an object but produces a mere value. Objects hold values;
values are wispy, evanescent things that usually have to be stored
back into objects right away, before they evaporate.
Now, if buf[n] "means" *(buf + n), and if "*" just needs a value,
then as long as buf+n produces a suitable value, all is good. The
buf+n part of course uses the "+" operator, which needs *two*
values, but again, it just needs values, not actual objects. If
you want buf and/or n to change by the next sequence point, you can
embed one or more "++" operators here:
*(buf + n++)
which parses as:
*((buf) + ((n)++))
which tells the compiler to find the value of "buf", find the value
of "n", compute n+1, schedule n+1 to be stuffed back into n, compute
"value of buf plus original value of n", and give that to the "*"
operator as a pointer to follow. Exactly when "n" changes is never
specified -- it might even change *first*, by doing something like:
n++;
*(buf + (n - 1))
-- but you are assured that n will be updated by the next sequence
point. (This is why "sequence points" are such a big deal; for
more about them, consult the FAQ.)
And, since a
always "means" *((a) + (b)), you can rewrite
*(buf + n++) as buf[n++], which is no doubt what you intended to
use way back at the beginning. If you want to confuse the next
person to maintain your code, you can even use the commutative
property of addition to change *(buf + n++) into *(n++ + buf),
and rewrite that as n++[buf].