From: ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe)
Newsgroups: comp.lang.c
Subject: Re: Union-initialization portability
Date: 2 Oct 92 06:35:35 GMT
Organization: Comp Sci, RMIT, Melbourne, Australia

In article <1aedrtINN774@gap.caltech.edu>, mcs@goblin.caltech.edu (Martin Shepherd) writes:
> struct Descriptor {
>   char *name;
>   VarType type;
>   union {
>     void *init;  /* The initialization field of the union */
>     int *iptr;
>     float *fptr;
>     char *cptr;
>   } valof;
> };

> float float_array[20];
> char char_array[9];
> float fvar;
> 
> #define NUM_VAR 3
> struct Descriptor test[NUM_VAR] = {
>  {"farray", FLOAT_PTR, {&float_array[0]} },
>  {"cvar",   CHAR_PTR,  {&char_array[0]}  },
>  {"fvar",   FLOAT_PTR, {&fvar}           }
> };

Let the compiler do the counting.  A handy macro:

	#define array_element_count(arrayid) \
	    (sizeof arrayid / sizeof arrayid[0])

Now you can initialise the array, and get the compiler to tell _you_
what NUM_VAR is:

	struct Descriptor test[] =
	    {
		{"farray",	FLOAT_PTR,	float_array},
		{"cvar",	CHAR_PTR,	char_array},
		{"fvar",	FLOAT_PTR,	fvar},
	    };
	#define NUM_VAR array_element_count(test)

The last comma is not an error

There are a lot of subtle points about unions.  The point you may not be
aware of is that a C compiler is allowed to use different representations
for pointers of different types.  There are two useful exceptions: void*
and char* must have the same representation, and all struct * pointers
have the same representation.

Let's focus on a much simpler example.

	union pun { void *a; float *b; } puny;
	float f;
	puny.a = &f;		/* no problem */
	*(puny.b) = 2.0;	/* I think you want to do this, and OH DEAR! */

Consider the following two machines.
    DEC-10 (any model before they added extended addressing).
	Pointers to objects made out of one or more whole 36-bit words
	(int, float, double) will fit in 18 bits.
	Pointers to smaller objects (9-bit chars, 18-bit shorts, such as
	one might use in a C system) require a full 36-bit word (containing
	an 18-bit word number, a bit offset, a bit field length, and some
	other stuff you really don't want to know about).
	void* and float* aren't even the same size!
    PRIME 50 series (I used a P400; an, um, interesting machine).
	Pointers to objects made out of one or more whole 16-bit words
	(short, int, float, double, pointers) will fit in 32 bits
	(16-bit word number, 12-bit segment number, validity bit, size bit,
	and 2-bit protection ring number).
	Pointers to smaller objects (the address format permits bit-level
	addressing, but the P400 only supported byte addressing) require
	a 3rd 16-bit word to hold the extra bit for a byte address.
	void* and float* aren't even the same size!
There are other architectures _still_in_use_ where for example
	Pointer to something made out of 1 or more whole "words"
	is a word number (perhaps with the top few bits special).
	Pointer to byte is word number*number of bytes per word + byte offset.
	(Is anyone still using 3Rivers/ICL Perqs?)
	void* and float* have the same size, but the bits don't mean the
	same things.

It is important for C programmers, and absolutely _crucial_ for C++
programmers, to remember that casts don't just change the type of a value,
casts are coercions that may change the size and/or representation of a
value as well.  

Union punning, however, does _not_ do the coercion, and is about as
machine dependent as you can imagine.

> (It does work on a sparc).

There are lots of machines around where it will work.
80*86 machines, 680?0 machines, SPARCs, NS32?32s, IBM /370s, MIPS, VAX,
enough to lull you into a false sense of security.  Basically, if a machine
has only one address format you are safe.  How portable do you want your
code to be?
-- 
RMIT policy requires the use of Gender Inclusive Language in all publications
and in all information conveyed to students, staff, and potential job
applicants.  Lapses into English are the sole responsibility of the author.
The Constitution of Australia does _not_ guarantee Freedom of Speech.