来自http://blog.chinaunix.net/u2/62910/showart_492571.html

假设在ANSI C程序中定义了一个名为MyStruct的结构类型,其中有一个名为MyField的字段,如何取得它在结构体中的偏移?

typedef struct MyStruct_tag
{
  // some fields
  ...

long MyField;

// other fields
  ...
} MyStruct;

最容易想到的方法应该与如下代码差不多:

size_t GetOffset()
{
  MyStruct s;

return (size_t)( (char*)(&s.MyField) - (char*)(&s) );
}

这段代码确实能完成任务,但为了取得偏移值,函数不得不定义了一个MyStruct结构体实例,可这有必要吗?仔细想想,结构体的内存布局是在什么时候由谁决定的?没错,是编译器在编译期确定的,它一旦被确定就不会改变了,而依赖于结构体内存布局的字段偏移也就随之确定并不再改变。既然在编译阶段编译器就洞悉了内幕,那么完全有理由要求它在编译期为程序提供这些信息。如何做呢?请看下面的代码:

#define MY_OFFSET (size_t)&(((MyStruct*)0)->MyField)

上面定义的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在这方面的能力则要弱许多。

或许因为求结构体字段偏移很常用,ANSI C在标准头文件stddef.h中就专门定义了一个形如offsetof(s,m)的宏来求任意一个结构类型中某个字段的偏移,而且绝大多数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++ */

可见这种简练而有效的方法已被C程序员接纳为一种惯用法(idiom)了。

ANSI C中取得结构体字段偏移量的常用方法的更多相关文章

  1. 指针直接赋值为整型AND利用宏定义求结构体成员偏移量

    首先我们要更正一个很熟悉的概念,那就是指针不仅仅是“地址”,指针还有一个很重要的特性,那就是“类型”. 指针初始化时,“=”的右操作数; 除外,该语句表示指针为空): 所以 ; 这样的代码是不允许的. ...

  2. 浅析C#中的结构体和类

    类和结构是 .NET Framework 中的常规类型系统的两种基本构造. 两者在本质上都属于数据结构.封装着一组总体作为一个逻辑单位的数据和行为. 数据和行为是该类或结构的"成员" ...

  3. matlab中的结构体

    今天用imfinfo函数 >> K = imfinfo(‘colorbar_copy1.jpg’) K = 包含以下字段的 struct: Filename: 'E:\matlab\col ...

  4. x264中重要结构体参数解释,参数设置,函数说明 <转>

    x264中重要结构体参数解释http://www.usr.cc/thread-51995-1-3.htmlx264参数设置http://www.usr.cc/thread-51996-1-3.html ...

  5. [转]C#中的结构体与类的区别

    C#中的结构体与类的区别   经常听到有朋友在讨论C#中的结构与类有什么区别.正好这几日闲来无事,自己总结一下,希望大家指点. 1. 首先是语法定义上的区别啦,这个就不用多说了.定义类使用关键字cla ...

  6. Go中的结构体

    前面我们或多或少的都使用了结构体这种数据结构,本身结构体也有很多特性,我们一一来看. 结构体的作用是将一个或者多个任一类型的变量组合在一起的数据类型,类似于我们在Java中class的作用.在结构体重 ...

  7. 在FLASH中读写结构体

    在FLASH中读写结构体 注意事项 编程(写数据)地址要对齐 写数据时,我们要指定写入的地址,如果写入地址为非对齐,则会出现编程对齐错误. 比如遵循32位(4字节)地址对齐,你的地址只能是4的倍数.0 ...

  8. C++中的结构体

    http://zhidao.baidu.com/link?url=8OYQSKV9mvSBc6Hkf9NsLQmipSge9VCZDJQGAZZs5PCBQ54UTmK98VRmAklEEAFYu7d ...

  9. C/C++中的结构体

    结构体定义 结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构.   结构体作用 结构体和其他类型基础数据类型一样,例如int类型,char类型 只不过结构体可以做成 ...

随机推荐

  1. 1688: [Usaco2005 Open]Disease Manangement 疾病管理( 枚举 )

    我一开始写了个状压dp..然后没有滚动就MLE了... 其实这道题直接暴力就行了... 2^15枚举每个状态, 然后检查每头牛是否能被选中, 这样是O( 2^15*1000 ), 也是和dp一样的时间 ...

  2. first-,second- and third-class value

    In computer science, a programming language is said to have first-class functions if it treats funct ...

  3. 前端面试题整理(css)

    1.介绍所知道的CSS hack技巧(如:_, *, +, \9, !important 之类). CSS hack的原理: 由于不同的浏览器和浏览器各版本对CSS的支持及解析结果不一样,以及CSS优 ...

  4. Oracle多表的简单查询

    Oracle多表的简单查询 .多表查询 多表查询是指基于两个和两个以上的表或是视图的查询. 问题:显示雇员名,雇员工资及所在部门的名字[笛卡尔集]? select t.ename,t.sal,t1.d ...

  5. Swift - 访问通讯录联系人(使用纯代码实现)

    1,通讯录访问介绍 通讯录(或叫地址簿,电话簿)是一个数据库,里面储存了联系人的相关信息.要实现访问通讯录有如下两种方式: (1)AddressBook.framework框架 : 没有界面,通过代码 ...

  6. android HorizontalListView

    最近搞android 用到一个HorizontalListView 网上搜了一把.有一些国人实现的.但也有一些基本上是cp人家的开源项目. 本人找了两个.记录一下. 其一:https://github ...

  7. tcp接收xml数据解析

    避免tcp接收xml数据时加上xml数据长度,根据xml数据特点来解析recv到的xml数据 int nPos1 = 0; int nPos2 = 0; int nTempPos = 0; int n ...

  8. 调用QQ截图

    var SHExecInfo: SHELLEXECUTEINFO; begin //截图前隐藏主程序窗口 Form1.Hide; //等待截图执行完成 SHExecInfo.cbSize := siz ...

  9. QT对话框中show和exec的区别

    转自:http://hi.baidu.com/wangjuns8/blog/item/24b382460dd1c1338694737d.html QDialog的显示有两个函数show()和exec( ...

  10. 用内存流 文件流 资源生成客户端(Delphi开源)

    正文:很多木马生成器就是用的内存流和文件流生成客户端的,废话不多说了,代码如下: unit Main; interface usesWindows, Messages, SysUtils, Varia ...