咱们在书写C程序的时候,有时候须要根据结构体成员变量的地址,获得结构体的地址,特别是咱们想用C来实现C++的继承特性的时候。
咱们对问题的分析以下:linux
为了便于分析,咱们给出一个实例来讲明函数
struct father_t { int a; char *b; double c; }f; char *ptr = &(f.b); //而不是 ptr = f.b; 这里ptr是b的地址,而不是它指向的地址。
根据C语言对struct类型的存储特性,咱们能够画这么一个图示:
经过分析图示,咱们能够看出,咱们只须要把当前知道的成员变量的地址ptr,减去它在结构体当中相对偏移4就的到告终构体的地址(ptr-4)。
在linux当中对此有一个很好的宏可使用,叫作 container_of, 放在 linux/kernel.h(http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/spa
)当中。它的定义以下所示:设计
#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER )
宏功能:得到一个结构体变量成员在此结构体中的偏移量。指针
1. ( (TYPE *)0 ) 将零转型为TYPE类型指针;
2. ((TYPE *)0)->MEMBER 访问结构中的数据成员;code
3. &( ( (TYPE *)0 )->MEMBER )取出数据成员的地址,即相对于0的偏移量,要的就这个;
4.(size_t)(&(((TYPE*)0)->MEMBER))结果转换类型,size_t应该最终为unsigned int类型。
此宏的巧妙之处在于将 0 转换成(TYPE*),这样结构体中成员的地址即为在此结构体中的偏移量。blog
示例:继承
#include <stdio.h> #define offsetof(TYPE, MEMBER) ((int)(&((TYPE *)0)->MEMBER)) struct _test_ { char x; int y; float z; }; int main(void) { int temp = -1; temp = offsetof(struct _test_, z); printf("temp = %d\n", temp); return 0; }
运行后结构为:temp = 8。 显然求出了 结构体成员变量 z 在结构体中的偏移量为 8。
若是是
这个技巧在linux内核里面很是常见,经过常量 地址的强转获得一个数据类型编译器
若是是char x,输出0;若是是y,输出4.io
若是不要
(int) 或(size_t)会怎么样?
编译不经过,没法将float *类型 转成float?
由于
&((TYPE *)0)->MEMBER) 获得的是一个指针,不能直接赋值给int 类型。
必须先用(int)强制转化下。
还有一个与这个有关的宏:
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
宏功能:从结构体(type)某成员变量(member)指针(ptr)来求出该结构体(type)的首指针。
对上面的定义,分析以下:
这就是从结构体某成员变量指针来求出该结构体的首指针。指针类型从结构体某成员变量类型转换为该结构体类型。
#include <stdio.h> #define offsetof(TYPE, MEMBER) ((int)(&((TYPE *)0)->MEMBER)) #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) struct _test_ { int x; int y; int z; }; void Assignment(struct _test_ *t) { t->x = 1; t->y = 2; t->z = 3; } void GetheadPoint(int *tz) { struct _test_ *p; int temp = -1; p = container_of(tz,struct _test_, z); //根据成员变量的地址得到该结构体的首地址 temp = p->y; //根据首地址得到其中另一个成员变量的值 printf("line31 = %d\n", temp); } int main(void) { int temp = -1; struct _test_ tmp; //定义一个结构体变量 Assignment(&tmp); //给这个变量赋值 GetheadPoint(&tmp.z); //只传给这个函数一个结构体成员变量的地址 printf("line43 tmp - >x = %d\n", tmp.x); return 0; } 运行结果为: line31 = 2 line43 tmp - >x = 1