how best to get the offset of a field within a struct?

F

funkyj

PROBLEM STATEMENT:
I want to calculate the byte offset of a field with a struct at
compile time without instantiating an instance of the struct at
runtime AND I want to do this in an ANSI standard compliant
fashion.

DISCUSSION:

Below is a sample program that demonstrates my solution. My questions
are:

(1) is this ANSI C compliant?
(2) is there a better way to solve this problem?


I think I'm on safe ground because casting and taking the address of
stuff does not cause memory to be accessed (i.e. pointers
dereferenced). In any event I seem to have satisfied GCC:
> cd c:/cygwin/home/fj/offset/
> make offset && ./offset
> gcc -ansi -pedantic -o offset offset.c
> MY_OFFSET(verybig_t, a) = 0x0
> MY_OFFSET(verybig_t, g) = 0x20
> MY_OFFSET(verybig_t, j) = 0x202a
> MY_OFFSET(verybig_t, p) = 0x203a
>
> Compilation finished at Fri Feb 17 11:41:58

Regards,
--jfc

================ begin sample program offset.c ================
#include <stdio.h>
typedef struct {
long a, b, c;
short e, f;
char name[13];
long g, h;
char stuff[1 << 13];
short i;
char j;
long k, l, m;
char n, o, p;
} verybig_t;

#define MY_OFFSET(type, field) ((unsigned long ) &(((type *)
0)->field))

int main(int argc, char * argv[])
{
printf("MY_OFFSET(verybig_t, a) = 0x%x\n", MY_OFFSET(verybig_t,
a));
printf("MY_OFFSET(verybig_t, g) = 0x%x\n", MY_OFFSET(verybig_t,
g));
printf("MY_OFFSET(verybig_t, j) = 0x%x\n", MY_OFFSET(verybig_t,
j));
printf("MY_OFFSET(verybig_t, p) = 0x%x\n", MY_OFFSET(verybig_t,
p));
return(0);
}
 
E

Eric Sosman

funkyj wrote On 02/17/06 14:44,:
PROBLEM STATEMENT:
I want to calculate the byte offset of a field with a struct at
compile time without instantiating an instance of the struct at
runtime AND I want to do this in an ANSI standard compliant
fashion.

Use the offsetof() macro said:
DISCUSSION:

Below is a sample program that demonstrates my solution. My questions
are:

(1) is this ANSI C compliant?

No.
(2) is there a better way to solve this problem?

Use the offsetof() macro said:
[snipped a variant of the non-portable pre-ANSI substitute
for the offsetof() macro, defined in <stddef.h>]
 
F

funkyj

Thanks! Apparently I wasn't that far off. My local include files
implement it like this:

#define __offsetof(type, field) ((size_t)(&((type *)0)->field))
/* ansi.h */
#define offsetof(type, member) __offsetof(type, member) /*
stddef.h */
 
C

Clark S. Cox III

PROBLEM STATEMENT:
I want to calculate the byte offset of a field with a struct at
compile time without instantiating an instance of the struct at
runtime AND I want to do this in an ANSI standard compliant
fashion.

DISCUSSION:

Below is a sample program that demonstrates my solution. My questions
are:

(1) is this ANSI C compliant?

No, it is not. (Dereferencing a NULL pointer leads to undefined behavior)
(2) is there a better way to solve this problem?

Yes. You don't need to solve it, it has already been solved for you.
Just use the "offsetof" macro already provided for you by standard C.
 
T

tedu

Clark said:
No, it is not. (Dereferencing a NULL pointer leads to undefined behavior)

assuming one doesn't wish to use offsetof, what potential problems
exist with the following (aside from non compatible usage)?

#define almostoffsetof(type, field, o) { \
type tmp_; \
o = &tmp_.field - &tmp_; \
}
 
E

Eric Sosman

tedu wrote On 02/17/06 16:24,:
assuming one doesn't wish to use offsetof,

Why assume such a silly thing? "Assuming one doesn't
wish to use parentheses ..."
what potential problems
exist with the following (aside from non compatible usage)?

#define almostoffsetof(type, field, o) { \
type tmp_; \
o = &tmp_.field - &tmp_; \
}

How about "It doesn't work" -- does that qualify as a
"potential problem?" Sheesh ...
 
F

Flash Gordon

funkyj said:
Thanks! Apparently I wasn't that far off. My local include files
implement it like this:

#define __offsetof(type, field) ((size_t)(&((type *)0)->field))
/* ansi.h */
#define offsetof(type, member) __offsetof(type, member) /*
stddef.h */

Just because your system implements offsetof like that does not make it
portable. In fact, if you use that method, rather than just using the
offsetof macro provided by your system, it most definitely is *not*
portable.

offsetof is provided as a standard macros specifically because it is
*impossible* to implement it portably.
--
Flash Gordon
Living in interesting times.
Web site - http://home.flash-gordon.me.uk/
comp.lang.c posting guidlines and intro -
http://clc-wiki.net/wiki/Intro_to_clc
 
F

Flash Gordon

tedu said:
assuming one doesn't wish to use offsetof, what potential problems
exist with the following (aside from non compatible usage)?

#define almostoffsetof(type, field, o) { \
type tmp_; \
o = &tmp_.field - &tmp_; \
}

struct tmp_ {int a; int b;} tmp_
size_t o;

....

if (flag)
almostoffsetof(tmp_, b, o);
else
almostoffsetof(tmp_, a, o);

Should show you a couple of potential problems, one of which has a
standard solution the other is rather harder to avoid.
--
Flash Gordon
Living in interesting times.
Web site - http://home.flash-gordon.me.uk/
comp.lang.c posting guidlines and intro -
http://clc-wiki.net/wiki/Intro_to_clc
 
K

Keith Thompson

tedu said:
assuming one doesn't wish to use offsetof, what potential problems
exist with the following (aside from non compatible usage)?

#define almostoffsetof(type, field, o) { \
type tmp_; \
o = &tmp_.field - &tmp_; \
}

It can't be used in an expression.
 
K

Keith Thompson

funkyj said:
PROBLEM STATEMENT:
I want to calculate the byte offset of a field with a struct at
compile time without instantiating an instance of the struct at
runtime AND I want to do this in an ANSI standard compliant
fashion.

Use the offsetof macro defined in <stddef.h>.

There is no portable way to implement offsetof in standard C. The
most common implementation dereferences a null pointer (invoking
undefined behavior) and depends on the compiler to recognize that it
doesn't actually have to generate code to perform that operation. The
standard does not guarantee that such an optimization will be
performed.

Code in the standard headers are allowed to do whatever non-portable,
implementation-defined, or just plain ugly things are necessary to
obtain the required results *with the particular compiler they're
written to work with*. That's why offsetof is in <stddef.h>. The
author of <stddef.h> is allowed to invoke undefined behavior as long
as it happens to yield the required result; you're not.
 
T

tedu

Eric said:
tedu wrote On 02/17/06 16:24,:

Why assume such a silly thing? "Assuming one doesn't
wish to use parentheses ..."

to further one's understanding of C? is that permissible?
How about "It doesn't work" -- does that qualify as a
"potential problem?" Sheesh ...

why not? i just noticed it's missing two casts. any other reason?
#define almostoffsetof(type, field, o) { \
type tmp_; \
o = (char *)tmp_.field - (char *)&tmp_; \
}
struct hanky { int a; int b; };
void f(void) {
struct hanky h;
int *x;
char *p;
ptrdiff_t o;
almostoffsetof(struct hanky, b, o);
p = (char *)&h;
p += o;
x = (int *)p; /* now x == &h.b */
}

why would x not be pointing to h.b?
 
J

Jordan Abel

Use the offsetof macro defined in <stddef.h>.

There is no portable way to implement offsetof in standard C. The
most common implementation dereferences a null pointer (invoking
undefined behavior) and depends on the compiler to recognize that it
doesn't actually have to generate code to perform that operation. The
standard does not guarantee that such an optimization will be
performed.

I thought that it was guaranteed that the side-effects of the argument
to sizeof will not be executed. i.e. sizeof printf("Hello, world!\n")
yields sizeof(int) and does not emit any output.

What's _not_ guaranteed is the ability to do pointer arithmetic on NULL.

You could always use a temporary, though, at the expense of not being
able to use it in an expression. Someone posting elsewhere in this
thread has done so.
 
E

Eric Sosman

tedu wrote On 02/17/06 17:45,:
to further one's understanding of C? is that permissible?

"Question authority." Furthering one's understanding
is not only permissible, but laudable. Occasionally, one
can even discover something new and useful by refusing to
use the established techniques. I read long ago that in
the fairly early days of electric lighting, new employees
at the light-bulb factory were instructed to frost the
insides of the clear bulbs so as to diffuse the light.
It couldn't be done, of course; this was a "hazing" of the
new employees, a "sleeveless errand." And one day, a new
kid who didn't know it was impossible figured out how to
do it ...

BUT there's a difference between constructive curiosity
and silliness that verges on the perverse. It's one thing
to try to come up with "something better" than offsetof, but
it's silly to propose something that's obviously worse. If
you had an idea for a niftier, slicker, and more convenient
alternative to offsetof, I'd encourage you to post it and
would be happy to review your effort. But when the thing
you've come up with is clunkier, stickier, and cruftier than
what already exists, that's another matter. In fact, it's
inferior to the already-dismissed-as-non-portable construct
the O.P. asked about. So why are you fiddling around?
why not? i just noticed it's missing two casts.

That's a "potential problem," I'd say.
any other reason?
#define almostoffsetof(type, field, o) { \
type tmp_; \
o = (char *)tmp_.field - (char *)&tmp_; \
}

What happens when

#define tmp_ getenv("TMPDIR")

appears earlier in the file?
 
K

Keith Thompson

Jordan Abel said:
I thought that it was guaranteed that the side-effects of the argument
to sizeof will not be executed. i.e. sizeof printf("Hello, world!\n")
yields sizeof(int) and does not emit any output.

Right (unless the argument is a VLA), but most implementations of
offsetof() don't use sizeof.
What's _not_ guaranteed is the ability to do pointer arithmetic on NULL.
Right.

You could always use a temporary, though, at the expense of not being
able to use it in an expression. Someone posting elsewhere in this
thread has done so.

Yes, but that's not an implementation of offsetof.
 
J

Jordan Abel

Right (unless the argument is a VLA), but most implementations of
offsetof() don't use sizeof.


Right.

And that's more likely to be a serious problem than the "phantom
dereference". [taking the address of a member is essentially the same as
pointer arithmetic, and has the same problems if it fails]
Yes, but that's not an implementation of offsetof.

No, but it answers the original question well enough.
 
T

tedu

Eric said:
would be happy to review your effort. But when the thing
you've come up with is clunkier, stickier, and cruftier than
what already exists, that's another matter. In fact, it's
inferior to the already-dismissed-as-non-portable construct
the O.P. asked about. So why are you fiddling around?

just for the exercise.
What happens when

#define tmp_ getenv("TMPDIR")

appears earlier in the file?

you learn not to do that. :)
 
T

tmp123

Keith said:
There is no portable way to implement offsetof in standard C.

I suposse the following two methods are not standard due to the pointer
conversions. But I do not in how many points they break the rules:

(Another subject, less important in some context, is the limit of
offset value)
(Sorry for the amount of parenthesis)

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
int f1;
char f2;
} s1;

static void *foo;
#define offsetof1(x,a) ( (char *)&( ((x *)foo)->a ) - (char *)foo )
#define offsetof2(x,a) ( *(char *) ( &(((x *)foo)->a) ) )

int main ( void )
{
/* init */
int i;
foo=malloc(256);
for(i=0;i<256;i++) ((char *)foo)=i;

/* method 1 */
printf("%d\n",offsetof1(s1,f2));

/* method 2 */
printf("%d\n",offsetof2(s1,f2));
}

Kind regards.
 
K

Keith Thompson

tmp123 said:
I suposse the following two methods are not standard due to the pointer
conversions. But I do not in how many points they break the rules:

(Another subject, less important in some context, is the limit of
offset value)
(Sorry for the amount of parenthesis)

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
int f1;
char f2;
} s1;

static void *foo;
#define offsetof1(x,a) ( (char *)&( ((x *)foo)->a ) - (char *)foo )
#define offsetof2(x,a) ( *(char *) ( &(((x *)foo)->a) ) )

foo is a static pointer object; it's therefore initialized to NULL.
(Since it's not const, you could mess it up by assigning a value to
foo.)

Both are similar to the common definition of offset(), except that
they use foo rather than a literal 0. Both dereference a null pointer
and count on the compiler to optimize well enough so that the
dereference isn't actually executed. offsetof2 seems to assumes that
a null pointer is all-bits-zero (though it's hard to say, since that
assumption isn't sufficient to make it work).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top