【问题标题】:Can size of pointers to non-union classes differ?指向非联合类的指针的大小可以不同吗?
【发布时间】:2014-08-27 10:24:04
【问题描述】:

我知道有些硬件平台需要更多信息才能指向char,而不是指向int(平台具有不可寻址字节,因此需要存储指向char 的指针指向单词的指针以及单词中字节的索引)。所以有可能sizeof(int*) < sizeof(char*)在这样的平台上。

指向非联合类的指针会发生类似的情况吗? C++ 允许在虚函数上使用协变返回类型。假设我们有这样的类:

struct Gadget
{
  // some content
};


struct Widget
{
  virtual Gadget* getGadget();
};

任何调用getGadget() 的代码在接收到Gadget* 时都必须工作,但是相同的代码(实际上是相同的编译后的二进制代码)在接收到指向类型的指针时必须工作也派生自Gadget(可能是在完全不同的库中定义的)。对于所有非联合类类型TU,我可以合理地看到这种情况发生的唯一方法是sizeof(T*) == sizeof(U*)

所以我的问题是,给定一个特定平台上的特定实用编译器(不包括假设的 Hell++),期望所有指向非联合类类型的指针都是相同的是否合理尺寸?或者是否有一个实际的原因,为什么编译器可能希望使用不同的大小,同时保持与协变返回类型的兼容?

在存在不同“级别”指针的平台上(例如__near__far),假设两者都应用了相同 属性。

【问题讨论】:

  • 附注:在嵌入式世界中有很多数据指针和代码指针大小不同的示例。
  • @Lindydancer 当然。但我只是明确地询问数据指针,并且只询问其中的一个子集。
  • @Casey language-lawyer 已被添加和删除一次。你确定它适用吗?毕竟,我问的是实际的实现,而不是一个假设的实现,它会遵循标准的文字,而在实践中做的事情完全愚蠢。

标签: c++ pointers sizeof language-lawyer


【解决方案1】:

C 有一个硬性要求,即指向所有结构类型的所有指针都具有相同的表示和对齐方式。

6.2.5 类型

27 [...] 所有指向结构类型的指针都应具有相同的表示和对齐要求。 [...]

C++ 实际上需要与 C 实现的二进制兼容性,因为标准对extern "C" 的要求,因此间接地,这要求所有指向在 C 中有效的结构类型(几乎是 POD 类型)的指针具有相同的表示和 C++ 中的对齐。

似乎对非 POD 类型没有这样的要求,因此在这种情况下允许实现使用不同的指针大小。您认为这行不通,但要以您为榜样,

struct G { };
struct H : G { };

struct W
{
  virtual G* f() { ... }
};
struct X : W
{
  virtual H* f() { ... }
};

可以翻译成(伪代码)

struct W
{
  virtual G* f() { ... }
};
struct X : W
{
  override G* f() { ... }
  inline H* __X_f() { return static_cast<H *>(f()); }
};

这仍然符合语言的要求。

两个指向结构类型的指针可能不相同的一个合理原因是,当 C++ 编译器被移植到具有设计不良 ABI 的现有 C 编译器的平台时。 G 是 POD 类型,因此 G * 需要与 C 中的完全相同。H 不是 POD 类型,因此 H * 不需要匹配任何 C 类型。

对于对齐,这实际上可能发生:真正发生的事情是典型 GNU/Linux 系统上的 x86-32 ABI 要求 64 位整数类型是 32 位对齐的,即使处理器的首选对齐实际上是64 位。现在又来了一位实现者,他们决定确实需要 64 位对齐,但如果他们想保持与现有实现的兼容性,就会陷入困境。

对于尺寸,我想不出会发生这种情况的合理场景,但我不确定这是否是我缺乏想象力。

【讨论】:

  • 即使GH 的非多态虚拟基类,因此the static_cast (or any other cast) is not possiblethe static_cast (or any other cast) is not possible,你认为这种技巧的实现是否可行?
  • @Angew 好问题。是的,这样的把戏仍然是可能的,它只需要反过来做:伪代码将是override G* f() final { return __X_f(); } virtual H* __X_f() { ... },编译器必须确保进一步覆盖f实际上最终会覆盖__X_f.
  • @Angew 实际上,考虑到这一点,编译器已经需要这样做,而且事实上已经这样做了,只要 derived * 不能简单地重新解释为 base *(包括你的例子)。
【解决方案2】:

据我了解,C 和 C++ 假设内存是线性字节可寻址的。然而,某些平台(早期 ARM)坚持字对齐的加载和存储。在这种情况下,编译器负责将指针舍入到字边界,然后在获取 char 时执行必要的位移操作。

但是由于这一切都只在加载和存储时完成,所有指针看起来都一样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-13
    • 2014-07-22
    • 2020-04-30
    • 1970-01-01
    相关资源
    最近更新 更多