看内核源码时,看到一个 宏定义,蛮有意思,ptr是成员变量的指针,type是指结构体的类型,member是成员变量的名字,container_of作用是在已知某一个成员变量的名字,指针和结构体类型的情况下,计算结构体的起始地址。用该成员变量的指针,减去它相对于结构体起始位置的偏移量。
typeof(((type*)0)->member) 获取member的类型。 定义一个临时指针指向ptr , 把__mptr 转换成char*类型,因为offsetof得到的偏移量是以字节为单位,两者相减得到结构体的起始位置,再强制转换为type类型。
- /**
-
* 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/Linux/kernel.h
这里面还有两个需要说明的地方,一个是typeof 另外一个是offsetof。
typedof 是个GCC的关键字,是GCC特性。可以通过一个变量的名字获得其类型,这是编译器能做到的。
offsetof 是一个宏定义,是C99 的一个标准,可以通过 man offsetof查看该宏。可以直接用
- #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
TYPE 表示一个结构体类型,MEMBER是结构体中的一个成员变量的名字,offsetof的作用是计算成员变量MEMBER相对于结构体起始位置的内存偏移量,以字节为单位。 先把0强制转化为TYPE类型的指针,然后找到成员变量MEMBER,用取地址符得到MEMBER的地址。因为结构体是从地址0开始,所以MEMBER的地址就是相对于结构体开始位置的偏移量。
- #include<stdio.h>
-
#include<stddef.h>
-
-
#define offset(type,member) ((size_t)&((type*)0)->member)
-
-
struct st
-
{
-
int a;
-
char b;
-
char c;
-
int d
-
};
-
-
struct sd
-
{
-
int a;
-
struct st b;
-
char c;
-
};
-
-
int main()
-
{
-
printf("%d\n",offset(struct st,c));
-
printf("%d\n",offset(struct st,d));
-
printf("%d\n",offset(struct sd,b));
-
printf("%d\n\n",offset(struct sd,c));
-
-
printf("%d\n",offsetof(struct st,c));
-
printf("%d\n",offsetof(struct sd,b));
-
printf("%d\n",offsetof(struct sd,c));
- struct sd test;
-
int *p=&test.c;
-
struct st *tp=&test.b;
-
-
struct sd *dp=container_of(p,struct sd,c);
-
struct st *dq=container_of(tp,struct st,b);
-
// 已知member c的地址,求的test首地址
-
printf("%X\n",&test);
-
printf("%X\n",dp); // 已知 test中 b c 的地址, 求得 test 的地址。
-
printf("%X\n",dq);
-
return 0;
-
-
}
- 5 //c
-
8 //d 编译器使d按照4字节倍数对齐了
-
4
-
16
-
-
5
-
4
-
16 //c相对于起始 偏移 sizeof(int)+ sizeof(struct st)=4+12=16
阅读(2946) | 评论(1) | 转发(4) |