包含1个指针的结构的大小

时间:2012-05-04 12:20:37

标签: c++ visual-c++ g++

假设我有以下类模板:

template<typename T>
struct Wrapper {
  T* t_;
  static void check() {
    static_assert(sizeof(Wrapper<T> == sizeof(T*), "Illegal assumption");
  }
};

我在C ++ 03标准中查看了C99标准,并且找不到我在static_assert中表达的假设的保证。我使用几个编译器选项在Visual C ++ 2008和2010(32位)以及linux(64位)上使用gcc进行了尝试,并确认了我的假设。

我的问题是:

  • 我的假设是否适用于Visual C ++ 2008/2010/11(windows)?
  • 对于gcc 4. *(linux)?
  • 对于编译器标志的任何组合?
  • 你知道这个假设不成立的任何编译器/平台吗?

我想编译器可能会在结构中添加一些填充,例如用于调试目的。但有没有一个编译器实际上这样做?

编辑:因为您在这里问的是我想要实现的目标:
我有成员函数,具有以下签名:

Someclass* OtherClass::fn();

我想像这样更改签名:

Wrapper<Someclass> OtherClass::fn();

这个包装器就像一些智能指针,即它关心指针的生命周期,因此当它超出范围时它会释放它。当函数跨越dll边界调用时,我想确保返回的值(现在是一个具体类型,而不仅仅是一个哑指针)在与指针大小相同的所有情况下(即编译器设置等)将会。计划/希望是支持调试/发布应用程序/ dll构建的所有组合 你可能会问:不,我不能使用boost :: shared_ptr&lt;&gt ;,std :: shared_ptr&lt;&gt ;,std :: unique_ptr&lt;&gt;等等,因为我们不想向dll用户公开boost,我们还不支持C ++ 11。

3 个答案:

答案 0 :(得分:3)

如果你想要它并且你有一个编译时检查,那么继续。据推测,你可以从中获得一些好处。

您无法保证任何有关填充的信息,但通常使用填充来进行对齐,以便该类型的数组使数组的每个成员(以及结构的每个成员)正确对齐。本机指针通常已经是正确的对齐大小,因此不需要填充,但不能保证。

这不是你可以用gcc检查的东西 - 它取决于目标架构,而不仅仅是编译器。

答案 1 :(得分:1)

调查标准后,我在§9.2[class.mem] 中找到了两个有趣的内容:

  

17 / 如果两个标准布局结构(第9类)类型具有相同数量的非静态数据成员和相应的非静态数据成员(按声明顺序),则它们是布局兼容的具有布局兼容类型(3.9)。

因此,只有一个指针的两个struct将是布局兼容的(因此,如果您的断言适用于此类型,则它适用于所有类型)。

  

20 / 指向标准布局结构对象的指针,使用reinterpret_cast进行适当转换,指向其初始成员(或者如果该成员是位字段,则指向其中的单位)它驻留),反之亦然。 [注意:因此,在标准布局结构对象中可能存在未命名的填充,但不是在其开头,以实现适当的对齐。 - 后注]

在笔记中我们了解到结构的开头不能填充。


根据您的目标, 20 / 可能就足够了。这意味着这有效:

void check(T* t) {
    Wrapper<T>* w = reinterpret_cast<Wrapper<T>*>(t);
    assert(w->t_ == t);
}

答案 2 :(得分:1)

我会说你的假​​设对大多数编译器和标志都是合理的。

对于任何T,编译器都能够创建T的数组,这些数组必须是连续的,因此它必须能够在没有填充的情况下创建它。因此,它放入结构中的任何填充都是纯粹可选的,而不是可能需要的。

另一方面,我有理由相信C或C ++标准都不能保证你所要求的。 最接近以保证它可以源于这样一个事实,即在C ++中它是一个标准布局结构,它限制了C ++编译器布局类似于C编译器的字段。 ,所以成员必须是升序,​​开头没有填充 - 但成员之间和/或最后一个成员之后仍然允许填充。

底线:如果你有一个指向第一个成员的指针,你可以安全地转换为指向struct(或反之亦然)的指针。如果你做一些像创建一个数组并将其编入索引就好像它是另一个数组的那样 - 你可以自己工作 - 它的工作机会非常好,但我很确定标准没有保证它。