摘要 :如何编译器静态地确定在编译过程中一个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
来访问数据。
对于POD(普通旧数据),规则通常是:
- 在结构中的每个构件具有一些大小s以及一些对齐要求一个。
- 编译器开始于一个尺寸S设为零和对准要求而作的设置为一(字节)。
- 编译器处理结构中每个部件,以便:
- 考虑成员的对齐要求一个。 如果S不是目前的倍数,然后加入足够的字节是如此,它的倍数。 这决定了该成员将去; 它会在偏移■从结构(S的电流值)的开始。
- 设置A到A和的最小公倍数。
- 添加s到S,预留的成员空间。
- 当上述过程为每个成员完成的,考虑结构的对齐要求A.如果S不是目前A的倍数,然后加入刚好够太,这是A的多
该结构的大小是S的值当上述已经完成。
另外:
- 如果任何部件是一个数组,它的大小是乘以每个元件的尺寸元件的数量,它的对齐要求是元件的对齐要求。
- 如果任何部件是一个结构,其大小和取向的要求如上述计算。
- 如果任何成员是联合:
- 集合S的最大成员的大小。
- 集A的所有成员对齐的最小公倍数。
- 如果S不是的倍数,加入足够的为S以使其A的多
考虑你的TestClass3
:
- S初始状态在0和A 1开始。
-
char buf[8]
需要8个字节和对齐1,因此S被增加8〜8,A保持1。 -
__m128i vect
需要16个字节和16对准首先,S必须增加到16以得到正确的对准。 然后,必须增加到16则S必须被增加16,使空间vect
,所以小号现在是32。 -
char buf2[8]
需要8个字节和对齐1,因此S被增加8至24,和A 16保持。 - 在结束时,S是24,这是不A(16)的倍数,因此s必须是增加8至32。
这样的尺寸TestClass3
为32个字节。
对于基本类型( int
, double
,等等),所述对准的要求是实施相关的,并且在很大程度上是由硬件来确定。 在许多处理器,它更快来加载和存储数据时,它具有一定的取向(通常当其在存储器地址是它的大小的倍数)。 除此之外,上述规则从逻辑很大程度上遵循; 便将每个成员,其中它必须以满足对准要求,而不使用更多的空间比必要的。
它是完全由编译器一类的大小是如何确定的。 编译器通常将编译,以匹配特定的应用程序二进制接口,这是依赖于平台的。
你观察到的行为,但是,是非常典型的。 编译器试图对准成员,使他们各自开始在它们的大小的倍数。 在的情况下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)(非联合)类的非静态数据成员被分配,使得后来构件具有类对象内的较高地址。
规则由使用中的应用程序二进制接口规范,它保证了对节目共享该接口不同的系统之间的兼容性一成不变的。
对于GCC,这是安腾ABI。
(遗憾的是它不再是公开的,但我没有找到一面镜子 。)
如果你想确保allignment你应该在这个岗位使用“杂注包(1)”在您的.h文件的样子: http://tedlogan.com/techblog2.html