1. container_of宏
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member)*__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
首先看下三个参数, ptr是成员变量的指针, type是指结构体的类型, member是成员变量的名字。
container_of宏的作用是通过结构体内某个成员变量的地址和该变量名,以及结构体类型,找到该结构体变量的地址。这里使用的是一个利用编译器技术的小技巧,即先求得结构成员在结构中的偏移量,然后根据成员变量的地址反过来得出主结构变量的地址。下面具体分析下各个部分。
2. typeof
首先看下typeof,是用于返回一个变量的类型,这是GCC编译器的一个扩展功能,也就是说typeof是编译器相关的。既不是C语言规范的所要求,也不是某个标准的一部分。
typeof
int main()
{
int a = 5;
//这里定义一个和a类型相同的变量b
typeof(a) b = 6;
printf("%d,%d\r\n",a,b);//5 6
return 0;
}
3. (((type *)0)->member)
((TYPE *)0)
将0转换为type类型的结构体指针,换句话说就是让编译器认为这个结构体是开始于程序段起始位置0,开始于0地址的话,我们得到的成员变量的地址就直接等于成员变量的偏移地址了。
(((type *)0)->member)
引用结构体中MEMBER成员。
typedef struct student{
int id;
char name[30];
int math;
}Student;
int main()
{
//这里时把结构体强制转换成0地址,然后打印name的地址。
printf("%d\r\n",&((Student *)0)->name);//4
return 0;
}
4. const typeof(((type * )0) ->member)*__mptr = (ptr);
这句代码意思是用typeof()获取结构体里member成员属性的类型,然后定义一个该类型的临时指针变量__mptr,并将ptr所指向的member的地址赋给__mptr;
为什么不直接使用 ptr 而要多此一举呢?我想可能是为了避免对 ptr 及prt 指向的内容造成破坏。
5. offsetof(type, member))
((size_t) &((TYPE*)0)->MEMBER)
size_t
是标准C库中定义的,在32位架构中被普遍定义为:
typedef unsigned int size_t;
而在64位架构中被定义为:
typedef unsigned long size_t;
可以从定义中看到,size_t是一个非负数,所以size_t通常用来计数(因为计数不需要负数区):
for(size_t i=0;i<300;i++)
为了使程序有很好的移植性,因此内核使用size_t,而不是int,unsigned。((size_t) &((TYPE*)0)->MEMBER)
结合之前的解释,我们可以知道这句话的意思就是求出MEMBER
相对于0地址的一个偏移值。
6. (type * )((char * )__mptr - offsetof(type, member))
这句话的意思就是,把 __mptr 转换成 char* 类型。因为 offsetof 得到的偏移量是以字节为单位。两者相减得到结构体的起始位置, 再强制转换成 type 类型。
7. 举例
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
typedef struct student
{
int id;
char name[30];
int math;
} Student;
int main()
{
Student stu;
Student *sptr = NULL;
stu.id = 123456;
strcpy(stu.name,"zhongyi");
stu.math = 90;
sptr = container_of(&stu.id,Student,id);
printf("sptr=%p\n",sptr);
sptr = container_of(&stu.name,Student,name);
printf("sptr=%p\n",sptr);
sptr = container_of(&stu.math,Student,math);
printf("sptr=%p\n",sptr);
return 0;
}
运行结果如下:
sptr=0xffffcb90
sptr=0xffffcb90
sptr=0xffffcb90
宏展开可能会看的更清楚一些
int main()
{
Student stu;
Student *sptr = NULL;
stu.id = 123456;
strcpy(stu.name,"zhongyi");
stu.math = 90;
//展开替换
sptr = ({ const unsigned char *__mptr = (&stu.id);
(Student *) ( (char *)__mptr - ((size_t) &((Student *)0)->id) );});
printf("sptr=%p\n",sptr);
//展开替换
sptr = ({ const unsigned char *__mptr = (&stu.name);
(Student *) ( (char *)__mptr - ((size_t) &((Student *)0)->name) );});
printf("sptr=%p\n",sptr);
//展开替换
sptr = ({ const unsigned int *__mptr = (&stu.math);
(Student *) ( (char *)__mptr - ((size_t) &((Student *)0)->math) );});
printf("sptr=%p\n",sptr);
return 0;
}