C语言结构体中不得不知的技巧-offsetof()与 container_of()的用法
引言: 结构体的使用赋予了C 语言“面向对象”的特性。我们经常将一系列变量、函数封装在一个结构体中,来方便管理这些参数。但是“封装”这种操作,带来好处的同时容易隐藏掉一些内部的细节,使得我们想了解结构体中的细节变得麻烦。最近看 linux 中关于结构体的使用中大量使用了 offsetof()
与 container_of()
,研究发现,他们的确很有用,遂作此文,作为分享。
功能简介
- offsetof() 原型如下:
offsetof(TYPE, MEMBER)
/*
1、功能:返回结构体成员 MEMBER 的相对结构体 TYPE 首地址的偏移量。
2、参数:TYPE是结构体类型,MEMBER是结构体中一个成员的名字。
*/
2)container_of() 原型如下:
container_of(ptr, type, member)
/*
1、功能:通过一个结构体成员的指针去获取整个结构体本身的指针,即通过局部成员地址获取整体结构体地址。
2、参数:ptr 是指向结构体中一个成员变量的地址的指针;type是结构体类型;member是结构体中对应成员变量的名字。
*/
代码验证
#undef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#undef container_of
/**
* copy from 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) );})
typedef struct custom_struct {
int a;
size_t b;
char c;
char d;
struct {
uint8_t id;
char *name;
} info;
}custom_struct;
void printf_name(size_t *p_b)
{
custom_struct *p_test = container_of(p_b, custom_struct, b);
printf("name is %s\r\n", p_test->info.name);
}
void app_main(void)
{
printf("init done\r\n");
custom_struct test = {1,2,'3','4',
.info = {
.name="custom",
}};
int adr_a = offsetof(custom_struct, a);
int adr_b = offsetof(custom_struct, b);
int adr_c = offsetof(custom_struct, c);
int adr_d = offsetof(custom_struct, d);
int adr_id = offsetof(custom_struct, info.id);
int adr_name = offsetof(custom_struct, info.name);
printf("adr_a=%d\r\n", adr_a);
printf("adr_b=%d\r\n", adr_b);
printf("adr_c=%d\r\n", adr_c);
printf("adr_d=%d\r\n", adr_d);
printf("adr_id=%d\r\n", adr_id);
printf("adr_name=%d\r\n", adr_name);
(void)memset(&test, 0, offsetof(custom_struct, info.id));
printf("a=%d\r\n", test.a);
printf("b=%d\r\n", test.b);
printf("c=%c\r\n", test.c);
printf("d=%c\r\n", test.d);
printf("test's ptr is %p\r\n", &test);
custom_struct *p_test = container_of(&test.b, custom_struct, b);
printf("test's ptr calculated from the sub is %p\r\n", p_test);
printf_name(&test.b);
}
输出结果:
init done
adr_a=0
adr_b=4
adr_c=8
adr_d=9
adr_id=12
adr_name=16
a=0
b=0
c=
d=
test's ptr is 0x3ffb5c80
test's ptr calculated from the sub is 0x3ffb5c80
name is custom
结果分析:
1)offsetof() 获取了成员相对于整个结构体的偏移地址。程序中使用 (void)memset(&test, 0, offsetof(custom_struct, info.id));
语句清除了结构体中部分变量的值。
2)结构体变量 test 的地址与 container_of() 计算获取的该结构体 变量的地址一致。更进一步地,在 printf_name()
内成功地通过结构体变量 test 的成员变量 b,访问到了 结构体变量 test 的成员 name 的值。成功实现从局部获取到整体。真令人开心阿,哈哈。
(代码可以直接复制粘贴进行验证奥~)
(码字不易,谢谢点赞或收藏,你的鼓励,我的动力)