【问题标题】:Member offset macro - need details成员偏移宏 - 需要详细信息
【发布时间】:2011-03-08 23:41:49
【问题描述】:

请看一下这个宏。用于 Symbian OS SDK,编译器基于 GCC(

#ifndef _FOFF
#if __GNUC__ < 4
#define _FOFF(c,f)          (((TInt)&(((c *)0x1000)->f))-0x1000)
#else
#define _FOFF(c,f)          __builtin_offsetof(c,f)
#endif
#endif

我知道它正在计算特定类/结构成员的偏移量。但我无法理解这个奇怪的说法是如何工作的——常数 0x1000 是什么,为什么会出现?有人可以向我解释一下吗?

【问题讨论】:

  • 这基本上是来自&lt;stddef.h&gt;offsetof()。如果您阅读 Plauger 关于 C 库的书,您会知道它是几乎需要语言功能但并不完全需要的东西之一——所有系统都有实现它的方法。有时,变体的地址是 0 而不是 0x1000;我曾经有一个编译器反对地址 0,即使那是它的&lt;stddef.h&gt; 中的内容;我改用 0x400(1 KB 偏移)解决了这个问题 - 破解了标题。那是很久以前的事了!
  • @Jonathan,我还没有听说过这样的书。它的全名是什么?
  • 这本书可能已经绝版了 - 但它是The Standard C Library,是 C89(而不是 C99)标准 C 库的实现。它对图书馆设计的方式和原因有很多宝贵的见解。

标签: c++ gcc symbian


【解决方案1】:

它在地址 0x1000 处计算 'f' 作为类/结构的成员的相对地址,然后减去 0x1000,以便只返回类/结构地址和成员函数地址之间的差异。我想一个非零值(即 0x1000)用于避免空指针检测。

【讨论】:

  • 谢谢,这是一个有趣的想法——关于非零值的目的。
【解决方案2】:

“如果有一个结构c 的成员正好从(完全对齐的;-)地址0x1000 开始,那么结构的成员f 应该在哪个地址?” -- 答案:您正在寻找的偏移量,当然减去结构的假设起始地址0x1000... 不同的是,AKA 距离或偏移量,计算为整数,否则地址算术中的自动缩放会抛出你关闭(从哪里开始)。

具体来说,表达式的哪些部分给您带来了问题?

内部&amp;(((c *)0x1000)-&gt;f) 是“位于0x1000 的假设结构c 的成员f 的地址。在它的前面是演员表(我假设TInt 是某种整数当然是类型),然后是 - 0x1000 以获取偏移量(也就是感兴趣的特定成员的地址与整个结构的开始之间的距离或差异)。

【讨论】:

  • 谢谢,你的回答给了我足够的细节来理解这个宏。但是你能解释一下你在这句话中的意思吗:“否则地址算术中的自动缩放会让你失望(从哪里开始)。”基本上,关于自动缩放。
  • @Haspem,例如,((int*)0x1008) - ((int*)0x1000)not 8,它是 8/sizeof(int)(在大多数现代架构上总共有 2 个,在一些旧的 16 上总共有 4 个位的,在某些 64 位的上可能是 1)。在 C 中,地址算术总是由我们获取地址的任何类型的sizeof 缩放!-) 因此,要以字节为单位获得地址算术,无需缩放,将地址转换为整数(或char*,我猜测,因为按照 C 语言标准,sizeof(char) 必须 为 1;-)。
  • 感谢您的详细说明!我现在试着想象,如果没有将左侧部分转换为整数,结果会是什么。右边是一个整数,所以我们在这里从地址中减去整数。我认为结果将是address_of_f_member - 0x1000 / sizeof(f_member)。我说的对吗?
  • @Haspem,不,“指向任何东西的指针”减去整数只是 C 语言规则的类型错误。
  • 谢谢!我刚刚在编译器中检查了这个(显然,我应该早点这样做,而不是问这样一个愚蠢的问题:))。
【解决方案3】:

Imo 0x1000 只是一个随机选择的数字。它不是一个有效的指针,你可以用零代替它。

它是如何工作的:

  1. 将 0x1000 转换为类指针(c 类型的指针)。 - (c*)0x1000
  2. 将指针指向 c 类的“f”成员 - &(((c *)0x1000)->f)
  3. 将其转换为 TInt。 ((TInt)&(((c *)0x1000)->f))
  4. 从指向 c 成员的指针的整数值中减去指向基址的指针的整数值(在本例中为 0x1000):(((TInt)&(((c *)0x1000)->f))-0x1000)

因为 f 没有被写入,所以没有 accessViolation/segfault。

您可能会使用零而不是 0x1000 并放弃减法(即只使用“((TInt)&(((c *)0x0000)->f))”),但也许作者认为从指向成员的指针是一种比尝试将指针直接转换为整数更“正确”的方式。或者,也许编译器提供了可以具有负偏移量的“隐藏”类成员(这在某些编译器中是可能的 - 例如 Delphi 编译器(我知道它不是 c++)提供了位于“自我”之前的多个隐藏“字段”(类似"this") 指针),在这种情况下使用 0x1000 而不是 0 是有意义的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-19
    • 2014-08-02
    • 2013-11-07
    • 1970-01-01
    • 2018-12-11
    • 2016-01-28
    • 2023-02-07
    • 1970-01-01
    相关资源
    最近更新 更多