在linux 内核中有一些骚操作的宏,以下是我对container_of宏的理解
举个例子假设有如下结构体
struct test {
int a;
int b,
int c;
} *test1;
一般来说,我有test结构体变量的指针test1,我可以用test->a,test->b,test->c来分别访问变量a,b,c.
假设我现在知道b变量的地址(指针),能不能找到test结构体变量母体的地址呢?
container_of宏就是来解决这个问题的,用法如下
#define offsetof(struct_t,member) ((size_t)(char *)&((struct_t *)0)->member)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
stuct test *result;
int *test_b = &test1->b;
result = container_of(test_b,struct test,b);
result 就是我们获得的struct test的地址,其实与test1是同一个值。
下面解析container_of这个宏
这么骚的操作的实现其实也只有两行代码哈哈,他主要依赖于gcc c编译器的内置关键字typeof,和offsetof这个宏
我们就具体问题具体分析对以下这一句进行宏展开
result = container_of(test_b,struct test,b);
具体分析,首先是第一行代码的解析
const typeof( ((type *)0)->member ) *__mptr = (ptr); \\这里宏展开为
const typeof( ((struct test *)0)->b) *__mptr = test_b; \\进一步展开为
const int *__mptr = test_b;
typeof其实就是获取 结构体成员b的类型,这里为int型,这句代码只是定义了一个变量__mptr,将test_b的值赋予它。当然__mptr的类型和test_b的类型是一致的,都为int *型。
然后是第二行代码的解析
(type *)( (char *)__mptr - offsetof(type,member) ); \\这里宏展开为
(type *)( (char *)__mptr - ((size_t)(char *)&(type *)0->member) ); \\ 进一步展开为
(struct test *)( (char *)__mptr - ((size_t)(char *)&(struct test *)0->b) ); \\ 进一步计算为
(struct test *)( (char *)__mptr - ((size_t)(char *)(4) ); \\ 进一步计算为
(struct test *)( (char *)__mptr - 4 ); \\ 进一步计算为
(struct test *)( (char *)test_b - 4 );
其实最后结果就是test_b的值减去4,然后再强制转换为struct test *型的指针而已。
比较trick的实现其实只是offsetof这个宏, 在此处的例子中,0这个地址强制转换为strcut test*的指针,然后访问其变量b,再获取b变量的地址,再其强制转换为无符号整型size_t.
因为这个struct test结构体地址从0开始,所以b变量的地址就是0+4,这个值了。
这个看似复杂的东西其实其本质都还是挺简单的。