通过成员变量地址获取结构体地址

Linux中有一个宏

#define container_of(ptr,type,member) 实现略
实现了通过成员变量地址获取结构体地址的功能。

今天我想好好想想这个实现的原理是怎么来的。

先定义一个结构体吧

typedef struct
{
    int a;
    int b;
    int c;
}ABC;
再来设计一个函数用来实现功能

int main(void)
{
    ABC abc;
    printf("abc : %p\n",&(abc));
    printf("abc.c : %p\n",&(abc.c));
    return 0;
}
main()刚刚好,嘿嘿

输出结果为

abc : 0022FF44
abc.c : 0022FF4C
因为成员c的地址比abc高,所以按照数学公式:差 = 成员C地址 - abc地址

如何求得这个差呢?

可以这样,把ABC的一个实例映射到0这个地址,这样成员c的地址就是在0的基础上网上加,进而此时成员c的地址

就是它们的差值

这样

printf("%u\n",&(((ABC*)0)->c));
不过编译,运行后会crash掉,因为0这个地址被printf检测到是非法地址,所以得这样

printf("%u\n",(size_t)&(((ABC*)0)->c));
这样,传给printf的参数不是一个地址,而是一个普通的常数

好,那么可以求abc地址了

printf("%p\n",&(abc.c) - (size_t)&(((ABC*)0)->c));
运行,结果是

abc : 0022FF2C
和abc的地址不对啊,为什么呢?

因为此处&(abc.c)这一句有问题。指针的加减操作的步长是按照这个指针的类型来定的,此处c是int型,则它 - 8,其实地址是 - 8 * sizeof(int).

你看,0022FF2C是不是比0022FF44少24.就是因为它上面的操作多减了24

所以,得这样

printf("abc : %p\n",(unsigned char*)&(abc.c) - (size_t)&(((ABC*)0)->c));
输出结果为

abc : 0022FF44
OK,目标达成。

现在用宏封装一下

#define container_in(ptr,TYPE,member) \
    (TYPE*)((unsigned char*)(ptr) - (size_t)&(((TYPE*)0)->member))
把差值强制转换为(TYPE*)是因为这个宏的目的就是返回(TYPE*)类型的指针

做个测试

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define container_in(ptr,TYPE,member) \
    (TYPE*)((unsigned char*)(ptr) - (size_t)&(((TYPE*)0)->member))

typedef struct
{
    char a;
    char b;
    char c;
}TEST1;;

typedef struct
{
    char a;
    char b;
}TEST2;;

typedef struct
{
    char a;
    TEST1 test1;
    short b;
    TEST2 test2;
    int c;
}ABC;

int main(void)
{
    ABC abc;
    ABC* p = NULL;
    abc.a = 1;
    abc.b = 2;
    abc.c = 4;
    p = container_in(&(abc.c),ABC,c);
    printf("%u %u %u\n",p->a,p->b,p->c);
    return 0;
}
输出结果

1 2 4

谢谢观赏!










  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值