最近因为某些原因,接触到一些C/C++比较基础的知识点,发现在这方面有些薄弱,故记录下来,作为积累。
首先,概念性的东西就不做记录。已知机器为了更加容易的取出struct里面的数据,会将里面的变量所占空间进行对齐。这是以空间换时间的一种方式。
因为数据所占字节数跟机器字长及编译器有关系,所以先列出常见的几种数据类型在不同的编译环境下所占字节数。
字节对应表 | |||
类型 | 16位 | 32位 | 64位 |
char | 1 | 1 | 1 |
char*(指针) | 2 | 4 | 8 |
short | 2 | 2 | 2 |
int | 2 | 4 | 4 |
unsigned int | 2 | 4 | 4 |
float | 4 | 4 | 4 |
double | 8 | 8 | 8 |
long | 4 | 4 | 8 |
long long | 8 | 8 | 8 |
unsigned long | 4 | 4 | 8 |
1.默认对齐方式
在默认对齐方式下,结构体成员的内存分配满足下面三个条件
- 结构体第一个成员的地址和结构体的首地址相同
- 结构体每个成员地址相对于结构体首地址的偏移量(offset)是该成员大小的整数倍,如果不是则编译器会在成员之间添加填充字节(internal adding)。
- 结构体总的大小要是其成员中最大size的整数倍,如果不是编译器会在其末尾添加填充字节(trailing padding)。
示例:(32位 Windows VS2015)
struct s1 {
char a; //1 偏移量 0
int b; //4 偏移量 1 -> 4
double c; //8 偏移量 8
char d; //1 偏移量 16
};
struct s2 {
int a; //4 偏移量 0
char b; //1 偏移量 4
char c; //1 偏移量 5
double d; //8 偏移量 6->8
};
int main()
{
cout << "s1 size = " << sizeof(s1) << endl;
cout << "s2 size = " << sizeof(s2) << endl;
char stop = getchar();
return 0;
}
运行结果为:
对于S1结构体,分析见注释,按照 条件2 ,得出大小为 17,又根据 条件3 ,得出最终大小为24。对于结构体S2,得出大小为 16,符合条件。
2.指定对齐方式
可以使用#pragma pack(N)来指定结构体成员的对齐方式
对于指定的对齐方式,其成员的地址偏移以及结构的总的大小也有下面三个约束条件
- 结构体第一个成员的地址和结构体的首地址相同
- 结构体每个成员的地址偏移需要满足:N大于等于该成员的大小,那么该成员的地址偏移需满足默认对齐方式(地址偏移是其成员大小的整数倍);N小于该成员的大小,那么该成员的地址偏移是N的整数倍。
- 结构体总的大小需要时N的整数倍,如果不是需要在结构体的末尾进行填充。
- 如果N大于结构体成员中最大成员的大小,则N不起作用,仍然按照默认方式对齐。