25

What does ((struct name *)0)->member) do in C?

The actual C statement I came across was this:

(char *) &((struct name *)0)->member)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
krisharav
  • 475
  • 1
  • 5
  • 12
  • 3
    [So](https://stackoverflow.com/questions/7180290/how-do-you-use-offsetof-on-a-struct), [many](https://stackoverflow.com/questions/713963/why-does-this-implementation-of-offsetof-work), [duplicates](https://stackoverflow.com/questions/6700114/portability-of-using-stddef-hs-offsetof-rather-than-rolling-your-own) . – 2501 Feb 01 '16 at 17:48
  • 1
    And its (undefined) behavior: https://stackoverflow.com/questions/26906621/does-struct-name-null-b-cause-undefined-behaviour-in-c11 – 2501 Feb 01 '16 at 19:31
  • I'm not actually looking to learn about offsetof() here. I'm specifically interested in knowing how the given statement works. It's not a duplicate IMHO – krisharav Feb 01 '16 at 20:32
  • 1
    Asking how offsetoff and your code works is the same. In any case, one of the duplicates answers just that. – 2501 Feb 01 '16 at 20:41

4 Answers4

27

This is a trick for getting the offset of struct's member called member. The idea behind this approach is to have the compiler compute the address of member assuming that the structure itself is located at address zero.

Using offsetof offers a more readable alternative to this syntax:

size_t offset = offsetof(struct name, member);
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • nice! thanks for pointer to wiki page as well. that didn't show up on Google:) – krisharav Feb 01 '16 at 16:58
  • 1
    But isn't it undefined behavior to dereference a null pointer ? – machine_1 Feb 01 '16 at 17:03
  • 3
    you are not de-referencing it actually. Merely obtaining the addres. – krisharav Feb 01 '16 at 17:10
  • great answer. unrelated, but are you *the* blinkenlight? creator of the towel.blinkenlights.nl telnet server? – Woodrow Barlow Feb 01 '16 at 17:18
  • @WoodrowBarlow No, I am not *that* blinkenlight :-( – Sergey Kalinichenko Feb 01 '16 at 17:31
  • @machine_1 that's why offsetof is better – Andrew Wolfe Feb 01 '16 at 18:54
  • 1
    @machine_1 It is actually undefined behavior, and as Andrew points out, that's one very good reason to use `offsetof` instead, which is the standard way to do it. If we take one step away from the spec, and explore compiler specific behavior, many compilers will implement this undefined behavior in a way that does what you intended. In fact, on many compilers, `offsetof` is actually defined in this way (compilers can always use compiler specific behavior in their implementations). It's a common enough pattern that the implementation has "leaked" out into the open, and people use it as... – Cort Ammon Feb 01 '16 at 19:53
  • ... though it was defined behavior. Far better not to get in that habit, though. `offsetof` has defined behavior. – Cort Ammon Feb 01 '16 at 19:55
  • What about portability (for both)? – Peter Mortensen Feb 01 '16 at 20:50
  • @PeterMortensen It's probably about the same, given that one is implemented in terms of the other. – Sergey Kalinichenko Feb 01 '16 at 21:36
7

(struct name *)0 is casting 0 to pointer to struct name.
&((struct name *)0)->member) is getting the address of member member.
(char *) &((struct name *)0)->member) is casting that address to char *.

In case anyone thinks that the above expression is dereferencing a NULL pointer, then note that there is no dereferencing here. It's all for getting the address of member number.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
haccks
  • 104,019
  • 25
  • 176
  • 264
5

(struct name*)0 gives you a struct pointer.

((struct name*)0)->member gives you the member of the struct where the pointer points to.

Adding & gives you the address of that member, and lastly

(char *) is to cast the obtained address to a char pointer.

Vincent Savard
  • 34,979
  • 10
  • 68
  • 73
Ian
  • 30,182
  • 19
  • 69
  • 107
1

On many compilers, the above expression will yield a char* which, while it isn't a valid pointer, has a one or both of the following properties:

  1. Casting the pointer directly to an integer type will yield the displacement of the indicated member within the structure.

  2. Subtracting (char*)0 from the pointer will yield the displacement of the indicated member within the structure.

Note that the C Standard imposes no requirements with regard to what may happen if code forms an invalid pointer value via the above means or any other, even if the code makes no attempt to dereference the pointer. While many compilers produce pointers with the indicated qualities, such behavior is not universal. Even on platforms where there exists a clear natural relationship between pointers and integers, some compiler vendors may decide that having programs behave as implied by such a relationship, it would be more "efficient" to instead have the compiler to assume that a program will never receive inputs that would cause the generation of invalid pointers and, consequently, that any code which would only be relevant if such inputs were received should be omitted.

supercat
  • 77,689
  • 9
  • 166
  • 211