1.9结构体在内存中的存储与字节对齐
结构体的大小本应该是所有成员的类型大小之和,但因为引入字节对齐的缘故,导致结构体真实的大小一般大于等于所有成员大小之和。成员在结构体中的位置对结构体的大小可能会造成影响。
对于MyStruct1,内存分配如下:
1.9.1 结构体字节对齐机制
出于效率的考虑,C语言引入了字节对齐机制,结构体变量占据的内存单元的个数应当大于等于其内部所有数据成员占据内存单元数的和。
一般来说,不同的编译器字节对齐机制有所不同,但还是有以下3条通用准则:
(1) 结构体的大小能够整除其最宽基本类型成员的大小;
(2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
(3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
说明:
字节对齐第(3)条准则提及最宽基本类型的概念,所谓基本类型是指像char、short、int、float、double这样的内置数据类型。“数据宽度”就是指其sizeof的大小。诸如结构体、共用体和数组等都不是基本数据类型。
注意:如下示例都是在32位编译环境下测试的。
示例:
struct MyStruct1
{
char ch1;
char ch2; // 两者不足4,填充
int i1; // 4
int i2; // 4
};
sizeof(struct MyStruct1) // 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
struct MyStruct2
{
int i1; // 4
char ch1;
char ch2; // 两者不足4,填充
char chs[11]; // 12
};
sizeof(struct MyStruct2) // 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
struct MyStruct3
{
char ch1; //4
int i; // 4
char chs[9]; // 12
};
sizeof(struct MyStruct3) // 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
struct MyStruct4
{
char ch1; // 8
double db; // 8
int i; // 4
char chs[9]; // 12
};
sizeof(struct MyStruct4) // 32
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
struct MyStruct5
{
int i; // 4
short s; // 4
char chs[11]; // 12
};
sizeof(struct MyStruct5) // 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
1.9.2 结构体嵌套结构体
当结构体嵌套结构体时,字节对齐所参考的最宽基本成员不局限与当前结构体,可能是所包含的结构体中的最宽基本成员。
struct MyStruct7
{
int i;
char ch;
};
struct MyStruct8
{
struct MyStruct7 s; //此时参考的是MyStruct7中最宽的int
char ch;
};
printf("%d\n", sizeof(struct MyStruct8)); //输出12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
1.9.3 offsetof(s,m)
在stddef.h 头文件中,可以使用offsetof来看某个元素相对结构体的起始位置偏移了多少个字节
typedef struct MyStruct6
{
char ch;
int i;
double d;
};
...
printf("%d,%d\n", sizeof(struct MyStruct6),offsetof(struct MyStruct6, i)); //输出 16,4
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
1.9.4 手动设置成员对齐
在vs2015中,依次点击 项目 –> 结构体 属性 –> C/C++ –> 代码生成,在如下界面的结构成员对齐选项进行设置:
例:
struct MyStruct7
{
int i;
char ch;
};
...
printf("%d\n", sizeof(struct MyStruct7));
// 当我们设置对齐字节为2字节时
// 输出为6
// 当我们设置对齐字节为8字节时
// 输出为8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
注意: 当我么设置的字节数小于结构体最宽基本成员时,以设置的为主;当设置的字节数大于结构体最宽成员时,以结构体最宽基本成员的大小为主:总结即是两者按最小的来计算