[comp.lang.c] Two standards problems

clive@ixi.UUCP (Clive Feather) (06/12/89)

The story so far:

I originally posted this as article <178@ixi.UUCP in comp.std.c. Following
the deafening silence, I am reposting it. Please let me emphasise that I am
interested only in *standards* answers - it is of no use to tell me that this
works on XYZ system (which proves nothing), doesn't work in XYZ system (which
isn't an ANSI compiler), or doesn't work on XYZ system (which has a compiler
which claims to be ANSI but gets something wrong). The answers will be used
to generate code which must be strictly conforming.

Now read on ...

I have two problems concerning ANSI-C, in relation to the X Window System
(you don't need to know about / be interested in X to understand these).

Firstly, the Xt Intrinsics define a macro called XtOffset. This is similar
to offsetof, but:

    (1) the type argument is a pointer to a structure
    (2) the field designator can specify a field of a field

In other words, suppose I have:

    typedef struct
    {
        int a;
        double b;
        struct
        {
            long x;
            char y;
            float z;
        } c;
        char *d;
    } funny;
    typedef funny *funny_p;

    unsigned int i;
    funny fred;
    char *cp;

then I can write:

    i = XtOffset (funny_p, x.y);

followed by:

    cp = ((char *) & fred) + i;
    *cp = 'x';

The present definition of XtOffset is:

    #define XtOffset(type,field) \
    ((unsigned int) (((char *) (& (((type) NULL)->field))) - ((char *) NULL)))

(Q1) Is the present definition legal and portable ANSI-C ?
(Q2) Is it possible to define XtOffset portably using offsetof ?
(Q3) If the answer to both the above is no, then is it possible to define XtOffset
     portably ?


The second, unrelated, problem is to do with structure padding. Suppose I have the
following two structures:

    typedef struct
    {
        struct
        {
            char c1;
            char c2;
            char c3;
            char c4;
            char c5;
            char c6;
        } all;
    }
    t1;

    typedef struct
    {
        struct
        {
            char c1;
            char c2;
            char c3;
        } upper;
        struct
        {
            char c4;
            char c5;
            char c6;
        } lower;
    }
    t2;

    union
    {
        t1 m1;
        t2 m2;
    } u;

(Q4) It is my understanding that c1, c2, and c3 must have the same position in
     the two versions, so that assigning to u.m1.all.c1 and then reading from
     u.m2.upper.c1 will yield the same value. Is this correct ?
(Q5) Can the compiler insert padding between t2.upper and t2.lower so that
     u.m1.all.c4 and u.m2.lower.c4 occupy different locations ?

Suppose I have the variables:

    t1 var1;
    t2 var2;

    t1 *pt1;
    t2 *pt2;

    pt1 = (t1 *) & var2;
    pt2 = (t2 *) & var1;

    /* pt1 points to var2 as a t1, and vice versa */

(Q6) Will writing to pt1->all.c1   write into var2.upper.c1, and
          writing to pt2->upper.c1 write into var1.all.c1        ?
(Q7) Will writing to pt1->all.c4   write into var2.lower.c4, and
          writing to pt2->lower.c4 write into var1.all.c4        ?
[These are equivalent to Q4 and Q5, but with casts instead of unions].

In all six questions, I am looking for answers in terms of what is portable to
*all* conforming systems.

For those who say "why on earth are you doing this ?", the answer is basically
"backwards compatibility"; incompatible changes must be avoided if at all possible.
-- 
Clive D.W. Feather
IXI Limited
clive@ixi.uucp
...!uunet!ukc!ixi!clive (riskier)