C++内存对齐
因为cpu在存取指令的时候,以字节长为单位进行存取,32位机器默认字节长4字节,64位机器默认长8字节。cpu每次读取都从对齐字节的整数倍开始读取,而存放数据cpu以对齐字节的整数倍进行存放,这要求我们对struct或class内的数据排布提出了要求。
看这样一段代码
#include<iostream>
using namespace std;
struct A {
char c;
short s[3];
char cs[3];
};
struct B {
short s[3];
char cs[3];
char c;
};
struct C{
char c;//8
double d;//16
int i[2];//24
};
int main()
{
A a;
B b;
C c;
printf("%p %p %p %p %d\n", &a,&a.c,a.s,a.cs,sizeof(A));
//0079FEB0 0079FEB0 0079FEB2 0079FEB8 12
// 0 -> 2 -> 8 -> 12
printf("%p %p %p %p %d\n", &b, &b.s, &b.cs, &b.c,sizeof(B));
//0079FE9C 0079FE9C 0079FEA2 0079FEA5 10
// 12 -> 16*1+2=18 -> 16*1+5=21 -> 21+1=22
printf("%p %p %p %p %d\n", &c, &c.c, &c.d, &c.i, sizeof(C));
//00F6F81C 00F6F81C 00F6F824 00F6F82C 24
// 12 -> 16*1+4 = 20 -> 16*1+12=28 -> 28+8=36
return 0;
}
struct A的大小占比为12字节
而struct B的大小占10字节
而struct C的大小占8字节?(为啥一个char c需要8字节?==》因为内存对齐是按成员变量中sizeof最大值作为对齐条件)
内存对齐规律
-
对于结构体的各个成员,第一个成员位于偏移为0的位置,结构体第一个成员的偏移量(offset)为0,以后每个成员相对于结构体首地址的offset都是该成员大小与有效对齐值中较小那个的整数倍,如有需要编译器会在成员之间加上填充字节。
-
除了结构成员需要对齐,结构本身也需要对齐,结构的长度必须是编译器默认的对齐长度和成员中最长类型中最小的数据大小的倍数对齐。
我们再来看看如果struct中有包含sturct呢?
#include<iostream>
using namespace std;
struct A {
char c;
short s[3];
char cs[3];
};
struct B {
short s[3];
char cs[3];
char c;
};
struct C{
A a;//12
char c;//16
double d;//24
int i[2];//32
};
struct D {
B b;//10
char c;//16
double d;//24
int i[2];//32
};
int main()
{
C c;
D d;
printf("%p %p %p %p %p %d\n", &c, &c.a ,&c.c, &c.d, &c.i, sizeof(C));
//010FFEBC 010FFEBC 010FFEC8 010FFECC 010FFED4 32
// 12 -> 16+8=24 -> 16+12=28 -> 16*2+4=36 -> 44
printf("%p %p %p %p %p %d\n", &d, &d.b, &d.c, &d.d, &d.i, sizeof(D));
// 010FFE94 010FFE94 010FFE9E 010FFEA4 010FFEAC 32
// 4 -> 14 ->16+4=20-> 16+12=28 -> 36
//
return 0;
}
对于C
对于D
如果把A/B放在C/D的最下面呢?
#include<iostream>
using namespace std;
struct A {
char c;
short s[3];
char cs[3];
};
struct B {
short s[3];
char cs[3];
char c;
};
struct C{
char c;//8
double d;//8
int i[2];//8
A a;//16
};
struct D {
char c;//8
double d;//8
int i[2];//8
B b;//16
};
int main()
{
C c;
D d;
printf("%p %p %p %p %p %d\n", &c ,&c.c, &c.d, &c.i, &c.a, sizeof(C));
//0113FBFC 0113FBFC 0113FC04 0113FC0C 0113FC14 40
// 12 -> 16+4=20 -> 16+12=28 -> 16*2+4=36 -> 36+16=52
printf("%p %p %p %p %p %d\n", &d, &d.c, &d.d, &d.i, &d.b, sizeof(D));
// 0113FBCC 0113FBCC 0113FBD4 0113FBDC 0113FBE4 40
// 12-> 16+4=20 ->16+12=28->16*2+4=36->36+16=52
//
return 0;
}
对于C
对于D
所以无论如何它们都要按照8的整数倍进行对齐。
而最后的A/B则因为超过了8所以按16对齐。