The Magical container_of() Macro and typeof()
来源:互联网 发布:华锋e路航v700端口数据 编辑:程序博客网 时间:2024/06/08 19:03
When you begin with the kernel, and you start to look around and read thecode, you will eventually come across this magical preprocessor construct.
What does it do? Well, precisely what its name indicates. It takes threearguments – apointer, type of the container, and the name of the memberthe pointer refers to. The macro will then expand to a new address pointing tothe container which accommodates the respective member. It is indeed aparticularly clever macro, but how the hell can this possibly work? Let meillustrate…
The first diagram illustrates the principle of the container_of(ptr, type,member)
macro for who might find the above description too clumsy.
Bellow is the actual implementation of the macro from Linux Kernel:
#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
At first glance, this might look like a whole lot of magic, but it isn’t quiteso. Let’s take it step by step.
Statements in Expressions
The first thing to gain your attention might be the structure of the wholeexpression. The statement should return a pointer, right? But there is justsome kind of weird({})
block with two statements in it. This in fact is aGNU extension to C language calledbraced-group within expression. Thecompiler will evaluate the whole block and use the value of the last statementcontained in the block. Take for instance the following code. It will print 5.
int x = ({1; 2;}) + 3;printf("%d\n", x);
typeof()
This is a non-standard GNU C extension. It takes one argument and returns itstype. Its exact semantics is throughly described ingccdocumentation.
int x = 5;typeof(x) y = 6;printf("%d %d\n", x, y);
Zero Pointer Dereference
But what about the zero pointer dereference? Well, it’s a little pointer magicto get the type of the member. It won’t crash, because the expression itselfwill never be evaluated. All the compiler cares for is its type. The samesituation occurs in case we ask back for the address. The compiler againdoesn’t care for the value, it will simply add the offset of the member to theaddress of the structure, in this particular case 0, and return the newaddress.
struct s { char m1; char m2;};/* This will print 1 */printf("%d\n", &((struct s*)0)->m2);
Also note that the following two definitions are equivalent:
typeof(((struct s *)0)->m2) c;char c;
offsetof(st, m)
This macro will return a byte offset of a member to the beginning of thestructure. It is even part of the standard library (available instddef.h
).Not in the kernel space though, as the standard C library is not present there.It is a little bit of the same 0 pointer dereference magic as we saw earlierand to avoid that modern compilers usually offer a built-in function, thatimplements that. Here is the messy version (from the kernel):
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
It returns an address of a member called MEMBER of a structure of type TYPEthat is stored in memory from address 0 (which happens to be the offset we’relooking for).
Putting It All Together
#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
When you look more closely at the original definition from the beginning ofthis post, you will start wondering if the first line is really good foranything. You will be right. The first line is not intrinsically important forthe result of the macro, but it is there for type checking purposes. And whatthe second line really does? It subtracts the offset of the structure’smember from its address yielding the address of the container structure.That’s it!
After you strip all the magical operators, constructs and tricks, it is thatsimple :-).
References
container_of()
explained by Greg KHcontainer_of()
definition in Linux Kernel- Statements and Declarations in Expressions
- Referring to a Type with
typeof
offsetof()
转自:http://broken.build/2012/11/10/magical-container_of-macro/
- The Magical container_of() Macro and typeof()
- The Magical container_of() Macro
- the macro # and ## (typing...)
- LightOJ 1342 - Aladdin and the Magical Sticks
- offsetof typeof container_of 浅析
- container_of,offsetof,与typeof
- linux kernel typeof container_of
- container_of,typeof详解
- typeof、offsetof、container_of
- offsetof ,container_of ,typeof 用法
- The difference of typeof and GetType method
- typeof、offsetof、container_of的解释
- Light OJ 1342 - Aladdin and the Magical Sticks (概率DP)
- 【概率DP】 LightOJ 1342 Aladdin and the Magical Sticks
- LightOJ-1342 Aladdin and the Magical Sticks(期望dp)
- macro与inline的区别 What is the difference between macro and inline?
- the macro of watch variable value and its memory content
- how eclipse CDT find the include and macro
- ThinkPHP---19--项目编译
- OC第五天
- ThinkPHP教程--20--系统流程
- Android - include属性使用方法
- 数据结构——树的基本概念
- The Magical container_of() Macro and typeof()
- Hadoop源码解读
- 编程之美 一排石头的游戏(拓展问题)
- python核心编程五——映像和集合
- 无论对小人物或者大人物都不要太谦卑
- MySQL同主机不同数据库的复制命令
- Maven创建android项目的步骤
- svn
- 关于FIN_WAIT1