繁体   English   中英

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

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

考虑以下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类型(例如通过添加构造函数),会有所不同吗?

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

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

是。 只要满足某些明显的要求,就可以保证它们是相同的。 这些明显的要求是关于目标平台/体系结构在对齐方式和字长方面都相同。 换句话说,如果您愚蠢到为不同的目标平台(例如32位和64位)编译C和C ++代码并尝试将它们混合使用,那么您将遇到麻烦,否则,您不必令人担心的是,基本上要求C ++编译器产生与在C中相同的内存布局,并且对于给定的字长和对齐方式,ABI在C中固定。

具体来说,我是否可以安全地将Rect2 *从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
....

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

是的,它们将始终相同。 您可以尝试在此处运行以下示例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