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检查是否为标准布局类“是的,只要有某些明显的要求”