I have a problem with using of container_of
macro in the Linux kernel. I have the following code
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type, member) );})
struct list_head
{
struct list_head *prev;
struct list_head *next;
};
struct fox
{
unsigned long tail_length;
unsigned long weight;
unsigned int is_fantastic;
/*Make this struct a node of the linked list*/
struct list_head list;
};
I want to make fox structure a node of the linked list.
int main(void)
{
struct list_head node_first = {.prev=NULL, .next=NULL};
struct fox first_f = {.tail_length=3, .weight=4, .is_fantastic=0, .list=node_first};
struct fox *second_f;
second_f = container_of(&node_first, struct fox, list);
printf("%lu\n", second_f->tail_length);
return 0;
}
I expected that I will see 3
in the terminal, since second_l
points to the firstf_f
structure, but I have 140250641491552
(some "random" value from the memory, as a think).
That's not how offsetof
is meant to be used. Your supposed to pass a pointer to data held in a struct to retrieve a pointer to the overall struct (So a struct list_head**
). Instead you pass a copy of a pointer to the wrong value ( &node_first
is different to &first_f.list
(what you're supposed to pass), as it is a copy)
For an example of how it works, consider this struct list_head
object:
(struct list_head) { // At 0x1000
.prev = 0, // At 0x1000
.next = 0 // At 0x1004
}
If you were to call container_of((struct list_head**) 0x1004, struct list_head, next)
, you would get (struct list_head*) 0x1000
.
This example is a bit confusing because of the same types, so here's another example:
#include <stddef.h>
#include <stdio.h>
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type, member) );})
struct holds_values {
int n;
char c;
};
struct holds_values* get_container(char* c_ptr) {
// If this was a pointer to a `c` in `holds_values`
// e.g., `&object.c`, that `char` is sizeof(int) bytes into
// the object, so this should be mostly the same as
// `(struct holds_values*) (c - sizeof(int))`
return container_of(c_ptr, struct holds_values, c);
}
int main() {
struct holds_values x = { .n = 5, .c = 'a' };
char* ptr = &x.c;
struct holds_values* y = get_container(ptr);
printf("%d\n%d\n", y == &x, y->n);
}
Change
second_f = container_of(&node_first, struct fox, list);
to
second_f = container_of(&first_f.list, struct fox, list);
Explanation :
This has nothing to do with kernel or Linux, it's C and GCC.
The line
struct fox first_f = {.tail_length=3, .weight=4, .is_fantastic=0, .list=node_first};
copies the contents of node_first
into first_f.list
. first_f
is NOT the container of node_first
, it contains a copy of node_first
.
If you want first_f
to be the container of node_first
you need to define:
struct fox first_f = {.tail_length=3, .weight=4, .is_fantastic=0, .list={.prev=NULL, .next=NULL}};
struct list_head *node_first = &first_f.list;
ie define node_first
as a pointer into the member of first_f
.
Note that you could define struct fox
to have
struct fox
{
// ...
struct list_head *list;
};
instead of
struct fox
{
// ...
struct list_head list;
};
ie to have struct fox
point to the list_head
, and then your original main()
would work.
But that's simply not the right way to use list_head
, list_head
is intended to be contained within other structs.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.