(I have no idea why you redirected this back to the C++ and Java
groups; even in comp.programming it seems a bit far afield, as it
were.)
Chris said:
... a C compiler must produce 0 for the result of offsetof() on any
union type and member:
union U {
char a;
int b;
float c;
double d;
struct S { int i[10]; } e;
};
...
assert(offsetof(union U, a) == 0);
assert(offsetof(union U, b) == 0);
assert(offsetof(union U, c) == 0);
assert(offsetof(union U, d) == 0);
assert(offsetof(union U, e) == 0);
This wouldnt compile on eoither a solaris, a linux, or a win32 system.
You will need to turn the above into a complete program, e.g.:
% cat t.c
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
union U {
char a;
int b;
float c;
double d;
struct S { int i[10]; } e;
};
int main(void) {
assert(offsetof(union U, a) == 0);
assert(offsetof(union U, b) == 0);
assert(offsetof(union U, c) == 0);
assert(offsetof(union U, d) == 0);
assert(offsetof(union U, e) == 0);
puts("success");
return 0;
}
% cc -O -Wall -W -o t t.c
% ./t
success
% cc -v
[snippage]
gcc version 3.2.3 20030502 (Red Hat Linux 3.2.3-34)
I could be wrong, but I do believe the standards allow [the offset]
to be something other than zero; ie it's not required.
This has to be constructed, but I think this sentence in the C99
draft I am using, section 6.5.2.1 ("Structure and union specifiers"),
paragraph 13, is the (or at least a) key one:
A pointer to a union object, suitably converted, points to each
of its members (or if a member is a bit-field, then to the unit
in which it resides), and vice versa.
Note that paragraph 12 immediately before this includes the lines:
A pointer to a structure object, suitably converted, points to
its initial member (or if that member is a bit-field, then to
the unit in which it resides), and vice versa. There may be
unnamed padding within a structure object, but not at its
beginning.
Given "struct S *p" whose initial member has type T, (T *)p is a
pointer to p->initial_member, i.e., (T *)p == &p->initial_member.
The definition of offsetof() is that it produces the offset
in bytes. Hence:
/* assume we already have: struct S { T initial_member; ... } *p; */
unsigned char *uc_p;
T *im_p;
T *offset_p;
unsigned char *im_p_as_uc;
im_p = &p->initial_member; /* straightforward */
im_p_as_uc = (unsigned char *)im_p; /* also straightforward */
uc_p = (unsigned char *)p; /* converted from "ptr to S" */
assert(uc_p == im_p_as_uc); /* required by paragraph 12 */
uc_p += offsetof(struct S, initial_member);
assert(uc_p == im_p_as_uc); /* still required by paragraph 12 */
Clearly if neither assert() may fail, uc_p has not moved, so
offsetof() produced 0.
The same technique can be applied to each union member to show that
it, too, must have its offsetof() being zero. The ultimate difference
between "struct" and "union" is that a struct's members are at
successively larger offsets, as determined by the size of each
previous member plus any post-member padding, while a union's
members are all at offset 0.