Bootstrap

container_of的用法

1. container_of的用途说明

container_of的主要作用是:通过已知的一个数据结构成员指针ptr,数据结构类型type,以及这个成员指针在这个数据结构中的成员名member,来获取指向这个数据结构的指针type *

2. offsetof的定义

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

offsetof是一个宏它的主要作用是:获取一个数据结构的成员,在这个数据结构中的偏移。
offsetof()宏函数它接受两个参数:一个是数据结构类型TYPE;另一个是在这个数据结构类型中的成员名。


offsetof获取成员的偏移的做法是:将0地址强制转换为TYPE * 类型的指针,所以0指针指向的TYPE类型的成员的地址都是以相对于0地址的偏移,所以返回成员的地址,就是要获得的偏移。

3. container_of的定义

/**
 * container_of - 通过三个参数,返回指向数据结构的指针
 * @ptr:	数据结构中指向某一成员的指针
 * @type:	数据结构类型
 * @member:	在数据结构中的成员
 *
 */
#define container_of(ptr, type, member) ({			\
	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
	(type *)( (char *)__mptr - offsetof(type,member) );})
  • 首先,将0地址强制转换为type * 指针。
  • 然后,再使用typeof()获取名为member成员的数据类型
  • 接着,定义一个临时成员变量指针__ptr,让它指向已知的成员变量指针ptr。
  • 然后,获取这个成员变量在这个数据结构的偏移量。
  • 最后,现在已经知道成员member的地址以及在数据结构的偏移量,将__mptr强制转换为char * 再减去偏移量就是要获取数据结构的地址。

4. 例子

test.c源文件

#include <stdio.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 profile
{
	char *name;
	unsigned int age;
	char *sex;
}profile_t;

static profile_t prop = {
	.name = "wzc",
	.age = 18,
	.sex = "male"
};
int main()
{
	profile_t *p = container_of(&prop.sex, struct profile, sex);
	printf("name = %s, age = %u, sex = %s\n", p->name, p->age, p->sex);
	printf("This number is = %d\n", ({1;2;3;4;}));
	return 0;
}

运行结果:
在这里插入图片描述

4.1 获取预编译文件test.i

$ gcc -E test.c -o test.i
# 8 "test.c"
typedef struct profile
{
 char *name;
 unsigned int age;
 char *sex;
}profile_t;

static profile_t prop = {
 .name = "wzc",
 .age = 18,
 .sex = "male"
};
int main()
{
 profile_t *p = ({ const typeof( ((struct profile *)0)->sex ) *__mptr = (&prop.sex); (struct profile *)( (char *)__mptr - ((size_t) &((struct profile *)0)->sex) );});
 printf("name = %s, age = %u, sex = %s\n", p->name, p->age, p->sex);
 printf("This number is = %d\n", ({1;2;3;4;}));
 return 0;
}

4.2 container_of宏定义中的“({})”解释

刚开始,对container_of()为什么能够有返回值有疑惑,它明明没有return语句,并且还是块语句(因为在大括号{}中),然后将小括号去掉,只留大括号,发现可以预编译成功,但是编译生成可执行文件时报错。
最后想了一下,小括号中的东西看做一个整体,在大括号中的最后一个语句的值会作为返回值,实验了一下,确实如此。

;