container_of用法及实现

一、container_of是什么

container_of是一个非常有用的宏,它可以通过指定结构体成员的地址,计算出整个结构体的起始地址。这个宏的定义如下:

```c

#define container_of(ptr, type, member)

((type *)((char *)(ptr) - offsetof(type, member)))

```

其中,ptr是指结构体成员的地址,type是结构体的类型,member是结构体成员的名称。offsetof是一个C库函数,它可以计算出结构体中某个成员的偏移量。

二、container_of的使用方法

我们可以通过一个例子来说明container_of的使用方法:

```c

#include

#include

struct student {

int id;

char name[10];

int age;

};

int main() {

struct student stu = {

.id = 1,

.name = "Alice",

.age = 18

};

int *age_ptr = &stu.age;

struct student *stu_ptr = container_of(age_ptr, struct student, age);

printf("id = %d, name = %s, age = %d\n", stu_ptr->id, stu_ptr->name, stu_ptr->age);

return 0;

}

```

我们先定义了一个结构体student,包含三个成员:id、name和age。在main函数中,我们创建了一个student类型的结构体变量stu,并初始化它的id、name和age成员。然后,我们获取age成员的地址,将它赋值给age_ptr。如果我们想根据age成员的地址获取整个结构体的地址,就可以使用container_of宏:

```c

struct student *stu_ptr = container_of(age_ptr, struct student, age);

```

其中,我们传入了age_ptr、struct student和age三个参数,这样container_of就可以计算出整个结构体的地址,将它赋值给了stu_ptr。最后,我们可以打印出整个结构体的内容。

三、container_of的实现

container_of的实现非常简单,它的核心代码只有一行:

```c

((type *)((char *)(ptr) - offsetof(type, member)))

```

这一行代码的意思是,首先将ptr强制转换为char *类型,这样可以在指针的运算中使其以字节为单位。然后,计算出member的偏移量,也就是整个结构体的起始地址到member成员的地址的距离。最后,将整个结构体的起始地址向前移动该距离,就可以得到整个结构体的地址。

虽然container_of的实现很简单,但是要注意一些问题:

1. container_of只能用于指向结构体成员的指针或数组元素,不能用于指向结构体本身或结构体成员的指针数组、函数指针等。

2. container_of只适用于C语言中的结构体类型,不适用于C++中的类类型。

3. 如果结构体中有多个相同类型的成员,使用container_of会产生歧义,这时应该使用其他的方法来获取整个结构体的地址。

四、container_of的案例说明

container_of的一个非常经典的用途是在Linux内核中,它被广泛用于链表操作。链表的每个节点都是一个结构体,包含一个指向下一个节点的指针。如果我们知道一个节点的指针,它的地址就是整个结构体地址的一部分。这时,就可以使用container_of宏来获取整个节点的地址,进而获取链表中其他节点的地址。

例如,在kernel/list.h头文件中就定义了一个双向链表结构体:

```c

struct list_head {

struct list_head *prev, *next;

};

```

每个节点都是一个list_head结构体,它包含一个指向前一个节点和后一个节点的指针。如果我们知道一个节点的指针,可以使用container_of宏获取整个节点的地址,例如:

```c

struct student {

int id;

char name[10];

struct list_head list;

};

struct student stu1 = {

.id = 1,

.name = "Alice",

.list = LIST_HEAD_INIT(stu1.list)

};

struct student stu2 = {

.id = 2,

.name = "Bob",

.list = LIST_HEAD_INIT(stu2.list)

};

struct list_head *pos;

struct student *stu_ptr;

list_for_each(pos, &stu1.list) {

stu_ptr = container_of(pos, struct student, list);

printf("id = %d, name = %s\n", stu_ptr->id, stu_ptr->name);

}

```

在这个例子中,我们定义了一个student结构体,包含id、name和list成员。list成员是一个list_head结构体,用于存储节点在双向链表中的前后关系。

在main函数中,我们创建了两个student类型的结构体变量stu1和stu2,并初始化它们的id、name和list成员。然后,我们使用list_for_each宏遍历链表,并使用container_of宏获取整个student结构体的地址。这样,我们就可以打印出整个链表的内容。

总之,container_of是一个非常有用的宏,可以帮助我们更方便地访问结构体成员。虽然它的实现非常简单,但是它在Linux内核以及其他许多开源项目中发挥了重要作用。 如果你喜欢我们三七知识分享网站的文章, 欢迎您分享或收藏知识分享网站文章 欢迎您到我们的网站逛逛喔!https://www.37seo.cn/

点赞(90) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿
发表
评论
返回
顶部