C语言高级用法---container_of()

在Linux内核源码中,实现和链表相关的接口list_entry()时,会调用container_of()宏定义,它的作用是:给定结构体中某个成员的地址、该结构体类型和该成员的名字获取这个成员所在的结构体变量的首地址。有点绕,没关系,接着往下看就能明白了。
container_of()宏定义实现如下所示

/**
 * 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) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})

要看懂上述代码,需要知道三个关键点:

  1. typeof(),取变量或表达式类型,可参考之前typeof的文章
  2. typeof( ((type *)0)->member ),用typeof( ((struct apple *)0)->color ) 解释更易理解,它的作用是获取结构体apple中变量color的类型,可参考之前的文章
  3. offsetof(),获取结构体中某个成员相对于该结构体首元素地址的偏移量

好了,再开始解释container_of(),其三个入参含义:

  1. ptr:结构体变量中某个成员的地址
  2. type:结构体类型
  3. member:该结构体变量的具体名字

比如如下结构体struct ipstore,假设已经有一个变量struct ipstore *ist1;,并给ist1分配好了内存且进行了初始化,在已知结构体成员list的地址的情况下,获取list所在的结构体变量ist1的首地址。此时有人就会问了,这里ist1明明是已知的,这么做不是自己给自己找麻烦吗?这是个好问题,但是不要忘记,本文到目前为止只是讲解container_of()的含义,并没有说它适合用在什么样的场景下,因为有一种使用场景,当链表是通过list串起来的时候,此时并不知道ist1的首地址,反而是知道list的地址,这时container_of()就非常合适了,内核中的链表就是这么做的。

struct list_head {
    struct list_head *next;
    struct list_head  *prev;
};

struct ipstore{
    unsigned long time;
    __u32 addr[4];
    struct list_head list;
};

所以,调用container_of()时具体的传参如下所示,其返回的结果是ist1的地址。

container_of(ist1->list, struct ipstore, list)

如下测试代码,通过container_of()取得了ip1的地址。

void container_of_test()
{
    struct ipstore ip1;
    struct ipstore *p1;

    p1 = container_of(&ip1.list, struct ipstore, list);

    printf("ip1's addr:0x%0x\n", &ip1);
    printf("p1's  addr:0x%0x\n", p1);
}
[root@xxx c_base]# ./a.out
ip1's addr:0xa5fe1fe0
p1's  addr:0xa5fe1fe0

你可能感兴趣的:(C语言基础,C语言高级用法)