c++ - C/C++ 中的内存对齐

标签 c++ c memory memory-management

我正在阅读《游戏编码完整》第 4 版。有一个关于内存对齐的话题。在下面的代码中,作者说第一个结构真的很慢,因为它既不是位对齐的也不是字节对齐的。第二个不是位对齐的,而是字节对齐的。最后一个很快,因为它是两者兼而有之。他说如果没有 pragma,编译器将对齐内存本身,这会导致内存浪费。我真的无法计算。

这是文本的一部分:-

If the compiler were left to optimize SlowStruct by adding unused bytes, each structure would be 24 bytes instead of just 14. Seven extra bytes are padded after the first char variable, and the remaining bytes are added at the end. This ensures that the entire structure always starts on an 8-byte boundary. That’s about 40 percent of wasted space, all due to a careless ordering of member variables.

这是粗体的结尾行:-

Don’t let the compiler waste precious memory space. Put some of your brain cells to work and align your own member variables.

请显示计算结果并更清楚地解释填充概念。

代码:-

#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)

最佳答案

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

ReallySlowStruct 如果使用的编译器支持非字节对齐的结构成员,则“d”的开头将位于结构第一个字节的第七位。听起来非常适合节省内存。问题在于,C 不允许位寻址。所以要将 newValue 保存到“d”成员,编译器必须做大量的移位操作:将“newValue”的前两位保存在 byte0 中,向右移动 6 位。然后将“newValue”向左移动两位并从字节 1 开始保存。字节 1 是一个非对齐的内存位置,这意味着批量内存传输指令将不起作用,编译器必须一次保存每个字节。

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

快速结构 这与目标机器上 int 的大小对齐。 “d”占用 8 个字节。因为字符都捆绑在一个地方,所以编译器不会填充它们,也不会浪费空间。每个字符只有 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/en-us/cpp/cpp/alignment-cpp-declarations?view=vs-2019

关于c++ - C/C++ 中的内存对齐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41719845/

相关文章:

Python C++ 绑定(bind)类型向上转换问题

c++ - cv::viz::Widget 是否可点击? (OpenCV C++)

C++ 创建巨大的 vector

c - 为什么我不能在 BSS 中获取静态变量?

ios - 弹出一个具有异步任务的 viewController,它会在 iOS 任务完成之前销毁吗?

c++ - 应用程序中的自定义库,我是否必须两次包含库依赖 header ?

c++ - 如何更改指针的 cout 格式

c - 一个函数中的局部变量的值在另一个函数中使用(C 编程)

c 从已使用的端口读取 udp 数据

c - 具有巨大局部变量的堆栈溢出?