现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

C语言扩展用法—container_of()

2019-10-26 20:52 工业·编程 ⁄ 共 1541字 ⁄ 字号 暂无评论

    在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) );})
要看懂上述代码,需要知道三个关键点

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


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

ptr:结构体变量中某个成员的地址
type:结构体类型
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);
}

给我留言

留言无头像?