邓际锋ID:soloist
83623次访问,排名1121好友1人,关注者2
soloist的文章
原创 39 篇
翻译 0 篇
转载 0 篇
评论 189 篇
soloist的公告
欢迎吹毛求疵,感谢您对任何错误的指正,包括技术的、语法的、用词的、标点的、典故的、引用资料的……
最近评论
qingbai:lua绝对是个好东西。但国内除了java就是.net,其他东西没法活。因为程序员得工作,得吃饭。国内有哪家公司用lua?唉没办法呀。国外是一片繁荣,“百家争鸣”,国内是“青一色”的java和.net!无奈!
zhangyilan:尽管没有在实际代码的编写中碰到这个问题,不过也先学习一下,免得出现问题了搞出清楚情况。
ddrmsdos:这篇文章写的太好了,分析的非常仔细,以前常常碰到这类问题,终于解了我多年的心头之患......
ollydbg23:楼主的这篇文章写的非常好啊!
我看了以后,还是挺有收获感的,多谢多谢!
我也是对汇编,c++的比较感兴趣,有空可以交流一下!
w2001:写得很好
文章分类
收藏
    相册
    好博链接
    C++罗浮宫
    cpper
    fixopen
    fmddlmyy
    neoragex2002
    whinah
    云风
    梦想风暴
    沉思者
    许式伟
    负暄琐话
    辣子鸡丁
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 ANSI C中取得结构体字段偏移值的惯用法收藏

    新一篇: Lua的function、closure和upvalue | 旧一篇: 在Lua中如何动态生成两个函数的复合函数

        假设在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)了。

    发表于 @ 2005年01月18日 22:59:00|评论(loading...)|编辑

    新一篇: Lua的function、closure和upvalue | 旧一篇: 在Lua中如何动态生成两个函数的复合函数

    评论

    #bitflying 发表于2005-11-23 23:40:00  IP: 218.88.0.*
    最初在读linux 内核时看到这种用法, 为c的简洁与精妙折服.
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © soloist