【Linux】container_of宏

【Linux】container_of宏_第1张图片

博客主页:PannLZ
系列专栏:《Linux系统之路》
不要让自己再留有遗憾,加油吧!


container_of宏

【Linux】container_of宏_第2张图片

假设有一个struct person,其定义如下:

struct person {
	int age;
	char *name;
} p;

只用age或name上的指针就可以检索包装(包含)该指针的整个结构。顾名思义,container_of宏用于查找指定结构字段的容器。该宏在
include/linux/kernel.h中定义,如下所示:

#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})

看起来好复杂,慢慢分析。

ptr是成员变量的指针, type是指结构体的类型, member是成员变量的名字。

typeof

typeof,是用于返回一个变量的类型,这是GCC编译器的一个扩展功能

(((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;
}

const typeof(((type )0)->member)__mptr = (ptr);

这句代码意思是用typeof()获取结构体里member成员属性的类型,然后定义一个该类型的指针变量mptr,并将ptr所指向的member的地址赋给mptr

offsetof(type, member))

((size_t) &((TYPE*)0)->MEMBER)
//size_t是一个非负数,所以size_t通常用来计数

((size_t) &((TYPE*)0)->MEMBER) 结合之前的解释,我们可以知道这句话的意思就是求出MEMBER相对于0地址的一个偏移值。

(type *)((char *)__mptr - offsetof(type, member))

这句话的意思就是,把 __mptr 转换成 char * 类型, 因为 offsetof 得到的偏移量是以字节为单位。 两者相减得到结构体的起始位置, 再强制转换成 type(该结构的) 类型。

也可以把它简单看为:

container_of(pointer, container_type,container_field);
  • pointer:指向结构字段的指针。
  • container_type:包装(包含)指针的结构类型。
  • container_field:pointer指向的结构内字段的名称。

通过一个例子来认识它的功能:

struct person somebody;
[...]
char *the_name_ptr = somebody.name;

struct person *the_person;
the_person = container_of(the_name_ptr, struct person, name);
//通过指向person结构的name成员的指针(地址),来获取指向struct结构体起始地址的指针

//驱动开发中的例子:
struct mcp23016 {
struct i2c_client *client;
struct gpio_chip chip;
}
/* 检索给定指针chip字段的mcp23016结构体*/
static inline struct mcp23016 *to_mcp23016(struct gpio_chip *gc)
{
		return container_of(gc, struct mcp23016,chip);
}
static int mcp23016_probe(struct i2c_client* client,const struct i2c_device_id *id)
{
	struct mcp23016 *mcp;
	[...]
	mcp = devm_kzalloc(&client->dev,sizeof(*mcp), GFP_KERNEL);
	if (!mcp)
		return -ENOMEM;
	[...]
}

参考文章:

1.https://blog.csdn.net/qq_33475105/article/details/119301916
2.https://radek.io/2012/11/10/magical-container_of-macro/

你可能感兴趣的:(Linux系统之路,linux,驱动开发,运维,c语言)