container_of()

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;
               ^

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值