C语言结构体中不得不知的技巧-offsetof()与 container_of()的用法

C语言结构体中不得不知的技巧-offsetof()与 container_of()的用法

引言: 结构体的使用赋予了C 语言“面向对象”的特性。我们经常将一系列变量、函数封装在一个结构体中,来方便管理这些参数。但是“封装”这种操作,带来好处的同时容易隐藏掉一些内部的细节,使得我们想了解结构体中的细节变得麻烦。最近看 linux 中关于结构体的使用中大量使用了 offsetof()container_of(),研究发现,他们的确很有用,遂作此文,作为分享。

功能简介

  1. 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 的值。成功实现从局部获取到整体。真令人开心阿,哈哈。
(代码可以直接复制粘贴进行验证奥~)
(码字不易,谢谢点赞或收藏,你的鼓励,我的动力)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

物联网老王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值