[net.lang.c] casting structures

throopw@dg_rtp.UUCP (02/19/86)

I'd like to correct a misleading and mistaken example posted in  message
<392@ccivax.UUCP>.   You may consider this a flamage warning.

>>Are there any other cases where casts are NEEDED besides:
>>              [some cases]

In message <392@ccivax.UUCP> <rb@ccivax.UUCP (rex ballard)> says:
>	Yes, accessing members of structures.
>	ie:
>	struct x y;
>	struct i j;
>	i.memb=y.memb;  /* some compilers hate this */
>	should be written:
>	i.memb= ((struct i)x).memb;
>
>	this is especially true if both structures contain 'memb' but
>	'memb' is different type or placement.
>	A union is probably preferred.

Where to begin?  Well, let's start out by pointing out that the 'i' in
'i.memb' must be a 'j' (both places), and the 'x' in '(struct i)x' must
be a 'y'. I can't think of any reasonable way of making this fragment
into a legal C program otherwise. Next, the definitions of 'memb' are
missing, so I'm not sure what is meant by "different type or placement".
Let's assume he means something like the following somewhat more
complete example:

        struct s1 {int a; char b;};
        struct s2 {char b; int a;};

        void f(){
            struct s1 x1;
            struct s2 x2;

            x1.a = x2.a;
            x1.a = ((struct s1)x2).a;
        }

Ok, now with a complete example in hand, let's see what's what.  To
start with, Harbison and Steele says (sec 6.5, pg 129, chant along if
you have the hymnal folks)

    An object of a structure or union type *T* may be converted only to
    a type that is the same as *T* (the trivial conversion).  There is
    no change of representation, except that the bit patterns in any
    unused "holes" in the structure or union are not necessarily
    preserved.

Amen.  From this I predict that any adequate typechecker will get sick
all over my complete example, and I am not let down by my favorite one.
It points out the cast as illegal, and raises not a peep over the
uncasted assignment that Rex claims might not work.

I will even stick my neck out further and assert that any compiler which
does not accept the uncasted expression is not a compiler for the C
language.  But what will a compiler do with the cast (as opposed to a
typechecker, which had better complain)?  Well, my handy-dandy "You
Asked For It, You Got It" C compiler allocates the structs like so:

            |       |       |       |       |       |       |
    s1:     |int a;     (32 bits)           |char b;|padding|
    s2:     |char b;|padding|int a;    (32 bits)            |
            |       |       |       |       |       |       |

For x1.a = x2.a, the compiler moves the 32 bits of integer in the s2
shaped record to the 32 bits of integer in the s1 shaped record.  For
the casted abomination, (got your barf bags ready?) it moves the 8 bits
of b, plus the 8 bits of padding, plus the first 16 bits of a in the s2
shaped structure, and places them in the 32 bits of a in the s1 shaped
structure.  (Trust me.  That's what it does.  I tried it out.)

Somehow I think that maybe (just maybe) Rex hasn't actually used his
handy-dandy coding tip in any working program... what do y'all think?

(Unless of course the posting was a joke, in which case I'd merely say
 that it was a moderately dangerous and ill-conceived joke.)
-- 
Wayne Throop at Data General, RTP, NC
<the-known-world>!mcnc!rti-sel!dg_rtp!throopw

rb@ccivax.UUCP (rex ballard) (02/24/86)

Well, another goofup!
First of all, the example given was poor to say the least.  It certainly
did not illustrate the problem.  Your right, I've never used it, but I
have seen it.

An example where casting was needed was this.
#define EOR 0370
char *x;
if (*x<EOR)
or
#define EOR '\370'
int *x;
if (*x<EOR)

(believe it or not, because of some #includes, both showed up in the same
project, in fact, the definitions changed mid-project)

guess what, the original compiler actually worked (gave the result desired)
with the second example and didn't work with:

#define EOR '\370'	/* compiler treated like unsigned */
char *x;
if(*x<EOR)

because of the nature of what was actually desired, the proper definition
would have been.

#define EOR (unsigned char)0370
or
#define EOR (unsigned char)'370'
unsigned char x;
if (*x<EOR)

moral: when in doubt, cast  (the next guy might figure out what you want)

Qualifier: the compiler that gave this problem accepted code which
contained so many syntax errors (missing semicolons...) that lint
died just looking at it.  (I think it was someone's kludge of bcpl or
what they thought C was before reading K&R :-)  It is a VERY OLD compiler.

Please excuse problems in the code, these are just "off the cuff" examples,
written near the end of the lunch hour.