offsetof宏

宏的作用: 计算结构体成员的偏移,从结构类型的开头返回字段成员的偏移量。

宏定义:#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER)

TYPE:结构名称(譬如struct definestruct)
((TYPE*)0): 取整数零并将其转换为指向TYPE 的指针。
((TYPE*)0)->m: 引用指向结构成员 m 的指针。
&(((TYPE*)0)->m):计算 m 的地址。
(size_t)&(((TYPE *)0)->m): 将结果转换为适当的数据类型。

/*
time:2020年8月31日14:14:34
objective:理解宏offsetof的用途 
function:自动计算结构体中变量的偏移量
author:wu_junwu 
*/
#include<stdio.h>

struct definestruct
{
	int a;
	char b;
	short c;
	long d;
}__attribute__((packed)); //__attribute__((packed))取消对齐访问,注意:attribute前后下划线分别是两段
/*
设置对齐访问
struct definestruct
{
	int a;
	char b;
	short c;
	long d;
}__attribute__((aligned(4)));设置4字节对齐访问
*/

//(type*)0定义0地址为type *类型,该类型指针指向一个type类型的结构体首地址,
//然后以type.member的方式访问结构体中的变量member,最后用&得到member的地址,也就是偏移量
#define offsetof(type,member) ((int)&((type*)0)->member)   //强制转换类型为int

int main(void)
{
    struct definestruct ds;
	struct definestruct *p2=NULL;
	p2=&(ds);
	int *p;
	p=&(ds.a);
	printf("结构体首元素地址p=%p\n",p);
	printf("结构体首元素地址ds=%p\n",&ds);
	printf("结构体首元素地址&(ds.a)=%p\n",&(ds.a));
	printf("ds2.b=%p\n",p2);
	printf("ds.b=%p\n",p);
	printf("结构体元素a的偏移量=%d\n",offsetof(struct definestruct,a));
	printf("结构体元素a的偏移量=%d\n",offsetof(struct definestruct,b));
	printf("结构体元素a的偏移量=%d\n",offsetof(struct definestruct,c));
	printf("结构体元素a的偏移量=%d\n",offsetof(struct definestruct,d));
	printf("变量b的首地址=%d\n",(int)&ds.b-(int)&ds.a);//结果是4;该处的4表示int整型,代表4个字节
    printf("变量b的首地址=%d\n",(char*)&ds.b-(char*)&ds.a);//结果是4;该处的4表示的是指针char*,代表4个字节。
	printf("变量b的首地址=%d\n",(int *)&ds.b-(int*)&ds.a);//结果是1;该处的1表示的是指针int*,代表4个字节。
/************************************************************
为什么指针类型的1代表的字节数是4呢?
解答:因为指针本质就是地址,在32位系统中,内存地址是就是按照4字节读取的,地址(指针)加上1就表示加上了4字节的长度
**************************************************************/
	//这样定义指针p1会导致编译错误:assignment from incompatible pointer type
	//指针类型不兼容;正确的应该将指针p1定义为int *p1=NULL
	/*
	struct definestruct *p1=NULL;
	p1=&(ds.a);
	printf("ds1.a=%p\n",p1);
	*/
	return 0;	
}

/*
conclusion:
offsetof宏的原理:
	(1)(type*)0  :这是一个强制类型转换,定义一个类型为type *的地址0,pointer1=0
	该0地址(指针)指向的对象是一个type类型(在这个例子中是struct definestruct类型)的结构体的首地址,也可以说是结构体中首元素的地址,他们的地址相同。
	(2)此时结构体的地址首地址为0;((type*)0)—>member   ((type*)0)表示一个type类型指针,
	通过该指针访问这个结构体中的变量member。
	(3)&((type*)0)—>member    等价于&(((type*)0)->member),得到变量member的地址。
	假设得到的地址是ox88888888,此时的指针pointer2=&(((type*)0)->member)=ox88888888,
	那么偏移量为subp=pointer2-pointer1;因为我们再第一步就将结构体的首地址强制转换成type*类型的0地址
	所以,指针pointer2既是我们变量member的地址也是变量member的偏移量。
*/
	
	
	
	
	
	
	
	
	
	
	
	
结构体首元素地址p=0xbfaeb04d
结构体首元素地址ds=0xbfaeb04d
结构体首元素地址&(ds.a)=0xbfaeb04d
ds2.b=0xbfaeb04d
ds.b=0xbfaeb04d
结构体元素a的偏移量=0
结构体元素a的偏移量=4
结构体元素a的偏移量=5
结构体元素a的偏移量=7
变量b的首地址=4
变量b的首地址=4
变量b的首地址=1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wu Junwu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值