C++ C++;成员布局

C++ C++;成员布局,c++,c,pointers,layout,member,C++,C,Pointers,Layout,Member,让我们有一个简单的结构(POD) 我可以假设以下代码是正确的吗?我可以假定没有任何间隙吗?标准怎么说?豆荚是真的吗?上课是真的吗 xyz v; float* p = &v.x; p[0] = 1.0f; p[1] = 2.0f; // Is it ok? p[2] = 3.0f; // Is it ok? 不,除第一个字段外,不允许这样做 从C++标准: 9.2班级成员 指向POD结构对象的指针, 使用 重新解释_cast,指向它的 初始成员(或如果该成员是 位字段,然后输入到 它驻留

让我们有一个简单的结构(POD)

我可以假设以下代码是正确的吗?我可以假定没有任何间隙吗?标准怎么说?豆荚是真的吗?上课是真的吗

xyz v;
float* p = &v.x;
p[0] = 1.0f;
p[1] = 2.0f; // Is it ok?
p[2] = 3.0f; // Is it ok?

不,除第一个字段外,不允许这样做

<>从C++标准:

9.2班级成员
指向POD结构对象的指针, 使用 重新解释_cast,指向它的 初始成员(或如果该成员是 位字段,然后输入到 它驻留)反之亦然。[注: 因此,可能存在未命名的问题 POD结构对象中的填充, 但不是在它的开始,如果必要的话 以实现适当的对齐


取决于硬件。标准明确允许POD类具有不指定的和不可预测的填充。我在C++维基百科页面上注意到这一点,并且用脚注引用了脚注。

> ISO/IEC(2003).ISO/IEC 1488∶2003(E):程序设计语言-C++ + 9.2类成员[类.MEM]第17版< /P>


但是,实际上,在普通硬件和编译器上,这是可以接受的。

这不是标准的保证,在许多系统上都不起作用。原因是:

  • 编译器可以针对目标平台适当地对齐结构成员,这可能意味着32位对齐、64位对齐或其他任何方式
  • 浮点的大小可能是32位或64位。不能保证它与结构成员对齐方式相同

这意味着
p[1]
可能与
xyz.y
位于同一位置,或者可能部分重叠,或者根本不重叠。

标准要求内存中的排列顺序与定义顺序匹配,但允许在它们之间进行任意填充。如果您有访问说明符(
public:
private:
protected:
)在成员之间,甚至有关订单的保证也会丢失

编辑:在所有三个成员都属于同一基本类型(即,它们本身不是结构或类似的东西)的特定情况下,您有相当大的机会——对于基本类型,对象的大小和对齐要求通常是相同的,因此它是可行的


OTOH,这只是偶然的,而且往往是一个缺点而不是优点;代码是错误的,因此理想情况下,它会立即失败,而不是看起来正常工作,直到您为公司的所有者提供演示的那一天,该公司将是您最重要的客户,届时它会(当然)在最可恶的可能的方式下失败……< /p> < p>这里的答案有点棘手。C++标准称POD数据类型将具有C布局可兼容性保证()。根据C规范的一节,如果

,结构的成员将按顺序排列。
  • 无可访问性修饰符差异
  • 数据类型没有对齐问题
  • 因此,是的,只要类型
    float
    在当前平台上具有兼容的对齐方式(它是平台字大小),此解决方案就可以工作。因此,这应该适用于32位处理器,但我的猜测是,对于64位处理器,它将失败。本质上,
    sizeof(void*)
    sizeof(float)不同的任何地方

    您的代码没有问题(只要它只处理在同一环境中生成的数据)。如果它是POD,则结构将按照声明的方式在内存中布局。但是,一般来说,您需要注意一个问题:编译器将在结构中插入填充,以确保每个成员的对齐要求都得到遵守

    你的榜样是什么

    struct xyz
    {
        float x;
        bool y;
        float z;
    };
    
    然后z将从结构中的8个字节开始,sizeof(xyz)将是12,因为
    float
    s(通常)是4字节对齐的

    同样,在本案中

    struct xyz
    {
        float x;
        bool y;
    };
    
    sizeof(xyz)==8,以确保((xyz*)ptr)+1返回符合x对齐要求的指针


    由于编译器/平台之间的对齐要求/类型大小可能会有所不同,因此此类代码通常不可移植。

    不,您可能不会假设没有间隙。您可以检查您的体系结构,如果没有间隙,并且您不关心可移植性,那么就可以了

    但是想象一下一个64位的体系结构,它有32位的浮点数。编译器可能会在64位边界上对齐结构的浮点数,并且

    p[1]
    
    会给你垃圾,还有

    p[2]
    
    会给你你认为从中得到的东西

    p[1]
    
    &c


    然而,您的编译器可能会给您一些打包结构的方法。它仍然不是“标准的”---该标准没有提供这样的东西,不同的编译器提供了非常不兼容的方法来实现这一点,但它可能更易于移植。

    正如其他人所指出的,该规范不保证对齐。许多人说对齐依赖于硬件,但实际上它也依赖于编译器。硬件可能支持许多不同的功能我记得PPC编译器在如何“打包”数据方面支持pragmas。你可以在“本机”边界上打包数据,也可以强制将其打包为32位边界,等等


    了解您正在尝试做的事情会很好。如果您正在尝试“解析”输入数据,最好使用真正的解析器。如果您要序列化,那么就编写一个真正的序列化程序。如果您尝试旋转位,例如驱动程序,那么设备规范应该为您提供一个特定的内存映射来写入。然后您可以编写POD结构,指定正确的对齐标记(如果支持),然后继续。

    如有疑问,请更改数据结构以适应应用程序:

    struct xyz
    {
        float  p[3];
    };  
    
    为了便于阅读,您可能需要考虑:

    struct xyz
    {
        enum { x_index = 0, y_index, z_index, MAX_FLOATS};
        float p[MAX_FLOATS];
    
        float  X(void) const {return p[x_index];}
        float  X(const float& new_x) {p[x_index] = new_x;}
    
        float  Y(void) const {return p[y_index];}
        float  Y(const float& new_y) {p[y_index] = new_y;}
    
        float  Z(void) const {return p[z_index];}
        float  Z(const float& new_z) {p[z_index] = new_z;}
    };
    
    甚至可以添加更多的封装:

    struct Functor
    {
      virtual void operator()(const float& f) = 0;
    };
    
    struct xyz
    {
      void for_each(Functor& ftor)
      {
         ftor(p[0]);
         ftor(p[1]);
         ftor(p[2]);
         return;
      }
      private:
         float p[3];
    }
    

    一般来说,如果数据结构需要以两种或两种以上不同的方式处理,可能需要重新设计数据结构;或者重新设计代码。

    让我们看看Doom III源代码:

    class idVec4 {
    public: 
        float           x;
        float           y;
        float           z;
        float           w;
        ...
        const float *   ToFloatPtr( void ) const;
        float *         ToFloatPtr( void );
        ...
    }
    
    ID_INLINE const float *idVec4::ToFloatPtr( void ) const {
        return &x;
    }
    
    ID_INLINE float *idVec4::ToFloatPtr( void ) {
        return &x;
    }
    
    它适用于许多系统。

    我想
    class idVec4 {
    public: 
        float           x;
        float           y;
        float           z;
        float           w;
        ...
        const float *   ToFloatPtr( void ) const;
        float *         ToFloatPtr( void );
        ...
    }
    
    ID_INLINE const float *idVec4::ToFloatPtr( void ) const {
        return &x;
    }
    
    ID_INLINE float *idVec4::ToFloatPtr( void ) {
        return &x;
    }
    
    struct xyz
    {
      float x, y, z;
      float& operator[] (unsigned int i)
      {
        switch (i)
        {
        case 0:
          return x;
          break;
        case 1:
          return y;
          break;
        case 2:
          return z;
          break;
        default:
          throw std::exception
          break;
        }
      }
    };