— conversion of array to pointer not limited to lvalues

N

nicolas.sitbon

hi everybody, In the new feature of C99, i find "— conversion of array
to pointer not limited to lvalues" : what does it mean? I don't find
any difference between C90 and C99 about conversion of array to
pointer.
Thanks.
 
C

Chris Torek

hi everybody, In the new feature of C99, i find "conversion of array
to pointer not limited to lvalues" : what does it mean?

It means:

#define N 100 /* or some other constant */

struct A {
char arr[N];
};

struct A f(void);

void example(void) {
char c;

c = f().arr[10];
}

is now valid (it was not valid in C89). The function f() returns
an "rvalue" (or more simply, a "value"), not an "lvalue" (something
that designates, or at least potentially designates, an object).

In most C code, the only time you encounter an array, it is an
array object -- an lvalue -- as in:

char buf[30];
...
buf[10] = 'x';

Here "buf" names the entire array, but the array is an object --
a region of data storage; in this case it is the array named "buf"
-- and the <object, array N of T, buf> triple is converted to a
<value, pointer to T, &buf[0]> triple. That is, the "lvalue" that
names the entire array is converted to an "rvalue" (i.e., an ordinary
value) that points to the first element of the array.

In C89, only lvalue-arrays were converted to rvalue-pointers.
Since rvalue-arrays are relatively rare -- they arise only from
function calls like the one above, and some other rarely-encountered
expressions -- one tends not to see this in real C code.

(The other reason one tends not to find this in "real" or "working"
C code is that compilers given source code like this will, at least
relatively often, produce object code that does not work correctly.
Even in C99, it is somewhat hard to use this conversion safely.
Consider, for instance:

void bad_example(void) {
char *p;

p = f().arr;
printf("[0] = %c\n", p[0]);
printf("[4] = %c\n", p[4]);
}

Depending on how the call is implemented, p[0] and p[4] may well
no longer exist by the time the printf() calls are made. The first
printf() may print the desired value, but the call to printf() may
change the byte accessed by the second call, so that the second
printf() prints something other than what the programmer expected.)
 
N

nicolas.sitbon

I'm sory but I don't understand the difference between a lvalue-arrays
and a rvalue-arrays?
Are you saying that
struct A
{
char arr[N];
};

and
char arr2[N];
are conceptually different?
 
B

Barry Schwarz

hi everybody, In the new feature of C99, i find "— conversion of array
to pointer not limited to lvalues" : what does it mean? I don't find
any difference between C90 and C99 about conversion of array to
pointer.

You didn't look carefully enough, though I don't know what the real
impact of the wording change is.

In C89, para 3.2.2.1 says "Except when ..., an lvalue that has
type ``array of type '' is converted ..."

In C99, para 6.3.2.1-3 say "Except when ..., an expression that has
type ‘‘array of type’’ is converted ..."

In the new wording, the expression need not be an lvalue. Maybe
someone who worked on the standard can explain how this affects the
average programmer or if it is something only a compiler writer would
care about.
 
H

Harald van Dijk

I'm sory but I don't understand the difference between a lvalue-arrays
and a rvalue-arrays?
Are you saying that
struct A
{
char arr[N];
};

and
char arr2[N];
are conceptually different?

Not directly, as far as the array is concerned. However, the former is an
allowable function return type, while the latter is not.
 
S

santosh

I'm sory but I don't understand the difference between a lvalue-arrays
and a rvalue-arrays?
Are you saying that
struct A
{
char arr[N];
};

and
char arr2[N];
are conceptually different?

Aren't they? They are different types. The first one is type struct A
and the second one is type char [N].

But the point is wrapping an array within a structure allows one to
return an array from functions, which cannot return plain arrays.
However the manner in which it is accessed is different from how you
would normally access an array, and this has been standardised by C99.
Since functions return values, you must store the entire structure (or
at least it's array member) in another object or you will lose it.

Please see Chris Torek's post. You can't get a better explanation than
that.
 
N

nicolas.sitbon

I'm sory but I don't understand the difference between a lvalue-arrays
and a rvalue-arrays?
Are you saying that
struct A
{
   char arr[N];
};
and
char arr2[N];
are conceptually different?

Not directly, as far as the array is concerned. However, the former is an
allowable function return type, while the latter is not.

the structure is returned, not the table directly.
 
S

santosh

I'm sory but I don't understand the difference between a
lvalue-arrays and a rvalue-arrays?
Are you saying that
struct A
{
char arr[N];
};
and
char arr2[N];
are conceptually different?

Not directly, as far as the array is concerned. However, the former
is an allowable function return type, while the latter is not.

the structure is returned, not the table directly.

A value of type struct A is returned which contains an array member of
type char [n]. You can do this too:

struct A ret = foo(); /* returns a struct A value */
printf("%c\n", ret.arr[0]);

instead of

printf("%c\n", foo().arr[0]);
 
N

nicolas.sitbon

On Mon, 04 Aug 2008 12:39:45 -0700, nicolas.sitbon wrote:
I'm sory but I don't understand the difference between a
lvalue-arrays and a rvalue-arrays?
Are you saying that
struct A
{
char arr[N];
};
and
char arr2[N];
are conceptually different?
Not directly, as far as the array is concerned. However, the former
is an allowable function return type, while the latter is not.
the structure is returned, not the table directly.

A value of type struct A is returned which contains an array member of
type char [n]. You can do this too:

 struct A ret = foo(); /* returns a struct A value */
 printf("%c\n", ret.arr[0]);

instead of

 printf("%c\n", foo().arr[0]);

OK, I know that, but except the fact that we can return the table
(which is in a structure), what's the difference between the two table?
 
B

Barry Schwarz

I'm sory but I don't understand the difference between a lvalue-arrays
and a rvalue-arrays?
Are you saying that
struct A
{
char arr[N];
};

and
char arr2[N];
are conceptually different?

I don't understand many facets of lvalue and rvalue either but one
obvious difference is that your struct is only a declaration of a type
while your arr2 is the definition of an object. If you define a
struct A named x, there is still one obvious difference. Passing x to
a function will result in a complete copy of the array arr being
passed to the function. Passing arr2 to a similar function will
result in the address of the array being passed. In the first case,
updates to array elements are local to the called function. In the
second, updates are made directly to the array in the calling
function.
 
N

nicolas.sitbon

I'm sory but I don't understand the difference between a lvalue-arrays
and a rvalue-arrays?
Are you saying that
struct A
{
  char arr[N];
};
and
char arr2[N];
are conceptually different?

I don't understand many facets of  lvalue and rvalue either but one
obvious difference is that your struct is only a declaration of a type
while your arr2 is the definition of an object.  If you define a
struct A named x, there is still one obvious difference.  Passing x to
a function will result in a complete copy of the array arr being
passed to the function.  Passing arr2 to a similar function will
result in the address of the array being passed.  In the first case,
updates to array elements are local to the called function.  In the
second, updates are made directly to the array in the calling
function.

I know all of this details but I have some difficulties with this
notion of lvalues/rvalues. In all case a table is nothing more than a
data storage so what's the difference that affects the "l/r
valuability" (oh my god, my poor English)?.
 
J

jameskuyper

On Mon, 04 Aug 2008 12:39:45 -0700, nicolas.sitbon wrote:
I'm sory but I don't understand the difference between a
lvalue-arrays and a rvalue-arrays?
Are you saying that
struct A
{
char arr[N];
};
and
char arr2[N];
are conceptually different?
Not directly, as far as the array is concerned. However, the former
is an allowable function return type, while the latter is not.
the structure is returned, not the table directly.

A value of type struct A is returned which contains an array member of
type char [n]. You can do this too:

�struct A ret = foo(); /* returns a struct A value */
�printf("%c\n", ret.arr[0]);

instead of

�printf("%c\n", foo().arr[0]);

OK, I know that, but except the fact that we can return the table
(which is in a structure), what's the difference between the two table?

None. Everything relevant to your question depends only upon that one
difference. As a result of that difference, any situation in which C
requires an lvalue is a situation that an array member of a struct
value returned by a function call doesn't satisfy. The implications of
this include the fact that you can't increment/decrement it with ++ or
--, and you can't assign a value to it. Such an array needn't reside
in addressable memory, because you can't take the address of that
array - it might sit in one or more registers. Since the subscript
operator is normally defined in terms of address arithmetic, you need
a special case like this to allow the elements of such an array to be
accessed.
 
K

Keith Thompson

I'm sory but I don't understand the difference between a lvalue-arrays
and a rvalue-arrays?
Are you saying that
struct A
{
  char arr[N];
};
and
char arr2[N];
are conceptually different?

I don't understand many facets of  lvalue and rvalue either but one
obvious difference is that your struct is only a declaration of a type
while your arr2 is the definition of an object.  If you define a
struct A named x, there is still one obvious difference.  Passing x to
a function will result in a complete copy of the array arr being
passed to the function.  Passing arr2 to a similar function will
result in the address of the array being passed.  In the first case,
updates to array elements are local to the called function.  In the
second, updates are made directly to the array in the calling
function.

I know all of this details but I have some difficulties with this
notion of lvalues/rvalues. In all case a table is nothing more than a
data storage so what's the difference that affects the "l/r
valuability" (oh my god, my poor English)?.

An lvalue is an expression that designates an object.

The difference here is that the value returned by a function isn't
(considered to be) an object, even if it's a structure.

If a function returns an array -- well, a function *can't* return an
array, so instead it will typically return a pointer to the first
element of an array. And that pointer has to point to an object
that's been created in some other manner (a declared object, a
malloc()ed object, a string literal, a piece of some larger object,
etc.).

If a function returns a structure, it just returns the *value* of that
structure; conceptually at least, there's no object that holds that
value and whose address you could take. (In practice, there's likely
to be a struct object somewhere in memory, created by the code
generated by the compiler, but that "object" isn't visible as an
object to C code, so it doesn't count.)

If the structure contains an array, then you have an array that's not
an object; the change in C99 allows indexing into that array.

As far as I know, this case (a function returning a structure that
contains an array) is the only case where you can have an array
expression without a corresponding array object.
 
N

nicolas.sitbon

I'm sory but I don't understand the difference between a lvalue-arrays
and a rvalue-arrays?
Are you saying that
struct A
{
  char arr[N];
};
and
char arr2[N];
are conceptually different?
I don't understand many facets of  lvalue and rvalue either but one
obvious difference is that your struct is only a declaration of a type
while your arr2 is the definition of an object.  If you define a
struct A named x, there is still one obvious difference.  Passing x to
a function will result in a complete copy of the array arr being
passed to the function.  Passing arr2 to a similar function will
result in the address of the array being passed.  In the first case,
updates to array elements are local to the called function.  In the
second, updates are made directly to the array in the calling
function.
I know all of this details but I have some difficulties with this
notion of lvalues/rvalues. In all case a table is nothing more than a
data storage  so what's the difference that affects the "l/r
valuability" (oh my god, my poor English)?.

An lvalue is an expression that designates an object.

The difference here is that the value returned by a function isn't
(considered to be) an object, even if it's a structure.

If a function returns an array -- well, a function *can't* return an
array, so instead it will typically return a pointer to the first
element of an array.  And that pointer has to point to an object
that's been created in some other manner (a declared object, a
malloc()ed object, a string literal, a piece of some larger object,
etc.).

If a function returns a structure, it just returns the *value* of that
structure; conceptually at least, there's no object that holds that
value and whose address you could take.  (In practice, there's likely
to be a struct object somewhere in memory, created by the code
generated by the compiler, but that "object" isn't visible as an
object to C code, so it doesn't count.)

If the structure contains an array, then you have an array that's not
an object; the change in C99 allows indexing into that array.

As far as I know, this case (a function returning a structure that
contains an array) is the only case where you can have an array
expression without a corresponding array object.

--
Keith Thompson (The_Other_Keith) (e-mail address removed)  <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"

OK, I understand what you say, sorry and thanks for your explanation.
it's now clear for me.
Thanks all.
 
H

Huibert Bol

Keith said:
As far as I know, this case (a function returning a structure that
contains an array) is the only case where you can have an array
expression without a corresponding array object.

A ternairy operator that results in a structure that contains an array
is a second.

- Huibert.
 
K

Keith Thompson

Huibert Bol said:
A ternairy operator that results in a structure that contains an array
is a second.

Sounds interesting, but I'm not quite seeing it. I'd think that in
order for the tenary operator to result in such a struct expression,
its second or third operand would have to be such a struct expression,
so that's not really a distinct case, any more than a parenthesized
function call would be.

I was thinking that a C99 compound literal might be another case, but
it's not, because it "provides an unnamed object whose value is given
by the initializer list" (C99 6.5.2.5p4). This is similar to an array
literal.
 
H

Harald van Dijk

Sounds interesting, but I'm not quite seeing it. I'd think that in
order for the tenary operator to result in such a struct expression, its
second or third operand would have to be such a struct expression,
Right.

so
that's not really a distinct case, any more than a parenthesized
function call would be.

If a is declared as
struct A a;
then
(rand() ? a : a)
is not an lvalue, even though a is.

(a = a)
is also not an lvalue, and is a third way of getting a non-lvalue
structure.
 
C

Chris Torek

(in fact, they must both be struct expressions, with identical
struct types)

They convert lvalues to rvalues, though.

If a is declared as
struct A a;
then
(rand() ? a : a)
is not an lvalue, even though a is.

(a = a)
is also not an lvalue, and is a third way of getting a non-lvalue
structure.

In C++, the rules are different. This is only *almost* irrelevant,
and this is why: In all of these cases, there is an underlying
lvalue "out there" (namely, the object named "a" in the above code
fragmnets), and it would have been possible to define the operators
so that the result *is* an lvalue. For instance, one could redefine
the ?: operator such that:

((a ? b : c) = d)

becomes meaningful whenever b and c have identical types, or even
merely types-that-are-compatible-with-d, with the meaning being
that of:

(a ? (b = d) : (c = d))

and in this case, ?: would not remove "lvalue-ness" from its second
and third operands. Similarly, assignment could have been defined
to leave lvalue-ness alone. (They are not, and we have to deal
with the language as it is, not as it might be, of course. But
this does relate to C in at least one way: C compiler bugs one
finds regarding use of func().array_member tend not to affect these
other forms of expression, or at least not as often, because those
bugs often arise from compiler-writers making mistakes in tracking
"lvalue-ness". Such a mistake winds up being "harmless" -- other
than failure to generate a required diagnostic, anyway -- when
there is an "underlying lvalue" that the compiler accidentally
uses. There is one for everything except func().array_member.
[In the func().array_member case, the "lvalue" the compiler manages
to come up with is often a stack temporary that is subsequently
clobbered. The runtime behavior seen when using this can be
difficult to debug.])
 
K

Keith Thompson

Chris Torek said:
(in fact, they must both be struct expressions, with identical
struct types)


They convert lvalues to rvalues, though.

Yes, thanks, that's the point I was missing. And thanks to Harald
too.

[snip]
 

Ask a Question

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

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

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top