union initializers idiosyncrasies

Discussion in 'C Programming' started by Neil Zanella, Sep 17, 2003.

  1. Neil Zanella

    Neil Zanella Guest

    Hello,

    I would like to know what the C standards (and in particular the C99
    standard) have to say about union initializers with regards to the
    following code snippet (which compiles fine under gcc 3.2.2 but
    does not produce the expected results, the expected results
    being the ones annotated in the comments in the code):

    #include <stdlib.h>

    union Foo {
    struct {
    int y1;
    union {
    int z1;
    float z2;
    } y2;
    } x;
    void *a;
    };

    int main(void) {
    union Foo foobar1 = { { 1, { 22 } } };
    union Foo foobar2 = { { 2, { 33.1 } } };
    /* expecting 1, OK */
    printf("foobar1: %d\n", foobar1.x.y1);
    /* expecting 22, OK */
    printf("foobar1: %d\n", foobar1.x.y2.z1);
    /* expecting 2, OK */
    printf("foobar2: %d\n", foobar2.x.y1);
    /* expecting 33.1, not 0.00 */
    printf("foobar2: %f\n", foobar2.x.y2.z2);
    }

    I would have thought, that seeing that 33.1 is a float and not an
    integer, the float would have been properly stored in the union.
    What does the standard say about the above code; is any of it
    illegal or is any of it subject to implementation dependent
    behavior, and if so then why didn't gcc complain?

    Thank you for your clarifications,

    Neil
    Neil Zanella, Sep 17, 2003
    #1
    1. Advertising

  2. Neil Zanella

    Jack Klein Guest

    On Tue, 16 Sep 2003 23:33:50 -0230, Neil Zanella <>
    wrote in comp.lang.c:

    >
    > Hello,
    >
    > I would like to know what the C standards (and in particular the C99
    > standard) have to say about union initializers with regards to the
    > following code snippet (which compiles fine under gcc 3.2.2 but
    > does not produce the expected results, the expected results
    > being the ones annotated in the comments in the code):
    >
    > #include <stdlib.h>
    >
    > union Foo {
    > struct {
    > int y1;
    > union {
    > int z1;
    > float z2;
    > } y2;
    > } x;
    > void *a;
    > };
    >
    > int main(void) {
    > union Foo foobar1 = { { 1, { 22 } } };
    > union Foo foobar2 = { { 2, { 33.1 } } };
    > /* expecting 1, OK */
    > printf("foobar1: %d\n", foobar1.x.y1);
    > /* expecting 22, OK */
    > printf("foobar1: %d\n", foobar1.x.y2.z1);
    > /* expecting 2, OK */
    > printf("foobar2: %d\n", foobar2.x.y1);
    > /* expecting 33.1, not 0.00 */
    > printf("foobar2: %f\n", foobar2.x.y2.z2);
    > }
    >
    > I would have thought, that seeing that 33.1 is a float and not an
    > integer, the float would have been properly stored in the union.
    > What does the standard say about the above code; is any of it
    > illegal or is any of it subject to implementation dependent
    > behavior, and if so then why didn't gcc complain?
    >
    > Thank you for your clarifications,
    >
    > Neil


    The nesting of unions has nothing at all to do with this, the results
    would be the same if y2 were defined as a separate union.

    You thought incorrectly. Prior to C99, and even under C99 using the
    syntax you are using, it is only possible to initialize the first
    member of a union, in definition order. The type of the initializer
    is irrelevant as long as it is assignment compatible with the
    destination type.

    Consider this:

    union x
    {
    int i; long l; float f; double d; long double ld;
    };

    union x q = { 1 }; /* initialize the int or the long? */
    union x r = { 1.0 }; /* float, double, or long double? */

    Why should you expect an error for the second initialization? There
    is nothing illegal about this:

    int i = 3.14159;

    ....although as a QOI issue you might like to see a warning from the
    compiler. If it actually refused to compile it, the compiler would be
    broken.

    The only time such code is illegal is if the initializer is not
    assignment compatible (without a cast) with the type being
    initialized:

    char *cp = 3.14159;

    ....requires a diagnostic.

    C99 has added designated initializers which allow you to initialize
    any specific member of a union, but the syntax is quite different.

    --
    Jack Klein
    Home: http://JK-Technology.Com
    FAQs for
    comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
    comp.lang.c++ http://www.parashift.com/c -faq-lite/
    alt.comp.lang.learn.c-c++ ftp://snurse-l.org/pub/acllc-c /faq
    Jack Klein, Sep 17, 2003
    #2
    1. Advertising

  3. Neil Zanella

    Neil Zanella Guest

    Hello,

    Thank you for your detailed reply...

    Jack Klein <> wrote in message:

    > You thought incorrectly. Prior to C99, and even under C99 using the
    > syntax you are using, it is only possible to initialize the first
    > member of a union, in definition order. The type of the initializer
    > is irrelevant as long as it is assignment compatible with the
    > destination type.


    The C99 standard discusses the above matter in Section 6.7.8.
    Furthermore,
    page 347 of K.N.King's C book which covers C95 states the following
    about
    unions:

    --- begin quote ---
    Like structures, unions can be copied using the = operator, passed to
    functions, and returned to functions.
    --- end quote ---

    This covers the legality of using assignment to copy unions. Then it
    states:

    --- begin quote ---
    Unions can even be initialized in a manner similar to structures.
    However,
    only the first member of a union can be given an initial value.
    --- end quote ---

    This is not true in C99 (see below). Even though only one member of a
    union
    can be initialized in C99 (which makes sense since all union members
    share
    the same storage space), we can now initialize any member of the
    union, not
    only the one that appears first in the union declaration. We do this
    by
    specifying the member name explicitly inside the union initializer
    list.
    From the C99 standard:

    > C99 has added designated initializers which allow you to initialize
    > any specific member of a union, but the syntax is quite different.


    Yes, from the C99 standard we can see the following example which
    shows
    us how this can be achieved:

    Section 6.7.8, paragraph 130, EXAMPLE 13

    --- begin quote ---
    union { /* ... */ } u = { .any_member = 42 };
    --- end quote ---

    This C99 idiom lends itself to good programming style. As long as we
    use
    member names in union initializer lists we can reorder the members of
    unions in the union definition.

    -------------------------------------------------------------------------

    What the book does not cover is the following:

    suppose I have something like:



    int main(void) {
    union foo { int c; double d; } x;
    /* ... */
    x.d = 3.3;
    /* ... */
    union foo y = x;
    /* let's check that it worked */
    printf("%g\n", x.d);
    /* it works with my compiler, outputs 3.3 with gcc 3.2.2 */
    return 0;
    }
    Here I am initializing union y with an expression, x, which is a
    union.
    Here it is true that d is not the first member of x. Nevertheless, is
    it
    true in this case that y.d == 3.3 after y gets declared as above? The
    C99 standard states the following:

    Section 6.7.8, paragraph 13, page 126

    --- begin quote ---
    The initializer for a structure or union object that has automatic
    storage duration shall be either an initializer list or a single
    expression that has compatible structure or union type. In the
    latter case, the initial value of the object, including unnamed
    members, is that of the expression.
    --- end quote ---

    So what is the value of the expression x in the above example? I guess
    the initial value of the object, that of the expression, is the union
    itself in the above code, and that the above code is legal in C99.

    Thanks you for your feedback and contributions,

    Neil
    Neil Zanella, Sep 18, 2003
    #3
  4. Neil Zanella

    Neil Zanella Guest

    Hello,

    Here is a very simple way to fix the problem in C99:

    > #include <stdlib.h>
    >
    > union Foo {
    > struct {
    > int y1;
    > union {
    > int z1;
    > float z2;
    > } y2;
    > } x;
    > void *a;
    > };
    >
    > int main(void) {
    > union Foo foobar1 = { { 1, { 22 } } };
    > /* union Foo foobar2 = { { 2, { 33.1 } } }; */


    union Foo foobar2 = { { 2, { .z2 = 33.1 } } };

    > /* expecting 1, OK */
    > printf("foobar1: %d\n", foobar1.x.y1);
    > /* expecting 22, OK */
    > printf("foobar1: %d\n", foobar1.x.y2.z1);
    > /* expecting 2, OK */
    > printf("foobar2: %d\n", foobar2.x.y1);
    > /* expecting 33.1, not 0.00 */
    > printf("foobar2: %f\n", foobar2.x.y2.z2);


    /* output: 33.099998: OK (with usual but unrelated roundoff error) */

    > }


    From the C99 standard:

    --- begin quote ---
    Forward, paragraph 5, page xiii
    - relaxed constraints on aggregate and union initializations
    --- end quote ---

    I guess this is what this remark refers to.

    Regards,

    Neil
    Neil Zanella, Sep 18, 2003
    #4
  5. Neil Zanella

    Jack Klein Guest

    On 17 Sep 2003 18:19:31 -0700, (Neil Zanella) wrote
    in comp.lang.c:

    > Hello,
    >
    > Thank you for your detailed reply...
    >
    > Jack Klein <> wrote in message:
    >
    > > You thought incorrectly. Prior to C99, and even under C99 using the
    > > syntax you are using, it is only possible to initialize the first
    > > member of a union, in definition order. The type of the initializer
    > > is irrelevant as long as it is assignment compatible with the
    > > destination type.

    >
    > The C99 standard discusses the above matter in Section 6.7.8.
    > Furthermore,
    > page 347 of K.N.King's C book which covers C95 states the following
    > about
    > unions:
    >
    > --- begin quote ---
    > Like structures, unions can be copied using the = operator, passed to
    > functions, and returned to functions.
    > --- end quote ---
    >
    > This covers the legality of using assignment to copy unions. Then it
    > states:
    >
    > --- begin quote ---
    > Unions can even be initialized in a manner similar to structures.
    > However,
    > only the first member of a union can be given an initial value.
    > --- end quote ---
    >
    > This is not true in C99 (see below). Even though only one member of a
    > union
    > can be initialized in C99 (which makes sense since all union members
    > share
    > the same storage space), we can now initialize any member of the
    > union, not
    > only the one that appears first in the union declaration. We do this
    > by
    > specifying the member name explicitly inside the union initializer
    > list.
    > From the C99 standard:
    >
    > > C99 has added designated initializers which allow you to initialize
    > > any specific member of a union, but the syntax is quite different.

    >
    > Yes, from the C99 standard we can see the following example which
    > shows
    > us how this can be achieved:
    >
    > Section 6.7.8, paragraph 130, EXAMPLE 13
    >
    > --- begin quote ---
    > union { /* ... */ } u = { .any_member = 42 };
    > --- end quote ---
    >
    > This C99 idiom lends itself to good programming style. As long as we
    > use
    > member names in union initializer lists we can reorder the members of
    > unions in the union definition.
    >
    > -------------------------------------------------------------------------
    >
    > What the book does not cover is the following:
    >
    > suppose I have something like:
    >
    >
    >
    > int main(void) {
    > union foo { int c; double d; } x;
    > /* ... */
    > x.d = 3.3;
    > /* ... */
    > union foo y = x;
    > /* let's check that it worked */
    > printf("%g\n", x.d);
    > /* it works with my compiler, outputs 3.3 with gcc 3.2.2 */
    > return 0;
    > }
    > Here I am initializing union y with an expression, x, which is a
    > union.
    > Here it is true that d is not the first member of x. Nevertheless, is
    > it
    > true in this case that y.d == 3.3 after y gets declared as above? The
    > C99 standard states the following:
    >
    > Section 6.7.8, paragraph 13, page 126
    >
    > --- begin quote ---
    > The initializer for a structure or union object that has automatic
    > storage duration shall be either an initializer list or a single
    > expression that has compatible structure or union type. In the
    > latter case, the initial value of the object, including unnamed
    > members, is that of the expression.
    > --- end quote ---


    You are still way over-thinking this, for some reason. You can
    initialize any modifiable lvalue object in C with the value of another
    object of compatible type. This has been true since the 1989 ANSI
    standard. That is so basic that it is pretty much taken for granted.

    When you write:

    union some_type foo = { /* some valid initializer */ };
    union some_type bar = foo;

    ....bar winds up being an exact member-wise copy of foo. In a
    structure that would mean that each member in the new structure would
    have the same value as the corresponding members of the initiating
    structure. For a union it means that the new union will be in the
    same "state" (my term, not the standard's) as the original one, namely
    the one valid member in the union will be the last stored member in
    the old union, and it will contain the same value.

    > So what is the value of the expression x in the above example? I guess
    > the initial value of the object, that of the expression, is the union
    > itself in the above code, and that the above code is legal in C99.


    The above code was legal in C89, and was a common extension at the
    time of publication of the first edition of K&R, in 1978. There is
    nothing new to C99 about it.

    There are differences between initialization and assignment, but they
    are not important here. The two lines I wrote above are not the same
    as the following three lines, but the result is the same:

    union some_type foo = { /* some valid initializer */ };
    union some_type bar;
    bar = foo;

    > Thanks you for your feedback and contributions,
    >
    > Neil


    --
    Jack Klein
    Home: http://JK-Technology.Com
    FAQs for
    comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
    comp.lang.c++ http://www.parashift.com/c -faq-lite/
    alt.comp.lang.learn.c-c++ ftp://snurse-l.org/pub/acllc-c /faq
    Jack Klein, Sep 18, 2003
    #5
  6. Neil Zanella

    Neil Zanella Guest

    Thanks Jack,

    Your lucid explanation explains very well the semantic issues I brought
    up on this newsgroup pertaining to union initialization and assignment.
    Please don't take things personally. Basically I was looking for
    something in the standard stating what you wrote here:

    --- begin Jack's quote ---
    For a union it means that the new union will be in the same "state"
    (my term, not the standard's) as the original one, namely the one
    valid member in the union will be the last stored member in
    the old union, and it will contain the same value.
    --- end Jack's quote ---

    That's what I expected, even though in pre-C99 initializer lists it
    is the first element in the union's definition that gets assigned,
    which is what made me wonder about other kind of initializations.
    Basically what I was trying to get at is that the standard should
    perhaps have included something explicit to reflect what you
    describe in the above quote, which is what we'd all expect.
    C can be quirky, and unless the standard documents it, you
    never know whether what you're doing is portable.

    Thank you and have a nice day!

    Neil

    Jack Klein <> wrote in message news:<>...

    > You are still way over-thinking this, for some reason. You can
    > initialize any modifiable lvalue object in C with the value of another
    > object of compatible type. This has been true since the 1989 ANSI
    > standard. That is so basic that it is pretty much taken for granted.
    >
    > When you write:
    >
    > union some_type foo = { /* some valid initializer */ };
    > union some_type bar = foo;
    >
    > ...bar winds up being an exact member-wise copy of foo. In a
    > structure that would mean that each member in the new structure would
    > have the same value as the corresponding members of the initiating
    > structure. For a union it means that the new union will be in the
    > same "state" (my term, not the standard's) as the original one, namely
    > the one valid member in the union will be the last stored member in
    > the old union, and it will contain the same value.
    >
    > > So what is the value of the expression x in the above example? I guess
    > > the initial value of the object, that of the expression, is the union
    > > itself in the above code, and that the above code is legal in C99.

    >
    > The above code was legal in C89, and was a common extension at the
    > time of publication of the first edition of K&R, in 1978. There is
    > nothing new to C99 about it.
    >
    > There are differences between initialization and assignment, but they
    > are not important here. The two lines I wrote above are not the same
    > as the following three lines, but the result is the same:
    >
    > union some_type foo = { /* some valid initializer */ };
    > union some_type bar;
    > bar = foo;
    >
    > > Thanks you for your feedback and contributions,
    > >
    > > Neil
    Neil Zanella, Sep 19, 2003
    #6
  7. On 17 Sep 2003 18:25:54 -0700, (Neil Zanella)
    wrote:
    <snip>
    > union Foo foobar2 = { { 2, { .z2 = 33.1 } } };

    <snip>
    > From the C99 standard:
    >
    > --- begin quote ---
    > Forward, paragraph 5, page xiii
    > - relaxed constraints on aggregate and union initializations
    > --- end quote ---
    >
    > I guess this is what this remark refers to.
    >

    No, that item refers to 6.7.8p3, which in C90 also applied to "an
    initializer list for an object that has aggregate or union type" even
    if automatic; pre-Standard such automatic variables might not even be
    initializable at all -- FAQ 1.31.

    The item "— designated initializers" on pxii refers to what you did.
    Hint: 6.7.8, which is entirely about "initialization" and
    "initializer"s, consistently uses "designation" and "designated" for
    this new feature.

    - David.Thompson1 at worldnet.att.net
    Dave Thompson, Sep 22, 2003
    #7
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Glen Able

    Class variable initializers

    Glen Able, Jan 29, 2004, in forum: Java
    Replies:
    0
    Views:
    326
    Glen Able
    Jan 29, 2004
  2. j l
    Replies:
    5
    Views:
    384
    A. Bolmarcich
    Feb 23, 2004
  3. James Robert Leek

    JNI Invocation: static UN-initializers?

    James Robert Leek, Aug 31, 2004, in forum: Java
    Replies:
    2
    Views:
    478
    James Robert Leek
    Aug 31, 2004
  4. Matt Garman
    Replies:
    1
    Views:
    652
    Matt Garman
    Apr 25, 2004
  5. Peter Dunker

    union in struct without union name

    Peter Dunker, Apr 26, 2004, in forum: C Programming
    Replies:
    2
    Views:
    854
    Chris Torek
    Apr 26, 2004
Loading...

Share This Page