Is buf[n]++ a lvalue?

S

Stub

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? Is this according to C Standard or is there
some reason for this?

If buf[n]++ isn't a lvalue, then why the following code works?

void func(char *a, char *b)
{
while(*a++=*b++){}
}
 
C

Christopher Benson-Manica

Stub said:
char buf[20];
buf[n]++ = 'A';//buf[n]++ isn't a lvalue
Why buf[n]++ isn't a lvalue? Is this according to C Standard or is there
some reason for this?
If buf[n]++ isn't a lvalue, then why the following code works?
while(*a++=*b++){}

Because *a++ is the same as *(a++). a is a pointer and so it's fine to
increment it. buf[n], however, is a character, which you cannot increment.
 
A

Artie Gold

Stub said:
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? Is this according to C Standard or is there
some reason for this?

Why would you think that these two options are mutually exclusive?
;-). Things in the C standard *are* there for a reason!

Most likely, however, the expression you want is:

buf[n++] = 'A';
If buf[n]++ isn't a lvalue, then why the following code works?

void func(char *a, char *b)
{
while(*a++=*b++){}
}

Because here the *pointers* are dereferenced (*a is an lvalue,
yes?), the assignment is performed (yielding the expression's value)
and finally each pointer is incremented.

HTH,
--ag
 
R

Richard Heathfield

Christopher said:
buf[n], however, is a character, which you cannot
increment.

Yes, you can.


#include <stdio.h>

int main(void)
{
char buf[] = "3.1415926";
int n = 0;
buf[n]++;
printf("%s\n", buf);
return 0;
}
 
B

Ben Pfaff

Stub said:
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? Is this according to C Standard or is there
some reason for this?

Suppose buf[n]++ were an lvalue. Then buf[n]++ = 'A'; would
invoke undefined behavior because it would modify buf[n] twice
between sequence points: both the ++ and the = operator would
modify buf[n]. So you really don't want buf[n]++ to be an
lvalue.
If buf[n]++ isn't a lvalue, then why the following code works?

void func(char *a, char *b)
{
while(*a++=*b++){}
}

*a++ is equivalent to *(a++). a++ isn't an lvalue, but *(a++)
is.
 
N

Noah Roberts

Stub said:
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?

because buf[n]++ evaluates to the byte value contained in buf[n]. You
are essentially saying something like:

'x' = 'A'

and that just doesn't fly.

NR
 
C

Chris Torek

(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].
 
E

Eric Sosman

Christopher said:
Stub said:
char buf[20];
buf[n]++ = 'A';//buf[n]++ isn't a lvalue
Why buf[n]++ isn't a lvalue? Is this according to C Standard or is there
some reason for this?
If buf[n]++ isn't a lvalue, then why the following code works?
while(*a++=*b++){}

Because *a++ is the same as *(a++). a is a pointer and so it's fine to
increment it. buf[n], however, is a character, which you cannot increment.

Er, no: it's perfectly possible to increment a character.
Here's a silly way to output the decimal digits:

char c;
for (c = '0'; c <= '9'; ++c)
putchar (c);

The problem in the O.P.'s code is that the thing on the
left of the assignment is a value, not a variable (I'm using
loose terminology here; words like "lvalue" are Standardese,
used in a very precise but possibly confusing way). The
O.P.'s attempted assignment makes as much sense as

a + b = 'A';
sin(x) = 'A';
rand() = 'A';
x++ = 'A';

.... where all share the same characteristic: the thing on the
left produces a value, not a reference to something storable.
 
A

Arthur J. O'Dwyer

Here's a silly way to output the decimal digits:

char c;
for (c = '0'; c <= '9'; ++c)
putchar (c);

Here's a silly nitpick:

#define SCHAR_MAX '9'
#define CHAR_MAX SCHAR_MAX

Undefined behavior upon integer overflow.

#define UCHAR_MAX '9'
#define CHAR_MAX UCHAR_MAX

Infinite loop.
:)

-Arthur,
non-ASCII Fridays!
 
E

Eric Sosman

Arthur J. O'Dwyer said:
Here's a silly nitpick:

#define SCHAR_MAX '9'
#define CHAR_MAX SCHAR_MAX

Undefined behavior upon integer overflow.

#define UCHAR_MAX '9'
#define CHAR_MAX UCHAR_MAX

Infinite loop.
:)

I'm ashamed to admit it, but I actually thought
of this possibility when writing the code shown. That
means I've been hanging around comp.lang.c *much* too
long, and my once-mighty brain is now merely a dusty
attic over-filled with pedantic quibbles and nitpicks.

There was once a regular on c.l.c. who signed himself
"The Human Lint." Arthur and I seem to be in danger of
becoming clones of "The Human DeathStar 9000."
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top