C结构中的内存对齐

时间:2011-03-25 17:14:32

标签: c++ c memory-alignment structure-packing

我正在使用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

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

10 个答案:

答案 0 :(得分:49)

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

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

第二个包含一个4字节的项,它与4字节边界对齐。由于它前面有6个字节,因此在v3i之间插入2个字节的填充,在short s中提供6个字节的数据,填充两个字节,再增加4个字节int中的数据总共为12个。

答案 1 :(得分:19)

忘记拥有不同的成员,即使你写了两个结构完全相同的结构,差异是它们声明的顺序不同,那么每个结构的大小可以(通常是)不同。

例如,请参阅此内容,

#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

最重要的是,标准没有讨论应该如何填充,因此编译器可以自由做出任何决定而你不能假设所有编译器做出同样的决定。

答案 2 :(得分:13)

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

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

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

实际规则根据平台而有所不同 - Data structure alignment上的维基百科页面有更多详细信息。

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

答案 3 :(得分:5)

假设:

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.

答案 4 :(得分:4)

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

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

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

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

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

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

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

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

  • 32位Linux要求2字节数据类型具有2字节对齐(它们的地址必须是偶数)。所有较大的数据类型必须具有4字节对齐(地址以0x00x40x80xC结尾。请注意,这也适用于大于4个字节的类型(例如doublelong double)。
  • 32位Windows更严格,如果类型的大小为K字节,则必须与K字节对齐。这意味着double只能放置在以0x00x8结尾的地址。唯一的例外是long double仍然是4字节对齐,即使它实际上是12字节长。
  • 对于Linux和Windows,在64位计算机上,K字节类型必须与K字节对齐。同样,long double是一个例外,必须是16字节对齐。

答案 5 :(得分:2)

每种数据类型都需要在其自身大小的内存边界上对齐。因此short需要在2字节边界上对齐,int需要在4字节边界上。同样,long long需要在8字节边界上。

答案 6 :(得分:2)

在第一个结构中,由于每个项的大小为short,因此整个结构可以在short边界上对齐,因此不需要在末尾添加任何填充。

在第二个结构中,int(可能是32位)需要进行字对齐,以便在v3i之间插入填充以对齐i

答案 7 :(得分:1)

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

Wikipedia合理清楚地解释了填充和对齐。

答案 8 :(得分:0)

标准没有说明具有完整类型的结构的布局 - 它取决于编译器。它决定它需要int来开始边界才能访问它,但由于它必须对短路进行子边界内存寻址,所以不需要填充它们

答案 9 :(得分:0)

听起来它基于每个var的大小与bounderies对齐,因此地址是所访问大小的倍数(因此短路对齐到2,整数对齐到4等),如果你移动了其中一个在int之后的短路,sizeof(mystruct)应该是10.当然这完全取决于正在使用的编译器以及它依次使用的设置。