C ++结构的布局是由标准设置的,还是至少在编译器之间是通用的?
我有一个结构,其中一个成员需要在16字节边界上对齐,如果我能保证字段的顺序,这会更容易。
另外,对于非虚拟类,第一个元素的地址是否也可能是结构的地址?
我对GCC和MSVS最感兴趣。
答案 0 :(得分:13)
C和C ++都保证字段将按照您定义的顺序在内存中布局。对于只保证POD类型 1 的C ++(任何合法的C结构[编辑:C89 / 90 - 不是,例如,C99 VLA]也有资格作为POD )。
编译器可以在成员之间和/或结构的末尾自由插入填充。大多数编译器都为您提供了一些控制它的方法(例如,#pragma pack(N)
),但它在编译器之间有所不同。
1 好吧,有一个他们没有想到的角落案例,它不能保证POD类型 - 访问说明符打破了订购保证:
struct x {
int x;
int y;
public:
int z;
};
这是POD类型,但public:
和y
之间的z
表示理论上可以重新排序。我很确定这纯粹是理论上的 - 我不知道在这种情况下是否会对成员进行重新排序的任何编译器(除非内存使我失败甚至比平时更糟,这在C ++ 0x中得到修复)。
编辑:标准的相关部分(至少大部分)是§9/ 4:
POD-struct是一个没有非易失性的聚合类 指向成员的指针的数据成员,非POD结构,非 POD-union(或这类类型的数组)或引用,并没有 用户定义的复制赋值运算符,没有用户定义的 析构函数。
和§8.5.1/ 1:
聚合是一个数组或类(第9条),没有用户 - 声明的构造函数(12.1),没有私有或受保护的非 静态数据成员(第11条),没有基类(第10条) 并且没有虚函数(10.3)。
和§9.2/ 12:
...非静态数据成员的分配顺序 由access-specifier分隔的是未指定的(11.1)。
虽然这受到§9.2/ 17的限制:
指向POD结构对象的指针,适当地使用转换 reinterpret_cast,指向其初始成员......
因此,(即使前面有public:
,你定义的第一个成员必须先在内存中。理论上可以重新排列由public:
说明符分隔的其他成员。
我还应该指出,对此有一些争论的余地。特别是,§9.2/ 14中还有一条规则:
如果两个POD-struct(第9节)类型具有相同数量的非静态数据成员,则它们是布局兼容的,并且相应的非静态数据成员(按顺序)具有布局兼容类型(3.9)。
因此,如果你有类似的东西:
struct A {
int x;
public:
int y;
public:
int z;
};
需要布局兼容:
struct B {
int x;
int y;
int z;
};
我很确定这是意图意味着两个结构的成员必须在内存中以相同的方式布局。由于第二个显然不能重新安排其成员,所以第一个也不应该重新排列。不幸的是,标准从未真正定义“布局兼容”的含义,使得论证充其量只是微弱。
答案 1 :(得分:5)
C ++继承了c在许多平台上有效工作的需要/愿望,因此将某些事情留给了编译器。除了要求元素以指定的顺序出现之外,这也是其中之一。
但是许多编译器支持#pragma
和选项,让您建立控制包装。请参阅编译器的文档。
答案 2 :(得分:4)
是否构建了C ++结构的方式 按标准设定,或至少 编译器之间有共同点吗?
标准中无法保证。我不会依赖具有相同对齐的编译器
我有一个结构,其中一个 成员需要在16字节上对齐 边界,这会更容易 如果我能保证订购的话 字段。
编译器不会更改字段的顺序。
以下是有关如何为GCC和MSVC设置它们的一些链接:
对于海湾合作委员会:http://developer.apple.com/mac/library/documentation/DeveloperTools/gcc-4.0.1/gcc/Structure_002dPacking-Pragmas.html
MSVC:http://msdn.microsoft.com/en-us/library/ms253935(VS.80).aspx和http://msdn.microsoft.com/en-us/library/2e70t5y1(VS.80).aspx
我会将它们保留为结构并使用extern“C”来确保它正常工作。也许不需要,但这肯定有用。
答案 3 :(得分:2)
C struct
s(和C ++ POD,兼容性)are required by the standard to have sequential layout。
编译器之间的唯一区别是对齐,但幸运的是,MSVC和GCC都支持#pragma pack
。
答案 4 :(得分:1)
如果要在MSVC下查看内存中所有类类型的内存布局,请将/d1reportAllClassLayout
开关添加到编译器命令行。
如果您想查看一个类的布局,请添加/d1reportSingleClassLayoutNNNNN
,其中NNNNN
是该类的名称。
答案 5 :(得分:0)
结构按顺序排列在内存中。但是,它们在内存中的对齐方式因操作系统而异。
对于大于4字节的内容,Windows和Linux之间存在差异。 Linux将它们对齐为好像它们是4个字节,因此例如双(8个字节)可以从p + 4,p + 8,p + 12等开始,其中p是结构的开始。在Windows中,双(8字节)需要从一个8的倍数开始,所以p + 8,p + 16,p + 24等。