Question
Is there a portable way to get a pointer to a structure if you have a pointer to a member of that structure?
There is a linked list implementation in Linux kernel (1):
struct list_head {
struct list_head *next, *prev;
};
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
The idea is that this implementation is generic. You can use it with any struct
type:
struct my_struct {
int my_data;
struct list_head node;
};
void example()
{
struct list_head head;
struct my_struct element1 = { 1 };
struct my_struct element2 = { 2 };
head.next = head.prev = &head; // head <-> head
list_add(&element1.node, &head); // head <-> {1} <-> head
list_add(&element2.node, &head); // head <-> {2} <-> {1} <-> head
struct my_struct *front_element = list_entry(&head.next, struct my_struct, node);
printf("front element data: %d\n", front_element->my_data); // will print "2"
}
Elements of list_head
are linked with each other, but there are only pointers to list_head
and there are no pointers to my_struct
(that contains list_head
inside). However having a pointer to the node
member of my_struct
, you can convert this pointer to a pointer to my_struct
itself using the list_entry
macro. This is done using tricky pointer arithmetic (the offset of the member in the struct is subtracted from the address of the member).
But the implementation of the container_of
macro is not portable because it uses gcc extensions and deferencing of null pointer (which is generally UB):
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
Is there a way to make a portable implementation of this macro?
Answer
Use offsetof
macro that comes with compiler (not UB).
#define container_of(ptr, type, member) \ ((type *) ((char *)(ptr) - offsetof(type, member)))
That looks clean to me. It's only spread across > two lines for SO.