结构体成员偏移地址

中午,一位同事问我一个问题把我问住了:
#define MY_OFFSET(MyStruct,MyField) (size_t)&(((MyStruct*)0)->MyField)。
使用这个语句编写代码为什么不发生错误。是啊,为什么“ ((MyStruct*)0)->”这么用都不崩溃呢?
上面定义的MY_OFFSET宏就是要的MyField的偏移。这样强制转换后的结构指针怎么可以用来访问结构体字段?其实这个表达式根本没有也不打算访问MyField字段。ANSI C标准允许任何值为0的常量被强制转换成任何一种类型的指针,并且转换结果是一个NULL指针,因此((MyStruct*)0)的结果就是一个类型为MyStruct*的NULL指针。如果利用这个NULL指针来访问MyStruct的成员当然是非法的,但&(((MyStruct*)0)->MyField)的意图并非想存取MyField字段内容,而仅仅是计算当结构体实例的首址为((MyStruct*)0)时MyField字段的地址。
如上做法避免了一定要实例化一个MyStruct对象,并且求值是在编译期进行,没有运行期负担。
当然,还有其他方法可以算出结构体成员的偏移地址,但无疑这种方法是上佳之选.:)
 
 
获取一个结构体中的某字段的偏移量,linux的内核代码中是这样定义实现的:
offset = (unsigned long)(&((type*)0))->member); 其中type就是结构体类型,member即某字段名。

这里简单的说明一下这句代码的意思,本来(type*)0就是一个空指针,使用空指针引用成员肯定会造成段错误,但是重点就是前门的
&这个符号,&符号表示我们只是取那个字段的地址,而不是要去引用这个字段的内容,所以不会造成段错误。

另外结构体中的字段在内存中分配的地址是连续的,当我们去读取某个字段时,就比如&((type*)0)->member,会先取结构体类型
type的地址,然后再计算member字段的偏移量(offset),再将type的地址加上member字段的偏移量,得到member字段真正的地
址,而这个动作是编译器帮我们完成的。

那么利用这个特性,我们就有办法得到member的偏移量。比如&ptr->member取member的地址,所以如果ptr的地址是100的话,那
么member的地址就是100+offset。那如果ptr是0的话,这样得到的结果就是offset,也就是member在type结构体中的偏移量。

最后而且利用这个特性,当知道member的地址时,我们也很容易得到结构体的地址值,即p = (type*)((char*)(ptr)-(unsigned long)(&((type*)0)->member))。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值