宏定义:得到一个field在结构体(struct type)中的偏移量
- #define OFFSETOF(type, field) ((size_t)&(((type *)0)->field))
-
- (type *)0:把0地址当成type类型的指针。
-
- ((type *)0)->field:对应域的变量。
-
- &((type *)0)->field:取该变量的地址,其实就等于该域相对于0地址的偏移量。
-
- (size_t)&(((type *)0)->field):将该地址(偏移量)转化为size_t型数据。
为什么是size_t呢?
首先size_t的定义是什么呢,在文件stddef.h中可以找到答案。
- typedef unsigned int size_t; /*mine is 32bit machine*/
ANSI C标准允许任何值为0的常量被强制转换成任何一种类型的指针,并且转换结果是一个NULL指针,因此((s*)0)的结果就是一个类型为s*的NULL指针。如果利用这个NULL指针来访问s的成员当然是非法的,但&(((s*)0)->m)的意图并非想存取s字段内容,而仅仅是计算当结构体实例的首址为((s*)0)时m字段的地址。聪明的编译器根本就不生成访问m的代码,而仅仅是根据s的内存布局和结构体实例首址在编译期计算这个(常量)地址,这样就完全避免了通过NULL指针访问内存的问题。
有人这样表达:
- #define OFFSETOF(type, field) ((size_t) \
- ((char *)&((type *)0)->field - (char *)(type *)0))
经验证效果是一样的,多增加的那部分就是0地址,相减后就是偏移量。
在嵌入式系统里,不同开发商,不同架构处理器和编译器都有不同的offsetof定义形式:
/* Keil 8051 */
#define offsetof(s,m) (size_t)&(((s *)0)->m)
/* Microsoft x86 */
#define offsetof(s,m) (size_t)(unsigned long)&(((s *)0)->m)
/* Motorola coldfire */
#define offsetof(s,memb) ((size_t)((char *)&((s *)0)->memb-(char *)0))
/* GNU GCC 4.0.2 */
#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
/*VC6.0*/
- Retrieves the offset of a member from the beginning of its parent structure.
-
- size_t offsetof( structName, memberName );
-
- Routine Required Header Compatibility
- offsetof <stddef.h> ANSI, Win 95, Win NT
offsetof虽然同样适用于union结构,但它不能用于计算位域(bitfield)成员在数据结构中的偏移量。
- typedef struct
-
- {
-
- unsigned int a:3;
-
- unsigned int b:13;
-
- unsigned int c:16;
-
- }foo;
使用offset(foo,a)计算a在foo中的偏移量,编译器会报错:error C2104: '&' on bit field ignored