container_of 详解
container_of宏是用在已知一个struct中的元素地址,计算出此元素所在结构体的首地址。
一、定义
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
其中 ptr 指的是元素的指针,type是结构体的类型,member是元素类型。
举个例子:
#include<stdio.h>
#include<stddef.h>
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
struct Test{
int m_a;
int m_b;
int m_c;
};
int main()
{
struct Test test;
test.m_b = 1;
int* ptr = &(test.m_b);
printf("container_of_addr = %x\n test_addr = %x\n", \
container_of(ptr, struct Test, m_b), &test);
return 0;
}
运行结果:
container_of_addr = 1429a730
test_addr = 1429a730
从这里可以看出,通过一个struct的成员的地址 ptr ,结构体的类型 struct Test,成员名 m_b。就可以得到ptr所对应的结构体变量的首地址。
二、原理
原理其实很简单,一句话就可以概括,就是通过元素的地址减去结构体成员在结构体中的地址偏移。如下图:
其中:offsetof(type, member)函数就是用来求offset(结构体成员在结构体中的地址偏移)。来看一看offsetof的定义:
#define offsetof(TYPE, MEMBER) ((SIZE_T) &((TYPE *)0)->MEMBER)
这里的 (type *)0 表示,骗编译器我有一个起始地址为0的类型为type的变量,&((TYPE *)0)->MEMBER 所以member的地址就是member在struct中的地址偏移。
通过 head = ptr - offset 就可以得到struct的首地址,再将head强制转换为相应的struct类型的指针即可。