[comp.lang.c] What's an LValue was A quick question

jimbo@tandem.com (Jim Lyon) (03/13/91)

In article <31306@shamash.cdc.com> bls@u02.svl.cdc.com (Brian Scearce) writes:
>The rules are pretty easy (especially if you have your copy of
>Harbison and Steele on your desk :-)
>
>0. variable names (excepting function, array and enum constant
>   names) are lvalues.
>1. e[k] is an lvalue, regardless of whether e and k are lvalues.
>2. (e) is an lvalue iff e is.
>3. e.name is an lvalue iff e is.
>4. e->name is an lvalue regardless of whether e is an lvalue.
>5. *e is an lvalue regardless of whether e is an lvalue.

Rules (1) and (5) need to be further qualified.  e[k] and *e are
lvalues regardless of whether e and k are lvalues, UNLESS the type
of e[k] or *e is either Array... or Function...

For example, if A is declared as "int A[10][10]", then "A[5]"
is not an lvalue, because the type of "A[5]" is "Array of int".

-- Jim Lyon
   jimbo@Tandem.Com

torek@elf.ee.lbl.gov (Chris Torek) (03/14/91)

>In article <31306@shamash.cdc.com> bls@u02.svl.cdc.com (Brian Scearce) writes:
>>The rules are pretty easy (especially if you have your copy of
>>Harbison and Steele on your desk :-)

[and then notes that this needs changes for ANSI C]

>>0. variable names (excepting function, array and enum constant
>>   names) are lvalues.

In ANSI C, arrays are also lvalues (but not modifiable lvalues).  This
is conceptually required to make sizeof() work without throwing in weird
rules.  (One can always argue that `non-modifiable lvalues' is a weird
rule :-) )

>>1. e[k] is an lvalue, regardless of whether e and k are lvalues.
>>2. (e) is an lvalue iff e is.
>>3. e.name is an lvalue iff e is.
>>4. e->name is an lvalue regardless of whether e is an lvalue.
>>5. *e is an lvalue regardless of whether e is an lvalue.

In article <1991Mar13.050555.26149@tandem.com> jimbo@tandem.com
(Jim Lyon) writes:
>Rules (1) and (5) need to be further qualified.  e[k] and *e are
>lvalues regardless of whether e and k are lvalues, UNLESS the type
>of e[k] or *e is either Array... or Function...

Half of the qualifier is correct for ANSI C (*e is not an lvalue if e
has type `pointer to function (args) returning T').  In addition, the
type of e[k] may never% be `function ...' because for this to be true,
either:

	e has type `pointer to function ...'  and k has an integral type

or:

	e has an integral type and k has type `pointer to function ...'

and you cannot add an integer value to a pointer-to-function-... since
the size of a `function-...' is unknown.&

-----
% Well, maybe never.  Since *p \equiv *(p + 0) \equiv p[0], one could
  claim that

	int (*fp)(void);
	...
	(fp[0])();

  should have the same effect as `(*fp)();'.  I do not know what rules
  X3.159-1989 gives here (my copy is in my office; I am not).  Thus, if
  either e or k is the integral constant 0, perhaps e[k] may have type
  `function (args) returning T'.

& GCC permits adding integers to function-pointer values, by pretending
  the pointers are byte pointers.  This feature is of dubious utility
  (if you wanted to do that you could just cast to `char *' and back).
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab EE div (+1 415 486 5427)
Berkeley, CA		Domain:	torek@ee.lbl.gov

sarima@tdatirv.UUCP (Stanley Friesen) (03/15/91)

In article <1991Mar13.050555.26149@tandem.com> jimbo@tandem.com (Jim Lyon) writes:
<In article <31306@shamash.cdc.com> bls@u02.svl.cdc.com (Brian Scearce) writes:
<>0. variable names (excepting function, array and enum constant
<>   names) are lvalues.
<>1. e[k] is an lvalue, regardless of whether e and k are lvalues.
<>2. (e) is an lvalue iff e is.
<>3. e.name is an lvalue iff e is.
<>4. e->name is an lvalue regardless of whether e is an lvalue.
<>5. *e is an lvalue regardless of whether e is an lvalue.
<
<Rules (1) and (5) need to be further qualified.  e[k] and *e are
<lvalues regardless of whether e and k are lvalues, UNLESS the type
<of e[k] or *e is either Array... or Function...

Well, as I understand the ANSI definition of lvalue (different than the
K&R1 definition), the original rules are correct as stated.  It is just
that in that case the result is not a *modifiable* lvalue, and is converted
in all value contexts into a pointer to the object.

<For example, if A is declared as "int A[10][10]", then "A[5]"
<is not an lvalue, because the type of "A[5]" is "Array of int".

Or rather, by ANSI definitions, it *is* an lvalue that is immediately
converted into a non-lvalue pointer of type "Pointer to int".
-- 
---------------
uunet!tdatirv!sarima				(Stanley Friesen)

scc@rlgvax.Reston.ICL.COM (Stephen Carlson) (03/15/91)

In article <1991Mar13.050555.26149@tandem.com> jimbo@tandem.com (Jim Lyon) writes:
>In article <31306@shamash.cdc.com> bls@u02.svl.cdc.com (Brian Scearce) writes:
>>The rules are pretty easy (especially if you have your copy of
>>Harbison and Steele on your desk :-)
>>
>>0. variable names (excepting function, array and enum constant
>>   names) are lvalues.
>>1. e[k] is an lvalue, regardless of whether e and k are lvalues.
>>2. (e) is an lvalue iff e is.
>>3. e.name is an lvalue iff e is.
>>4. e->name is an lvalue regardless of whether e is an lvalue.
>>5. *e is an lvalue regardless of whether e is an lvalue.
>
>Rules (1) and (5) need to be further qualified.
[ Points well taken deleted, please refer to original article. ]

Rules (1) and (4) are technically redundant.

Since e[k] <=> *((e)+(k)), rule (1) is a special case of Rule (5).

Since e->name <=> (*e).name, rule (4) is a special case of Rules (3), (2),
and (5):
	*e			must be an lvalue	By rule 5
=>	(*e)		must be an lvalue	By rule 2
=>	(*e).name	must be an lvalue	By rule 3
=>  e->name		must be an lvalue	By definition of ->
Hence, rule (4).

However, it is better to present a practical set of rules than a logically
minimal set of rules.

-- 
Stephen Carlson           | ICL OFFICEPOWER Center    | In theory, theory and
scc@rlgvax.reston.icl.com | 11490 Commerce Park Drive | practice are the same.
..!uunet!rlgvax!scc       | Reston, VA  22091         |

sean@hcx2.ssd.csd.harris.com (Sean Burke) (03/16/91)

In article <10898@dog.ee.lbl.gov>, torek@elf.ee.lbl.gov (Chris Torek) writes:
|> >In article <31306@shamash.cdc.com> bls@u02.svl.cdc.com (Brian Scearce) writes:
|> >>The rules are pretty easy (especially if you have your copy of
|> >>Harbison and Steele on your desk :-)
|> 
|> [and then notes that this needs changes for ANSI C]
|> 
|> >>0. variable names (excepting function, array and enum constant
|> >>   names) are lvalues.
|> >>1. e[k] is an lvalue, regardless of whether e and k are lvalues.
|> >>2. (e) is an lvalue iff e is.
|> >>3. e.name is an lvalue iff e is.
|> >>4. e->name is an lvalue regardless of whether e is an lvalue.
|> >>5. *e is an lvalue regardless of whether e is an lvalue.
|> 
|> In article <1991Mar13.050555.26149@tandem.com> jimbo@tandem.com
|> (Jim Lyon) writes:
|> >Rules (1) and (5) need to be further qualified.  e[k] and *e are
|> >lvalues regardless of whether e and k are lvalues, UNLESS the type
|> >of e[k] or *e is either Array... or Function...
|> 
|> Half of the qualifier is correct for ANSI C (*e is not an lvalue if e
|> has type `pointer to function (args) returning T').  In addition, the
|> type of e[k] may never% be `function ...' because for this to be true,
|> either:
|> 
|> 	e has type `pointer to function ...'  and k has an integral type
|> 
|> or:
|> 
|> 	e has an integral type and k has type `pointer to function ...'
|> 
|> and you cannot add an integer value to a pointer-to-function-... since
|> the size of a `function-...' is unknown.&
|> 

So where would:

   struct { int array[10] ; } func() ;

   func().array[k]

fit into these rules?

I guess you could argue that its covered becuase

  func().array

is implicitly taking the address of something that is not an lvalue,
and thats an error in itself.

torek@elf.ee.lbl.gov (Chris Torek) (03/16/91)

In article <2647@travis.csd.harris.com> sean@hcx2.ssd.csd.harris.com
(Sean Burke) writes:
>So where would:
>
>   struct { int array[10] ; } func() ;
>
>   func().array[k]
>
>fit into these rules?
>
>I guess you could argue that its covered becuase
>
>  func().array
>
>is implicitly taking the address of something that is not an lvalue,
>and thats an error in itself.

This is correct.  X3.159-1989%, p. 40:

	A postfix expression followed by a dot . and an identifier
	designates a member of a structure or union object. ... is
	an lvalue if the first expression is an lvalue.

Thus, `func().array' denotes something that is not an lvalue but would
(if it existed) be a value of type `array N of ...'.  Since no such
type exists, it is clear that no expression of the form func().array
exists. :-)

Seriously: the value from func() is an `rvalue' structure and an attempt
to name its `array' member is illegal.  GCC permits it as an extension.
-----
% Actually, I am still using a 1988 draft.
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 415 486 5427)
Berkeley, CA		Domain:	torek@ee.lbl.gov