container_of学习笔记

最近在学习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\n",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\n",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宏来做类型检测,显得更加的直观明了,错了还可以有提示。

posted @ 2017-10-19 08:32  立体风  阅读(10317)  评论(0编辑  收藏  举报