container_of()
/** * container_of - cast a member of a structure out to the containing * struct * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * */ Simple Version: #define container_of(ptr, type, member) ({ \ (type *)( (char *)(ptr) - offsetof(type,member) );}) Linux Version with type safety: #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
Lets first analyse simple version with an Example:
struct student { char name[20]; int roll_no; int course_code; int marks; struct list_head student_list; };
struct student stu;
uint32_t *pMarks = &stu.marks;
struct student *pStu = container_of(pMarks, struct student, marks);
if (pStu == &stu) // True
Can you get address of stu variable using pMarks ?
Yes, Just subtract distance between start of struct to marks variable(20 + 4 + 4) from
pMarks : pMarks – (20 + 4 + 4) is equal to &stu
if we ignore padding and alignment. This explains the subtraction operation in
(type *)( (char *)(ptr) - offsetof(type,member) .
offsetof()
So now we have to find a way to calculate the offset of a variable in struct.
You must have known that every process has its own address space and starting address of
this address space starts with 0.
![]() |
| Process Address Space Example. |
As you know we can type cast any address without caring what is stored inside. For example.
char buffer[20]; struct student * pStu = (struct student *)buffer; //valid
if base address of buffer is starting with 0x0000 then the address of marks will be equal to (0 + offset) => offset. so &marks is equal to offset of marks in the struct student. But no buffer can start with 0 address that is reserved for code. But we can typecast constants also same as buffer so there is no problem in typecasting 0 itself.
This offsetof macro does same thing as we discussed to calculate offset of a member variable in struct.
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
offsetof(struct student, marks) => &(((size_t)(struct student *)0)->marks)
Now Lets Focus on Linux version of container_of():
why do we need an extra complicated line if this simple version works fine. Lets first discuss about compilation warning about incompatible pointer assignment.
int a; char *str = &a; //This line will give compilation warning.
typeof()
int a;
typeof(a) b = 10; //Equivalent to int b = 10;
Conclusion
#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr);
(type *)( (char *)__mptr - offsetof(type,member) );})
conainer_of(pMarks, struct student, marks)
=>
const typeof(((struct student *)0)->marks) *__mptr = (pMarks);
(struct student *) ((char *) __mtr - offsetof(struct student, marks));
=>
const struct student * __mptr = (pMarks);
(struct student *) ((char *) __mtr - offsetof(struct student, marks));
If you have doubt about there two lines in macro with semicolons so which one will be
returned.
int a = ({int x = 4; 5;}); printf("a == %d", a);
Output: a == 5

