《程序员面试宝典》中给出的答案:
#define FIND(struc, e) (size_t)&(((struc *)0)->e)
上面定义的MY_OFFSET宏就是要的MyField的偏移。这样强制转换后的结构指针怎么可以用来访问结构体字段?其实这个表达式根本没有也不打算访问MyField字段。ANSI C标准允许任何值为0的常量被强制转换成任何一种类型的指针,并且转换结果是一个NULL指针,因此((MyStruct*)0)的结果就是一个类型为MyStruct*的NULL指针。如果利用这个NULL指针来访问MyStruct的成员当然是非法的,但&(((MyStruct*)0)->MyField)的意图并非想存取MyField字段内容,而仅仅是计算当结构体实例的首址为((MyStruct*)0)时MyField字段的地址。聪明的编译器根本就不生成访问MyField的代码,而仅仅是根据MyStruct的内存布局和结构体实例首址在编译期计算这个(常量)地址,这样就完全避免了通过NULL指针访问内存的问题。又因为首址的值为0,所以这个地址的值就是字段相对于结构体基址的偏移。
如上做法避免了一定要实例化一个MyStruct对象,并且求值是在编译期进行,没有运行期负担。实际上这种利用编译器掌握的整个程序的信息以在编译期计算某些值的方法与现在C++编程中很流行的(静态)元编程技术类似,只不过C++程序员可以利用模板技术在编译期完成非常复杂的计算,而缺乏模板支持的ANSI C在这方面的能力则要弱许多。
实际上,这个功能很多编译器已经实现:
// VC7.1
#ifdef _WIN64
#define offsetof(s,m) (size_t)( (ptrdiff_t)&(((s *)0)->m) )
#else
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
//lcc-win32, last updated:Monday, 13-Dec-2004 04:05:23 EST
#define offsetof(s,m) (int)&(((s *)0)->m)
//Borland C++ 5.5.1 for WIN32
#define offsetof( s_name, m_name ) (_SIZE_T)&(((s_name _FAR *)0)->m_name)
//MinGW 3.1.0 (GCC 3.2.3)
#ifndef __cplusplus
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#else /* C++ */
/* The reference cast is necessary to thwart an operator& that might
be applicable to MEMBER´s type. See DR 273 for details. */
#define offsetof(TYPE, MEMBER) (reinterpret_cast <size_t> /
(&reinterpret_cast <char &>(static_cast <TYPE *> (0)->MEMBER)))
#endif /* C++ */
注意到在GCC中,对于是否为CPP作了判断,因为它考虑到重载&运算符的情况,所以将MEMBER强制转换成了char &
例子:
struct A {
int i;
int x;
char c;
A* operator &() {
cout << "Hi\n";
return 0;
}
};
struct B {
int i;
A a;
};
这里重载了A的&运算符,若使用define FIND(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 便会得到错误的结果。
我的GCC中,
#ifndef __cplusplus
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#else
/* The cast to "char &" below avoids problems with user-defined
"operator &", which can appear in a POD type. */
#define offsetof(TYPE, MEMBER) \
(__offsetof__ (reinterpret_cast <size_t> \
(&reinterpret_cast <const volatile char &> \
(static_cast<TYPE *> (0)->MEMBER))))
#endif /* C++ */
__offsetof__宏不知道是做什么的,空操作?
google之找到:
http://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Offsetof.html
6.10 Offsetof
G++ uses a syntactic extension to implement the offsetof
macro.
In particular:
__offsetof__ (expression)
is equivalent to the parenthesized expression, except that the expression is considered an integral constant expression even if it contains certain operators that are not normally permitted in an integral constant expression. Users should never use __offsetof__
directly; the only valid use of __offsetof__
is to implement the offsetof
macro in <stddef.h>
.
顺带提一句,这也是linux中实现链表的基础之一