'Interesting C macro, how is it able to take a field within a struct and return a reference to the struct?
I saw an interesting design for a doubly-linked-list inside the Pintos operating system and I don't quite understand how it was able to achieve this behavior. I wrote a small adaptation to demo what I'm referring to.
#include <stdio.h>
#include <stdint.h>
#include "list.h"
struct foo {
int x;
struct list_node node;
};
int main()
{
struct list list;
list_initialize(&list);
struct foo n1 = {1, NULL};
list_push_back(&list, &n1.node);
struct list_node *e = list_get_front(&list);
struct foo *ptr = list_entry(e, struct foo, node);
printf("%d", ptr->x);
return 0;
}
In essence, list.h
implements two structures; namely, list
and list_node
. A list can be made for any arbitrary struct so long as the struct includes a list_node
field. A reference to the structure can be returned by passing a pointer to it's list_node
using the following macro defined in list.h
.
#define list_entry(LIST_NODE, STRUCT, MEMBER) \
((STRUCT *) ((uint8_t *) &(LIST_NODE)->next \
- offsetof (STRUCT, MEMBER.next)))
Solution 1:[1]
The list_node struct declares a known field name, that is used to develop the result.
The key is found in the expression:
(uint8_t *)&(LIST_NODE)->next - offsetof (STRUCT, MEMBER.next)
offsetof() gives the offset in bytes of the specified field in the specified struct.
(uint8_t *)&(LIST_NODE)->next returns a byte pointer, that points to (the beginning of) the specified member of the specified node.
When you subtract the byte offset of the field from the pointer to the field, you get a byte pointer to (the beginning of) the struct.
The macro then casts this to a pointer to the struct.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | John R. Strohm |