1 结构体
假如我们有一个结构体Student,其定义如下:
typedef struct student
{
int id;
char name[30];
int math;
}Student;
通过Student stu定义stu变量后,我们可以通过stu.id或stu.name来获取stu的成员。但如果想反过来,通过stu.id或者stu.name来获取stu变量的起始地址好像就没那么简单了,linux中的container_of宏就是为解决此问题而生的。
2 container_of
在linux中container_of宏定义如下:
/**
* 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) ({ \
void *__mptr = (void *)(ptr); \
BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
!__same_type(*(ptr), void), \
"pointer type mismatch in container_of()"); \
((type *)(__mptr - offsetof(type, member))); })
container_of宏的包含3个参数:ptr、type和member,分别说明如下:
ptr: 第三个参数member的地址,这里可以理解为&stu.id或&stu.name;
type: 包含ptr的结构体名称,这里可以理解为Student;
member: ptr在结构体中的成员名。
为便于理解,我们通过用程序代码模拟如下:
#include <stdio.h>
#include <string.h>
#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;
int *idPtr;
char *namePtr;
int *mathPtr;
idPtr = &stu.id;
namePtr = (char*)&stu.name;
mathPtr = &stu.math;
printf("idPtr = %p\n", idPtr);
printf("namePtr = %p\n", namePtr);
printf("mathPtr = %p\n", mathPtr);
stu.id = 110;
strcpy(stu.name,"guodegang");
stu.math = 100;
sptr = container_of(idPtr, Student,id);
printf("container ptr = %p\n",sptr);
sptr = container_of(namePtr, Student,name);
printf("container ptr = %p\n",sptr);
sptr = container_of(mathPtr, Student,math);
printf("container ptr = %p\n",sptr);
return 0;
}
运行结果:
idPtr = 0x7ffc75765a70
namePtr = 0x7ffc75765a74
mathPtr = 0x7ffc75765a94
container ptr = 0x7ffc75765a70
container ptr = 0x7ffc75765a70
container ptr = 0x7ffc75765a70
idPtr实际就是stu变量的地址,namePtr对应的是stu.name地址,mathPtr对应的是stu.math地址。
后面通过idPtr、namePtr、mathPtr都可以成功取得stu变量的地址。