0

I have recently stumbled upon this macro using address a manipulation trick, in a file dedicated to adding generic doubly linked queues to any struct:

#define queue_entry(ptr_link, type, listfield) \
    ((type *)((char *)(ptr_link)-(unsigned long)(&((type *)0)->listfield)))

A working example of this macro would be :

struct link {
    struct link* next;
    struct link* prev;
};

struct mystruct {
    //...
    struct link chaining;
    //...
}

int main() {
    struct link ex_link;
    struct mystruct *ex_struct = malloc(...);
    ex_struct->chaining = ex_link;
    queue_entry(&ex_link, struct mystruct, chaining); // returns ex_struct
    return 0;
}
  • Why is &((type *)0)->listfield working? I would expect all kinds of compiler errors at this point, due to a null pointer dereferencing. So what happens exactly? I have only seen something similar once before in the Linux kernel: #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)), which was apparently working because of sizeof being evaluated at compile time. Here, there is no such thing.
  • Why the respective casts to unsigned long and char *? As in, why use two different types, and that are seemingly unrelated to the queue?
Community
  • 1
  • 1
pie3636
  • 795
  • 17
  • 31
  • 2
    Use offsetof, for everything else there's:https://stackoverflow.com/questions/26906621/does-struct-name-null-b-cause-undefined-behaviour-in-c11 – 2501 May 26 '16 at 17:44
  • 1
    @2501: You are right, an application should rely on this. FYI,many implementations define `offsetof` in a similar way (those which use `0x0` as bit-representation of a null pointer). – too honest for this site May 26 '16 at 17:55
  • Alright, that made it clear, what about the casts though? – pie3636 May 27 '16 at 06:51

0 Answers0