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
谢谢观赏!