Eric Sosman
7/24/2011 12:12:00 AM
On 7/23/2011 4:54 PM, Jack Boot wrote:
> Hello:
>
> This came up in a code review recently, I've never heard of it before,
> opinions welcome.
>
> There is a struct that looks a bit like this
>
> struct foo {
> int a[10];
> long b;
> ...
> };
>
> Now I have a function that starts thusly:
>
> void
> bar ( struct foo * f )
> {
> int * ip = (int *) foo;
I guess you mean `f' here.
> ...
> }
>
> The reviewer recommended, that for maximal portability I should replace
> this by:
>
> int * ip = ( (int *) ( ( (unsigned char *) foo ) + offsetOf(struct foo,
> a) ) );
>
> I've never seen this construction before, also I think type punning is
> inherently non-portable so I'm not sure how much this buys you.
I don't think the reviewer is concerned about portability, because
the code as it stands is entirely portable: A pointer to a struct can
be converted to a pointer to its first element, and vice versa, and
nothing will go wrong.
BUT what happens if somebody adds a new element `x' to the struct
and puts it before `a', so `a' is no longer the first element? The
code becomes "portably incorrect," and the compiler is unlikely to warn
you about the error.
If what you need is a pointer to the start of the `a' array, use
int *ip = f->a;
This construct will find `a' no matter where it sits in the struct --
and has the further virtue that the the compiler will squawk if somebody
changes `a' to an array of `long' instead of `int'.
If you also require (for some reason not apparent in the code as
shown) that `a' be first, you can
assert(offsetof(struct foo, a) == 0);
.... or use a "compile-time assertion" (GIYF) to make the same test.
--
Eric Sosman
esosman@ieee-dot-org.invalid