Add comments to container_of() wiki to understand its implementation.
the Linux kernel uses offsetof() to implement container_of(), which allows something like a mixin type to find the structure that contains it:[5]
#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
This macro is used to retrieve an enclosing structure from a pointer to a nested element, such as this iteration of a linked list of my_struct objects:
struct my_struct { const char *name; struct list_node list; }; extern struct list_node * list_next(struct list_node *); struct list_node *current = /* ... */ while (current != NULL) { struct my_struct *element = container_of(current, struct my_struct, list); printf("%s\n", element->name); current = list_next(&element->list); }
The linux kernel implementation of container_of uses a GNU C extension called statement expressions.[6] It's possible a statement expression was used to ensure type safety and therefore eliminate potential accidental bugs.
Comments 1: what's statement expressions? see doc. In short, it's a compound statement enclosed in parentheses "()"圆括号. A compound statement is a sequence of statements surrounded by braces "{}"大括号. The last thing in the compound statement should be an expression followed by a semicolon; the value of this subexpression serves as the value of the entire construct. For example:
({ int y = cc; int z; if (y > 0) z = y; else z = - y; z; })
is a valid (though slightly more complex than necessary) expression for the absolute value of foo().
Comments 2: what's typeof()? see doc. In short, it returns the type of its argument. The syntax of typeof
looks like sizeof
, but the construct acts semantically like a type name defined with typedef
. Here is an example with a typename as the argument:
typeof (int *) p;
Here the type described is that of pointers to int
, then declare p. It is like following statements:
typedef INT_POINTER (int *); INT_POINTER p;
Comments 3: what's the purpose of const typeof( ((type *)0)->member ) *__mptr = (ptr);? it's used to ensure type safety, that's ensure ptr has the same type with the address of member, such as they are both type of int *; Why the need to ensure type safety? To eliminate potential accidental bugs. Why this statement can ensure type safety? Because compiler will give a warning when do assignment from incompatible pointer type, for example the following assignment p=s; results in a warning:
int *p;
char *s = "tst";
p = s;
t.c:10:7: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
p = s;
^
Another way to ensure type safety. There is, however, a way to implement the same behaviour without using statement expressions while still ensuring type safety:
#define container_of(ptr, type, member) ((type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member)))
At first glance, this implementation may seem more complex than necessary, and the unusual use of the conditional operator may seem out of place. A simpler implementation is possible:
#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))
This implementation would also serve the same purpose, however, there's a fundamental omission in terms of the original linux kernel implementation. The type of ptr is never checked against the type of the member, this is something that the linux kernel implementation would catch.
In the aforementioned type-checked implementation, the check is performed by the unusual use of the conditional operator (三元运算符 ? : ). The constraints of the conditional operator specify that if the operands to the conditional operator are both pointers to a type, they must both be pointers to compatible types. In this case, despite the fact that the value of the third operand of the conditional expression will never be used, the compiler must perform a check to ensure that (ptr)
and &((type *)0)->member
are both compatible pointer types.
Comments 1: why the conditional operator ( ? : ) can ensure type safety? As states above, there is constraints for ?: , that's if the operands to the conditional operator are both pointers to a type, they must both be pointers to compatible types. For example the following warning is due to type mismatch since s is char * while p after : is int *:
int *p;
char *s = "tst";
p = 1 ? s : p;
t.c:12:15: warning: pointer type mismatch in conditional expression
p = 1 ? s : p;
^