linux内核常用宏 container_of

在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,这个值了。

这个看似复杂的东西其实其本质都还是挺简单的。

猜你喜欢

转载自blog.csdn.net/a827143452/article/details/85544304