为什么我们使用container_of
宏?
container_of(pointer, container_type, container_field);
在LDD中说
"此宏采用指向名为
container_field
的字段的指针 类型为container_type
的结构,并返回指向的结构 包含结构"。
我的问题是:
答案 0 :(得分:3)
让我举个例子:
struct A
{
int some_data;
int other_data;
};
现在让我们说有这个功能:
int get_some_data_from_other(int *other)
{
struct A *a = container_of(other, struct A, other_data);
return a->some_data;
}
正如您所看到的,我们可以通过知道指针所指向的结构的哪个字段来告诉包含给定struct A
的原始int *other
是什么。 这里的关键是我们不要引用结构本身,而只是指向其中一个成员的指针。
这看起来很荒谬,但实际上在一些非常聪明的结构中很有用。一个非常常见的例子是内核创建链表的方式。我建议你阅读this post on kernelnewbies.org。让我们看一个简短的例子:
struct whatever
{
/* whatever data */
struct list_head mylist;
};
所以struct whatever
有一些数据,但它也想充当链表中的节点。他们在学校教你的是拥有一个不同的结构,包含next
/ prev
指针以及指向struct whatever
(或void *
)的指针。这样,您就拥有了通过其获取数据的节点。
按照所有软件工程标准,这实际上是一件好事。但是,软件工程标准对于效率的影响很小。请参阅Why should I have written ZeroMQ in C, not C++ (part II)。
底线是,使用传统方法,您必须与数据节点分开分配链接列表节点,即您将内存分配,释放,碎片和缓存未命中等的开销加倍。 Linux内核的方式恰恰相反。每个数据节点都包含一个通用链表节点。通用链表节点对数据或它们的分配方式一无所知,只知道如何连接到其他链表节点。
让我们深入了解一下:
+-------------------+ +---------------------+ +---------------------+
| | | | | |
| WHATEVER DATA | | WHATEVER DATA 2 | | WHATEVER DATA 3 |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
+-------------------+ +---------------------+ +---------------------+
| |----->| |----->| |
| mylist | | mylist 2 | | mylist 3 |
| |<-----| |<-----| |
+-------------------+ +---------------------+ +---------------------+
您拥有的链接列表是struct list_head
内指向其他struct list_head
的指针。请注意,他们并不指向struct whatever
,而是指向<{1}} 里面这些结构。
假设您有一个节点mylist
。您想要找到下一个节点。你能做什么?首先,您可以struct whatever w
获取指向下一个节点的w.mylist.next
的指针。现在,您必须能够提取包含该节点的实际mylist
。那就是使用struct whatever
的地方:
container_of
最后,请注意Linux内核有遍历链表的所有节点的宏,这通常不是你想要的,所以你实际上并不需要直接使用struct whatever w_next = container_of(w.mylist.next, struct whatever, mylist);
答案 1 :(得分:1)
Why do we use container_of macro ?
宏的容器用于获取指向包含元素的结构的开头的指针。
例如
struct container {
int some_other_data;
int this_data;
}
指向int *my_ptr
成员的指针this_data
,您可以使用宏来获取指向结构容器*my_container
的指针:
struct container *my_container;
my_container = container_of(my_ptr, struct container, this_data);
将this_data
的偏移量考虑到结构的开头对于获取正确的指针位置至关重要。
实际上,您只需从指针this_data
中减去成员my_ptr
的偏移量即可获得正确的位置。
另见here,明确你的疑惑。
答案 2 :(得分:0)
是的,您可以使用自己编写的演员/作业,但它有一些优势。
效率 - 使用宏而不是声明整个演员表并不容易吗?
维护 - 如果每个人都使用宏,只需编辑宏,就可以在整个项目中轻松更改指针分配的方式。
可读性 - 什么是易读?一个明确的演员或宏为你做的吗?
安全性 - 您可能相信此宏可以运行,并且它运行任何重要的强制转换和类型检查,比您自己的代码更好。