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

考虑以下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; } 
  • Rect1Rect2对象的内存布局是否始终相同?

  • 具体来说,我可以安全地从Rect2*Rect1*并假设Rect2对象中的所有四个intRect2匹配到Rect1的四个int吗?

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

  • 我会这么认为,但我也认为(合法地)可以在Rect2::posRect2::size之间填充。 所以为了确保,我会添加特定于编译器的属性来“打包”字段,从而保证所有的int都相邻且紧凑。 这不是关于C与C ++的关系,而是关于在使用两种语言编译时可能使用两个“不同”编译器的事实,即使这些编译器来自单个供应商。
  • 使用reinterpret_cast将指向一种类型的指针转​​换为指向另一种类型的指针,您可能会违反“严格别名”规则。 假设你之后取消引用指针,在这种情况下你会这样做。
  • 添加构造函数不会改变布局(虽然它会使类非POD),但是在两个字段之间添加private访问说明符可能会改变布局(实际上,不仅在理论上)。

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

是。 只要某些明显的要求成立,它们就保证是相同的。 这些明显的要求是关于目标平台/架构在对齐和字大小方面是相同的。 换句话说,如果你愚蠢地为不同的目标平台编译C和C ++代码(例如,32位对64位)并尝试混合它们,那么你将遇到麻烦,否则,你不必担心,C ++编译器基本上需要产生与C中相同的内存布局,并且对于给定的字大小和对齐,ABI在C中是固定的。

具体来说,我可以安全地从Rect2 * reterpret_cast到Rect1 *并假设Rect2对象中的所有四个int值一对一匹配到Rect1中的四个整数?

是。 从第一个答案开始。

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

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

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

 #include  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/

是的,他们将永远是一样的。 您可以尝试在此处运行以下示例cpp.sh它按预期运行。

  // Example program #include  #include  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(&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(&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; }