Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址。
Container_of的定义如下:
其实它的语法很简单,只是一些指针的灵活应用,它分两步:
第一步,首先定义一个临时的数据类型(通过typeof( ((type *)0)->member )获得)与ptr相同的指针变量__mptr,然后用它来保存ptr的值。
第二步,用(char *)__mptr减去member在结构体中的偏移量,得到的值就是整个结构体变量的首地址(整个宏的返回值就是这个首地址)。
其中的语法难点就是如何得出成员相对结构体的偏移量?
其中代码难以理解的地方就是它灵活地运用了0地址。如果觉得&( (struct test_struct *)0 )->ch这样的代码不好理解,那么我们可以假设在0地址分配了一个结构体变量struct test_struct a,然后定义结构体指针变量p并指向a(struct test_struct *p = &a),如此我们就可以通过&p->ch获得成员ch的地址。由于a的首地址为0x0,所以成员ch的首地址为0x4。
实例代码:
#include <stdio.h>
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
struct test_struct {
int num;
char ch;
float fl;
};
int main(void)
{
struct test_struct my_test = {1, '2', 3};
printf("&my_test.num = [%ld]\n", &my_test.num);
printf("&my_test.ch = [%ld]\n", &my_test.ch);
printf("&my_test.fl = [%ld]\n", &my_test.fl);
printf("container_of(struct test_struct, num) = %ld\n", container_of(&my_test.num, struct test_struct, num));
printf("container_of(struct test_struct, ch) = %ld\n", container_of(&my_test.ch, struct test_struct, ch));
printf("container_of(struct test_struct, fl) = %ld\n", container_of(&my_test.fl, struct test_struct, fl));
return 0;
}
运行结果:
&my_test.num = [140733208695616]
&my_test.ch = [140733208695620]
&my_test.fl = [140733208695624] //虽然ch位字符型,但还是分配了4个字节
container_of(struct test_struct, num) = 140733208695616
container_of(struct test_struct, ch) = 140733208695616
container_of(struct test_struct, fl) = 140733208695616