container_of() macro and list_entry() Explained.

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
Zero Pointer Derefrence.

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.

 

So If someone passes mismatched type and members in container_of(ptr, type, member) Compiler

 

will give warning and people can correct it at compile time otherwise such mistakes can create bugs.

typeof()

type x; typeof(x) gets replaced with type. For example :

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

 

Leave a comment