container_of 简述
container_of 是 Linux 中最常用的宏之一,不仅仅在上一章的链表数据结构中,在很多的地方都有使用到,他的作用是:
根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针,以便对结构体的其他成员进行操作
是不是超有用?来,先贴代码为敬:
/**
* 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) );})
container_of (ptr, type, member) 中:
ptr 代表当前一个数据类型的指针
type 代表这个数据的类型
member 代表这个数据在期望获取的结构体中的哪一个成员
这个宏的返回值就是整个结构体的指针。
举个栗子:
struct test_struct {
int num;
char ch;
float f1;
};
int main(void)
{
struct test_struct *test_struct;
struct test_struct init_struct ={12,'a',12.3};
char *ptr_ch = &init_struct.ch;
test_struct = container_of(ptr_ch,struct test_struct,ch);
printf("test_struct->num =%d\n",test_struct->num);
printf("test_struct->ch =%c\n",test_struct->ch);
printf("test_struct->f1 =%f\n",test_struct->f1);
return 0;
}
执行结果:
test_struct->num =12
test_struct->ch =a
test_struct->f1 =12.300000
原理分析
将调用展开:
1. container_of(ptr_ch,struct test_struct,ch) ({ \
2. = const typeof( ((struct test_struct *)0)->ch ) *__mptr = (ptr_ch); \
3. (struct test_struct*)( (char *)__mptr - offsetof(struct test_struct, ch) );})
typeof 是GNU C对标准C的扩展,它的作用是根据变量获取变量的类型,因此,上述代码中的第2行的作用是首先使用 typeof 获取结构体域变量 ch 的类型为 char,然后定义了一个 char 指针类型的临时变量 __mptr,并将实际结构体变量中的域变量的指针ptr_ch 的值赋给临时变量__mptr。
上述代码中的第3行的 offsetof 的定义在 include/linux/stddef.h:
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
可见,offsetof 的实现原理如上所述,就是取结构体中的域成员相对于地址0的偏移地址,也就是域成员变量相对于结构体变量首地址的偏移。因此,offsetof(struct test_struct, ch)调用返回的值就是 ch 结构体成员相对于 struct test_struct 结构体中的偏移。
(struct test_struct*)( (char *)__mptr - offsetof(struct test_struct, ch) );})
的解释就是使用 __mptr - ch成员的offset 量,得到了最终的结构体指针: