container_of学习
[TOC]
定义
定义1:
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - ((unsigned long) &((type *)0)->member)); })
定义2:
//获取结构体成员地址偏移
#define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER)
//根据结构成员地址找到结构首地址
#define container_of(ptr, type, member) \
((type *)(void *)((char *)(ptr) - offsetof(type, member)))
头文件包含:
offsetof宏需要包含stddef.h头文件,container_of宏需要包含linux/kernel.h头文件。
作用:
根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针
用法说明:
获取member的父结构
ptr: 父结构实例member的指针
type: 父结构
member: 父结构成员名
通俗的解释就是:
参数3是参数2这个结构体的一个成员的名字,而不是类型名 ;
参数1是一个指针,它指向参数3这个成员。
定义解析
typeof
typeof是GNU C对标准C的扩展,它的作用是根据变量获取变量的类型2。
例如:
int x = 5;
typeof(x) y = 6;
printf("%d %d\n", x, y);
典型应用:
//定义一个安全的max宏
#define max(a,b) \
({ typeof (a) _a = (a); \
typeof (b) _b = (b); \
_a > _b ? _a : _b; })
定义1中代码:
const typeof(((type *)0)->member) * __mptr = (ptr);
的作用是首先使用typeof获取结构体域变量member3的类型为 type3,然后定义了一个type3指针类型的临时变量__mptr,并将实际结构体变量中的域变量的指针memp的值赋给临时变量__mptr
(char *)__mptr转换为字节型指针
(type )将字节型的结构体起始指针转换为type 型的结构体起始指针
offsetof
offsetof就是取结构体中的域成员相对于地址0的偏移地址,也就是域成员变量相对于结构体变量首地址的偏移。
代码 | 含义 |
---|---|
( (TYPE *)0 ) | 将零转型为TYPE类型指针; |
( (TYPE *)0 )->MEMBER | 访问结构中的数据成员; |
&( ( (TYPE *)0 )->MEMBER ) | 取出数据成员的地址; |
(size_t)(&(((TYPE*)0)->MEMBER)) | 结果转换类型。 |
其中:将0转换成(TYPE*),结构体以内存空间首地址0作为起始地址,则成员地址自然为偏移地址。
实例
实例一(一般用法):
struct demo_struct {
type1 member1;
type2 member2;
type3 member3;
type4 member4;
};
struct demo_struct demo;
已知变量demo中的某一个域成员变量的指针,比如:
type3 *memp = get_member_pointer_from_somewhere();
此时,如果需要获取指向整个结构体变量的指针,如下:
struct demo_struct *demop = container_of(memp, struct demo_struct, member3);
实例二(项目工程中常用):
typedef struct tagDemo
{
u32 instanceId;
void *ctrArray[10];
int *ObjArray[10];
u64 other;
}Demo_S;
已知:int **Res,数组ObjArray中索引index
instance = (Demo_S*)container_of(Res, Demo_S, ObjArray[Index]);
有时候用container_of会导致pclint不过,这时候的替代写法如下:
instance = (Demo_S *)(void *)((char *)Res - offsetof(Demo_S, ObjArray) - sizeof(int*)*Index);
offsetof也可以只得到数组首地址,然后再偏移
实验
代码(这里复用了3,然后加入数组的偏移实验,来验证上面的实例):
//cont_of.c
#include <stdio.h>
#include <stdlib.h>
#define NAME_STR_LEN 32
#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_info
{
int id;
char name[NAME_STR_LEN];
int age;
}student_info;
int main()
{
size_t off_set = 0;
off_set = offsetof(student_info, id);
printf("id offset: %u\n",off_set);
off_set = offsetof(student_info, name);
printf("name offset: %u\n",off_set);
off_set = offsetof(student_info, name[10]);
printf("name[10] offset: %u\n",off_set);
off_set = offsetof(student_info, age);
printf("age offset: %u\n",off_set);
student_info *stu = (student_info *)malloc(sizeof(student_info));
student_info *ptr = container_of(&(stu->age), student_info, age);
student_info *ptr1 = container_of(&(stu->name[10]), student_info, name[10]);
printf("stu address:%p\n", stu);
printf("ptr address:%p\n", ptr);
printf("ptr1 address:%p\n", ptr1);
return 0;
}
编译运行:
gcc cont_of.c -o cc
./cc
运行结果如下:
id offset: 0
name offset: 4
name[10] offset: 14
age offset: 36
stu address:0x602010
ptr address:0x602010
ptr1 address:0x602010