Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

lvalues and rvalues

14 views
Skip to first unread message

Nicklas Karlsson

unread,
Apr 6, 2010, 3:37:07 PM4/6/10
to
Hello,

I am quite confused about this, so I split it in to subquestions, here
we go:

1. Is an lvalue the expression itself, or is it the result yielded by
evaluation of an expression? Take this for example: int i = 10; int *p
= &i; and then dereference: *p; is the expression "*p" the thing you
call an lvalue or is an lvalue the result yielded by evaluating that
lvalue-expression?

2. Rvalues, is rvalues the result yielded by an rvalue-expression, or
is it the expression itself? If its the result yielded by evaluation
of an rvalue-expression is an rvalue a plain value?

3. I am quite sure that in a conversion from C to Assembly an lvalue-
expression yields an an address when evaluated, however, in C an
lvalue seems to have properties, an address and a type, again, using
the example of *p, the lvalue will be of type "int", if an lvalue-
expression always yields an address when evaluated, C could keep track
of the addresses type, so if the lvalue (the address) is "0x0" and C
knows the addresses type is "int*" does C everytime it encounters that
lvalue say "here you can only read/write an int because the address is
int*" and therefor the type of the lvalue is "int"? But if its just an
address how can it have a type other then the addresses type? That
makes me wonder what an lvalue acctually is in C, how should I think
about it? It's something that keeps track of an address and type?

Kind regards // Nicklas

John Bode

unread,
Apr 6, 2010, 3:56:55 PM4/6/10
to
On Apr 6, 2:37 pm, Nicklas Karlsson <karlsson.nick...@gmail.com>
wrote:

> Hello,
>
> I am quite confused about this, so I split it in to subquestions, here
> we go:
>
> 1. Is an lvalue the expression itself, or is it the result yielded by
> evaluation of an expression? Take this for example: int i = 10; int *p
> = &i; and then dereference: *p; is the expression "*p" the thing you
> call an lvalue or is an lvalue the result yielded by evaluating that
> lvalue-expression?
>

The expression is the lvalue (quoting H&S: "An lvalue is an expression
that refers to an object in such a way that the object may be examined
or altered."). So i, p, and *p are all lvalues, while &i and 10 are
not.

> 2. Rvalues, is rvalues the result yielded by an rvalue-expression, or
> is it the expression itself? If its the result yielded by evaluation
> of an rvalue-expression is an rvalue a plain value?

Again, it refers to the expression. Any expression that cannot be an
lvalue is an rvalue.

>
> 3. I am quite sure that in a conversion from C to Assembly an lvalue-
> expression yields an an address when evaluated, however, in C an
> lvalue seems to have properties, an address and a type, again, using
> the example of *p, the lvalue will be of type "int", if an lvalue-
> expression always yields an address when evaluated, C could keep track
> of the addresses type, so if the lvalue (the address) is "0x0" and C
> knows the addresses type is "int*" does C everytime it encounters that
> lvalue say "here you can only read/write an int because the address is
> int*" and therefor the type of the lvalue is "int"? But if its just an
> address how can it have a type other then the addresses type? That
> makes me wonder what an lvalue acctually is in C, how should I think
> about it? It's something that keeps track of an address and type?
>
> Kind regards // Nicklas

You're overthinking things a bit. The distinction between lvalues and
rvalues has mostly to do with what legally may be the target of an
assignment; type is a separate issue.

For example, if the type of x is long, then the type of x++ is also
long, but x++ is not an lvalue.

Keith Thompson

unread,
Apr 6, 2010, 3:59:01 PM4/6/10
to
Nicklas Karlsson <karlsson...@gmail.com> writes:
> I am quite confused about this, so I split it in to subquestions, here
> we go:

Yes, it's confusing. The C standard's definitions of "lvalue" and
"rvalue" aren't necessarily consistent with their historical usage.

> 1. Is an lvalue the expression itself, or is it the result yielded by
> evaluation of an expression? Take this for example: int i = 10; int *p
> = &i; and then dereference: *p; is the expression "*p" the thing you
> call an lvalue or is an lvalue the result yielded by evaluating that
> lvalue-expression?

An "lvalue" is, roughly, an expression that designates an object.
(The C90 and C99 standard both managed to get the definition
seriously wrong, but that's the gist of it.)

> 2. Rvalues, is rvalues the result yielded by an rvalue-expression, or
> is it the expression itself? If its the result yielded by evaluation
> of an rvalue-expression is an rvalue a plain value?

A footnote in the standard says

What is sometimes called ‘‘rvalue’’ is in this International
Standard described as the ‘‘value of an expression’’.

So an lvalue is a kind of expression, and an rvalue is the
value that results from evaluating an expression.

> 3. I am quite sure that in a conversion from C to Assembly an lvalue-
> expression yields an an address when evaluated, however, in C an
> lvalue seems to have properties, an address and a type, again, using
> the example of *p, the lvalue will be of type "int", if an lvalue-
> expression always yields an address when evaluated, C could keep track
> of the addresses type, so if the lvalue (the address) is "0x0" and C
> knows the addresses type is "int*" does C everytime it encounters that
> lvalue say "here you can only read/write an int because the address is
> int*" and therefor the type of the lvalue is "int"? But if its just an
> address how can it have a type other then the addresses type? That
> makes me wonder what an lvalue acctually is in C, how should I think
> about it? It's something that keeps track of an address and type?

An lvalue is not an address, though it's a related concept. An lvalue
doesn't point to an object, it "designates" an object.

For example, given

int x;

the expression ``x'' is an lvalue. It refers to the object x, not to
the address of that object (that would be ``&x'', which is *not* an
lvalue).

Whether an expression is an lvalue or not is relevant only in some
contexts, namely those contexts that require an lvalue. The most
obvious example is the left side of an assignment. For example:

x = 42;

is valid, because x is an value, but

41 = 42;

is not, because 41 is not an lvalue. The right hand side needn't be
an lvalue; only the *value* of the RHS is needed. But the left hand
side must designate an object

<http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf> is the
latest post-C99 draft; it includes the material from the C99 standard
plus the three Technical Corrigenda. Section 6.3.2.1 discusses
lvalues. Be aware that the definition of the term in the first
paragraph is flawed, but it's otherwise a good explanation.

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Nicklas Karlsson

unread,
Apr 6, 2010, 4:44:38 PM4/6/10
to
On Apr 6, 9:59 pm, Keith Thompson <ks...@mib.org> wrote:

Im not sure I follow, an rvalue is the value of an expression, its not
an expression? While an lvalue is an expression? But an lvalue is an
expression that does not evaluate to an rvalue? And since an lvalue is
an expression it evaluates to something, what exactly does it evaluate
to? Something that has a designated object and has a type? I read that
PDF and it said: "if an lvalue does not designate an object when it is
evaluated..." so it is something that designates an object? Am I
thinking wrong? And the expression does evaluate to an address? I read
this: http://accu.org/index.php/journals/227 it sorta says "every
expression yields either an lvalue or an rvalue and accordingly every
expression is called either an lvalue or an rvalue expression." that
is wrong, well it appears to be right now anyhow?

Morris Keesan

unread,
Apr 6, 2010, 5:11:03 PM4/6/10
to
On Tue, 06 Apr 2010 15:59:01 -0400, Keith Thompson <ks...@mib.org> wrote:

> int x;
...


> Whether an expression is an lvalue or not is relevant only in some
> contexts, namely those contexts that require an lvalue. The most
> obvious example is the left side of an assignment. For example:
> x = 42;
> is valid, because x is an value

[obviously a slip of the keyboard, and intended to read "x is an lvalue"].

The other obvious example of a context that requires an lvalue is the
operand of the unary & operator. An lvalue is something that has an
address, so

&x

is a valid expression, while

&42

is not. Given

const int y = 42;

y is an lvalue, so &y is a valid expression, while &42 is not, even though
y cannot appear on the left side of an assignment operator (because it's
an unmodifiable lvalue).


--
Morris Keesan -- mke...@post.harvard.edu

bartc

unread,
Apr 6, 2010, 6:24:06 PM4/6/10
to

"Nicklas Karlsson" <karlsson...@gmail.com> wrote in message
news:687e94da-dbcf-4dfe...@u31g2000yqb.googlegroups.com...

> Hello,
>
> I am quite confused about this, so I split it in to subquestions, here
> we go:
>
> 1. Is an lvalue the expression itself, or is it the result yielded by
> evaluation of an expression? Take this for example: int i = 10; int *p
> = &i; and then dereference: *p; is the expression "*p" the thing you
> call an lvalue or is an lvalue the result yielded by evaluating that
> lvalue-expression?

A rule of thumb might be: if you can apply the & operator to it
(address-of), then it can be an lvalue (with a few exceptions such as
function names)

But here:

int A,B;

A = B;

Both A,B could be lvalues, but only A is being used as one. (In assembly,
you will find little difference between how the two are treated:

mov [A],[B] # assuming memory-to-memory allowed

(I'm done a bit of compiler work; I would handle the assignment above as
follows:

*(&A) = B;

Ie. apply address-of to the left-hand-side, then dereference. The * and &
cancel out, but it has now verified the left-side is an lvalue. If you try
and write:

56 = B;

this obviously won't work (since &56 is not allowed).)

>
> 2. Rvalues, is rvalues the result yielded by an rvalue-expression, or
> is it the expression itself? If its the result yielded by evaluation
> of an rvalue-expression is an rvalue a plain value?

There will be some technical interpretation somewhere or other. To me, the
rvalue would be the result of evaluating the expression: a value with a
type. (In some languages, this is not quite so clear-cut; fortunately this
is C.)

> 3. I am quite sure that in a conversion from C to Assembly an lvalue-
> expression yields an an address when evaluated,

Try looking at some assembly output.

> however, in C an
> lvalue seems to have properties, an address and a type, again, using
> the example of *p, the lvalue will be of type "int", if an lvalue-
> expression always yields an address when evaluated, C could keep track
> of the addresses type, so if the lvalue (the address) is "0x0" and C
> knows the addresses type is "int*" does C everytime it encounters that
> lvalue say "here you can only read/write an int because the address is
> int*" and therefor the type of the lvalue is "int"? But if its just an
> address how can it have a type other then the addresses type? That
> makes me wonder what an lvalue acctually is in C, how should I think
> about it? It's something that keeps track of an address and type?

int A=78;

Here, several things are happening:

'A' A is really the name of a location, or address, containing 78.
Let's say this is address 0x400100.
The type of 'A' is pointer-to-int. However, C automatically
dereferences such names (in common with other languages), so:

A Will dereference the 'A' address with value 0x400100, to fetch
the value 78. This is now type int.

&A This overrides the automatic dereference to allow access to the
address. The value is 0x400100 with type pointer-to-int.

Which of this is an lvalue? Well A *can* be an lvalue, because you
can use & on it to obtain it's address (0x400100), allowing you to
store another value in there:

A = 79;

But I don't think an lvalue is specifically the address of something. Here:

enum {C=12};

'C' is the name of a constant value '12'. There is no address, and
therefore it can't be an lvalue. The type I would guess is just int.


--
Bartc

Keith Thompson

unread,
Apr 6, 2010, 5:38:22 PM4/6/10
to
Nicklas Karlsson <karlsson...@gmail.com> writes:
[...]

> Im not sure I follow, an rvalue is the value of an expression, its not
> an expression? While an lvalue is an expression?

Yes, given the way the C standard defines the terms.

> But an lvalue is an
> expression that does not evaluate to an rvalue?

No.

Keep in mind that the C standard doesn't generally use the term
"rvalue". The only two occurences of the term are in that footnote
and in the corresponding index entry. If you're discussing C, it's
best to avoid the term "rvalue" altogether.

If an lvalue (a kind of expression) appears in a context that requires
an lvalue, it doesn't yield a result in the ordinary sense; rather, it
serves to designate (or identify) the object that's required in that
context. If it appears on the LHS of an assignment operator, it
designates the object to which a value is to be assigned. If it
appears as the operand of a unary "&" operator, it designates the
object whose address is taken. And so forth.

If an lvalue appears in a context that *doesn't* require an lvalue, it
yields an ordinary value -- specifically, it yields the stored value
of the object that it designates.

Thus:

int x = 10;
int y = 20;
x = y;

In the assignment, both ``x'' and ''y'' are lvalues. Since ``x'' is
in a context that requires an lvalue, it identifies the object to be
written to (the previous value of that object, 10, is ignored). Since
''y'' is in a context that *doesn't* require an lvalue, it yields the
value (20) stored in the object that it designates (y).

(I'm using ``y'' to refer to the expression consisting of the
identifier y and just y to refer to the object. This isn't a standard
typographical convention, just the one I'm using here.)

> And since an lvalue is
> an expression it evaluates to something, what exactly does it evaluate
> to?

It depends on the context; see above.

> Something that has a designated object and has a type? I read that
> PDF and it said: "if an lvalue does not designate an object when it is
> evaluated..." so it is something that designates an object? Am I
> thinking wrong?

An lvalue is an expression that designates an object.

Actually, that's not *quite* accurate. Given:
int *ptr = NULL;
the expression *ptr is an lvalue, even though it doesn't happen to
refer to any object at run time. This is the subtlety that has
tripped up the authors of the standard in trying to define the term.
(I won't go into the gory details yet again unless you really want me
to.)

What the standard probably should say is "if an lvalue does not
designate an object when it is evaluated *in a context that requires
an lvalue*, the behavior is undefined. And the definition should say
something along the lines that an lvalue is an expression that
*potentially* designates an object.

Given:

int x = 42;
int *ptr;

the expressions ``x'' and` ``*ptr'' are both lvalues. ``x''
unconditionally designates an object. ``*ptr'' may or may not
designate an object, depending on the value currently stored in
ptr; this is where the potential for undefined behavior comes in.
``42'' cannot possibly designate an object and is not an lvalue,
so an attempt to use it in a context requiring an lvalue will be
flagged at compile time.

> And the expression does evaluate to an address?

No. Evaluating an lvalue in a context that requires an lvalue
identifies an object. It doesn't yield the object's address. It
could well be implemented by computing the object's address, but
consider this:
register int reg;
reg = 42;
The object reg has no address, but ``reg'' nevertheless designates
that object. The same applies to bit fields, which also have no
address.

> I read
> this: http://accu.org/index.php/journals/227 it sorta says "every
> expression yields either an lvalue or an rvalue and accordingly every
> expression is called either an lvalue or an rvalue expression." that
> is wrong, well it appears to be right now anyhow?

It "sorta says"? Actually, you quoted the exact words, but you
didn't quote the beginning of the sentence:

*In C++* every expression yields either an lvalue or an rvalue and


accordingly every expression is called either an lvalue or an
rvalue expression.

I think that C++ defines the terms differently than C does. For
information on C++, see elsewhere.

I mentioned that the C standard doesn't use the traditional meanings
of the terms. I'll summarize my understanding of what those
traditional meanings are.

In a very simple language without objects, each expression yields a
value when evaluated. ``2 + 2'' yields the value 4, for example.

Once you introduce objects, assuming you want to treat assignments as
expressions, there are two possible ways to evaluate an expression.
You can either evaluate it to determine its value (``2 + 2'' yields
4, ``x + 3'' yields 45 if you've previously stored the value 42 in
x) *or* you can evaluate it to determine what object it designates,
ignoring any value that might be stored in that object. Which of
these things happens depends on the context in which the expression
appears. Traditionally, the former is referred to as "evaluating
an expression for its rvalue", and the latter as "evaluating an
expression for its lvalue". An rvalue is just an ordinary value of
some type. An lvalue is something that, rather than being a *value*
of some type, identifies an *object* of some type. The traditional
terminology unifies these two things (having a value vs. identifying
an object) into the single concept of "value", divided into "rvalue"
and "lvalue". The names of course come from the contexts in which
they appear, the right side vs. the left side of an assignment.

Note that some expressions, such as ``2 + 2'' cannot be evaluated for
their lvalues. (In C terms, ``2 + 2'' is not an lvalue.)

The authors of the C standard chose to drop the concept of "rvalue"
(other than a brief mention in a footnote which matches the
traditional meaning) and redefine "lvalue" as a kind of expression
rather than as the result of evaluating such an expression.

I'm sorry this is so long; I didn't have time to make it shorter.

Nicklas Karlsson

unread,
Apr 6, 2010, 6:38:57 PM4/6/10
to
On Apr 6, 11:38 pm, Keith Thompson <ks...@mib.org> wrote:
> > this:http://accu.org/index.php/journals/227it sorta says "every

Thanks for the reply guys!

So an lvalue does NOT evaluate in a traditional sense (yield a value)
but its being EVALUATED, so that the compiler can identify an object
and a size, the result of the evaluation specifies an object and a
type (and an object is a memory address right?) so if its on the left
side of assignenment it tells the compiler where to put the rvalue and
how much it can put, and if it appears on the right hand of assignment
it tells the compiler where to read the rvalue and how much to read?
Then you confuse me (:P) and say "and the latter as 'evaluating an
expression for its lvalue'", I thought we establised that the
expression itself was the lvalue?

In this: http://publib.boulder.ibm.com/infocenter/macxhelp/v6v81/index.jsp?topic=/com.ibm.vacpp6m.doc/language/ref/clrc05lvalue.htm
they call rvalues expressions too, but that is faulty (as the C-
standard says its a value of an expression), it may only apply to C++?
It also says: "When an lvalue appears in a context that requires an
rvalue, the lvalue is implicitly converted to an rvalue." that just
means that it starts to reads the rvalue at the address the lvalue
identifies? The lvalue is still evaluated right?

See I dont have a C-bot but a C++-bot and it shows precedence like
this ((*p) = 5); for *p = 5; and the (*p) must show that its being
evaluated right? But it shows i = 5; as (i = 5) but both are
expressions so I dont get why precedence differs?

Keith Thompson

unread,
Apr 6, 2010, 7:17:42 PM4/6/10
to
Nicklas Karlsson <karlsson...@gmail.com> writes:
[...]
> Thanks for the reply guys!

When you post a followup, please trim any quoted material that isn't
necessary. In particular, please don't quote signatures.

> So an lvalue does NOT evaluate in a traditional sense (yield a value)
> but its being EVALUATED, so that the compiler can identify an object
> and a size,

Yes, but I wouldn't place so much emphasis on the size.

An lvalue (which is an expression) is of a particular type (and the
size is an attribute of that type). It identifies an object, which is
treated as being of that type. Usually that's going to be the
declared typethe object. If it's not (say, if there are pointer
conversions involved), then the type of the lvalue is what determines
the behavior.

A silly example:

int n;
*(unsigned int*)&n = 43;

Here n is declared to be of type int, but the lvalue
*(unsigned int*)&n
is of type unigsned int.

> the result of the evaluation specifies an object and a
> type (and an object is a memory address right?)

An object is, by definition, a "region of data storage in the
execution environment, the contents of which can represent values"
(C99 3.14). An object is not an address; an object (usually) *has* an
address.

> so if its on the left
> side of assignenment it tells the compiler where to put the rvalue and
> how much it can put,

It tells the compiler the identify and type of the object into which
the value is to be stored. Remember I suggested avoiding the term
"rvalue"; the value to be stored is the value resulting from the
evaluation of the RHS expression.

> and if it appears on the right hand of assignment
> it tells the compiler where to read the rvalue and how much to read?

If an lvalue appears as the RHS of an assignment, it's just evaluated,
yielding the value stored in the designated object.

> Then you confuse me (:P) and say "and the latter as 'evaluating an
> expression for its lvalue'", I thought we establised that the
> expression itself was the lvalue?

In C terms, yes. As I said, the C standard assigns a non-traditional
meaning to the term "lvalue". In C, the expression itself is the
lvalue.

> In this: http://publib.boulder.ibm.com/infocenter/macxhelp/v6v81/index.jsp?topic=/com.ibm.vacpp6m.doc/language/ref/clrc05lvalue.htm
> they call rvalues expressions too, but that is faulty (as the C-
> standard says its a value of an expression), it may only apply to C++?
> It also says: "When an lvalue appears in a context that requires an
> rvalue, the lvalue is implicitly converted to an rvalue." that just
> means that it starts to reads the rvalue at the address the lvalue
> identifies? The lvalue is still evaluated right?

You're asking about C++ in a C newsgroup. lvalue-to-rvalue conversion
is a C++ concept, not a C concept. C++ has its own set of newsgroups
(and FAQs, which you should check before posting).

(I just checked the C++ standard. C++ definitely defines "rvalue"
differently than C does. Its definition of "lvalue' may or may not
be consistent with C's.)

> See I dont have a C-bot but a C++-bot and it shows precedence like
> this ((*p) = 5); for *p = 5; and the (*p) must show that its being
> evaluated right? But it shows i = 5; as (i = 5) but both are
> expressions so I dont get why precedence differs?

Precedence has nothing to do with it.

Nicklas Karlsson

unread,
Apr 6, 2010, 7:56:54 PM4/6/10
to
> You're asking about C++ in a C newsgroup.  lvalue-to-rvalue conversion
> is a C++ concept, not a C concept.  C++ has its own set of newsgroups
> (and FAQs, which you should check before posting).

Sorry, from the looks of it I thought that link was talking about both
C and C++


So if an identifier is declared and used as an lvalue, the compiler
knows that lvalues type because its the type which the the identifier
is declared with, and if its pointer conversion the compiler knows the
lvalues type from context (for example *(int*)0x0 will be an lvalue of
type "int"), so is it the expression itself that has a type or does
that expression evaluate so that the compiler knows its type (and
therefor size), like "this lvalue evaluated, the object it designated
starts at address ****** and the type of that object is"?

An object is, and I quote: "a region of data storage in the execution
environment, the contents of which can represent values" so its an
entire region of memory (usally), which is usally identified by a
memory address?

"If an lvalue appears as the RHS of an assignment, it's just

evaluated, yielding the value stored in the designated object." so
there is no conversion going on? it evaluates the lvalue and reads the
data of the object that lvalue identified?

"Precedence has nothing to do with it."

I thought it was order of evaluation, and I'm just wondering why one
lvalue (indirection) is different from an identifer as an lvalue in
terms of evaluation, but its probably off topic since its a C++ bot
and what I linked earlier about C++ lvalues seem different than C's

"knows what type is going to be used for that object"

Stephen Sprunk

unread,
Apr 6, 2010, 9:46:01 PM4/6/10
to
On 06 Apr 2010 16:38, Keith Thompson wrote:
> Given:
>
> int x = 42;
> int *ptr;
>
> the expressions ``x'' and` ``*ptr'' are both lvalues. ``x''
> unconditionally designates an object. ``*ptr'' may or may not
> designate an object, depending on the value currently stored in
> ptr; this is where the potential for undefined behavior comes in.

"ptr" also unconditionally designates an object. (I'm sure you knew
that; I just point it out for completeness.)

> I mentioned that the C standard doesn't use the traditional meanings
> of the terms. I'll summarize my understanding of what those
> traditional meanings are.

> ...


> An rvalue is just an ordinary value of
> some type. An lvalue is something that, rather than being a *value*
> of some type, identifies an *object* of some type. The traditional
> terminology unifies these two things (having a value vs. identifying
> an object) into the single concept of "value", divided into "rvalue"
> and "lvalue". The names of course come from the contexts in which
> they appear, the right side vs. the left side of an assignment.
>
> Note that some expressions, such as ``2 + 2'' cannot be evaluated for
> their lvalues. (In C terms, ``2 + 2'' is not an lvalue.)
>
> The authors of the C standard chose to drop the concept of "rvalue"
> (other than a brief mention in a footnote which matches the
> traditional meaning) and redefine "lvalue" as a kind of expression
> rather than as the result of evaluating such an expression.

Is dropping the term "rvalue" why the C Standard's definition of
"lvalue" is so tortuous--and continues to get even more so with each
revision as they try to correct problems with the prior one?

Digesting what you have said, this is how I mentally process the terms:

. A modifiable lvalue is any expression which _could_ have a meaningful
result if it appeared on the LHS of an assignment.
. A non-modifiable lvalue is any expression which _would_ be a
modifiable lvalue if it weren't const.
. An rvalue is any expression which isn't an lvalue.
. If an lvalue occurs in a context which does not require an lvalue, it
is evaluated to produce an rvalue.

Granted, that definitely isn't how the C Standard uses or defines the
terms, but aside from that, is it accurate enough for someone other than
a compiler author? Or is there some subtle flaw in there that will bite
me someday?

S

--
Stephen Sprunk "God does not play dice." --Albert Einstein
CCIE #3723 "God is an inveterate gambler, and He throws the
K5SSS dice at every possible opportunity." --Stephen Hawking

Keith Thompson

unread,
Apr 6, 2010, 10:08:09 PM4/6/10
to
Stephen Sprunk <ste...@sprunk.org> writes:
[...]

> Is dropping the term "rvalue" why the C Standard's definition of
> "lvalue" is so tortuous--and continues to get even more so with each
> revision as they try to correct problems with the prior one?

I don't think so. I think the problem is with "lvalue", not with
"rvalue". I find lvalues to be a more complicated concept anyway, for
either definition.

> Digesting what you have said, this is how I mentally process the terms:
>
> . A modifiable lvalue is any expression which _could_ have a meaningful
> result if it appeared on the LHS of an assignment.
> . A non-modifiable lvalue is any expression which _would_ be a
> modifiable lvalue if it weren't const.
> . An rvalue is any expression which isn't an lvalue.
> . If an lvalue occurs in a context which does not require an lvalue, it
> is evaluated to produce an rvalue.
>
> Granted, that definitely isn't how the C Standard uses or defines the
> terms, but aside from that, is it accurate enough for someone other than
> a compiler author? Or is there some subtle flaw in there that will bite
> me someday?

Your definition of "rvalue" directly contradicts the standard's
definition; an "rvalue" is not an expression, it's the result of
evaluating an expression.

I'd probably drop your third bullet and replace "rvalue" by "value" in
the fourth.

Phil Carmody

unread,
Apr 7, 2010, 2:02:52 AM4/7/10
to
"bartc" <ba...@freeuk.com> writes:
> "Nicklas Karlsson" <karlsson...@gmail.com> wrote in message
> news:687e94da-dbcf-4dfe...@u31g2000yqb.googlegroups.com...
>> Hello,
>>
>> I am quite confused about this, so I split it in to subquestions, here
>> we go:
>>
>> 1. Is an lvalue the expression itself, or is it the result yielded by
>> evaluation of an expression? Take this for example: int i = 10; int *p
>> = &i; and then dereference: *p; is the expression "*p" the thing you
>> call an lvalue or is an lvalue the result yielded by evaluating that
>> lvalue-expression?
>
> A rule of thumb might be: if you can apply the & operator to it
> (address-of), then it can be an lvalue (with a few exceptions such as
> function names)
>
> But here:
>
> int A,B;
>
> A = B;
>
> Both A,B could be lvalues, but only A is being used as one. (In assembly,
> you will find little difference between how the two are treated:
>
> mov [A],[B] # assuming memory-to-memory allowed
>
> (I'm done a bit of compiler work; I would handle the assignment above as
> follows:
>
> *(&A) = B;

So you'd not want your variables in registers? Why on earth not?

Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1

Message has been deleted
Message has been deleted

Keith Thompson

unread,
Apr 7, 2010, 11:58:25 AM4/7/10
to
r...@zedat.fu-berlin.de (Stefan Ram) writes:
> r...@zedat.fu-berlin.de (Stefan Ram) writes:
>>An lvalue is an expression.
>
> Having read parts of the discussion:
>
> I'd put it in these words, if I was invited to write a
> specification for the language C:
>
> Every expression might designate an object and/or a
> value. (For the definition of the object and the value,
> see the chapter "expressions", where this is specified
> for each type of expression.)
>
> Every operator can require each of its operands to
> designate an object and/or a value. (See the chapter
> "expressions", where this is specified for each
> operator.)
>
> For example, the assignment operator »=« requires its
> left operand to designate an object and its right
> operand to designate a value. It then writes this value
> into that object.

Clarification: All expressions designate values; only some expressions
designate objects.

And of course some expressions that are lvalues may not designate
objects depending on the values of certain subexpresions; ``*ptr''
designates an object if and only if ptr happens to contain a valid
non-null address.

Kenneth Brody

unread,
Apr 7, 2010, 2:03:44 PM4/7/10
to
On 4/6/2010 3:37 PM, Nicklas Karlsson wrote:
> Hello,
>
> I am quite confused about this, so I split it in to subquestions, here
> we go:
>
> 1. Is an lvalue the expression itself, or is it the result yielded by
> evaluation of an expression? Take this for example: int i = 10; int *p
> =&i; and then dereference: *p; is the expression "*p" the thing you

> call an lvalue or is an lvalue the result yielded by evaluating that
> lvalue-expression?
[...]

I like to think of this in an overly-simplified manner: an lvalue is
something that can go on the left side of an assignment, and an rvalue is
something that can go on the right side of an assignment.

So, given:

int i = 10;

the "i" is an lvalue, and "10" is an rvalue.

Given:

int *p = &i;

the "*p" is an lvalue, and "&i" is an rvalue.

As I said, this is an overly-simplified description, but it makes it easy to
understand the basics.

--
Kenneth Brody

Nicklas Karlsson

unread,
Apr 7, 2010, 3:23:24 PM4/7/10
to
Let me summarize what I make of this (hopefully its correct):

What is an lvalue?
An expression that designates (identifies) an object, when evaluated
the compiler knows what object is being designated and what type that
object has.

What is an rvalue?
The result of an expression that is not an lvalue appearing in LHS (an
lvalue, when evaluated, yields an rvalue if it appears on RHS, that
is, the value of the object being designated is being fetched, however
if its on LHS it does not evaluate to an rvalue but rather evaluates
so the compiler knows where to store an rvalue, that is, it identifies
an object and what type that object has).

What is an object?
A region of data storage, usally an object is a place in memory, so it
usally has a starting address, but it could also be stored in other
ways, for example in the CPU's register and therefor won't have an
ordinary address.

Silly question, the lvalue (expression) itself has no type right? But
when its evaluated the compiler finds out what type the object that
the lvalue identifies has?

Keith Thompson

unread,
Apr 7, 2010, 3:58:05 PM4/7/10
to
Nicklas Karlsson <karlsson...@gmail.com> writes:
> Let me summarize what I make of this (hopefully its correct):
>
> What is an lvalue?
> An expression that designates (identifies) an object, when evaluated
> the compiler knows what object is being designated and what type that
> object has.

Close. Evaluation happens at run time; the compiler doesn't
necessarily know which object is being designated. It does know
the type of the object -- or rather the type imposed by the lvalue.
This might not be the declared type of the object itself. In fact,
the object might not even have a declared type.

Some examples (I haven't taken the time to test these):

int x, y, *ptr;
switch (rand() % 3) {
case 0: ptr = NULL; break;
case 1: ptr = &x; break;
case 2: ptr = &y; break;
}
*ptr; /* an lvalue, but the compiler has no idea whether it
designates x, y, or no object at all */

void *vptr = malloc(sizeof(int));
assert(vptr != NULL);
/* We've just created an object, but it has no inherent type. */
*(int*)vptr; /* the type is imposed by the lvalue */
*(unsigned*)vptr; /* same object, different lvalue, different type */


> What is an rvalue?
> The result of an expression that is not an lvalue appearing in LHS (an
> lvalue, when evaluated, yields an rvalue if it appears on RHS, that
> is, the value of the object being designated is being fetched, however
> if its on LHS it does not evaluate to an rvalue but rather evaluates
> so the compiler knows where to store an rvalue, that is, it identifies
> an object and what type that object has).

Too many words. An rvalue is the value of an expression, that's all.
It's also a term that the Standard doesn't use, and I see little point
in using it while discussing C. <OT>(C++ is another matter.)</OT>

> What is an object?
> A region of data storage, usally an object is a place in memory, so it
> usally has a starting address, but it could also be stored in other
> ways, for example in the CPU's register and therefor won't have an
> ordinary address.

I'd say "address" rather than "starting address". In C, an address is
typed. It doesn't point to the beginning of an object, it points to
the object.

> Silly question, the lvalue (expression) itself has no type right? But
> when its evaluated the compiler finds out what type the object that
> the lvalue identifies has?

No, an lvalue, like any expression, has a type.

lawrenc...@siemens.com

unread,
Apr 7, 2010, 2:37:31 PM4/7/10
to
Keith Thompson <ks...@mib.org> wrote:
>
> What the standard probably should say is "if an lvalue does not
> designate an object when it is evaluated *in a context that requires
> an lvalue*, the behavior is undefined.

Isn't that addition redundant? I can't think of *any* context where an
lvalue that doesn't desginate an object can be safely evaluated.
--
Larry Jones

I'm a genius. -- Calvin

lawrenc...@siemens.com

unread,
Apr 7, 2010, 2:40:11 PM4/7/10
to
Stephen Sprunk <ste...@sprunk.org> wrote:
>
> . A non-modifiable lvalue is any expression which _would_ be a
> modifiable lvalue if it weren't const.

An array is a non-modifiable lvalue but is not const (only the elements
can be const in C, not the array itself).
--
Larry Jones

I don't want to be THIS good! -- Calvin

Keith Thompson

unread,
Apr 7, 2010, 4:29:16 PM4/7/10
to
lawrenc...@siemens.com writes:
> Keith Thompson <ks...@mib.org> wrote:
>> What the standard probably should say is "if an lvalue does not
>> designate an object when it is evaluated *in a context that requires
>> an lvalue*, the behavior is undefined.
>
> Isn't that addition redundant? I can't think of *any* context where an
> lvalue that doesn't desginate an object can be safely evaluated.

Yes, you're right. (I get the feeling this isn't the first time I've
made that mistake.)

The current standard says (C99 6.3.2.1p1):

An lvalue is an expression with an object type or an incomplete
type other than void;

if an lvalue does not designate an object when it is evaluated,
the behavior is undefined.

The second part of the sentence is ok. For example:

int i;
int *ptr = NULL;

i = *ptr;

*ptr is an lvalue, it's being evaluated in a context that doesn't
require an lvalue, and the behavior is undefined because it doesn't
currently designate an object. <OT>(I think the way C++ expresses it
is that it's evaluated as an lvalue, and then an lvalue-to-rvalue
conversion is performed.)</OT>

The real problem is that the first part of the sentence is too broad;
it makes 42 an lvalue, which it clearly is not intended to be.

The C90 standard's definition (an lvalue is an expression ... that
designates an object) was too narrow; it implied that *ptr is an
lvalue if and only if ptr currently contains a valid non-null pointer
value, whereas lvalue-ness needs to be determinable at compile time.

Either an lvalue needs to be defined as an expression that
*potentially* designates an object (defining "potentially" is tricky),
or the definition needs to enumerate the set of expressions that are
lvalues (I think they can be defined syntactically), or it can just
say that an lvalue is an expression whose description in section 6.5
says it's an lvalue.

Nicklas Karlsson

unread,
Apr 7, 2010, 4:57:47 PM4/7/10
to
"I'd say "address" rather than "starting address". In C, an address
is
typed. It doesn't point to the beginning of an object, it points to
the object."

So since an address is typed, int* for example, one says that it
points to the entire object? It points to an "int", but nevertheless
it still points to the first allocated byte of size(int) bytes
allocated for an int

"No, an lvalue, like any expression, has a type."

I thougt that the result yielded by an expression has a type, not the
expression itself, could you give an example of how an expression has
a type?

"Too many words. An rvalue is the value of an expression, that's all.
It's also a term that the Standard doesn't use, and I see little point
in using it while discussing C. <OT>(C++ is another matter.)</OT>"

OK, but even tho the term is avoided, could I re-sentence and say
something along the lines with: An rvalue is the result (value)
yielded any expression, except if the expression is an lvalue
appearing on LHS?

Keith Thompson

unread,
Apr 7, 2010, 5:05:17 PM4/7/10
to
Nicklas Karlsson <karlsson...@gmail.com> writes:
> "I'd say "address" rather than "starting address". In C, an address
> is typed. It doesn't point to the beginning of an object, it points
> to the object."
>
> So since an address is typed, int* for example, one says that it
> points to the entire object? It points to an "int", but nevertheless
> it still points to the first allocated byte of size(int) bytes
> allocated for an int

Well, that's one way to look at it, but I find it clearer to think of
an address (pointer value) as pointing to the entire object. It's one
way in which a C pointer value is conceptually different, and at a
higher level, than a machine address.

> "No, an lvalue, like any expression, has a type."
>
> I thougt that the result yielded by an expression has a type, not the
> expression itself, could you give an example of how an expression has
> a type?

An expression has the type of the value that it yields. For example,
the expression ``2 + 2'' is of type int, as does the value, 4, that it
yields.

I'm not 100% certain that the standard expresses it this way, but it
seems reasonable.

> "Too many words. An rvalue is the value of an expression, that's all.
> It's also a term that the Standard doesn't use, and I see little point
> in using it while discussing C. <OT>(C++ is another matter.)</OT>"
>
> OK, but even tho the term is avoided, could I re-sentence and say
> something along the lines with: An rvalue is the result (value)
> yielded any expression, except if the expression is an lvalue
> appearing on LHS?

I suppose so.

Message has been deleted

Eric Sosman

unread,
Apr 7, 2010, 5:37:45 PM4/7/10
to
On 4/7/2010 5:05 PM, Keith Thompson wrote:
> Nicklas Karlsson<karlsson...@gmail.com> writes:
>> "I'd say "address" rather than "starting address". In C, an address
>> is typed. It doesn't point to the beginning of an object, it points
>> to the object."
>>
>> So since an address is typed, int* for example, one says that it
>> points to the entire object? It points to an "int", but nevertheless
>> it still points to the first allocated byte of size(int) bytes
>> allocated for an int
>
> Well, that's one way to look at it, but I find it clearer to think of
> an address (pointer value) as pointing to the entire object. It's one
> way in which a C pointer value is conceptually different, and at a
> higher level, than a machine address.

It seems to me that an int* could address any byte of an
int object (maybe even no byte at all), so long as the platform
knows how to get at the entire int given the int* value.
Converting to a char* must produce a pointer to the [0] element
of a sizeof(int)-byte array that overlays the int object, but
that's no obstacle: conversion need not be a bit-for-bit copy.

Here's a challenge: If an int* does *not* address the first
byte of an int object, can a portable[*] program detect the fact?

[*] "Portable" instead of "strictly conforming," because the
output of an S.C. program cannot depend on implementation-defined,
unspecified, or undefined behavior. Since the representation of
an int* value is unspecified, an S.C. program that detected something
about it could not report what it discovered. But let's take an
informal notion of "portable" to mean that we're not allowed to
inspect the bits of the int* itself and try to find the bits of
a char* somewhere inside it. What can a "portable" program do to
show or refute "An int* holds the address of the byte just after
the int object," for example?

--
Eric Sosman
eso...@ieee-dot-org.invalid

Keith Thompson

unread,
Apr 7, 2010, 5:42:14 PM4/7/10
to
Keith Thompson <ks...@mib.org> writes:
> Nicklas Karlsson <karlsson...@gmail.com> writes:
[...]
>> I thougt that the result yielded by an expression has a type, not the
>> expression itself, could you give an example of how an expression has
>> a type?
>
> An expression has the type of the value that it yields. For example,
> the expression ``2 + 2'' is of type int, as does the value, 4, that it
> yields.
>
> I'm not 100% certain that the standard expresses it this way, but it
> seems reasonable.
[...]

In fact the standard does talk about the type of an expression.
Just a few examples:

C99 6.5.1p3:
A constant is a primary expression. Its type depends on its form
and value, as detailed in 6.4.4.

p4:
A string literal is a primary expression. It is an lvalue with
type as detailed in 6.4.5.

p5:
A parenthesized expression is a primary expression. Its type and
value are identical to those of the unparenthesized expression.

6.5.2.1p1:
One of the expressions shall have type ‘‘pointer to object
_type_’’, the other expression shall have integer type, and the
result has type ‘‘_type_’’.
(Underscores denote italics.)

Phil Carmody

unread,
Apr 7, 2010, 5:42:41 PM4/7/10
to
Keith Thompson <ks...@mib.org> writes:
> Nicklas Karlsson <karlsson...@gmail.com> writes:
>> "I'd say "address" rather than "starting address". In C, an address
>> is typed. It doesn't point to the beginning of an object, it points
>> to the object."
>>
>> So since an address is typed, int* for example, one says that it
>> points to the entire object? It points to an "int", but nevertheless
>> it still points to the first allocated byte of size(int) bytes
>> allocated for an int
>
> Well, that's one way to look at it, but I find it clearer to think of
> an address (pointer value) as pointing to the entire object. It's one
> way in which a C pointer value is conceptually different, and at a
> higher level, than a machine address.

Yeah, but no, but yeah, but no, but most architectures have some
kind of indexed indirect addressing mode; so an address register
could be numerically equal to the address of the start of an object,
but used to access the whole object.

And in fact on my alpha, the machine address points to *more*
than the object, if the object is less than 64 bits - char access
requires post-processing to get the single octet desired from the
whole word.

Nicklas Karlsson

unread,
Apr 7, 2010, 5:58:26 PM4/7/10
to
Thanks guys, and a gold star to Keith :)

Keith Thompson

unread,
Apr 7, 2010, 6:05:50 PM4/7/10
to
Phil Carmody <thefatphi...@yahoo.co.uk> writes:
> Keith Thompson <ks...@mib.org> writes:
>> Nicklas Karlsson <karlsson...@gmail.com> writes:
>>> "I'd say "address" rather than "starting address". In C, an address
>>> is typed. It doesn't point to the beginning of an object, it points
>>> to the object."
>>>
>>> So since an address is typed, int* for example, one says that it
>>> points to the entire object? It points to an "int", but nevertheless
>>> it still points to the first allocated byte of size(int) bytes
>>> allocated for an int
>>
>> Well, that's one way to look at it, but I find it clearer to think of
>> an address (pointer value) as pointing to the entire object. It's one
>> way in which a C pointer value is conceptually different, and at a
>> higher level, than a machine address.
>
> Yeah, but no, but yeah, but no, but most architectures have some
> kind of indexed indirect addressing mode; so an address register
> could be numerically equal to the address of the start of an object,
> but used to access the whole object.

And an address register might very well contain something that looks
like, and can be operated on as, an integer.

A C pointer value, conceptually at least, does not.

> And in fact on my alpha, the machine address points to *more*
> than the object, if the object is less than 64 bits - char access
> requires post-processing to get the single octet desired from the
> whole word.

--

Phil Carmody

unread,
Apr 7, 2010, 6:06:22 PM4/7/10
to
Eric Sosman <eso...@ieee-dot-org.invalid> writes:
> On 4/7/2010 5:05 PM, Keith Thompson wrote:
>> Nicklas Karlsson<karlsson...@gmail.com> writes:
>>> "I'd say "address" rather than "starting address". In C, an address
>>> is typed. It doesn't point to the beginning of an object, it points
>>> to the object."
>>>
>>> So since an address is typed, int* for example, one says that it
>>> points to the entire object? It points to an "int", but nevertheless
>>> it still points to the first allocated byte of size(int) bytes
>>> allocated for an int
>>
>> Well, that's one way to look at it, but I find it clearer to think of
>> an address (pointer value) as pointing to the entire object. It's one
>> way in which a C pointer value is conceptually different, and at a
>> higher level, than a machine address.
>
> It seems to me that an int* could address any byte of an
> int object (maybe even no byte at all), so long as the platform
> knows how to get at the entire int given the int* value.
> Converting to a char* must produce a pointer to the [0] element
> of a sizeof(int)-byte array that overlays the int object, but
> that's no obstacle: conversion need not be a bit-for-bit copy.
>
> Here's a challenge: If an int* does *not* address the first
> byte of an int object, can a portable[*] program detect the fact?

Sometimes. If the int* compares equal to 0, then it clearly
does not address the first byte of an int object. The answer
can not be 'always', IMHO, as I can't see any way a portable
program can know if a pointer's been freed or not.

Ike Naar

unread,
Apr 7, 2010, 6:06:28 PM4/7/10
to
In article <d5604e99-62ff-48f7...@z11g2000yqz.googlegroups.com>,

Nicklas Karlsson <karlsson...@gmail.com> wrote:
>"No, an lvalue, like any expression, has a type."
>
>I thougt that the result yielded by an expression has a type, not the
>expression itself, could you give an example of how an expression has
>a type?

If pd has type pointer-to-double, then the expression ``*pd''
has type double, even when pd does not point to an object.

Phil Carmody

unread,
Apr 7, 2010, 6:16:06 PM4/7/10
to
Keith Thompson <ks...@mib.org> writes:
> Phil Carmody <thefatphi...@yahoo.co.uk> writes:
>> Keith Thompson <ks...@mib.org> writes:
>>> Nicklas Karlsson <karlsson...@gmail.com> writes:
>>>> "I'd say "address" rather than "starting address". In C, an address
>>>> is typed. It doesn't point to the beginning of an object, it points
>>>> to the object."
>>>>
>>>> So since an address is typed, int* for example, one says that it
>>>> points to the entire object? It points to an "int", but nevertheless
>>>> it still points to the first allocated byte of size(int) bytes
>>>> allocated for an int
>>>
>>> Well, that's one way to look at it, but I find it clearer to think of
>>> an address (pointer value) as pointing to the entire object. It's one
>>> way in which a C pointer value is conceptually different, and at a
>>> higher level, than a machine address.
>>
>> Yeah, but no, but yeah, but no, but most architectures have some
>> kind of indexed indirect addressing mode; so an address register
>> could be numerically equal to the address of the start of an object,
>> but used to access the whole object.
>
> And an address register might very well contain something that looks
> like, and can be operated on as, an integer.
>
> A C pointer value, conceptually at least, does not.

I don't see how that addresses my point. A machine's address
register can be used to refer to more than just an atomic
entity at a single address.

Keith Thompson

unread,
Apr 7, 2010, 6:16:28 PM4/7/10
to
r...@zedat.fu-berlin.de (Stefan Ram) writes:
> Nicklas Karlsson <karlsson...@gmail.com> writes:
>>"I'd say "address" rather than "starting address". In C, an
>>address is typed. It doesn't point to the beginning of an
>>object, it points to the object."
>
> int main( void ){ char a[ 9 ]; char * p = a; return !p; }
>
> Above, p »points« to the beginning of the object a.

It points to the first element of the object a. It does so because
the expression ``a'' (of type char[9]) was implicitly converted to
type char*.

Another example:

struct big_struct {
/* a bunch of member declarations */
};
struct big_struct arr[10];
struct big_struct *ptr = arr; /* another implicit conversion */

Now the expression ``ptr'' points to the first element of arr,
which is of type struct big_struct. It doesn't, I would say,
point to *the beginning* of the structure; it points to the entire
structure. On the other hand, ``(char*)ptr'' points to the first
byte of the structure.

On the other hand, it's a bit difficult to say what ``(void*)ptr''
points to.

I'm not claiming that my way of looking at it (pointers point to
entire objects) is right and yours (pointers point to the beginning of
objects) is wrong. I just find my way clearer and more consistent
with C semantics. It more clearly reflects the ways in which C
pointers are not the same as machine-level addresses.

[...]

Keith Thompson

unread,
Apr 7, 2010, 6:53:25 PM4/7/10
to

I'm not sure exactly what your point is. Can you clarify? C doesn't
describe pointers in terms of the behavior of a machine's address
register.

A machine address register typically contains, say, 32 or 64
bits of data that usually refer to some location in memory.
The meaning of those 32 or 64 (or whatever) bits of data depends
on what instructions are used to manipulate them. (Some machines
don't have address registers; they just store addresses in general
purpose registers.)

A C address / pointer value, on the other hand, has a type associated
with it (though this association probably exists only in the source
code and at compile time).

If you have an array of structures, then a pointer to the entire
array, a pointer to the first element of the array, and a pointer
to the first member of the first element of the array are likely
to have the same representation, but I don't think that implies
that a pointer doesn't (conceptually) point to the entire object.

Seebs

unread,
Apr 7, 2010, 6:53:50 PM4/7/10
to
On 2010-04-07, Keith Thompson <ks...@mib.org> wrote:
> If you have an array of structures, then a pointer to the entire
> array, a pointer to the first element of the array, and a pointer
> to the first member of the first element of the array are likely
> to have the same representation, but I don't think that implies
> that a pointer doesn't (conceptually) point to the entire object.

In particular, you can have three pointers which, converted to (void *),
compare equal, but which point to different objects. One could point
to the array, one to the first structure in that array, and one to the
first element in that structure.

More interestingly, so far as I can tell, the three pointers could have
different logical bounds, such that a bounds-checking implementation could
react differently to attempts to memcpy a large number of bytes from or
to them, even though the pointers compare equal.

-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet...@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!

Stephen Sprunk

unread,
Apr 7, 2010, 7:20:41 PM4/7/10
to
On 07 Apr 2010 17:53, Keith Thompson wrote:
> A machine address register typically contains, say, 32 or 64
> bits of data that usually refer to some location in memory.
> The meaning of those 32 or 64 (or whatever) bits of data depends
> on what instructions are used to manipulate them. (Some machines
> don't have address registers; they just store addresses in general
> purpose registers.)
>
> A C address / pointer value, on the other hand, has a type associated
> with it (though this association probably exists only in the source
> code and at compile time).

A good point. A C pointer has a type, which implies a size; a machine
address does not have a type, so the particular instruction(s) that
use/manipulate that (typeless) address must imply the size of the
object. The compiler uses pointer type information to choose those
instructions, though...

> If you have an array of structures, then a pointer to the entire
> array, a pointer to the first element of the array, and a pointer
> to the first member of the first element of the array are likely
> to have the same representation, but I don't think that implies
> that a pointer doesn't (conceptually) point to the entire object.

True, at a C level, but is there really a difference at the machine
level on real-world implementations?

More importantly, assuming a T is a multi-byte object and pointers are
simple memory addresses, is it possible for &T to be the address of a
byte _other than_ the first (i.e. lowest) byte of the object?

S

--
Stephen Sprunk "God does not play dice." --Albert Einstein
CCIE #3723 "God is an inveterate gambler, and He throws the
K5SSS dice at every possible opportunity." --Stephen Hawking

Stephen Sprunk

unread,
Apr 7, 2010, 7:50:49 PM4/7/10
to
On 07 Apr 2010 15:57, Nicklas Karlsson wrote:
> "I'd say "address" rather than "starting address". In C, an address
> is typed. It doesn't point to the beginning of an object, it points to
> the object."
>
> So since an address is typed, int* for example, one says that it
> points to the entire object?

Yes.

> It points to an "int", but nevertheless it still points to the first
> allocated byte of size(int) bytes allocated for an int

I think that would fall into the "implementation detail" category. In
theory, the representation _could_ be the address of the _last_ byte of
the object, and conversion to (char *) could subtract sizeof(int)-1
bytes; a conforming program couldn't tell the difference. I doubt any
implementation (other than the famed DS9k) is actually that evil, though.

> "No, an lvalue, like any expression, has a type."
>
> I thougt that the result yielded by an expression has a type, not the
> expression itself, could you give an example of how an expression has
> a type?

Consider sizeof(1+1). The expression is not evaluated, so there is no
result and therefore no type to the result. Where would sizeof get the
type information it needs if not from the expression itself?

The type of the result of evaluating an expression is the same as the
type of the expression itself, though, so it's generally not an
important distinction.

> "Too many words. An rvalue is the value of an expression, that's all.
> It's also a term that the Standard doesn't use, and I see little point
> in using it while discussing C. <OT>(C++ is another matter.)</OT>"
>
> OK, but even tho the term is avoided, could I re-sentence and say
> something along the lines with: An rvalue is the result (value)
> yielded any expression, except if the expression is an lvalue
> appearing on LHS?

The LHS of an assignment operator is just the most obvious place one
needs an lvalue; there are others (e.g. the operand of ++).

Keith Thompson

unread,
Apr 7, 2010, 8:15:10 PM4/7/10
to
Stephen Sprunk <ste...@sprunk.org> writes:
> On 07 Apr 2010 17:53, Keith Thompson wrote:
>> A machine address register typically contains, say, 32 or 64
>> bits of data that usually refer to some location in memory.
>> The meaning of those 32 or 64 (or whatever) bits of data depends
>> on what instructions are used to manipulate them. (Some machines
>> don't have address registers; they just store addresses in general
>> purpose registers.)
>>
>> A C address / pointer value, on the other hand, has a type associated
>> with it (though this association probably exists only in the source
>> code and at compile time).
>
> A good point. A C pointer has a type, which implies a size; a machine
> address does not have a type, so the particular instruction(s) that
> use/manipulate that (typeless) address must imply the size of the
> object. The compiler uses pointer type information to choose those
> instructions, though...

Note that the size is just one of many attributes of the type. The
type also affects which operations can be performed on the pointed-to
object. I suggest that the emphasis on size implies an concentration
on the machine-level behavior rather than on C semantics.

>> If you have an array of structures, then a pointer to the entire
>> array, a pointer to the first element of the array, and a pointer
>> to the first member of the first element of the array are likely
>> to have the same representation, but I don't think that implies
>> that a pointer doesn't (conceptually) point to the entire object.
>
> True, at a C level, but is there really a difference at the machine
> level on real-world implementations?

Probably not, but there's no fundamental reason why there couldn't be.

Except that there are systems where a pointer to char is represented
differently than a pointer to int. On word-addressed machines, char*
pointers need to store offset information as well as the machine
address.

> More importantly, assuming a T is a multi-byte object and pointers are
> simple memory addresses, is it possible for &T to be the address of a
> byte _other than_ the first (i.e. lowest) byte of the object?

No, because it isn't the address of a byte at all. It's the address
of an object of type T.

I know that's not what you meant to ask, so here are a couple of ways
to say what I think you meant.

Is it possible for the address of T to have a different representation
than the address of T's first byte? Alternatively, is it possible for
the result of converting the address of T to, say, uintptr_t to
differe from the result of converting the address of T's first byte to
uintptr_t?

I think the answer is yes. I think I would be possible (but perverse)
for an implementation to represent the address of T the same way as
the address of the *last* byte of T, or just past the end of T. If
so, then converting from some_big_type* to void* or to char* would
have to internally subtract sizeof T (or sizeof T - 1) from the
machine address.

And think about "fat pointers". Imagine a system where a C pointer
object more than just a machine address. It might, for example,
contain the base address of the largest containing object, the size of
that object, the offset of the pointed-to object, and the size of the
pointed-to object. Such a representation is useful for run-time
bounds checking.

Keith Thompson

unread,
Apr 7, 2010, 8:36:51 PM4/7/10
to
Nicklas Karlsson <karlsson...@gmail.com> writes:
> "I'd say "address" rather than "starting address". In C, an address
> is
> typed. It doesn't point to the beginning of an object, it points to
> the object."
>
> So since an address is typed, int* for example, one says that it
> points to the entire object? It points to an "int", but nevertheless
> it still points to the first allocated byte of size(int) bytes
> allocated for an int
[...]

Can you please use the conventional "> " prefix for quoted text,
and leave the attribution line in place? It makes it much easier to
follow the discussion. I see you're posting through Google Groups;
it should handle this for you automatically.

frank

unread,
Apr 8, 2010, 2:57:56 AM4/8/10
to
On Apr 7, 1:58 pm, Keith Thompson <ks...@mib.org> wrote:

At least 2 good things happened in this thread. OP surmised what he
had gathered. You included referent source to illustrate. Makes for
good reading.
--

Phil Carmody

unread,
Apr 8, 2010, 3:22:02 AM4/8/10
to

Nor do I imply that it does, in fact quite the opposite, as we were
in the context of your contrast:

>>>>> a C pointer value is conceptually different, and at a
>>>>> higher level, than a machine address.

Which is what we agree on. However where we diverge is this part:

>>>>> I find it clearer to think of
>>>>> an address (pointer value) as pointing to the entire object.

where that is being contrasted to machine addresses.

Whenever I write assembly language, I never think of the addresses
and address registers as being untyped. If they are addresses of
structs, then there always be a range of meaningful offsets (which
I will have symbolic names for) off which I can index them, and if
they are addresses of elements of an array, there's always just one
meaningful delta to increment or decrement them by (which again I
will have a symbolic name for). The language doesn't enforce this,
but you were talking about the conceptual level, and conceptually
it appears I view assembly language as being at a higher level than
you. A6 may be the address of the first byte of an object, but it's
also the address of the object, and usually I'd prefer to keep that
latter abstraction in mind than the former.

> A machine address register typically contains, say, 32 or 64
> bits of data that usually refer to some location in memory.
> The meaning of those 32 or 64 (or whatever) bits of data depends
> on what instructions are used to manipulate them. (Some machines
> don't have address registers; they just store addresses in general
> purpose registers.)
>
> A C address / pointer value, on the other hand, has a type associated
> with it (though this association probably exists only in the source
> code and at compile time).

A full enough assembly language can also support such an abstract
approach, even if it doesn't enforce it. Where in C I'd see a
pointer to type thingy, in assembly I see an address of a thingy,
not just a raw abstract address.

I've seen a lot of assembly language for a lot of different
architectures, and I appear to be in a minority, but quite often
I see assembly language code where I can see an almost direct
mapping onto C. (This is typically the 'scare' assembly - "it
needs to be fast so we must write it in assembly!", where all
they do is what they could have done in C anyway, and don't
necessarily end up with anything better than what a good C
compiler could have done.)

> If you have an array of structures, then a pointer to the entire
> array, a pointer to the first element of the array, and a pointer
> to the first member of the first element of the array are likely
> to have the same representation, but I don't think that implies
> that a pointer doesn't (conceptually) point to the entire object.

Where do you get the impression I think otherwise? I just don't
think that's a contrast with assembly language, I think that in
assembly languge they may (conceptually) point to the entire
object too.

Call me a high level assembler (but not HLA) programmer, if you will.
Actually, don't. Because I find that I can saturate pipelines in C,
and never need to resort to assembly language unless writing something
hardware-specific (and that will typically just be one or two
instructions), preferring something with a modicum of portability any
day.

Phil Carmody

unread,
Apr 8, 2010, 3:42:33 AM4/8/10
to
Seebs <usenet...@seebs.net> writes:
> On 2010-04-07, Keith Thompson <ks...@mib.org> wrote:
>> If you have an array of structures, then a pointer to the entire
>> array, a pointer to the first element of the array, and a pointer
>> to the first member of the first element of the array are likely
>> to have the same representation, but I don't think that implies
>> that a pointer doesn't (conceptually) point to the entire object.
>
> In particular, you can have three pointers which, converted to (void *),
> compare equal, but which point to different objects. One could point
> to the array, one to the first structure in that array, and one to the
> first element in that structure.

... and if that first element is an array ...

> More interestingly, so far as I can tell, the three pointers could have
> different logical bounds, such that a bounds-checking implementation could
> react differently to attempts to memcpy a large number of bytes from or
> to them, even though the pointers compare equal.

Do such fat pointer implementations really exist, check a significant
proportion of the accesses, and compile real world code into something
that works? I can't see how a pointer to a single object in an array
would be distinguished from a pointer to the rest of the elements of
the array from that single object onwards.

You (the caller) don't want fblag(FILE*fp,...) accessing fp[1], but
you don't mind strstuff(char*s,...) accesing s[1], s[2], ... . How
do you make that distinction?

Seebs

unread,
Apr 8, 2010, 3:54:18 AM4/8/10
to
On 2010-04-08, Phil Carmody <thefatphi...@yahoo.co.uk> wrote:
> Do such fat pointer implementations really exist, check a significant
> proportion of the accesses, and compile real world code into something
> that works? I can't see how a pointer to a single object in an array
> would be distinguished from a pointer to the rest of the elements of
> the array from that single object onwards.

I am told that such implementations exist, basically as extremely
elaborate instrumentation of code. It is certainly possible to make one
that correctly checks absolutely all accesses -- and even to make it
tolerably efficient by static analysis so you don't have to keep rechecking
things.

> You (the caller) don't want fblag(FILE*fp,...) accessing fp[1], but
> you don't mind strstuff(char*s,...) accesing s[1], s[2], ... . How
> do you make that distinction?

When fp was returned from fopen, it was already tagged with the information
that it pointed to a single FILE. When you pass s in to strstuff(), it's
an existing pointer which was somehow derived from an object, or allocated,
or something. So we know its bounds.

Basically, you can't get a pointer without the compiler at some point having
known what it pointed to. Therefore, you can check bounds.

Richard Bos

unread,
Apr 8, 2010, 8:46:43 AM4/8/10
to
Eric Sosman <eso...@ieee-dot-org.invalid> wrote:

> On 4/7/2010 5:05 PM, Keith Thompson wrote:
> > Well, that's one way to look at it, but I find it clearer to think of
> > an address (pointer value) as pointing to the entire object. It's one
> > way in which a C pointer value is conceptually different, and at a
> > higher level, than a machine address.
>
> It seems to me that an int* could address any byte of an
> int object (maybe even no byte at all), so long as the platform
> knows how to get at the entire int given the int* value.

Strictly speaking, of course, Keith is correct and an int * addresses
the entire int; it isn't required to even be able to address any byte
separately.
Take, for example, those historical computers on which byte pointers
were larger than word pointers, because they needed extra information on
which byte in a word was pointed at. An int *, on such a computer, would
probably be implemented as a word pointer, and point at an entire memory
word. Only void * and char * would be imlpemented as byte pointers, and
only for them would the question "Which byte does it address?" even make
sense.

> Here's a challenge: If an int* does *not* address the first
> byte of an int object, can a portable[*] program detect the fact?

> But let's take an informal notion of "portable" to mean that we're


> not allowed to inspect the bits of the int* itself

In that case, no.

If you're allowed to interpret an int * with a valid value as a set of
unsigned chars, and compare those to the bytes of a char * set to the
first byte of the same int, then you can tell that these bytes are
- always the same, at least as far as your test cases are concerned, or
- always different, with the same proviso, or
- sometimes the same, sometimes different (which would be _very_
unexpected, but AFAICT legal).
In the second (and even the third) case, you could then proceed to do
the same thing with an int * and a char * to the second byte of the int.

In the first and second cases, you would have a reasonable assumption,
though not absolute certainty, about the relative representation of
int * and char *.

Richard

Phil Carmody

unread,
Apr 8, 2010, 5:06:40 PM4/8/10
to
Seebs <usenet...@seebs.net> writes:
> On 2010-04-08, Phil Carmody <thefatphi...@yahoo.co.uk> wrote:
>> Do such fat pointer implementations really exist, check a significant
>> proportion of the accesses, and compile real world code into something
>> that works? I can't see how a pointer to a single object in an array
>> would be distinguished from a pointer to the rest of the elements of
>> the array from that single object onwards.
>
> I am told that such implementations exist, basically as extremely
> elaborate instrumentation of code. It is certainly possible to make one
> that correctly checks absolutely all accesses -- and even to make it
> tolerably efficient by static analysis so you don't have to keep rechecking
> things.
>
>> You (the caller) don't want fblag(FILE*fp,...) accessing fp[1], but
>> you don't mind strstuff(char*s,...) accesing s[1], s[2], ... . How
>> do you make that distinction?
>
> When fp was returned from fopen, it was already tagged with the information
> that it pointed to a single FILE. When you pass s in to strstuff(), it's
> an existing pointer which was somehow derived from an object, or allocated,
> or something. So we know its bounds.

Hmmm, what prevents...

static FILE __system_fps[3] = { /* populated in some system-specific way */ };

stdin = &__system_fps[0];
stdout = &__system_fps[1];
stderr = &__system_fps[2];

> Basically, you can't get a pointer without the compiler at some point having
> known what it pointed to. Therefore, you can check bounds.

Not always when there's a separation between the compiler and the
standard library.

Seebs

unread,
Apr 8, 2010, 5:15:59 PM4/8/10
to
On 2010-04-08, Phil Carmody <thefatphi...@yahoo.co.uk> wrote:
> Hmmm, what prevents...

Nothing. :)

> static FILE __system_fps[3] = { /* populated in some system-specific way */ };
> stdin = &__system_fps[0];
> stdout = &__system_fps[1];
> stderr = &__system_fps[2];

That would be fine, I think.

>> Basically, you can't get a pointer without the compiler at some point having
>> known what it pointed to. Therefore, you can check bounds.

> Not always when there's a separation between the compiler and the
> standard library.

According to the standard, there is not. :)

Phil Carmody

unread,
Apr 8, 2010, 6:18:03 PM4/8/10
to
Seebs <usenet...@seebs.net> writes:
> On 2010-04-08, Phil Carmody <thefatphi...@yahoo.co.uk> wrote:
>> Hmmm, what prevents...
>
> Nothing. :)
>
>> static FILE __system_fps[3] = { /* populated in some system-specific way */ };
>> stdin = &__system_fps[0];
>> stdout = &__system_fps[1];
>> stderr = &__system_fps[2];
>
> That would be fine, I think.
>
>>> Basically, you can't get a pointer without the compiler at some point having
>>> known what it pointed to. Therefore, you can check bounds.
>
>> Not always when there's a separation between the compiler and the
>> standard library.
>
> According to the standard, there is not. :)

According to the standard, processing of phase 8 may be separate from
phase 7 in 5.1.1.2 Translation phases. Phases 1-7 do not necessarily
have any information about the Library components linked to in phase 8.

Seebs

unread,
Apr 8, 2010, 6:33:21 PM4/8/10
to
On 2010-04-08, Phil Carmody <thefatphi...@yahoo.co.uk> wrote:
> According to the standard, processing of phase 8 may be separate from
> phase 7 in 5.1.1.2 Translation phases. Phases 1-7 do not necessarily
> have any information about the Library components linked to in phase 8.

True, but "the implementation" as a whole is entitled to have such information
if it wants to.

The claim I'm making is that an implementation *can* correctly implement such
bounds checking, not that it is *required* to be designed so that it can do
so. If an implementer chooses to separate these components in such a way that
proper bounds-checking is impossible, then that implementation can't do bounds
checking. That doesn't prevent other implementations from making choices
which allow them to do it.

bartc

unread,
Apr 8, 2010, 8:07:02 PM4/8/10
to
"Keith Thompson" <ks...@mib.org> wrote in message
news:lnvdc35...@nuthaus.mib.org...

> Nicklas Karlsson <karlsson...@gmail.com> writes:
>> Let me summarize what I make of this (hopefully its correct):

>> Silly question, the lvalue (expression) itself has no type right? But
>> when its evaluated the compiler finds out what type the object that
>> the lvalue identifies has?
>
> No, an lvalue, like any expression, has a type.

In:

int a,b;

a=b;

what's the type of the lvalue?

--
Bartc

bartc

unread,
Apr 8, 2010, 8:16:48 PM4/8/10
to
"Phil Carmody" <thefatphi...@yahoo.co.uk> wrote in message
news:87fx37h...@kilospaz.fatphil.org...
> "bartc" <ba...@freeuk.com> writes:


>> int A,B;
>>
>> A = B;

>> I would handle the assignment above as follows:
>>
>> *(&A) = B;
>
> So you'd not want your variables in registers? Why on earth not?

In my case I didn't have to contend with explicit register variables (that
would more likely be the optimiser's job).

But the *,& trick depends on the exact form of the left-hand-side, and any
register lhs would be treated differently.

In any case no actual * or & ops are explicitly inserted; it's just how the
AST is processed.

--
Bartc

Keith Thompson

unread,
Apr 8, 2010, 7:34:41 PM4/8/10
to
Phil Carmody <thefatphi...@yahoo.co.uk> writes:
> Keith Thompson <ks...@mib.org> writes:
>> Phil Carmody <thefatphi...@yahoo.co.uk> writes:
>>> Keith Thompson <ks...@mib.org> writes:
[...]
[...]

Ok.

I tend to think of assembly language as a one-to-one (or very nearly
so) representation of machine language. In my view, any typing
information you impose on an address in that context is part of your
mental model of the program, not something that's part of the language
itself.

For example, if I increment a struct foo* in C, the fact that it
advances sizeof(struct foo) bytes is part of the semantics of the
language. If I do the same thing with, say, an ADD instruction in
assembly, that's not part of the semantics of the ADD instruction.

Mentally imposing higher-level concepts on asembly language programs
is a very good thing. It's just not, in my experience, part of the
language itself.

Disclaimer: I haven't written any assembly language in longer than I
care to admit.

Keith Thompson

unread,
Apr 8, 2010, 7:37:00 PM4/8/10
to

I see two lvalues there, ``a'' and ``b'' (the latter is being used in
a context that doesn't require an lvalue). Both of them are of type
int.

That seems sufficiently obvious that I suspect there was some other
meaning in your question, but I don't know what it might be.

bartc

unread,
Apr 8, 2010, 9:13:33 PM4/8/10
to
"Keith Thompson" <ks...@mib.org> wrote in message
news:lnwrwh3...@nuthaus.mib.org...

> "bartc" <ba...@freeuk.com> writes:
>> "Keith Thompson" <ks...@mib.org> wrote in message
>> news:lnvdc35...@nuthaus.mib.org...
>>> Nicklas Karlsson <karlsson...@gmail.com> writes:
>>>> Let me summarize what I make of this (hopefully its correct):
>>
>>
>>>> Silly question, the lvalue (expression) itself has no type right? But
>>>> when its evaluated the compiler finds out what type the object that
>>>> the lvalue identifies has?
>>>
>>> No, an lvalue, like any expression, has a type.
>>
>> In:
>>
>> int a,b;
>>
>> a=b;
>>
>> what's the type of the lvalue?
>
> I see two lvalues there, ``a'' and ``b'' (the latter is being used in
> a context that doesn't require an lvalue). Both of them are of type
> int.
>
> That seems sufficiently obvious that I suspect there was some other
> meaning in your question, but I don't know what it might be.

Only that the OP seemed to have got the idea that an lvalue was some sort of
address, in which case it would have a type that was 'pointer-to-something'.
But in fact lvalues and rvalues look the same, and even have the same types!

But I would say that lvalue-ness was a kind of property or attribute of a
term, which you can't deduce just from looking at the expression or even
knowing it's type. It that case, it wouldn't be meaningful for an lvalue to
have a type.

--
Bartc

Keith Thompson

unread,
Apr 8, 2010, 8:39:34 PM4/8/10
to

Hmm.

The lvalue-ness of an expression is a binary attribute of the
expression, i.e., a given expression either is or is not an lvalue.
So "lvalue-ness" doesn't have a type. But *an lvalue* is an
expression, which therefore does have a type.

An lvalue doesn't have a pointer type (unless it happens to designate
an object of pointer type), but it does have a type (the type of the
object it designates).

If you look through section 6.5 of the standard, you'll find that the
section for each type of expression states whether it's an lvalue or
not. You very nearly *can* tell whether an expression is an lvalue or
not just by looking at it. If it's an object name, or if the
highest-level operator is unary * or [], it's an lvalue. A
parenthesized lvalue is an lvalue; a parenthesized non-lvalue is not
an lvalue. And so forth.

Uno

unread,
Apr 8, 2010, 10:11:29 PM4/8/10
to
Keith Thompson wrote:

> You very nearly *can* tell whether an expression is an lvalue or
> not just by looking at it.

Is there an extra negative in this sentence?
--
Uno

Keith Thompson

unread,
Apr 9, 2010, 1:00:01 AM4/9/10
to

No, no, of course not.

Rephrasing:

You very nearly *can* tell

whether or not an expression is an lvalue


just by looking at it.

--

Phil Carmody

unread,
Apr 9, 2010, 4:45:50 AM4/9/10
to
Keith Thompson <ks...@mib.org> writes:
> Phil Carmody <thefatphi...@yahoo.co.uk> writes:
[SNIP - my approach to assembly]

> Mentally imposing higher-level concepts on asembly language programs
> is a very good thing. It's just not, in my experience, part of the
> language itself.

In general, certainly. I remember TASM used to have a reasonably
rich syntax which would encourage a higher level approach.

> Disclaimer: I haven't written any assembly language in longer than I
> care to admit.

I'm a bit of a pretender myself. The last 2 times I sat down to
write asm were about 5 years back.

One was FP stuff on x86, and managing the stack was so horrible
I wrote a perl script to generate the actual assembly from a high-
level wrapper language. (I'd basically name the values on the stack
and the script would track their positions as they bubbled up and
down. And I used infix notation for operations.)

The other was just before I ran my C code through Freescale's
emulator, and it told me that I was saturating every single
pipeline at every clock tick. Quickest coding session ever -
what do I need to optimise? Nothing! Job done.

Asm only when I need it as C isn't up for the job, and recently
I simply don't need it. I'm very impressed with gcc nowadays,
I really am. (OK, vendor compilers can sometimes beat it, but I
know they explicitly mentioned that their goal was make it output
the equivalent of what DEC/Compaq's C compiler emitted on the
alpha, and they have pushed very hard to achieve that.)

bartc

unread,
Apr 9, 2010, 6:14:48 AM4/9/10
to
"Keith Thompson" <ks...@mib.org> wrote in message
news:lnocht3...@nuthaus.mib.org...
> "bartc" <ba...@freeuk.com> writes:

>> But I would say that lvalue-ness was a kind of property or attribute
>> of a term, which you can't deduce just from looking at the expression
>> or even knowing it's type. It that case, it wouldn't be meaningful for
>> an lvalue to have a type.
>
> Hmm.
>
> The lvalue-ness of an expression is a binary attribute of the
> expression, i.e., a given expression either is or is not an lvalue.
> So "lvalue-ness" doesn't have a type. But *an lvalue* is an
> expression, which therefore does have a type.
>
> An lvalue doesn't have a pointer type (unless it happens to designate
> an object of pointer type), but it does have a type (the type of the
> object it designates).
>
> If you look through section 6.5 of the standard, you'll find that the
> section for each type of expression states whether it's an lvalue or
> not. You very nearly *can* tell whether an expression is an lvalue or
> not just by looking at it.

Not in isolation. You also need to look at the definitions of any names
involved.

#define a 100
enum {b=101};
int c=102;

a=42; /* not lvalue */
b=43; /* not lvalue */
c=44;

Plus other examples (function names, struct members) which generate other
errors even before it checks for lvalue-ness.

--
Bartc

Keith Thompson

unread,
Apr 9, 2010, 11:35:36 AM4/9/10
to
"bartc" <ba...@freeuk.com> writes:
> "Keith Thompson" <ks...@mib.org> wrote in message
> news:lnocht3...@nuthaus.mib.org...
[...]

>> If you look through section 6.5 of the standard, you'll find that the
>> section for each type of expression states whether it's an lvalue or
>> not. You very nearly *can* tell whether an expression is an lvalue or
>> not just by looking at it.
>
> Not in isolation. You also need to look at the definitions of any
> names involved.

Which is why I wrote "nearly". But yes, thank you for providing
concrete examples.

> #define a 100
> enum {b=101};
> int c=102;
>
> a=42; /* not lvalue */
> b=43; /* not lvalue */
> c=44;
>
> Plus other examples (function names, struct members) which generate
> other errors even before it checks for lvalue-ness.

I would argue that if something attempts to assign a value to a
function name, it's not an expression in the first place, and
therefore not what I was talking about.

On the other hand, ``x + y'' is definitely not an lvalue, and
``x[y]]'' definitely is (assuming both are valid expressions).

Joe Wright

unread,
Apr 9, 2010, 9:24:11 PM4/9/10
to
On 4/9/2010 01:00, Keith Thompson wrote:
> Uno<merril...@q.com> writes:
>> Keith Thompson wrote:
>>> You very nearly *can* tell whether an expression is an lvalue or
>>> not just by looking at it.
>>
>> Is there an extra negative in this sentence?
>
> No, no, of course not.
>
> Rephrasing:
>
> You very nearly *can* tell
> whether or not an expression is an lvalue
> just by looking at it.
>
I don't understand the 'very nearly' qualifier. Can we identify an
expression which designates an object by looking at it? How else?

--
Joe Wright
"If you rob Peter to pay Paul you can depend on the support of Paul."

Keith Thompson

unread,
Apr 10, 2010, 12:00:32 AM4/10/10
to
Joe Wright <joeww...@comcast.net> writes:
> On 4/9/2010 01:00, Keith Thompson wrote:
>> Uno<merril...@q.com> writes:
>>> Keith Thompson wrote:
>>>> You very nearly *can* tell whether an expression is an lvalue or
>>>> not just by looking at it.
>>>
>>> Is there an extra negative in this sentence?
>>
>> No, no, of course not.
>>
>> Rephrasing:
>>
>> You very nearly *can* tell
>> whether or not an expression is an lvalue
>> just by looking at it.
>>
> I don't understand the 'very nearly' qualifier. Can we identify an
> expression which designates an object by looking at it? How else?

Case 1:

int x;
/* The expression ``x'' is an lvalue. */

Case 2:

enum { x };
/* The expression ``x'' is not an lvalue. */

In many cases ("very nearly" may have been an overstatement), you can
tell whether a given expression is an lvalue or not just by looking at
it. ``*x'' and ``x[y]'' are lvalues; ``&x'' and ``2+2'' are not.

J de Boyne Pollard

unread,
Apr 10, 2010, 3:07:16 AM4/10/10
to
> >> You very nearly *can* tell whether an expression is an lvalue or
> >> not just by looking at it.
>
> > Is there an extra negative in this sentence?
>
> No, no, of course not.
>
> Rephrasing:
>
> You very nearly *can* tell
> whether or not an expression is an lvalue
> just by looking at it.

For what it's worth: Several authorities consider "whether or not"
to be synonymous with "whether", and the "or not" to be, as M.
Uno might have been enquiring, superfluous.

J de Boyne Pollard

unread,
Apr 10, 2010, 3:18:36 AM4/10/10
to
> (I just checked the C++ standard.  C++ definitely defines
> "rvalue" differently than C does.  Its definition of
> "lvalue" may or may not be consistent with C's.)

At this point, what the current C++ standard's definition
of the concept may be is less interesting than all of the
new concepts that were put into the FCD at the last
minute just recently. The FCD for the C++ Standard has
lvalues, rvalues, glvalues, xvalues, and prvalues.

I'd be interested in your views on those, given what
you wrote earlier in this thread.

Joe Wright

unread,
Apr 10, 2010, 12:26:13 PM4/10/10
to
On 4/10/2010 00:00, Keith Thompson wrote:
> Joe Wright<joeww...@comcast.net> writes:
>> On 4/9/2010 01:00, Keith Thompson wrote:
>>> Uno<merril...@q.com> writes:
>>>> Keith Thompson wrote:
>>>>> You very nearly *can* tell whether an expression is an lvalue or
>>>>> not just by looking at it.
>>>>
>>>> Is there an extra negative in this sentence?
>>>
>>> No, no, of course not.
>>>
>>> Rephrasing:
>>>
>>> You very nearly *can* tell
>>> whether or not an expression is an lvalue
>>> just by looking at it.
>>>
>> I don't understand the 'very nearly' qualifier. Can we identify an
>> expression which designates an object by looking at it? How else?
>
> Case 1:
>
> int x;
> /* The expression ``x'' is an lvalue. */
>
> Case 2:
>
> enum { x };
> /* The expression ``x'' is not an lvalue. */
>
> In many cases ("very nearly" may have been an overstatement), you can
> tell whether a given expression is an lvalue or not just by looking at
> it. ``*x'' and ``x[y]'' are lvalues; ``&x'' and ``2+2'' are not.
>
So now 'many cases'. Can you give an example of an expression whose
'lvalueness' cannot be determined by simple inspection?

Tim Rentsch

unread,
Apr 10, 2010, 12:34:06 PM4/10/10
to
Eric Sosman <eso...@ieee-dot-org.invalid> writes:

> On 4/7/2010 5:05 PM, Keith Thompson wrote:

>> Nicklas Karlsson<karlsson...@gmail.com> writes:
>>> "I'd say "address" rather than "starting address". In C, an address
>>> is typed. It doesn't point to the beginning of an object, it points
>>> to the object."
>>>
>>> So since an address is typed, int* for example, one says that it
>>> points to the entire object? It points to an "int", but nevertheless
>>> it still points to the first allocated byte of size(int) bytes
>>> allocated for an int
>>

>> Well, that's one way to look at it, but I find it clearer to think of
>> an address (pointer value) as pointing to the entire object. It's one

>> way in which a C pointer value is conceptually different, and at a


>> higher level, than a machine address.
>

> It seems to me that an int* could address any byte of an
> int object (maybe even no byte at all), so long as the platform
> knows how to get at the entire int given the int* value.

> Converting to a char* must produce a pointer to the [0] element
> of a sizeof(int)-byte array that overlays the int object, but
> that's no obstacle: conversion need not be a bit-for-bit copy.


>
> Here's a challenge: If an int* does *not* address the first
> byte of an int object, can a portable[*] program detect the fact?

> [snip explanation of "portable"]

This question has no answer because it has no meaning. In some
implementations we might say a particular pointer does or does
not address the first byte of the designated object, but there's
no way to give the phrase a meaning that makes sense in all
implementations. The notion of whether a pointer "addresses the
first byte of (some) object" just doesn't exist in the abstract
machine. Also apparently not in some actual machines either, but
what matters is whether such a notion exists in the abstract
machine, or, if one prefers, whether it _must_ exist in all
possible implementations on all possible machines. And the
answer to that question is just "no".

Tim Rentsch

unread,
Apr 10, 2010, 12:36:45 PM4/10/10
to
Stephen Sprunk <ste...@sprunk.org> writes:

> On 07 Apr 2010 17:53, Keith Thompson wrote:
>> A machine address register typically contains, say, 32 or 64
>> bits of data that usually refer to some location in memory.
>> The meaning of those 32 or 64 (or whatever) bits of data depends
>> on what instructions are used to manipulate them. (Some machines
>> don't have address registers; they just store addresses in general
>> purpose registers.)
>>
>> A C address / pointer value, on the other hand, has a type associated
>> with it (though this association probably exists only in the source
>> code and at compile time).
>

> A good point. A C pointer has a type, which implies a size; a machine
> address does not have a type, so the particular instruction(s) that
> use/manipulate that (typeless) address must imply the size of the
> object. The compiler uses pointer type information to choose those
> instructions, though...


>
>> If you have an array of structures, then a pointer to the entire
>> array, a pointer to the first element of the array, and a pointer
>> to the first member of the first element of the array are likely
>> to have the same representation, but I don't think that implies
>> that a pointer doesn't (conceptually) point to the entire object.
>

> True, at a C level, but is there really a difference at the machine
> level on real-world implementations?
>
> More importantly, assuming a T is a multi-byte object and pointers are
> simple memory addresses, is it possible for &T to be the address of a
> byte _other than_ the first (i.e. lowest) byte of the object?

Certainly it is. Didn't you just explain this yourself
in another posting a short time later (or was it earlier)?

Tim Rentsch

unread,
Apr 10, 2010, 12:48:25 PM4/10/10
to
Phil Carmody <thefatphi...@yahoo.co.uk> writes:

Technically 'stdin' is a macro. But it could be defined
thusly

#define stdin stdin
extern FILE *const stdin = &__system_fps[0];
/* etc */

Tim Rentsch

unread,
Apr 10, 2010, 12:49:57 PM4/10/10
to
Keith Thompson <ks...@mib.org> writes:

> Joe Wright <joeww...@comcast.net> writes:
>> On 4/9/2010 01:00, Keith Thompson wrote:
>>> Uno<merril...@q.com> writes:
>>>> Keith Thompson wrote:
>>>>> You very nearly *can* tell whether an expression is an lvalue or
>>>>> not just by looking at it.
>>>>
>>>> Is there an extra negative in this sentence?
>>>
>>> No, no, of course not.
>>>
>>> Rephrasing:
>>>
>>> You very nearly *can* tell
>>> whether or not an expression is an lvalue
>>> just by looking at it.
>>>
>> I don't understand the 'very nearly' qualifier. Can we identify an
>> expression which designates an object by looking at it? How else?
>
> Case 1:
>
> int x;
> /* The expression ``x'' is an lvalue. */
>
> Case 2:
>
> enum { x };
> /* The expression ``x'' is not an lvalue. */
>
> In many cases ("very nearly" may have been an overstatement), you can
> tell whether a given expression is an lvalue or not just by looking at
> it. ``*x'' and ``x[y]'' are lvalues; ``&x'' and ``2+2'' are not.

If we have

int x[10][20];

then 'x[y]' is not an lvalue.

Tim Rentsch

unread,
Apr 10, 2010, 12:59:46 PM4/10/10
to

In informal English, or in formal technical English?
What's appropriate in one might not be appropriate
in the other. In formal technical English, saying
"whether or not" clearly removes a possible ambiguity;
it also allows expressing a condition of being possible
to know that something IS something else, without always
being able to know that something ISN'T something else.
For example, we can know for sure that a given program has
no uninitialized variables, but we can't always know for
sure that a program _doesn't_ have uninitialized
variables.

SG

unread,
Apr 10, 2010, 1:25:37 PM4/10/10
to
Tim Rentsch wrote:
>
> If we have
>
>     int x[10][20];
>
> then 'x[y]' is not an lvalue.

Not true. &x[y] is valid and yields a pointer of type int(*)[20].
Since the unary & requires an lvalue expression operand x[y] cannot be
an rvalue. In fact, it is an lvalue of type int[20]. Array-to-pointer
decay only happens if it's used in a context where an rvalue is
needed.

In C++ the behaviour is exactly the same and only more apparent in
combination with references and template argument deduction.

Cheers,
SG

Tim Rentsch

unread,
Apr 10, 2010, 2:18:49 PM4/10/10
to
SG <s.ges...@gmail.com> writes:

> Tim Rentsch wrote:
>>
>> If we have
>>
>> int x[10][20];
>>
>> then 'x[y]' is not an lvalue.
>
> Not true. &x[y] is valid and yields a pointer of type int(*)[20].
> Since the unary & requires an lvalue expression operand x[y] cannot be
> an rvalue. In fact, it is an lvalue of type int[20]. Array-to-pointer
> decay only happens if it's used in a context where an rvalue is

> needed. [snip]

Of course what I meant was 'x[y]' as an expression by
itself is not an lvalue. In the context of '&x[y]'
the sub-expression 'x[y]' is an lvalue, but I was
speaking of 'x[y]' not in that context.

I don't mind being corrected, but please don't
deliberately misunderstand me to supply a
"correction".

Keith Thompson

unread,
Apr 10, 2010, 2:38:14 PM4/10/10
to
Joe Wright <joeww...@comcast.net> writes:
> On 4/10/2010 00:00, Keith Thompson wrote:
[...]

>> Case 1:
>>
>> int x;
>> /* The expression ``x'' is an lvalue. */
>>
>> Case 2:
>>
>> enum { x };
>> /* The expression ``x'' is not an lvalue. */
>>
>> In many cases ("very nearly" may have been an overstatement), you can
>> tell whether a given expression is an lvalue or not just by looking at
>> it. ``*x'' and ``x[y]'' are lvalues; ``&x'' and ``2+2'' are not.
>>
> So now 'many cases'. Can you give an example of an expression whose
> 'lvalueness' cannot be determined by simple inspection?

Look up a few lines. You see that expression ``x''? Can you
determine whether it's an lvalue by simple inspection?

Isn't that exactly what I was just saying?

Eric Sosman

unread,
Apr 10, 2010, 2:53:34 PM4/10/10
to
On 4/10/2010 12:34 PM, Tim Rentsch wrote:
> Eric Sosman<eso...@ieee-dot-org.invalid> writes:
>> [...]

>> Here's a challenge: If an int* does *not* address the first
>> byte of an int object, can a portable[*] program detect the fact?
>> [snip explanation of "portable"]
>
> This question has no answer because it has no meaning. [...]

Very good! You've grasped my point, exactly!

--
Eric Sosman
eso...@ieee-dot-org.invalid

Eric Sosman

unread,
Apr 10, 2010, 2:56:16 PM4/10/10
to
On 4/10/2010 12:49 PM, Tim Rentsch wrote:
>
> If we have
>
> int x[10][20];
>
> then 'x[y]' is not an lvalue.

No, it *is* an lvalue (assuming a valid `y'). It's not a
"modifiable lvalue," but it's an lvalue. 6.3.2.1p1.

--
Eric Sosman
eso...@ieee-dot-org.invalid

Tim Rentsch

unread,
Apr 10, 2010, 3:06:49 PM4/10/10
to
Eric Sosman <eso...@ieee-dot-org.invalid> writes:

> On 4/10/2010 12:49 PM, Tim Rentsch wrote:
>>
>> If we have
>>
>> int x[10][20];
>>
>> then 'x[y]' is not an lvalue.
>
> No, it *is* an lvalue (assuming a valid `y'). It's not a
> "modifiable lvalue," but it's an lvalue. 6.3.2.1p1.

Surely you must know what I mean. The expression
'x[y]' is an array and as such it is converted to
a pointer to the array's first element and is no
longer an lvalue. 6.3.2.1p3.

Keith Thompson

unread,
Apr 10, 2010, 3:54:47 PM4/10/10
to

That conversion isn't always done.

Expressions never appear in isolation. Even in the statement
x[y];
the expression ``x[y]'' is part of an expression statement. There's
always some context, and that context determines whether or not the
conversion takes place.

Given the above declaration, if x[y] appears in a context in which
the conversion takes place, it's not an lvalue; otherwise, it is.

Eric Sosman

unread,
Apr 10, 2010, 3:57:43 PM4/10/10
to
On 4/10/2010 3:06 PM, Tim Rentsch wrote:
> Eric Sosman<eso...@ieee-dot-org.invalid> writes:
>
>> On 4/10/2010 12:49 PM, Tim Rentsch wrote:
>>>
>>> If we have
>>>
>>> int x[10][20];
>>>
>>> then 'x[y]' is not an lvalue.
>>
>> No, it *is* an lvalue (assuming a valid `y'). It's not a
>> "modifiable lvalue," but it's an lvalue. 6.3.2.1p1.
>
> Surely you must know what I mean.

Apparently not. I mistook your "`x[y] is not an lvalue"
to mean that `x[y]' is not an lvalue. Sorry about that.

> The expression
> 'x[y]' is an array and as such it is converted to
> a pointer to the array's first element and is no
> longer an lvalue. 6.3.2.1p3.

... which has nothing to do with the lvalue-ness of the
expression `x[y]'. It talks of the non-lvalue-ness of a value
*derived from* that expression in some contexts (those not
covered by the paragraph's first phrase, which begins with the
telltale word "Except").

--
Eric Sosman
eso...@ieee-dot-org.invalid

Tim Rentsch

unread,
Apr 10, 2010, 4:21:25 PM4/10/10
to
Keith Thompson <ks...@mib.org> writes:

> Tim Rentsch <t...@alumni.caltech.edu> writes:
>> Eric Sosman <eso...@ieee-dot-org.invalid> writes:
>>> On 4/10/2010 12:49 PM, Tim Rentsch wrote:
>>>>
>>>> If we have
>>>>
>>>> int x[10][20];
>>>>
>>>> then 'x[y]' is not an lvalue.
>>>
>>> No, it *is* an lvalue (assuming a valid `y'). It's not a
>>> "modifiable lvalue," but it's an lvalue. 6.3.2.1p1.
>>
>> Surely you must know what I mean. The expression
>> 'x[y]' is an array and as such it is converted to
>> a pointer to the array's first element and is no
>> longer an lvalue. 6.3.2.1p3.
>
> That conversion isn't always done.
>
> Expressions never appear in isolation. Even in the statement
> x[y];
> the expression ``x[y]'' is part of an expression statement. There's
> always some context, and that context determines whether or not the
> conversion takes place.
>
> Given the above declaration, if x[y] appears in a context in which
> the conversion takes place, it's not an lvalue; otherwise, it is.

Quite right. I'd already responded on this point, in
my followup to SG, so I didn't bother saying it
again.

Tim Rentsch

unread,
Apr 10, 2010, 4:49:28 PM4/10/10
to
Eric Sosman <eso...@ieee-dot-org.invalid> writes:

It seems to me you're being deliberately obtuse. However, ignoring
that, your statement above is still at odds with the text in the
Standard. 6.3.2.1p3 (first sentence):

Except when it is the operand of the sizeof operator or the
unary & operator, or is a string literal used to initialize
an array, an expression that has type ``array of type'' is
converted to _an expression_ [emphasis added] with type
``pointer to type'' that points to the initial element of
the array object and is not an lvalue.

The Standard is talking about the lvalue-ness of an expression,
not of a value. Furthermore I think most C developers with some
exposure to the term 'lvalue' in it Standardese sense would say
the same thing -- given 'int x[10][20];', the expression 'x[10]'
doesn't behave the same way as a simple variable reference (which
is an lvalue), in that it doesn't designate an object, cause a
read access on the RHS of an assignment, etc[*] -- that is, it's
not an lvalue. Since the Standard agrees with my usage, and
since most other folks seem to know what I mean, your comments
left me rather baffled.


[*] Yes, for the nth time, whether 'x[10]' behaves as an
lvalue depends on context, such as '&x[10]' when it does.
My usage of 'x[10]' is meant to apply to the typical
cases, not the exceptional ones such as '&x[10]'.

Keith Thompson

unread,
Apr 10, 2010, 5:21:25 PM4/10/10
to
Tim Rentsch <t...@x-alumni2.alumni.caltech.edu> writes:
[...]

> If we have
>
> int x[10][20];
>
> then 'x[y]' is not an lvalue.

On further thought, yes, it is, regardless of the context in which it
appears.

C99 6.5.2.1p2:

The definition of the subscript operator [] is that E1[E2] is
identical to (*((E1)+(E2))).

C99 6.5.3.2p4:

The unary * operator denotes indirection. 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.

And just for completeness, C99 6.5.1p5:

A parenthesized expression ... is an lvalue ... if the
unparenthesized expression is ... an lvalue.

(Please feel free to confirm that I haven't left out anything relevant
to this case.)

Unless I've missed something (which is always possible), if you want
to argue that it's not an lvalue, your argument is with the Standard.

Tim Rentsch

unread,
Apr 10, 2010, 5:32:56 PM4/10/10
to
Keith Thompson <ks...@mib.org> writes:

> Tim Rentsch <t...@x-alumni2.alumni.caltech.edu> writes:
> [...]
>> If we have
>>
>> int x[10][20];
>>
>> then 'x[y]' is not an lvalue.
>
> On further thought, yes, it is, regardless of the context in which it
> appears.
>
> C99 6.5.2.1p2:
>
> The definition of the subscript operator [] is that E1[E2] is
> identical to (*((E1)+(E2))).
>
> C99 6.5.3.2p4:
>
> The unary * operator denotes indirection. 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.
>
> And just for completeness, C99 6.5.1p5:
>
> A parenthesized expression ... is an lvalue ... if the
> unparenthesized expression is ... an lvalue.
>
> (Please feel free to confirm that I haven't left out anything relevant
> to this case.)
>
> Unless I've missed something (which is always possible), if you want
> to argue that it's not an lvalue, your argument is with the Standard.

Please see my response to Eric Sosman. 6.3.2.1p3 seems pretty
clear that an expression with array type becomes (in most
contexts) an expression that is not an lvalue. And this agrees,
I think, with most people's intuition about what an lvalue is
(assuming of course that they're at least passingly familiar with
'lvalue' as it is used in the Standard).

SG

unread,
Apr 10, 2010, 5:43:14 PM4/10/10
to
On 10 Apr., 22:49, Tim Rentsch wrote:
>
>     Except when it is the operand of the sizeof operator or the
>     unary & operator, or is a string literal used to initialize
>     an array, an expression that has type ``array of type'' is
>     converted to _an expression_ [emphasis added] with type
>     ``pointer to type'' that points to the initial element of
>     the array object and is not an lvalue.
>
> The Standard is talking about the lvalue-ness of an expression,
> not of a value.  Furthermore I think most C developers with some
> exposure to the term 'lvalue' in it Standardese sense would say
> the same thing -- given 'int x[10][20];', the expression 'x[10]'
> doesn't behave the same way as a simple variable reference (which
> is an lvalue), in that it doesn't designate an object, cause a
> read access on the RHS of an assignment, etc[*] -- that is, it's
> not an lvalue.  Since the Standard agrees with my usage, and
> since most other folks seem to know what I mean, your comments
> left me rather baffled.

When you say 'x[y]' is not an lvalue then you include this implicit
conversion and refer to the expression you get after the conversion.
When I said 'x[y]' is an lvalue I excluded this conditional implicit
conversion and referred to the expression before the implicit
conversion. I was probably as buffled as you are now when I got your
response where you accused me of "deliberately misunderstanding"
things. :-)

In practice, these differing POVs don't matter in C. But they actually
do matter in C++ and the POV that x[y] is an lvalue just like x is an
lvalue allows you to get away with more regularity and fewer special
case rules. I guess you havn't read the C++ standard (which is where
I'm coming from) just like I just skimmed through the C standard for
the first time. I hereby admit that I didn't expect to see such
wording you can find in 3.2.1/3. That would at least explain the
situation.

Cheers,
SG

Keith Thompson

unread,
Apr 10, 2010, 6:08:52 PM4/10/10
to

It says it "is converted to" such an expression, not (quite) that it
"becomes" such an expression. So does the unconverted expression
still exist, and is *it* an lvalue?

Darned if I know. But yes, you have a good point.

Tim Rentsch

unread,
Apr 10, 2010, 6:42:01 PM4/10/10
to
SG <s.ges...@gmail.com> writes:

> On 10 Apr., 22:49, Tim Rentsch wrote:
>>
>> Except when it is the operand of the sizeof operator or the
>> unary & operator, or is a string literal used to initialize
>> an array, an expression that has type ``array of type'' is
>> converted to _an expression_ [emphasis added] with type
>> ``pointer to type'' that points to the initial element of
>> the array object and is not an lvalue.
>>
>> The Standard is talking about the lvalue-ness of an expression,
>> not of a value. Furthermore I think most C developers with some
>> exposure to the term 'lvalue' in it Standardese sense would say
>> the same thing -- given 'int x[10][20];', the expression 'x[10]'
>> doesn't behave the same way as a simple variable reference (which
>> is an lvalue), in that it doesn't designate an object, cause a
>> read access on the RHS of an assignment, etc[*] -- that is, it's
>> not an lvalue. Since the Standard agrees with my usage, and
>> since most other folks seem to know what I mean, your comments
>> left me rather baffled.
>
> When you say 'x[y]' is not an lvalue then you include this implicit
> conversion and refer to the expression you get after the conversion.
> When I said 'x[y]' is an lvalue I excluded this conditional implicit
> conversion and referred to the expression before the implicit
> conversion.

I didn't understand that from your posting. I thought
you were talking only about the '&x[y]' case (or
similar ones).

> I was probably as buffled as you are now when I got your
> response where you accused me of "deliberately misunderstanding"
> things. :-)

When I wrote "please don't deliberately misunderstand me to
supply a 'correction'", it was meant only as a request regarding
future behavior, not as an accusation over past behavior.


> In practice, these differing POVs don't matter in C. But they actually
> do matter in C++ and the POV that x[y] is an lvalue just like x is an
> lvalue allows you to get away with more regularity and fewer special
> case rules. I guess you havn't read the C++ standard (which is where
> I'm coming from) just like I just skimmed through the C standard for
> the first time. I hereby admit that I didn't expect to see such
> wording you can find in 3.2.1/3. That would at least explain the
> situation.

I haven't read the C++ standard in any detail but I will take
your word for it that it's different. The fact that there _is_ a
difference reinforces my view that it's important to distinguish
such expressions as not being lvalues in C.

Tim Rentsch

unread,
Apr 10, 2010, 6:59:23 PM4/10/10
to
Keith Thompson <ks...@mib.org> writes:

Yes, I was being casual in my wording, which I thought was okay
since I'd referred to an earlier posting where I gave exact
wording.

> So does the unconverted expression
> still exist, and is *it* an lvalue?

In a sense I think it does, because it's also possible to
have expressions that have array type that are not lvalues
(meaning, even before conversion), and this can be relevant
in some contexts. (*Disclaimer: I think. The case I'm
thinking of is accessing an array member of an value
expression with a structure value, and I don't think too
much about the rules for such cases because doing anything
too exotic in those cases is IMO bordering on insanity.
YMMV.)

> Darned if I know. But yes, you have a good point.

My main point is both that it's the language of the Standard and
it's how most people with C experience think of expressions with
array type. I wasn't meaning to argue anything controversial,
just point out a case that's not completely uncommon but is
sometimes easy to overlook. My comment to your earlier posting
certainly wasn't meant as a correction, nor did I think I was
telling you anything you didn't already know. I guess I should
have made that clearer.

Uno

unread,
Apr 10, 2010, 7:49:44 PM4/10/10
to
Keith Thompson wrote:
> "bartc" <ba...@freeuk.com> writes:
>> "Keith Thompson" <ks...@mib.org> wrote in message
>> news:lnocht3...@nuthaus.mib.org...
> [...]
>>> If you look through section 6.5 of the standard, you'll find that the
>>> section for each type of expression states whether it's an lvalue or
>>> not. You very nearly *can* tell whether an expression is an lvalue or

>>> not just by looking at it.
>> Not in isolation. You also need to look at the definitions of any
>> names involved.


Sorry, Keith, for spurring you into extra innings on this. If you look
above, you can see how I read an extra not into it.

I counted them, and there were 2, which in english might cancel each
other out. (Russian not so much.)

My eyes have had a hard year a concussion, dryness and allergies.

snip
--
Uno

pete

unread,
Apr 10, 2010, 11:44:00 PM4/10/10
to
Eric Sosman wrote:
>
> On 4/10/2010 12:49 PM, Tim Rentsch wrote:
> >
> > If we have
> >
> > int x[10][20];
> >
> > then 'x[y]' is not an lvalue.
>
> No, it *is* an lvalue (assuming a valid `y'). It's not a
> "modifiable lvalue," but it's an lvalue. 6.3.2.1p1.

y only has to be valid enough to compile.
Whether or not the value of y causes x[y] to be undefined,
has no bearing on whether or not x[y] is an lvalue.

--
pete

J de Boyne Pollard

unread,
Apr 11, 2010, 3:20:12 AM4/11/10
to
> > For what it's worth: Several authorities consider "whether or not"
> > to be synonymous with "whether", and the "or not" to be, as M.
> > Uno might have been enquiring, superfluous.
>
> In informal English, or in formal technical English?

The authorities that I've seen don't make a distinction
when talking about "whether or not".

> In formal technical English, saying "whether or not"

> clearly removes a possible ambiguity; [...]

What ambiguity?

> For example, we can know for sure that a given
> program has no uninitialized variables, but we
> can't always know for sure that a program
> _doesn't_ have uninitialized variables.

You've just apparently said the same thing twice
("has no uninitialized variables" "does not have
uninitialized variables") and cast the twain as
opposites. Did you make a typing error there?

Stephen Sprunk

unread,
Apr 11, 2010, 4:48:24 PM4/11/10
to
On 06 Apr 2010 21:08, Keith Thompson wrote:
> Stephen Sprunk <ste...@sprunk.org> writes:
> [...]
>> Is dropping the term "rvalue" why the C Standard's definition of
>> "lvalue" is so tortuous--and continues to get even more so with each
>> revision as they try to correct problems with the prior one?
>
> I don't think so. I think the problem is with "lvalue", not with
> "rvalue". I find lvalues to be a more complicated concept anyway, for
> either definition.

Well, I thought that defining "lvalue" would be more straightforward if
there were something to contrast it to, as opposed to it being a lone
partial subset of "value".

>> Digesting what you have said, this is how I mentally process the terms:
>>
>> . A modifiable lvalue is any expression which _could_ have a meaningful
>> result if it appeared on the LHS of an assignment.
>> . A non-modifiable lvalue is any expression which _would_ be a
>> modifiable lvalue if it weren't const.
>> . An rvalue is any expression which isn't an lvalue.
>> . If an lvalue occurs in a context which does not require an lvalue, it
>> is evaluated to produce an rvalue.
>>
>> Granted, that definitely isn't how the C Standard uses or defines the
>> terms, but aside from that, is it accurate enough for someone other than
>> a compiler author? Or is there some subtle flaw in there that will bite
>> me someday?
>
> Your definition of "rvalue" directly contradicts the standard's
> definition; an "rvalue" is not an expression, it's the result of
> evaluating an expression.

Point taken.

> I'd probably drop your third bullet and replace "rvalue" by "value" in
> the fourth.

See above.

S

--
Stephen Sprunk "God does not play dice." --Albert Einstein
CCIE #3723 "God is an inveterate gambler, and He throws the
K5SSS dice at every possible opportunity." --Stephen Hawking

Stephen Sprunk

unread,
Apr 11, 2010, 4:49:40 PM4/11/10
to
On 07 Apr 2010 13:40, lawrenc...@siemens.com wrote:

> Stephen Sprunk <ste...@sprunk.org> wrote:
>>
>> . A non-modifiable lvalue is any expression which _would_ be a
>> modifiable lvalue if it weren't const.
>
> An array is a non-modifiable lvalue but is not const (only the elements
> can be const in C, not the array itself).

Argh! Thanks for pointing out that case. It would never occur to me to
do that, but mainly because I already know it's not legal; one _could_
define the semantics of assignment to an array, but C does not.

Stephen Sprunk

unread,
Apr 11, 2010, 4:53:58 PM4/11/10
to
On 08 Apr 2010 02:42, Phil Carmody wrote:
> Seebs <usenet...@seebs.net> writes:
>> On 2010-04-07, Keith Thompson <ks...@mib.org> wrote:
>> More interestingly, so far as I can tell, the three pointers could have
>> different logical bounds, such that a bounds-checking implementation could
>> react differently to attempts to memcpy a large number of bytes from or
>> to them, even though the pointers compare equal.

>
> Do such fat pointer implementations really exist, check a significant
> proportion of the accesses, and compile real world code into something
> that works?

There is/was a bounds-checking implementation of GCC which used fat
pointers, but last I checked it didn't work too well since it wasn't
binary-compatible with non-checked libraries...

Still, it's completely legal, and one day someone might figure out how
to make such a beast practical.

> I can't see how a pointer to a single object in an array
> would be distinguished from a pointer to the rest of the elements of
> the array from that single object onwards.
>

> You (the caller) don't want fblag(FILE*fp,...) accessing fp[1], but
> you don't mind strstuff(char*s,...) accesing s[1], s[2], ... . How
> do you make that distinction?

I suppose one might distinguish between passing "arr" and passing
"&arr[0]"; the former would inherit the bounds of the entire array
object, but the latter would get the bounds of the specified element.

Stephen Sprunk

unread,
Apr 11, 2010, 4:56:57 PM4/11/10
to
On 10 Apr 2010 11:36, Tim Rentsch wrote:
> Stephen Sprunk <ste...@sprunk.org> writes:
>> More importantly, assuming a T is a multi-byte object and pointers are
>> simple memory addresses, is it possible for &T to be the address of a
>> byte _other than_ the first (i.e. lowest) byte of the object?
>
> Certainly it is. Didn't you just explain this yourself
> in another posting a short time later (or was it earlier)?

After a bit of thinking, I took a stab at how such an implementation
_might_ work, but I'm not 100% sure that it'd be conforming. Keith's
responses (to the above) seems to indicate it would be, but I'm still
not convinced either way.

Ben Bacarisse

unread,
Apr 11, 2010, 6:01:11 PM4/11/10
to
Stephen Sprunk <ste...@sprunk.org> writes:

> On 08 Apr 2010 02:42, Phil Carmody wrote:

<snip>


>> I can't see how a pointer to a single object in an array
>> would be distinguished from a pointer to the rest of the elements of
>> the array from that single object onwards.
>>
>> You (the caller) don't want fblag(FILE*fp,...) accessing fp[1], but
>> you don't mind strstuff(char*s,...) accesing s[1], s[2], ... . How
>> do you make that distinction?
>
> I suppose one might distinguish between passing "arr" and passing
> "&arr[0]"; the former would inherit the bounds of the entire array
> object, but the latter would get the bounds of the specified element.

I don't see how it could be allowed to, at least in the simple case of
an array of some basic data type. If, say, 'arr' is an array of chars,
then calling strlen(arr) and strlen(&arr[0]) must behave in the same
way. In fact, &arr[0] is defined to be arr+0 by 6.5.3.2 p3 and the +0
can't change the (implied) bounds.

To answer Phil's question, I don't think fblag can be prevented from
accessing fp[1] if fp is really a pointer to anywhere but the very end
of an array. That does not invalidate the idea of using fat pointers,
it just means that they can't police every situation.

--
Ben.

Tim Rentsch

unread,
Apr 12, 2010, 4:41:04 PM4/12/10
to
Eric Sosman <eso...@ieee-dot-org.invalid> writes:

> On 4/10/2010 12:34 PM, Tim Rentsch wrote:
>> Eric Sosman<eso...@ieee-dot-org.invalid> writes:
>>> [...]
>>> Here's a challenge: If an int* does *not* address the first
>>> byte of an int object, can a portable[*] program detect the fact?
>>> [snip explanation of "portable"]
>>
>> This question has no answer because it has no meaning. [...]
>
> Very good! You've grasped my point, exactly!

Did I? It sounded like you asking a question,
not making a point.

Tim Rentsch

unread,
Apr 12, 2010, 4:56:44 PM4/12/10
to
Stephen Sprunk <ste...@sprunk.org> writes:

> On 10 Apr 2010 11:36, Tim Rentsch wrote:
>> Stephen Sprunk <ste...@sprunk.org> writes:
>>> More importantly, assuming a T is a multi-byte object and pointers are
>>> simple memory addresses, is it possible for &T to be the address of a
>>> byte _other than_ the first (i.e. lowest) byte of the object?
>>
>> Certainly it is. Didn't you just explain this yourself
>> in another posting a short time later (or was it earlier)?
>
> After a bit of thinking, I took a stab at how such an implementation
> _might_ work, but I'm not 100% sure that it'd be conforming. Keith's
> responses (to the above) seems to indicate it would be, but I'm still
> not convinced either way.

Certainly there could be a conforming implementation along those
lines, for example for 'T' being 'int'. Any invertible mapping can
be applied to the bits of a memory address to make up the actual
bits in a stored pointer. There are certain constraints related to
certain classes of pointer types (eg, pointers to structs all have
the same representation and alignment), but if we limit ourselves to
the "related" pointer types for a particular scalar type (eg, int *,
const int *, volatile int *, etc), all of those could use the
mapping "base address + sizeof (int)" for the actual stored value.
The key observation is that the mapping is invertible; flipping all
the bits would also work. The trick of adding the size of the
pointed-to type does not work for structs, because pointers to
structs have to have the same representation, and different structs
have different sizes (basically, there is a problem with any kind of
pointer type where what it's pointing at might be an incomplete
type). But pointer to 'int' doesn't have these problems.

Tim Rentsch

unread,
Apr 12, 2010, 5:19:41 PM4/12/10
to
J de Boyne Pollard <J.deBoyn...@Tesco.NET> writes:

>> > For what it's worth: Several authorities consider "whether or not"
>> > to be synonymous with "whether", and the "or not" to be, as M.
>> > Uno might have been enquiring, superfluous.
>>
>> In informal English, or in formal technical English?
>
> The authorities that I've seen don't make a distinction
> when talking about "whether or not".

Ahhh. This strengthens my belief that this rule makes more sense in
informal English than it does in formal technical English.


>> In formal technical English, saying "whether or not"
>> clearly removes a possible ambiguity; [...]
>
> What ambiguity?

When "whether" is part of a statement of implication, the distinction
between a one-way implication and a two-way implication.


>> For example, we can know for sure that a given
>> program has no uninitialized variables, but we
>> can't always know for sure that a program
>> _doesn't_ have uninitialized variables.
>
> You've just apparently said the same thing twice
> ("has no uninitialized variables" "does not have
> uninitialized variables") and cast the twain as
> opposites. Did you make a typing error there?

Although I probably could have found better phrasing, I meant what I
said as I typed it, and it illustrates the kind of ambiguity I
referred to. Given a conservative algorithm for detection of
uninitialized variables, we can, for some programs, be sure that
those programs have no uninitialized variables. What we _cannot_ do
is to look at an arbitrary program and then either (a) state with
certainty that it has no uninitialized variables, or (b) state with
certainty that some variables are used without being given initial
values. In other words this is a one-way implication; _if_ we say
a program has no uninitialized variables, then we can be sure that
it has no such variables, but if we _don't_ say a program has no
uninitialized variables then can't be sure either that it does or
doesn't have uninitialized variables. The algorithm can't be smart
enough in all cases to say Yea or Nay with absolute certainty.

Getting back to the original issue, the algorithm tells us _whether_
there is no problem, but it doesn't tell us _whether or not_ there
is no problem, because if it doesn't say there is no problem, there
might be a problem or there might not be, but we can't be sure which.

Does that make more sense now?

Ersek, Laszlo

unread,
Apr 12, 2010, 5:54:56 PM4/12/10
to
On Mon, 12 Apr 2010, Tim Rentsch wrote:

> Although I probably could have found better phrasing, I meant what I
> said as I typed it, and it illustrates the kind of ambiguity I referred
> to. Given a conservative algorithm for detection of uninitialized
> variables, we can, for some programs, be sure that those programs have
> no uninitialized variables. What we _cannot_ do is to look at an
> arbitrary program and then either (a) state with certainty that it has
> no uninitialized variables, or (b) state with certainty that some
> variables are used without being given initial values. In other words
> this is a one-way implication; _if_ we say a program has no
> uninitialized variables, then we can be sure that it has no such
> variables, but if we _don't_ say a program has no uninitialized
> variables then can't be sure either that it does or doesn't have
> uninitialized variables. The algorithm can't be smart enough in all
> cases to say Yea or Nay with absolute certainty.

Similarly, "testing can prove only the presence of bugs, not their
absence".

Cheers,
lacos

J de Boyne Pollard

unread,
Apr 13, 2010, 2:12:18 AM4/13/10
to
> >> > For what it's worth: Several authorities consider "whether or not"
> >> > to be synonymous with "whether", and the "or not" to be, as M.
> >> > Uno might have been enquiring, superfluous.
>
> >> In informal English, or in formal technical English?
>
> > The authorities that I've seen don't make a distinction
> > when talking about "whether or not".
>
> Ahhh.  This strengthens my belief that this rule makes more sense
> in informal English than it does in formal technical English.

It should do exactly the reverse, since it shows that only you
are making this distinction, and the rest of the world is not.
Go and read what Prai Jei wrote in this thread about the
fundamental meaning of the word "whether".

> >> In formal technical English, saying "whether or not"
> >> clearly removes a possible ambiguity; [...]
>
> > What ambiguity?
>
> When "whether" is part of a statement of implication,
> the distinction between a one-way implication and a
> two-way implication.

That's not an ambiguity with "whether".

> >> For example, we can know for sure that a given
> >> program has no uninitialized variables, but we
> >> can't always know for sure that a program
> >> _doesn't_ have uninitialized variables.
>
> > You've just apparently said the same thing twice
> > ("has no uninitialized variables" "does not have
> > uninitialized variables") and cast the twain as
> > opposites.  Did you make a typing error there?
>
> Although I probably could have found better phrasing,
> I meant what I said as I typed it, and it illustrates
> the kind of ambiguity I referred to.  

No, it doesn't. Saying the same thing twice doesn't
create ambiguity. What it does illustrate is confusion
on the writer's part when xe then asserts that there's
opposition between the two statements of the same thing.

> [... a lot of stuff unrelated to "whether" elided ...]


>
> Getting back to the original issue, the algorithm tells
> us _whether_ there is no problem, but it doesn't tell us
> _whether or not_ there is no problem, because if it
> doesn't say there is no problem, there might be a
> problem or there might not be, but we can't be sure which.
>
> Does that make more sense now?

No, because again you are asserting that two statements
of the same thing are somehow different. There is no
difference here between something determining "whether
there is no problem" and that same thing determining
"whether there is a problem or not". The same
determination is being made.

I suggest going and looking the word up, and going and
reading (say) Eric Partridge on the subject. "whether"
really does mean a choice between two. "Or not" as one
of those choices in an is/isn't dichotomy is redundant,
and only really useful for emphasis. And as Prai Jei
said, "whether or not X", for "whether X or not", is
really placing the "or" in the wrong place, possibly
by false analogy to the idiom "whether or no", which
means "in all cases" and where the "or" is actually
between the two "whether" choices and a third "no"
choice that is in opposition to both
-- i.e. (A OR B) OR !(A OR B).

Tim Rentsch

unread,
Apr 13, 2010, 11:59:35 AM4/13/10
to
J de Boyne Pollard <J.deBoyn...@Tesco.NET> writes:

>> >> > For what it's worth: Several authorities consider "whether or not"
>> >> > to be synonymous with "whether", and the "or not" to be, as M.
>> >> > Uno might have been enquiring, superfluous.
>>
>> >> In informal English, or in formal technical English?
>>
>> > The authorities that I've seen don't make a distinction
>> > when talking about "whether or not".
>>
>> Ahhh. This strengthens my belief that this rule makes more sense
>> in informal English than it does in formal technical English.
>
> It should do exactly the reverse, since it shows that only you
> are making this distinction, and the rest of the world is not.

Yes, isn't it interesting how different people can draw
opposite conclusions from the same data.

> Go and read what Prai Jei wrote in this thread about the
> fundamental meaning of the word "whether".

Sorry, I don't have the proper tools to track that down.
But please feel free to summarize his (her?) comments.

>> >> In formal technical English, saying "whether or not"
>> >> clearly removes a possible ambiguity; [...]
>>
>> > What ambiguity?
>>
>> When "whether" is part of a statement of implication,
>> the distinction between a one-way implication and a
>> two-way implication.
>
> That's not an ambiguity with "whether".

I understand that's your view. Other people hold
different views.

>> >> For example, we can know for sure that a given
>> >> program has no uninitialized variables, but we
>> >> can't always know for sure that a program
>> >> _doesn't_ have uninitialized variables.
>>
>> > You've just apparently said the same thing twice
>> > ("has no uninitialized variables" "does not have
>> > uninitialized variables") and cast the twain as
>> > opposites. Did you make a typing error there?
>>
>> Although I probably could have found better phrasing,
>> I meant what I said as I typed it, and it illustrates
>> the kind of ambiguity I referred to.
>
> No, it doesn't. Saying the same thing twice doesn't
> create ambiguity. What it does illustrate is confusion
> on the writer's part when xe then asserts that there's
> opposition between the two statements of the same thing.

Again, I understand that's how you interpret the
writing. Other people interpret it differently.

>> [... a lot of stuff unrelated to "whether" elided ...]
>>
>> Getting back to the original issue, the algorithm tells
>> us _whether_ there is no problem, but it doesn't tell us
>> _whether or not_ there is no problem, because if it
>> doesn't say there is no problem, there might be a
>> problem or there might not be, but we can't be sure which.
>>
>> Does that make more sense now?
>
> No, because again you are asserting that two statements
> of the same thing are somehow different. There is no
> difference here between something determining "whether
> there is no problem" and that same thing determining
> "whether there is a problem or not". The same
> determination is being made.

At the risk of sounding like a broken record, I realize
that how other people read the two statements doesn't
always match how you read them.

> I suggest going and looking the word up, and going and
> reading (say) Eric Partridge on the subject. "whether"
> really does mean a choice between two. "Or not" as one
> of those choices in an is/isn't dichotomy is redundant,
> and only really useful for emphasis. And as Prai Jei
> said, "whether or not X", for "whether X or not", is
> really placing the "or" in the wrong place, possibly
> by false analogy to the idiom "whether or no", which
> means "in all cases" and where the "or" is actually
> between the two "whether" choices and a third "no"
> choice that is in opposition to both
> -- i.e. (A OR B) OR !(A OR B).

I may do that sometime. However, no matter who thinks
what about how people should interpret English, it
doesn't necessarily follow that that's how people do
interpret English.

It is loading more messages.
0 new messages