C / C ++中的内存对齐

问题描述 投票:2回答:1

我正在阅读Game Coding Complete第4版。有一个关于内存对齐的话题。在下面的代码中,作者说第一个结构非常慢,因为它既不是位对齐也不是字节对齐。第二个不是位对齐而是字节对齐。最后一个很快,因为它们都是。他说,如果没有编译指示,编译器将调整内存本身,这会导致内存浪费。我无法真正得到计算结果。

这是文本的一部分: -

如果编译器通过添加未使用的字节来优化SlowStruct,则每个结构将是24个字节而不是14个。在第一个char变量之后填充七个额外字节,并在末尾添加剩余字节。这可确保整个结构始终以8字节边界开始。这大约是浪费空间的40%,所有这些都归因于成员变量的粗心排序。

这是以粗体显示的结尾行: -

不要让编译器浪费宝贵的内存空间。让你的一些脑细胞工作并对齐你自己的成员变量。

请告诉我计算并更清楚地解释填充概念。

码:-

#pragma pack(push, 1)
struct ReallySlowStruct
{
    char c : 6;
    __int64 d : 64;
    int b : 32;
    char a : 8;
};

struct SlowStruct
{
    char c;
    __int64 d;
    int b;
    char a;
};

struct FastStruct
{
   __int64 d;
   __int b;
   char a;
   char c;
   char unused[2];
};
#pragma pack(pop)
c++ c memory memory-management
1个回答
6
投票

本书中给出的示例高度依赖于使用的编译器和计算机体系结构。如果您在自己的程序中测试它们,您可能会得到与作者完全不同的结果。我将假设一个64位架构,因为作者也从我在描述中阅读的内容中做到了。让我们逐个看一下这些例子:

ReallySlowStruct如果使用的编译器支持非字节对齐的struct成员,则“d”的开头将位于struct的第一个字节的第七位。对于节省内存非常有用。这个问题是,C不允许位地址。因此,为了将newValue保存到“d”成员,编译器必须执行大量的位移操作:在byte0中保存“newValue”的前两位,向右移位6位。然后将“newValue”向左移两位并从字节1开始保存。字节1是非对齐的存储单元,这意味着大容量存储器传输指令不起作用,编译器必须一次保存每个字节。

SlowStruct它变得更好。编译器可以摆脱所有的bit-fiddling。但是写“d”仍然需要一次写入每个字节,因为它没有与原始的“int”大小对齐。 64位系统上的本机大小为8.因此,每个不能被8除的的内存地址一次只能访问一个字节。更糟糕的是,如果我关闭打包,我将浪费大量的内存空间:每个后跟一个int的成员都将填充足够的字节,让整数从一个可被8分割的内存位置开始。在这种情况下:char a和c都占用8个字节。

FastStruct这与目标机器上的int大小对齐。 “b”占用8个字节。因为字符都捆绑在一个地方,所以编译器不会填充它们并且不会浪费空间。字符每个只有1个字节,所以我们不需要填充它们。完整的结构总共增加了16个字节。可除以8,因此不需要填充。

© www.soinside.com 2019 - 2024. All rights reserved.