一、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/
发表评论 取消回复