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