最近在学习c语言宏编程,看到了container_of宏,深入学习了一天,做个笔记留念。
1、看一下书上写的container_of的版本:
#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)); })
2、举一个实例:
int main(int argc, char *argv[]) { struct test{ int num; char ch; }t1={100,'c'}; char *pch=&t1.ch; struct test *ptt=container_of(pch,struct test,ch); printf("num=%d ",ptt->num); return 0; }
替换后的结果:
int main(int argc, char *argv[]) { struct test{ int num; char ch; }t1={100,'c'}; char *pch=&t1.ch; struct test *ptt=({ const typeof(((struct test *)0)->ch) *__ptmp=(pch); (struct test *)((char *)__ptmp - ((size_t) &((struct test *)0)->ch)); }); printf("num=%d ",ptt->num); return 0; }
如果替换后的结果你还能看懂,说明你是真明白了,呵呵,有没有兴趣自己写一遍替换后的代码?
3、多余的不说了,网上有的是讲解的,这里就说二点:
1、container_of宏第一步是做类型检查的,也就是检查ptr是否是指向结构成员member的,如果我们用typeof求出来的类型和ptr不一致,那么编译器会报错。为啥要做这个检查呢?因为ptr和member都是人工输入的参数,宏要保证它们是结构体成员和其相关联的指针,这个宏才有意义,所以类型检查是必须的。
2、第二步相减时,把mptr指针强转成(char *)是因为,char指针减法只移一个字节,如果这样才可能得出准确的地址,否则,改为int类型,在减1就移动4个就乱了。
4、我有研究了最新4.12kernel的该宏,发现有了变化:
/** * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * */ #define container_of(ptr, type, member) ({ void *__mptr = (void *)(ptr); BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && !__same_type(*(ptr), void), "pointer type mismatch in container_of()"); ((type *)(__mptr - offsetof(type, member)));
哇,这里有出现一个新宏:__same_type,赶紧用ctags查一下定义,在include/linux/compiler.h中:
/* Are two types/vars the same type (ignoring qualifiers)? */ #ifndef __same_type # define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) #endi
有2个新变化,显得更加高大上了,第一,用void *取代了char *来做减法,第二,用__same_type宏来做类型检测,显得更加的直观明了,错了还可以有提示。