我们知道结构中的元素将按照其声明的顺序存储。
如何提取结构中元素的位移?我们可以利用offsetof宏。
通常,我们将结构类型和联合类型(或具有琐碎构造函数的类)称为普通旧数据(POD)类型,这些类型将用于聚合其他数据类型。以下非标准宏可用于从结构变量的基址获取元素的字节移位。
#define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT))
零强制转换为结构类型,并且访问所需元素的地址,该地址强制转换为size_t 。按照标准, size_t的类型为unsigned int 。整个表达式导致字节数,然后将ELEMENT放置在结构中。
例如,以下代码返回16个字节(在32位计算机上考虑填充)作为结构Pod中字符变量c的位移。
#include
#define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT))
typedef struct PodTag
{
int i;
double d;
char c;
} PodType;
int main()
{
printf("%d", OFFSETOF(PodType, c) );
getchar();
return 0;
}
在上面的代码中,以下表达式将返回元素c在结构PodType中的位移。
OFFSETOF(PodType, c);
在预处理阶段之后,上面的宏扩展为
((size_t)&(((PodType *)0)->c))
由于我们考虑将0作为结构变量的地址,因此c将被放置在其基地址的16个字节之后,即0x00 + 0x10。将&应用于结构元素(在本例中为c)将返回元素的地址0x10。将地址转换为unsigned int (size_t)会导致元素放置在结构中的字节数。
注意:我们可能认为地址运算符&是多余的。在宏中没有地址运算符的情况下,该代码取消引用放置在NULL地址处的结构元素。它在运行时导致访问冲突异常(分段错误)。
请注意,还有其他方法可以根据编译器的行为来实现offsetof宏。最终目标是提取元素的位移。在另一篇文章中,我们将在喜欢的列表中看到offsetof宏的实际用法,以连接相似的对象(例如线程池)。
参考:
1. Linux内核代码。
2. http://msdn.microsoft.com/en-us/library/dz4y9b9a.aspx
3. GNU C / C++编译器文档