这本书中的例子高度依赖于所用的编译器和计算机体系结构。如果您在自己的程序中测试它们,可能会得到与作者完全不同的结果。我将假设64位架构,因为作者也是这样做的,从我在描述中读到的。
让我们逐个看一下这些例子:
ReallySlowStruct如果使用的编译器支持非字节对齐结构成员,则“d”的起始位置将位于结构的第一个字节的第七位。对于节省内存来说听起来很不错。但问题是,C语言不允许按位寻址。因此,为了将newValue保存到“d”成员中,编译器必须执行大量的位移操作:将“newValue”的前两位保存在byte0中,向右移动6位。然后将“newValue”左移两位并从byte 1开始保存它。Byte 1是非对齐的存储位置,这意味着批量内存传输指令无法工作,编译器必须一次保存每个字节。
SlowStruct情况有所改善。编译器可以摆脱所有位操作。但是,写入“d”仍需要一次一个字节地写入,因为它不对齐到本机“int”大小。64位系统上的本机大小为8。因此,每个不能被8整除的内存地址只能一次访问一个字节。更糟糕的是,如果我关闭打包,则会浪费大量的内存空间:每个紧随整数后面的成员都将使用足够的字节进行填充,以使整数从可被8整除的内存位置开始。在这种情况下:char a和c都将占用8个字节。
FastStruct 它对齐到目标机器上int的大小。正如它应该占用8个字节的“d”。因为所有字符都集中在一起,编译器不会对它们进行填充并浪费空间。字符只有1个字节,所以我们不需要对它们进行填充。完整结构的总大小为16个字节。可被8整除,因此不需要填充。
在大多数情况下,您无需关心对齐问题,因为默认对齐已经是最优的。但是,在某些情况下,通过为数据结构指定自定义对齐方式,可以实现显著的性能提升或节省内存。
在内存空间方面,编译器以一种自然对齐结构的方式进行填充每个元素。
struct x_
{
char a; // 1 byte
int b; // 4 bytes
short c; // 2 bytes
char d; // 1 byte
} bar[3];
struct x_
被编译器填充,因此变成:
// Shows the actual memory layout
struct x_
{
char a; // 1 byte
char _pad0[3]; // padding to put 'b' on 4-byte boundary
int b; // 4 bytes
short c; // 2 bytes
char d; // 1 byte
char _pad1[1]; // padding to make sizeof(x_) multiple of 4
} bar[3];
来源: https://learn.microsoft.com/zh-cn/cpp/cpp/alignment-cpp-declarations?view=vs-2019