如何获取结构体某成员的偏移地址
我们假设结构体定义如下所示:
#include <stdio.h>#include <stdlib.h>struct test_s{ int pad1; int pad2; int pad3; int pad4; int pad5; };
思路1: 非常简单,直接用地址差值即可求得。
int main(int argc, char *argv[]){ struct test_s sss; struct test_s *t = &sss; printf("%d\n", (int)&(t->pad2)); return 0;}
思路2: 考虑宏定义的实现。(最佳思路!)
ANSI C标准允许任何值为0的常量被强制转换成任何一种类型的指针,并且转换结果是一个NULL指针,因此( (test_s*)0 )的结果就是一个类型为test_s*的NULL指针。如果利用这个NULL指针来访问s的成员当然是非法的,但&(((test_s*)0)->m)的意图并非想存取test_s字段内容,而仅仅是计算当结构体实例的首址为((s*)0)时m字段的地址。聪明的编译器根本就不生成访问m的代码,而仅仅是根据test_s的内存布局和结构体实例首址在编译期计算这个(常量)地址,这样就完全避免了通过NULL指针访问内存的问题。
#define OFFSET(Type, member) (size_t)&( ((Type*)0)->member) )如上做法避免了一定要实例化一个结构体对象,并且求值是在编译期进行,没有运行期负担。因此是该问题的首选方案。
int main(int argc, char *argv[]){ size_t offset = OFFSET(test_s, pad4); return 0}
/* Define offsetof macro */#ifdef __cplusplus #ifdef _WIN64#define offsetof(s,m) (size_t)( (ptrdiff_t)&reinterpret_cast<const volatile char&>((((s *)0)->m)) )#else#define offsetof(s,m) (size_t)&reinterpret_cast<const volatile char&>((((s *)0)->m))#endif #else #ifdef _WIN64#define offsetof(s,m) (size_t)( (ptrdiff_t)&(((s *)0)->m) )#else#define offsetof(s,m) (size_t)&(((s *)0)->m)#endif #endif/* __cplusplus */