在进行内核驱动开发的时候,经常可以看到container_of
的身影,其作用就是获取包含某个成员的结构体变量地址,函数原型如下所示;
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
container_of
在Linux
内核源码/linux/kernel.h
中,其宏定义如下所示;
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#include
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
struct human_mod{
int head;
char eye;
float foot;
};
int main(){
struct human_mod me;
struct human_mod *p;
me.head = 1;
me.eye = 2;
me.foot = 2;
p = (struct human_mod *)container_of(&me.head, struct human_mod, head);
printf("head:%d\r\n",p->head);
printf("eye :%d\r\n",p->eye );
printf("foot:%f\r\n",p->foot);
return 0;
}
测试结果如下所示;
在标准C中是不支持typeof
语法的,在gnu
扩展语法中可以支持;
typeof
可以得到表达式的类型;
具体参考:https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html#C-Extensions
这条语句的目的就是获取TYPE
类型中MEMBER
成员的相对偏移地址;
在C标准库中(stddef.h
)也可以找到offsetof
;由于在内核空间中没有标准C
库,因此就出现了下面的宏定义;
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
这样写会不会报错?
答案是不会,因为编译器只关心类型,只要不存在访问非法地址的内容就不会出现问题,那也是程序运行时的事情,而非编译时的事情;
也就是我们经常说的访问野指针,空指针,但是上述语句显然没有对指针指向的内存进行访问。
下面先简单测试一下:
#include
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
int main(){
printf("head:%d\r\n",offsetof(struct human_mod , head));
printf("eye :%d\r\n",offsetof(struct human_mod , eye));
printf("foot:%d\r\n",offsetof(struct human_mod , foot));
return 0;
}
在这里面究竟发生了哪些事情;
简单解释一下,基本上是以下几个步骤;
1. 0
2. ((TYPE *)0)
3. ( ((TYPE *)0)->MEMBER )
4. &( ((TYPE *)0)->MEMBER )
5. ( (size_t) &( ((TYPE *)0)->MEMBER )
0
;TYPE
类型(这里的类型是由我们来输入的);TYPE
类型指针指向的MEMBER
成员;MEMBER
成员的地址;size_t
类型;整体的过程,结合上面的代码,具体如下图所示;
最终的运行结果;
运行结果如下:
当我们从这篇文章的开头开始查看这个宏定义的时候,甚至会怀疑第一行是否真的正确的。但事实证明,这样使用是正确的;
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
在最终剥离这些眼花缭乱的运算符,结构和技巧之后,整体就相对显得比较简单了。