C/C++;结构内存布局等效性 考虑以下C结构和C++结构声明: extern "C" { // if this matters typedef struct Rect1 { int x, y; int w, h; } Rect1; } struct Vector { int x; int y; } struct Rect2 { Vector pos; Vector size; }

C/C++;结构内存布局等效性 考虑以下C结构和C++结构声明: extern "C" { // if this matters typedef struct Rect1 { int x, y; int w, h; } Rect1; } struct Vector { int x; int y; } struct Rect2 { Vector pos; Vector size; },c++,c,struct,memory-alignment,C++,C,Struct,Memory Alignment,Rect1和Rect2对象的内存布局是否始终相同 具体来说,我是否可以安全地重新解释从Rect2*到Rect1*的强制转换,并假设Rect2对象中的所有四个int值都与Rect1中的四个int值一一匹配 如果我将Rect2更改为非POD类型(例如,通过添加构造函数),是否会有所不同 是的,它们总是一样的。 您可以在这里尝试运行下面的示例 它按照你的预期运行 // Example program #include <iostream> #include <string>

Rect1
Rect2
对象的内存布局是否始终相同

  • 具体来说,我是否可以安全地
    重新解释从
    Rect2*
    Rect1*
    的强制转换,并假设
    Rect2
    对象中的所有四个
    int
    值都与
    Rect1
    中的四个
    int
    值一一匹配

  • 如果我将
    Rect2
    更改为非POD类型(例如,通过添加构造函数),是否会有所不同


  • 是的,它们总是一样的。 您可以在这里尝试运行下面的示例 它按照你的预期运行

      // Example program
    #include <iostream>
    #include <string>
    
    typedef struct Rect1 {
      int x, y;
      int w, h;
    } Rect1;
    
    struct Vector {
      int x;
      int y;
    };
    
    struct Rect2 {
      Vector pos;
      Vector size;
    };
    
    
    struct Rect3 {
      Rect3():
       pos(),
       size()
      {}
      Vector pos;
      Vector size;
    };
    
    
    int main()
    {
    
      Rect1 r1;
      r1.x = 1;
      r1.y = 2;
      r1.w = 3;
      r1.h = 4;
      Rect2* r2 = reinterpret_cast<Rect2*>(&r1);
      std::cout << r2->pos.x << std::endl;
      std::cout << r2->pos.y << std::endl;
      std::cout << r2->size.x << std::endl;
      std::cout << r2->size.y << std::endl;
    
    
      Rect3* r3 = reinterpret_cast<Rect3*>(&r1);
      std::cout << r3->pos.x << std::endl;
      std::cout << r3->pos.y << std::endl;
      std::cout << r3->size.x << std::endl;
      std::cout << r3->size.y << std::endl;
    }
    
    //示例程序
    #包括
    #包括
    typedef结构Rect1{
    int x,y;
    int w,h;
    }Rect1;
    结构向量{
    int x;
    int-y;
    };
    结构Rect2{
    向量位置;
    载体大小;
    };
    结构Rect3{
    Rect3():
    pos(),
    大小()
    {}
    向量位置;
    载体大小;
    };
    int main()
    {
    Rect1-r1;
    r1.x=1;
    r1.y=2;
    r1.w=3;
    r1.h=4;
    Rect2*r2=重新解释铸件(&r1);
    标准::cout pos.x
    • 我想是的,但我也认为(法律上)在
      Rect2::pos
      Rect2::size
      之间可能会有填充。因此,为了确保这一点,我会在“pack”中添加特定于编译器的属性字段,从而保证所有的<代码> INT/CARS> s是紧密的,这是关于C和C++的,更重要的是,当编译两种语言时,即使编译器来自单个供应商,也可能使用两个不同的编译器。
    • 使用
      reinterpret\u cast
      将指向一种类型的指针转换为指向另一种类型的指针,可能会违反“严格别名”规则。假设您随后取消引用该指针,在本例中,您会这样做
    • 添加构造函数不会改变布局(尽管它会使类成为非POD),但在两个字段之间添加访问说明符(如
      private
      )可能会改变布局(实际上,不仅仅是理论上)
    Rect1和Rect2对象的内存布局是否始终相同

    是的。只要有明显的需求,它们就保证是相同的。这些明显的要求是关于目标平台/架构在对齐和字大小方面是相同的。换句话说,如果你愚蠢到编译不同目标平台(例如,32位对64位)的C和C++代码。然后尝试混合它们,这样你就有麻烦了,否则,你就不用担心,C++编译器基本上需要产生与C一样的内存布局,而ABI在给定的字大小和对齐方式中被固定在C中。 具体来说,我可以安全地将_cast从Rect2*重新解释为Rect1*并假设Rect2对象中的所有四个int值都与Rect1中的四个int值一一匹配吗

    是的,这是第一个答案

    如果我将Rect2更改为非POD类型(例如,通过添加构造函数),是否会有所不同


    不,或者至少不再是。唯一重要的是,该类仍然是一个类,不受构造函数或任何其他非虚拟成员的影响。这自C++11(2011)标准以来一直有效。在此之前,该语言是关于“POD类型”的,正如我刚才为标准布局提供的链接中所解释的那样。如果您有一个C++11之前的编译器,那么它很可能仍然按照与C++11标准相同的规则工作(C++11标准规则(适用于标准布局和普通类型)基本上是按照所有编译器供应商已经完成的工作编写的).

    对于像您这样的标准布局类,您可以轻松检查结构的成员是如何从结构开始定位的

    #include <cstddef>
    
    int x_offset = offsetof(struct Rect1,x); // probably 0
    int y_offset = offsetof(struct Rect1,y); // probably 4
    ....
    pos_offset = offsetof(struct Rect2,pos); // probably 0
    ....
    
    #包括
    int x_offset=offsetof(struct Rect1,x);//可能为0
    int y_offset=offsetof(struct Rect1,y);//可能是4
    ....
    pos_offset=offsetof(struct Rect2,pos);//可能为0
    ....
    

    是的,它们总是一样的。您应该能够安全地强制转换并访问与测试无关的intsrelated,因为您的测试没有证明任何东西,并且您的代码违反了规则。​+1但最后一项是不正确的。添加构造函数不应该影响任何东西,他的类仍然是标准布局。我非常确定
    Rect1
    Rect2
    是布局兼容的,所以实现他想要的最好方法是使用包含这两种类型的
    union
    。@Praetorian“实现他想要的最好方法是使用<代码>联合<代码>”如果他打算给C++类型添加一个构造函数,那么它就不再是微不足道的(仍然是标准布局,而不是琐碎的)。这意味着他不能把它放在联合< /C>中。在任何情况下,很明显,OP打算通过C代码和C++代码交互,例如通过将<代码> StR2*<代码>传递给C函数,将C解释为<代码>矩形1*>代码>,反之亦然,这是普通的实践,也是标准Layo的全部原因。ut规则存在于C++11中。@MikaelPersson是的,这正是我正在做的。而且,我的代码中的
    Vector
    Rect2
    版本确实有构造函数。@Praetorian似乎我可以在没有联合的情况下完成我想要的。@MikaelPersson(之前错过了你评论的后半部分,所以现在回复整件事)。允许您在联合中放置非平凡类型。您是对的,存在用于与其他语言互操作的标准布局规则,但这仍然不允许类型双关(在实践中可能会起作用)。您可以使用
    char*
    将别名添加到对象中,或者如果它们与布局兼容,请将它们固定在一个并集中,并使用该并集来更改类型。谢谢,我不知道
    的偏移量。这可能会派上用场。不必客气。还可以使用std::is_standard_layout::v检查是否为标准布局类“是的,只要有某些明显的要求”