Mechanics of calculating structure-member offsets

C

cman

What are the mechanics involved in the calculation of an offset of a
structure member, demonstrated in this piece of code?

#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

Suppose you have struct foobar and want to compute the offset of
element boo, this is how it is done:

(unsigned long)(&((struct foobar *)0)->boo)

What does memory address 0 refer to?

cman
 
U

user923005

What are the mechanics involved in the calculation of an offset of a
structure member, demonstrated in this piece of code?

#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

Suppose you have struct foobar and want to compute the offset of
element boo, this is how it is done:

(unsigned long)(&((struct foobar *)0)->boo)

What does memory address 0 refer to?

The right way to get byte offsets in structs is to use the offsetof()
macro.
From the C-FAQ:

2.14: How can I determine the byte offset of a field within a
structure?

A: ANSI C defines the offsetof() macro, which should be used if
available; see <stddef.h>. If you don't have it, one possible
implementation is

#define offsetof(type, mem) ((size_t) \
((char *)&((type *)0)->mem - (char *)(type *)0))

This implementation is not 100% portable; some compilers may
legitimately refuse to accept it.

See question 2.15 below for a usage hint.

References: ISO Sec. 7.1.6; Rationale Sec. 3.5.4.2; H&S
Sec. 11.1 pp. 292-3.

If you want to understand how it works, then just run it through the
preprocessor of your compiler and it will immediately become obvious
what is going on.
P.S.
Don't use this custom one unless your broken compiler does not provide
the real thing.
 
W

Walter Roberson

What are the mechanics involved in the calculation of an offset of a
structure member, demonstrated in this piece of code?
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

Dubious ones. C semantics are such that even referring to an
invalid pointer is not allowed (except to compare it to a
null pointer constant), so the above is non portable even though
the memory at location 0 (or just a little thereafter) is not
actually referenced.

It is, though, legal for an implementation to use this kind of
grunge in the implementation headers, as implementation headers
may make use of implementation behaviour that mere mortals ought not
to deal with.

Suppose you have struct foobar and want to compute the offset of
element boo, this is how it is done:
(unsigned long)(&((struct foobar *)0)->boo)
What does memory address 0 refer to?

It's just a 0, converted to an address. Structures are laid out
in increasing address order, so if you know the address of a
structure member and subtract the address of the first element
of the structure, you get the offset of the member into the structure.
But if the structure address has been set as 0, you are subtracting
off 0, which of course doesn't take any extra steps to do, making
the expression simpler.

This requires reliance on behaviour left unspecified in the standard.
Do not use this trick yourself in any program you wish to
be portable.
 
K

Keith Thompson

user923005 said:
The right way to get byte offsets in structs is to use the offsetof()
macro.
From the C-FAQ:

2.14: How can I determine the byte offset of a field within a
structure? [snip]
If you want to understand how it works, then just run it through the
preprocessor of your compiler and it will immediately become obvious
what is going on.
P.S.
Don't use this custom one unless your broken compiler does not provide
the real thing.

And if your implementation doesn't provide offsetof() (it's defined in
<stddef.h>), find a different one. The standard requires offsetof()
to exist; if it doesn't, your implementation is *badly* broken, and
anything else in the language could be missing or broken.
 
M

Martin Ambuhl

cman said:
What are the mechanics involved in the calculation of an offset of a
structure member, demonstrated in this piece of code?

#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

#define list_entry(type,member) offsetof(type,member)
Suppose you have struct foobar and want to compute the offset of
element boo, this is how it is done:

(unsigned long)(&((struct foobar *)0)->boo)

No, it's not. This is how it is done:
offsetof(struct foobar, boo);
 
S

Sensei

And if your implementation doesn't provide offsetof() (it's defined in
<stddef.h>), find a different one. The standard requires offsetof()
to exist; if it doesn't, your implementation is *badly* broken, and
anything else in the language could be missing or broken.


Just a curiosity: is offsetof() required by the standard to be a
#define? Can I check against it by simply using the following code?

#ifndef offsetof
#error .........
#endif
 
S

santosh

Sensei said:
Just a curiosity: is offsetof() required by the standard to be a
#define?

As far as I can tell, yes.
Can I check against it by simply using the following code?

#ifndef offsetof
#error .........
#endif

Presumably, you can.
 
F

Flash Gordon

santosh wrote, On 03/03/07 08:51:
As far as I can tell, yes.


Presumably, you can.

Although I can't see why you would want to seeing as it has been
required as part of the C standard since 1989.
 
K

Keith Thompson

Sensei said:
Just a curiosity: is offsetof() required by the standard to be a
#define?
Yes.

Can I check against it by simply using the following code?

#ifndef offsetof
#error .........
#endif

Well, probably. In a conforming implementation, that will just tell
you whether you've #include'd <stddef.h> (and not explicitly undefined
offsetof afterward). In a non-conforming implementation, how do you
know that #ifndef is going to work properly?

It's possible to have a working compiler and a broken <stddef.h>, but
I really don't think there's much point in testing for it (unless
you're writing a conformance test suite). If you need offsetof(),
just go ahead and use it; if it's not defined, you'll get an error
message. (The compiler won't treat it as a call to an external
function because the first argument is a type name.)
 
C

CBFalconer

santosh said:
As far as I can tell, yes.


Presumably, you can.

However that is a silly thing to do. Just #include <stddef.h> and
you KNOW it is available. If you don't include stddef.h you KNOW
it is not available (it is not allowable to #define things
available in the standard headers).
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,008
Latest member
Rahul737

Latest Threads

Top