struct accessing

Tim McDaniel mcdaniel at uicsrd.csrd.uiuc.edu
Tue Jul 4 16:01:16 AEST 1989


All bracketed references refer to a section in Appendix A of K&R,
version 2.

In article <470002 at gore.com> jacob at gore.com (Jacob Gore) writes:
>> Close, but no cigar.  The pointer subtraction yields an error message,
>> since &fred_proto is of type "struct fred *" and &fred_proto.tom is of
>> type "int *".
>
>I suppose casting one into the other is non-portable?

Generally, the only pointer-pointer casts that cause guaranteed
results are casting an A* into a B* and back into an A*, which causes
the final result to be equal to the starting value.  However, B has to
require less or equally strict storage alignment, and "alignment" is
implementation-dependent (except that "char" has the least alignment
restrictions). [6.6]

There is a special dispensation, though: "If a pointer to a structure
is cast to the type of a pointer to its first member, the result
refers to the first member".  [8.3]

So
        int *         mordecai;
        struct fred * haman;
        mordecai = (int *) &fred_proto;                 /* blessed */
        haman    = (struct fred *) &fred_proto.tom;     /* cursed  */

>>     #define DICK	(&fred_proto.dick - &fred_proto.tom)
>>     #define HARRY	(&fred_proto.harry - &fred_proto.tom)
>> Even if tom is the first int member of a struct fred, and all the int
>> members are contiguous, it still isn't guaranteed to work.  Pointer
>> subtraction is defined by pANS C only over pointers into the same
>> array.  In particular, a compiler can put an arbitrary number of bytes
>> of padding between struct members, and such padding wouldn't have to
>> be a multiple of sizeof(int) bytes long.
>
>Why is the padding relevant?  The only requirement is that a field's offset
>is the same for all instances of the struct.

"A non-[bit]field member of a structure is aligned at an addressing
boundary dependent on its type; therefore, there may be unnamed holes
in a strcture".  [A8.3]

Suppose pointer subtraction were permitted between structure members.
Consider a sample storage layout for "struct fred" on some 32-bit
machine:
        __ __ __ __ | __ __ __ __ | __ __ __ __
        tom           dick          harry
Since pointer subtraction yields "a signed integral value representing
the displacement between the pointed-to objects; pointers to
successive objects differ by 1" [7.7], DICK is 1 and HARRY is 2, and
all is well with the Universe.

But consider another possible storage layout:
        __ __ __ __ | xx xx __ __ | __ __ xx xx | __ __ __ __
        tom                 dick                  harry
where "x"s refer to "unnamed holes".  Then DICK, which is
        (&fred_proto.dick - &fred_proto.tom)
is ... what?  1.5?  No, the result has to be an integer.  But "dick"
and "tom" are not separated by an integer multiple of "sizeof (int)"
bytes.  If the result of the subtraction is either 1 or 2, only half
of "dick" would be accessed, and some undefined filler value would be
brought along.

"The value [of a pointer subtraction] is undefined unless the pointers
point to objects within the same array" [7.7].  pANS C could have
required that consecutive non-bitfield members of the same type have
no padding between them, and it wouldn't be at all hard to implement
on any architecture I can think of.  I guess that nobody considered
such a special case because nobody saw a need for it.

The moral of the story is highly offensive: lbhe pbzcvyre'f
vzcyrzragbe fubhyq chg uvf "ybat" qvpx arkg gb gbz naq uneel.  Vs vg
ehof ntnvafg "fubeg"f pbagnvavat na haanzrq ubyr, vg znl trg pubccrq
va unys.
--
"Let me control a planet's oxygen supply, and I don't care who makes
the laws." - GREAT CTHUHLU'S STARRY WISDOM BAND (via Roger Leroux)
 __
   \         Tim, the Bizarre and Oddly-Dressed Enchanter
    \               mcdaniel at uicsrd.csrd.uiuc.edu
    /\       mcdaniel%uicsrd@{uxc.cso.uiuc.edu,uiuc.csnet}
  _/  \_     {uunet,convex,pur-ee}!uiucuxc!uicsrd!mcdaniel



More information about the Comp.lang.c mailing list