和菜鸟一起学linux之container_of实例
公司的i2c和spi总线的所有东西都要交给我负责了,下午开始看了i2c的代码,发现了个container_of的宏,甚是不解,网上找了点资料看看,自己写了些小测试程序,终于明白了,看来以前学算法没有学好c的基础就是现在这个下场啊,看这些东西要半天,进度都没有了。还是记录下来吧,以后看着就知道了。
#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type*)((char *)__mptr - offsetof(type,member));})
看着上面的代码,顿时会觉得懵了,看不懂额。宏的定义也就这样吗,慢慢来,一步一步来。
首先还是看下例子吧,例子最最明显了。
#include <stdio.h> struct myclass { int Chinese; int English; }; struct student { char name[10]; struct myclass cla; }stu={"eastmoon", {100, 99}}; #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));}) int main(void) { struct student *stu_ptr; int offset; struct myclass *mycla = &stu.cla; printf("stu: %p\n", &stu); printf("mycla: %p\n", mycla); #if 1 stu_ptr = container_of(mycla, struct student, cla); #endif #if 0 const typeof(((struct student *)0)->cla) *__mptr = mycla; offset = offsetof(struct student, cla); stu_ptr = (struct student *)((char *)__mptr - offset); printf("offsetof stu.cla = %d\n", offset); #endif printf("name: %s\n", stu_ptr->name); printf("Chinese: %d\n", stu_ptr->cla.Chinese); printf("English: %d\n", stu_ptr->cla.English); return 0; }
当就调用container_of的时候,其运行结果如下:
当分步调用的时候,其运行结果如下:
这里定义了一个student结构体,然后这里有其myclass结构体的课程成绩。首先我们知道了stu这个结构体的数据,然后有个指针mycla = &stu.cla,然后调用了
stu_ptr = container_of(mycla, struct student, cla);
其实就是为了得到stu这个结构体变量的指针。
总的来说就是:我们可以根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针。
然后我们来看看分步的调用
const typeof(((struct student *)0)->cla) *__mptr = mycla;
其中的(struct student *)0非常巧妙,就是把0转换为struct student类型,主要是其没有任何偏移。所以,这里就是把__mptr的的指针指向mycla。
offset = offsetof(struct student, cla);
这里就是求cla在struct student中的偏移了。然后看看运行结果,为什么是12呢?
struct student { char name[10]; struct myclass cla; }stu={"eastmoon", {100, 99}};
看着明明name这个数组才10啊,char又是一个字节的。其实其存放是要对齐的,这里是4字节对齐的,10个字节可以分为4+4+2,所以接着就只能是12了,如果把name[10]改为name[8],那么偏移就是8了。
最后
stu_ptr = (struct student *)((char *)__mptr - offset);
这里把mycla指向的地址减去偏移,那么就是整个结构体的地址了。
哎,其实还是挺简单的,只是接触的少了,所以看得有点晕了。以后慢慢会好多的。