摘要:编译期间编译器如何静态确定C ++类的大小?
详情:
我试图了解规则用于确定类将使用多少内存,以及内存如何对齐。
例如,以下代码声明了4个类。前2个是16个字节。但是3是48个字节,即使它包含与前2个相同的数据成员。虽然第四个类与第三个类具有相同的数据成员,但只是以不同的顺序,但它是32个字节。
#include <xmmintrin.h>
#include <stdio.h>
class TestClass1 {
__m128i vect;
};
class TestClass2 {
char buf[8];
char buf2[8];
};
class TestClass3 {
char buf[8];
__m128i vect;
char buf2[8];
};
class TestClass4 {
char buf[8];
char buf2[8];
__m128i vect;
};
TestClass1 *ptr1;
TestClass2 *ptr2;
TestClass3 *ptr3;
TestClass4 *ptr4;
int main() {
ptr1 = new TestClass1();
ptr2 = new TestClass2();
ptr3 = new TestClass3();
ptr4 = new TestClass4();
printf("sizeof TestClass1 is: %lu\t TestClass2 is: %lu\t TestClass3 is: %lu\t TestClass4 is: %lu\n", sizeof(*ptr1), sizeof(*ptr2), sizeof(*ptr3), sizeof(*ptr4));
return 0;
}
我知道答案与班级数据成员的对齐有关。但我试图准确理解这些规则是什么以及如何在编译步骤中应用它们,因为我有一个具有__m128i
数据成员的类,但数据成员不是16字节对齐的,这导致当编译器使用movaps
生成代码来访问数据时的段错误。
答案 0 :(得分:13)
对于POD(普通旧数据),规则通常是:
当完成上述操作时,结构的大小是S的值。
此外:
考虑您的TestClass3
:
char buf[8]
需要8个字节且对齐1,因此S增加8到8,A保持为1。__m128i vect
需要16个字节和对齐16.首先,S必须增加到16才能给出正确的对齐方式。然后A必须增加到16.然后S必须增加16才能为vect
腾出空间,所以S现在是32。char buf2[8]
需要8个字节且对齐1,因此S增加8到24,A仍为16。因此TestClass3
的大小为32个字节。
对于基本类型(int
,double
等等),对齐要求取决于实现,主要由硬件决定。在许多处理器上,当数据具有特定的对齐时(通常当它在内存中的地址是其大小的倍数时),加载和存储数据会更快。除此之外,上述规则主要来自逻辑;他们将每个成员放在必须满足对齐要求的地方,而不使用超出必要的空间。
答案 1 :(得分:8)
完全取决于编译器如何确定类的大小。编译器通常会编译以匹配某个应用程序二进制接口,该接口与平台有关。
然而,您观察到的行为非常典型。编译器正在尝试对齐成员,以便它们各自以其大小的倍数开始。对于TestClass3
,其中一个成员的类型为__m128i
和sizeof(__m128i) == 16
。因此,它会尝试将该成员对齐,以16的倍数开始。第一个成员的类型为char[8]
,因此占用8个字节。如果编译器将_m128i
对象直接放在第一个成员之后,它将从位置8开始,这不是16的倍数:
0 8 16 24 32 48
┌───────────────┬───────────────────────────────┬───────────────┬┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
│ char[8] │ __m128i │ char[8] │
└───────────────┴───────────────────────────────┴───────────────┴┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
所以相反,它更喜欢这样做:
0 8 16 24 32 48
┌───────────────┬┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┬───────────────────────────────┬───────────────┐┄┄┄
│ char[8] │ │ __m128i │ char[8] │
└───────────────┴┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┴───────────────────────────────┴───────────────┘┄┄┄
这给它一个48字节的大小。
当你重新排序成员以获得TestClass4
时,布局变为:
0 8 16 24 32 48
┌───────────────┬───────────────┬───────────────────────────────┬┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
│ char[8] │ char[8] │ __m128i │
└───────────────┴───────────────┴───────────────────────────────┴┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
现在一切都正确对齐 - 数组的偏移量是1的倍数(元素的大小),而__m128i
对象的偏移量是16的倍数 - 总大小是32个字节。
编译器本身不进行此重新排列的原因是因为标准指定类的后续成员应具有更高的地址:
分配具有相同访问控制(第11条)的(非联合)类的非静态数据成员,以便后面的成员在类对象中具有更高的地址。
答案 2 :(得分:0)
答案 3 :(得分:-1)
如果要确保对齐,则应使用h文件中的“pragma pack(1)” 看看这篇文章: http://tedlogan.com/techblog2.html