Initializing Unions

Do you have a question? Post it now! No Registration Necessary

Translate This Thread From English to

Threaded View
Hi,

I have a general 32-bit value :

typedef union
{
  signed     __int32 l ;
  unsigned __int32 ul ;
  float                    f ;
  signed __int16     n[2] ;
  unsigned __int16 un[2] ;
  signed __int8 c[4]  ;
  unsigned __int8 uc[4]  ;
} tVal32 ;

you get the idea.

Now when I need to initialize these objects (e.g. in const arrays of tVal32)
, I can only do it in the signed __int32 format, 'coz that's how C works.

Actually my situation is a bit more complex than this as I use tVal32 as
part of a structure :

typedef struct
{
    tVal32 Val ;
    tVal32  Max ;
    tVal32 Min ;
   unsigned __int32 ulAttributes ;
} tParam ;

I then want to define a constant array of tParam objects, where some of the
Val, Max, Min fields are float, some __int32 etc

Does anyone know a slick way of doing this ?

The only way I have found is to define individual types for each flavour of
tParam (i.e. tFloatParam, tS32Param etc) and then define them as a list of
individual objects, and rely on the compiler / linker putting these in order
in memory as if they were and array of tParam.

This seems questionable as it produces irritating warning about unredeemed
variables and assumes the compiler / linker will always lay the objects out
in memory as if they were an array simply because they appear like that in
the source code.

I hope I made my question clear, and any help will be appreciated.

Gary.




Re: Initializing Unions
unreferenced - not unredeemed !!!

Quoted text here. Click to load it
tVal32)
Quoted text here. Click to load it
the
of
order
out



Re: Initializing Unions
Hi Gary,

Note that I modified the union and put the float in first position, so it
initializes correctly.
The sample compiles no error no warning and the result in test function
(ul_foo) will be 0x00001234

If you like to have a different initialization change the order of the
members.

Please read the excerpt of ANSI/ISO/IEC:9899-1999 section 6.7.8
to understand the code sample below.

to cite:
"...
17 Each brace-enclosed initializer list has an associated current object.
When no designations are present, subobjects of the current object are
initialized in order according to the type of the current object: array
elements in increasing subscript order, structure
members in declaration order,

and the first named member of a union.127)  <<<<<<<<<< This is important for
you !

In contrast, a designation causes the following initializer to begin
initialization of the subobject
described by the designator. Initialization then continues forward in order,
beginning
with the next subobject after that described by the designator.128)

18 Each designator list begins its description with the current object
associated with the
closest surrounding brace pair. Each item in the designator list (in order)
specifies a
particular member of its current object and changes the current object for
the next
designator (if any) to be that member.129) The current object that results
at the end of the
designator list is the subobject to be initialized by the following
initializer.

19 The initialization shall occur in initializer list order, each
initializer provided for a
particular subobject overriding any previously listed initializer for the
same subobject; all
subobjects that are not initialized explicitly shall be initialized
implicitly the same as
objects that have static storage duration.

20 If the aggregate or union contains elements or members that are
aggregates or unions,
these rules apply recursively to the subaggregates or contained unions. If
the initializer of
a subaggregate or contained union begins with a left brace, the initializers
enclosed by
that brace and its matching right brace initialize the elements or members
of the
subaggregate or the contained union. Otherwise, only enough initializers
from the list are
taken to account for the elements or members of the subaggregate or the
first member of
the contained union; any remaining initializers are left to initialize the
next element or
member of the aggregate of which the current subaggregate or contained union
is a part.

21 If there are fewer initializers in a brace-enclosed list than there are
elements or members
of an aggregate, or fewer characters in a string literal used to initialize
an array of known
size than there are elements in the array, the remainder of the aggregate
shall be
initialized implicitly the same as objects that have static storage
duration.


footnotes:

127) If the initializer list for a subaggregate or contained union does not
begin with a left brace, its
subobjects are initialized as usual, but the subaggregate or contained union
does not become the
current object: current objects are associated only with brace-enclosed
initializer lists.
128) After a union member is initialized, the next object is not the next
member of the union; instead, it is
the next subobject of an object containing the union.
129) Thus, a designator can only specify a strict subobject of the aggregate
or union that is associated with
the surrounding brace pair. Note, too, that each separate designator list is
independent.

..."

============= SNIP SNIP ============


#define __int8 char
#define __int16 int
#define __int32 long

typedef union
{
  float                    f ;
  signed     __int32 l ;
  unsigned __int32 ul ;
  signed __int16     n[2] ;
  unsigned __int16 un[2] ;
  signed __int8 c[4]  ;
  unsigned __int8 uc[4]  ;
} tVal32 ;

typedef struct
{
    tVal32 Val ;
    tVal32  Max ;
    tVal32 Min ;
   unsigned __int32 ulAttributes ;
} tParam ;
/*
I then want to define a constant array of tParam objects, where some of the
Val, Max, Min fields are float, some __int32 etc
*/


/* you may use the const qualifier to protect the object from modification
or force ROM storage */

/* const*/ tParam tParam_objects[3] = {\
,
,

};


void union_test(void)
{
volatile unsigned long ul_foo; /* volatile used to keep compiler from
optimizing away ul_foo */

ul_foo = (tParam_objects[0]).ulAttributes >> 16 & 0x0000FFFFUL;
/* result: 0x00001234 */
}

============= SNIP SNIP ============


hope that helps.


--
with kind regards


--
-----------------------------------------
Jan Homuth
We've slightly trimmed the long signature. Click to see the full one.
Re: Initializing Unions
Jan,

Thanks for this.

What I actually want to do is to have an array of tParam objects were some
of the .val fields are floats, and some __int32's and some unsigned
__int32's,

i.e. I'd like to do something like :

const*/ tParam tParam_objects[3] =
{
    ,
    ,
    
};

Where element 0 has it's .val .max & .min fields initialized as type float,
element 1 as type __int32 and element 2 as type unsigned __int32.

I know this isn't possible in this form in C so I'm looking for ways of
achieving the same thing in a tidy manner.

Thanks.
Gary

Quoted text here. Click to load it
for
order,
order)
Quoted text here. Click to load it
initializers
union
initialize
not
union
aggregate
is
the
works.
of
unredeemed
in



Re: Initializing Unions
Gary,
try casting.

Like:


#define __int8 char
#define __int16 int
#define __int32 long

/*********** Note: i changed the order *************/
/* since unsigned long is the most handy format to brutally cast any "4 byte
something" into, without conversion */
typedef union
{
  unsigned __int32 ul ;
  float                    f ;
  signed     __int32 l ;
  signed __int16     n[2] ;
  unsigned __int16 un[2] ;
  signed __int8 c[4]  ;
  unsigned __int8 uc[4]  ;
} tVal32 ;

typedef struct
{
    tVal32 Val ;
    tVal32  Max ;
    tVal32 Min ;
   unsigned __int32 ulAttributes ;
} tParam ;

/* previous example , clean , with floats */
#if 0
tParam tParam_objects[3] = {\
,
,

};
#endif

/* now mean, with unsigned long and "crowbar" casts */
tParam tParam_objects[3] =
{
    {(unsigned long) ((float) 1.0), (unsigned long) ((float)2.0),(unsigned
long) ((float)3.0),0x12344321UL},
    {(unsigned long) 1234L,(unsigned long) 35678L,(unsigned long)
9876L,0x23455432UL},
    {(unsigned long) 0x8000000UL,(unsigned long) 0x8789761UL,(unsigned long)
0x78991231UL,0x34566543UL}
};


void union_test(void)
{
volatile unsigned long ul_foo;

ul_foo = (tParam_objects[0]).ulAttributes >> 16 & 0x0000FFFFUL;

}

If yo run this in the simulator/debugger and observe the content of the
three objects in the data watch window,
(hex display on where necessary, you can see after startup that the stuff is
correctly initialized.

I've been using the TASKING M16C v2.3r1 and C166 v8.0r1 Toolchain. I assume
the result will be the same in MSVC++ or Borland.


--
with kind regards


--
-----------------------------------------
Jan Homuth
We've slightly trimmed the long signature. Click to see the full one.
Re: Initializing Unions
Quoted text here. Click to load it



If by correctly, Jan means that tParam_objects[0].Val.f == 1.0F, he is
mistaken.  The cast in the initializer only changes the type and not the
value, so that tParam_objects[0].Val.ul is initialized to 1UL, and
.Val.f is not defined.

See Dave Hansen's message on doing what the OP wanted in C99 (I know
that Jan gave the reference earlier, but Dave gives the example).  In
C90, I can't think of any elegant way to do this, except for run-time
assignment, rather than initialization, which won't work for ROM-based
tables and also takes more code.  

If faced with this, I might write a utility to initialize at run-time,
then have it write a file containing unifirm initialization constants
(with original values in comments), which would be included from the
application program.  The requires that the setup machine use the same
representation for data as the target system, or that the program
contains explicit conversion.  The most likely conversion needed for
different machines is endian adjustment.

Thad

Re: Initializing Unions
Thad,
thank you for clarification.

I agree on the runtime initialization using a function.
This is the cleanest way of initializing the diversity of types.

Quoted text here. Click to load it


Ouch !
You are right.
The example really does an initialization of tParam_objects[0].Val.ul. to
1UL
tParam_objects[0].Val.f has an undefined value.

My idea was to use the cast to assign the binary pattern created by IEEE754
32 bit single precision to tParam_objects[0].Val.ul. Bad luck. I see it now.
I stumbled over the good old implicit conversion rule that is also valid for
initialization.


Mea Culpa ...

/jan

snipped-for-privacy@acm.org...
Quoted text here. Click to load it
((float)2.0),(unsigned
Quoted text here. Click to load it
long)
Quoted text here. Click to load it
stuff is



Re: Initializing Unions
On Wed, 16 Jul 2003 00:51:11 GMT, "Gary Pace"

Quoted text here. Click to load it
[...]
Quoted text here. Click to load it

If you have C99 (or a compiler that aspires to the new standard) you
can use designated initializers, like so (warning: untested):

   tParam param_array[] =
   {
      { {.ul = 1}, {.ul = 10}, {.ul = 0}, 0x123 },
      { {.f = 3.14}, {.f = 6.28}, , 0x456 },
      { {.un = }, {.un = }, {.un = }, 0},
      {
        {.uc = {[0] = 1,[1] = 0}}
        {.uc = {[0] = 3, [1] = 0, [2] = 0, [3] = 0}},
        {.uc = {[0] = 0}},
         0x12345678
      },
      /* etc. */
   };

Regards,

                               -=Dave
--
Change is inevitable, progress is not.

Re: Initializing Unions

Quoted text here. Click to load it

Short of C99, I can only think two ways to do this, neither of them
what I'd call "slick".

1. Allocate and initialize the storage in assembly.  Possibly in-
line assembly, which I'm not a big fan of personally.

2. Encapsulate your array in another union with a struct used only
for inialization purposes, perhaps with a macro to hide the
encapsulation where the array is referenced.  E.g.:

union
{
  struct
  {
    /* element 0, tParam = { f, ul, uc, ul } */
    float val0_f;
    unsigned __int32 val0_ul;
    unsigned __int8 val0_uc[4];
    unsigned __int32 val0_ulAttributes ;
    /* element 1, tParam = { l, f, ul, ul } */
    signed __int32 val1_l;
    float val1_f;
    unsigned __int32 val1_ul;
    unsigned __int32 val0_ulAttributes ;
    /* etc... */
  } s;
  tParam a[ ];
} param_array_init =
{
  {
    1.0, "abc", 3, 0xff,
    -1, 2.0, 3, 0xf0,
    /* etc... */
  }
};

#define   param_array  param_arry_init.a

Quoted text here. Click to load it

That could prettify the above somewhat, especially if you only need
a few of the 343 possible flavors of the tParam struct.

Quoted text here. Click to load it

That is not a safe assumption, in general.

Quoted text here. Click to load it

- Fred

Re: Initializing Unions
Thanks everyone.

In the end I can up with defining a number of type specific types
(tFloatPar, tS32Par etc) and then for each "parameter table" I defined a
structured type listing what I needed. These could then be defined as
initialized (actually const) objects.

typedef struct
{
    tFloatParam   p0 ;
    tFloatParam   p1;
    tS32Param    p2 ;
} tParamTable ;

static const tParamTable ParamTable =
{
        ,
        ,
        
} ;

I then accessed this at run-time using my tVal32 type by :

{
        tParam *ptParam ;

        ptParam = (tParam*)&ParamTable ;
}

Not very flexible, but since each parameter table is static to a file, it's
not too bad

Quoted text here. Click to load it
tVal32)
Quoted text here. Click to load it
the
of
order
out



Site Timeline