— conversion of array to pointer not limited to lvalues

Discussion in 'C Programming' started by nicolas.sitbon, Aug 4, 2008.

  1. 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
    nicolas.sitbon, Aug 4, 2008
    1. Advertisements

  2. nicolas.sitbon

    Chris Torek Guest

    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.)
    Chris Torek, Aug 4, 2008
    1. Advertisements

  3. 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];

    char arr2[N];
    are conceptually different?
    nicolas.sitbon, Aug 4, 2008
  4. You didn't look carefully enough, though I don't know what the real
    impact of the wording change is.

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

    In C99, para 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.
    Barry Schwarz, Aug 4, 2008
  5. Not directly, as far as the array is concerned. However, the former is an
    allowable function return type, while the latter is not.
    Harald van Dijk, Aug 4, 2008
  6. nicolas.sitbon

    santosh Guest

    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
    santosh, Aug 4, 2008
  7. the structure is returned, not the table directly.
    nicolas.sitbon, Aug 4, 2008
  8. nicolas.sitbon

    santosh Guest

    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]);
    santosh, Aug 4, 2008
  9. 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?
    nicolas.sitbon, Aug 4, 2008
  10. 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
    Barry Schwarz, Aug 4, 2008
  11. 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)?.
    nicolas.sitbon, Aug 4, 2008
  12. nicolas.sitbon

    jameskuyper Guest

    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
    jameskuyper, Aug 4, 2008
  13. 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,

    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, Aug 5, 2008
  14. OK, I understand what you say, sorry and thanks for your explanation.
    it's now clear for me.
    Thanks all.
    nicolas.sitbon, Aug 5, 2008
  15. nicolas.sitbon

    Huibert Bol Guest

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

    - Huibert.
    Huibert Bol, Aug 5, 2008
  16. 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 This is similar to an array
    Keith Thompson, Aug 6, 2008
  17. If a is declared as
    struct A a;
    (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
    Harald van Dijk, Aug 6, 2008
  18. nicolas.sitbon

    Chris Torek Guest

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

    They convert lvalues to rvalues, though.

    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.])
    Chris Torek, Aug 6, 2008
  19. Yes, thanks, that's the point I was missing. And thanks to Harald

    Keith Thompson, Aug 6, 2008
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.