简体   繁体   English

C / C ++ Struct内存布局等效

[英]C/C++ Struct memory layout equivalency

Consider the following C struct and C++ struct declarations: 考虑以下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;
}
  • Are the memory layouts of Rect1 and Rect2 objects always identical? Rect1Rect2对象的内存布局是否始终相同?

  • Specifically, can I safely reinterpret_cast from Rect2* to Rect1* and assume that all four int values in the Rect2 object are matched one on one to the four int s in Rect1 ? 具体来说,我可以放心reinterpret_castRect2*Rect1* ,并假设所有四个int中的值Rect2对象匹配一对一的四个int以秒Rect1

  • Does it make a difference if I change Rect2 to a non-POD type, eg by adding a constructor? 如果我将Rect2更改为非POD类型(例如通过添加构造函数),会有所不同吗?

  • I would think so, but I also think there could (legally) be padding between Rect2::pos and Rect2::size . 我会这样认为,但我也认为Rect2::posRect2::size之间可能(合法地)填充。 So to make sure, I would add compiler-specific attributes to "pack" the fields, thereby guaranteeing all the int s are adjacent and compact. 因此,为确保确定,我将添加特定于编译器的属性以“打包”字段,从而确保所有int都是相邻且紧凑的。 This is less about C vs. C++ and more about the fact that you are likely using two "different" compilers when compiling in the two languages, even if those compilers come from a single vendor. 这与C与C ++的关系不那么多,而更多的事实是,当使用两种语言进行编译时,即使这些编译器来自同一家供应商,也可能会使用两个“不同的”编译器。
  • Using reinterpret_cast to convert a pointer to one type to a pointer to another, you are likely to violate "strict aliasing" rules. 使用reinterpret_cast将一种类型的指针转​​换为另一种类型的指针,您可能会违反“严格别名”规则。 Assuming you do dereference the pointer afterward, which you would in this case. 假设您之后确实取消了指针的引用,在这种情况下,您将取消引用。
  • Adding a constructor will not change the layout (though it will make the class non-POD), but adding access specifiers like private between the two fields may change the layout (in practice, not only in theory). 添加构造函数不会更改布局(尽管它将使类变为非POD),但是在两个字段之间添加诸如private访问说明符可能会更改布局(实际上,不仅在理论上)。

Are the memory layouts of Rect1 and Rect2 objects always identical? Rect1和Rect2对象的内存布局是否始终相同?

Yes. 是。 As long as certain obvious requirements hold, they are guaranteed to be identical. 只要满足某些明显的要求,就可以保证它们是相同的。 Those obvious requirements are about the target platform/architecture being the same in terms of alignment and word sizes. 这些明显的要求是关于目标平台/体系结构在对齐方式和字长方面都相同。 In other words, if you are foolish enough to compile the C and C++ code for different target platforms (eg, 32bit vs. 64bit) and try to mix them, then you'll be in trouble, otherwise, you don't have to worry, the C++ compiler is basically required to produce the same memory layout as if it was in C, and ABI is fixed in C for a given word size and alignment. 换句话说,如果您愚蠢到为不同的目标平台(例如32位和64位)编译C和C ++代码并尝试将它们混合使用,那么您将遇到麻烦,否则,您不必令人担心的是,基本上要求C ++编译器产生与在C中相同的内存布局,并且对于给定的字长和对齐方式,ABI在C中固定。

Specifically, can I safely reinterpret_cast from Rect2* to Rect1* and assume that all four int values in the Rect2 object are matched one on one to the four ints in Rect1? 具体来说,我是否可以安全地将Rect2 *从Rect2 *重新解释为Rect1 *,并假设Rect2对象中的所有四个int值都与Rect1中的四个int一对一匹配?

Yes. 是。 That follows from the first answer. 从第一个答案开始。

Does it make a difference if I change Rect2 to a non-POD type, eg by adding a constructor? 如果我将Rect2更改为非POD类型(例如通过添加构造函数),会有所不同吗?

No, or at least, not any more. 不,或者至少不再。 The only important thing is that the class remains a standard-layout class, which is not affected by constructors or any other non-virtual member. 唯一重要的是,该类仍然是标准布局类,不受构造函数或任何其他非虚拟成员的影响。 That's valid since the C++11 (2011) standard. 从C ++ 11(2011)标准开始有效。 Before that, the language was about "POD-types", as explained in the link I just gave for standard-layout. 在此之前,该语言是关于“ POD类型”的,正如我刚才在标准布局中给出的链接中所述。 If you have a pre-C++11 compiler, then it is very likely still working by the same rules as the C++11 standard anyway (the C++11 standard rules (for standard-layout and trivial types) were basically written to match what all compiler vendors did already). 如果您使用的是C ++ 11之前的编译器,那么很有可能仍按与C ++ 11标准相同的规则进行工作(C ++ 11标准规则(对于标准布局和平凡类型)基本上是编写以匹配所有编译器供应商已经做的事情。

For a standard-layout class like yours you could easily check how members of a structure are positioned from the structure beginning. 对于像您这样的标准布局类,您可以轻松地从结构开始检查结构成员的位置。

#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
....

http://www.cplusplus.com/reference/cstddef/offsetof/ http://www.cplusplus.com/reference/cstddef/offsetof/

Yes, they will always be the same. 是的,它们将始终相同。 You could try running the below example here cpp.sh It runs as you expect. 您可以尝试在此处运行以下示例cpp.sh它按预期运行。

  // 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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM