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的地址不对啊,为什么呢?
因为此处&(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));
输出结果为
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;
- }
输出结果
原文地址:http://blog.csdn.net/ma52103231/article/details/20036705