Let me give you an example:
struct A
{
int some_data;
int other_data;
};
Now let's say there is this function:
int get_some_data_from_other(int *other)
{
struct A *a = container_of(other, struct A, other_data);
return a->some_data;
}
As you can see, we are able to tell what is the original struct A
that contains the given int *other
, by knowing which field of the struct that pointer supposedly points to. The key here is that we don't have a reference to the struct itself, but just a pointer to one of its members.
This may seem absurd, but is actually useful in some very clever constructs. One very common example is the way the kernel creates linked lists. I suggest you read this post on kernelnewbies.org. Let's see a short example of it:
struct whatever
{
/* whatever data */
struct list_head mylist;
};
So struct whatever
has some data, but it also wants to act as a node inside a linked list. What they teach you in school is to have a different struct, that contains the next
/prev
pointers as well as a pointer to struct whatever
(or void *
). That way, you have nodes through which you get to your data.
By all software engineering standards, that's actually a good thing. But software engineering standards have little regards w.r.t efficiency. See Why should I have written ZeroMQ in C, not C++ (part II).
Bottom line is, with the conventional method, you have to allocate the linked list node separately from the data node, i.e. you double the overhead of memory allocation, deallocation, fragmentation and cache misses among others. The way the Linux kernel does it is the opposite. Each data node contains a generic linked list node. The generic linked list node does not know anything about the data or how they were allocated and knows only how to connect to other linked list nodes.
So let's take a deeper look:
+-------------------+ +---------------------+ +---------------------+
| | | | | |
| WHATEVER DATA | | WHATEVER DATA 2 | | WHATEVER DATA 3 |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
+-------------------+ +---------------------+ +---------------------+
| |----->| |----->| |
| mylist | | mylist 2 | | mylist 3 |
| |<-----| |<-----| |
+-------------------+ +---------------------+ +---------------------+
What you have as linked list are pointers inside struct list_head
that point to other struct list_head
s. Note that they don't point to struct whatever
, but to the mylist
inside those structs.
Say you have a node, struct whatever w
. You want to find the next node. What can you do? First, you can do w.mylist.next
to get a pointer to the mylist
of the next node. Now you have to be able to extract the actual struct whatever
containing that node. That's where container_of
is used:
struct whatever w_next = container_of(w.mylist.next, struct whatever, mylist);
Finally, note that the Linux kernel has macros to traverse over all nodes of a linked list, which is more often than not what you want, so you don't actually need to use container_of
directly yourself.