56

我正在使用 32 位机器,所以我想内存对齐应该是 4 个字节。假设我有这个结构:

typedef struct {
    unsigned short v1;
    unsigned short v2;
    unsigned short v3;
} myStruct;

普通添加的大小是 6 个字节,我想对齐的大小应该是 8,但sizeof(myStruct)返回我 6。

但是,如果我写:

typedef struct {
    unsigned short v1;
    unsigned short v2;
    unsigned short v3;
    int i;
} myStruct;

普通添加大小为 10 个字节,对齐大小应为 12,这次是sizeof(myStruct) == 12.

有人可以解释有什么区别吗?

4

10 回答 10

58

至少在大多数机器上,类型只与类型本身一样大的边界对齐[编辑:你不能真正要求任何“更多”对齐,因为你必须能够创建数组,并且你无法将填充插入数组]。在您的实现中,short显然是 2 个字节和int4 个字节。

这意味着您的第一个结构与 2 字节边界对齐。由于所有成员都是 2 个字节,因此在它们之间没有插入填充。

第二个包含一个 4 字节的项目,它与 4 字节的边界对齐。由于它前面有 6 个字节,因此在v3和之间插入了 2 个字节的填充,在 si中提供了 6 个字节的数据,在shorts 中提供了两个字节的填充,在 the 中提供了另外 4 个字节的数据,int总共 12 个。

于 2011-03-25T17:19:04.857 回答
23

忘记拥有不同的成员,即使您编写了两个成员完全相同的结构,不同之处在于它们声明的顺序不同,那么每个结构的大小可以(并且通常是)不同的。

例如,看到这个,

#include <iostream>
using namespace std;
struct A
{
   char c;
   char d;
   int i; 
};
struct B
{
   char c;
   int i;   //note the order is different!
   char d;
};
int main() {
        cout << sizeof(A) << endl;
        cout << sizeof(B) << endl;
}

用 编译它gcc-4.3.4,你会得到这个输出:

8
12

也就是说,即使两个结构具有相同的成员,大小也是不同的!

Ideone 的代码:http: //ideone.com/HGGVl

底线是标准没有讨论应该如何进行填充,因此编译器可以自由地做出任何决定,并且您不能假设所有编译器都做出相同的决定。

于 2011-03-25T17:23:42.237 回答
13

默认情况下,值根据其大小对齐。所以像 a 这样的 2 字节值short在 2 字节边界上对齐,像 an 这样的 4 字节值int在 4 字节边界上对齐

在您的示例中,之前添加了 2 个字节的填充i以确保i落在 4 字节的边界上。

(整个结构在至少与结构中最大值一样大的边界上对齐,因此您的结构将与 4 字节边界对齐。)

实际规则因平台而异 -数据结构对齐的 Wikipedia 页面有更多详细信息。

编译器通常允许您通过(例如)#pragma pack指令控制打包。

于 2011-03-25T17:20:25.530 回答
8

假设:

sizeof(unsigned short) == 2
sizeof(int)            == 4

然后我个人会使用以下内容(您的编译器可能会有所不同):

unsigned shorts are aligned to 2 byte boundaries
int will be aligned to 4 byte boundaries.


typedef struct
{
   unsigned short v1;    // 0 bytes offset
   unsigned short v2;    // 2 bytes offset
   unsigned short v3;    // 4 bytes offset
} myStruct;              // End 6 bytes.


// No part is required to align tighter than 2 bytes. 
// So whole structure can be 2 byte aligned.

typedef struct
{
    unsigned short v1;      // 0 bytes offset
    unsigned short v2;      // 2 bytes offset
    unsigned short v3;      // 4 bytes offset
    /// Padding             // 6-7 padding (so i is 4 byte aligned)
    int i;                  // 8 bytes offset
} myStruct;                 // End 12 bytes

// Whole structure needs to be 4 byte aligned.
// So that i is correctly aligned.
于 2011-03-25T18:41:38.990 回答
5

首先,虽然填充的细节由编译器决定,但操作系统也对对齐要求施加了一些规则。此答案假定您使用的是 gcc,尽管操作系统可能会有所不同

要确定给定结构及其元素占用的空间,您可以遵循以下规则:

首先,假设结构总是从所有数据类型正确对齐的地址开始。

然后对于结构中的每个条目:

  • 所需的最小空间是由 给出的元素的原始大小sizeof(element)
  • 元素的对齐要求是元素基本类型的对齐要求。值得注意的是,这意味着数组的对齐要求与char[20]普通数组的要求相同char

最后,结构整体的对齐要求是其每个元素的对齐要求的最大值。

gcc 将在给定元素之后插入填充,以确保下一个元素(或者如果我们谈论最后一个元素,则为结构)正确对齐。它永远不会重新排列结构中元素的顺序,即使这样会节省内存。

现在对齐要求本身也有点奇怪。

  • 32 位 Linux 要求 2 字节数据类型具有 2 字节对齐(它们的地址必须是偶数)。所有较大的数据类型必须具有 4 字节对齐(地址以、 或0x0结尾0x4)。请注意,这也适用于大于 4 个字节的类型(例如和)。0x80xCdoublelong double
  • 32 位 Windows 更严格,如果一个类型的大小为 K 字节,则它必须是 K 字节对齐的。这意味着 adouble只能放在以0x0or结尾的地址0x8。唯一的例外是long double它仍然是 4 字节对齐的,即使它实际上是 12 字节长。
  • 对于 Linux 和 Windows,在 64 位机器上,K 字节类型必须是 K 字节对齐的。同样,这long double是一个例外,必须是 16 字节对齐的。
于 2011-03-25T18:41:05.293 回答
3

每种数据类型都需要在其自身大小的内存边界上对齐。所以 ashort需要在 2 字节边界上对齐,并且 aint需要在 4 字节边界上。同样, along long需要位于 8 字节边界上。

于 2011-03-25T17:18:15.473 回答
2

第二个原因sizeof(myStruct)是在 32 位边界12之间插入v3i对齐的填充。i它有两个字节。

维基百科合理清楚地解释了填充和对齐。

于 2011-03-25T17:17:33.427 回答
2

在您的第一个结构中,由于每个项目都是 size short,因此整个结构可以在short边界上对齐,因此不需要在最后添加任何填充。

在第二个结构中, int (大概是 32 位)需要是字对齐的,因此它在v3和之间插入填充i以 align i

于 2011-03-25T17:20:00.087 回答
0

该标准并没有说明具有完整类型的结构的布局——这取决于编译器。它决定它需要 int 从边界开始访问它,但由于它必须对短路进行子边界内存寻址,因此无需填充它们

于 2011-03-25T17:18:51.430 回答
0

听起来它是根据每个 var 的大小与边界对齐的,因此地址是被访问大小的倍数(因此短裤对齐到 2,整数对齐到 4 等),如果您在之后移动了其中一个短裤int,sizeof(mystruct)应该是 10。当然,这一切都取决于正在使用的编译器以及它依次使用的设置。

于 2011-03-25T17:21:05.983 回答