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

时间:2015-01-08 04:21:57

标签: c++ c struct memory-alignment

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

  • 具体来说,我可以安全reinterpret_castRect2*Rect1*,并假设int对象中的所有四个Rect2值都匹配一个在int中的一个Rect1上?

  • 如果我将Rect2更改为非POD类型(例如,通过添加构造函数?

4 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:0)

  

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

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

  

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

是。从第一个答案开始。

  

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

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

答案 2 :(得分:0)

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

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

答案 3 :(得分:-3)

是的,它们永远都是一样的。 您可以尝试在此处运行以下示例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;
}