offset宏:求结构体成员变量的偏移值。
#define offset(TYPE, MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
(1) offset宏的作用是:用宏来计算结构体某个元素和接哦固体首地址的偏移量(实质是通过编译器来帮助我们计算)
(2) offset宏的原理:我们虚拟一个type类型结构体变量,然后用type.member的方式来访问那个member元素,整个结构体变量的首地址是0,相当于整个结构体变量的起始地址是0,而member元素的首地址是&((TYPE*)0)->MEMBER,所以这个数字减去0还是这个数字,取到的结构体成员的绝对地址就变成了member元素相当于整个结构体变量首地址的偏移量
对于这个宏可以大致分为5步:
对这个宏的讲解我们大致可以分为以下4步进行讲解:
0) 0 //内存地址开始于0
1) ( (TYPE *)0 ) //0地址强制 "转换" 为 TYPE结构类型的指针;
2) ((TYPE *)0)->MEMBER //引用TYPE结构中的MEMBER数据成员;
3) &( ( (TYPE *)0 )->MEMBER) //取地址符&, 取出TYPE结构中的数据成员MEMBER的地址;
4) (size_t)(&(((TYPE*)0)->MEMBER)) //将取到的地址强制转化为size_t类型。
代码只所以没有风险,是因为这里面没有写任何内存地址,甚至没有访问任何内存位置。只是操作了指向这些位置的指针,而指针一般存储在机器寄存器或是本地堆栈中
container_of宏:根据成员地址、结构体类型和成员名来计算结构体的首地址
定义如下:
#define container_of(ptr, type, member) ({ const typeof( ((type *)0)->member ) *__mptr = (ptr); (type *)( (char *)__mptr - offsetof(type,member) );})
创建一个结构体,用offsetof和container_of获取结构体的地址和内部成员相对于结构体地址的偏移量。
代码实例:
#include <stdio.h>
#define offsetof(type, member) ((size_t)&((type *)0)->member)
#define container_of(ptr, type, member) ({ const typeof( ( (type *)0)->member ) *__mptr = (ptr); (type *)( (char *)__mptr - offsetof(type,member) );})
上面宏的含义是先求出结构体成员的偏移地址,再用成员的实际地址减去偏移地址就得到了结构体的首地址
struct test{
char a;
int b;
short c;
char *p;
double d;
}__attribute__((aligned(4)));
int main(int argc, char **argv)
{
struct test s;
printf("offset a=%lu\n",offsetof(struct test,a));
printf("offset b=%lu\n",offsetof(struct test,b));
printf("offset c=%lu\n",offsetof(struct test,c));
printf("offset p=%lu\n",offsetof(struct test,p));
printf("offset d=%lu\n",offsetof(struct test,d));
printf("s=%p\n",container_of(&s.a,struct test,a));
printf("s=%p\n",container_of(&s.p,struct test,p));
return 0;
}
运行结果:
offset a=0
offset b=4
offset c=8
offset p=16
offset d=24
s=0x7fffc8590cf0
s=0x7fffc8590cf0