公司的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的时候,其运行结果如下:

和菜鸟一起学linux之container_of实例_测试

 

当分步调用的时候,其运行结果如下:


 

和菜鸟一起学linux之container_of实例_算法_02

       这里定义了一个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指向的地址减去偏移,那么就是整个结构体的地址了。

哎,其实还是挺简单的,只是接触的少了,所以看得有点晕了。以后慢慢会好多的。