struct和union占用内存详解

来源:互联网 发布:中学生网络成瘾 编辑:程序博客网 时间:2024/06/05 03:16

一、struct与union占用内存

1.1 struct内存对齐

1.1.1 问题引入

代码如下:

#include <iostream>using namespace std;struct Test_A{    char x;    char y;    int z;};struct Test_B{    char x;    int z;    char y;};struct Test_C{    int z;    char x;    char y;};int main(){    struct Test_A a;    memset(&a, 0, sizeof(a));    struct Test_B b;    memset(&b, 0, sizeof(b));    struct Test_C c;    memset(&c, 0, sizeof(c));    // Print the memory size of the struct    cout << sizeof(a) << endl;  // 8 Bytes    cout << sizeof(b) << endl;  // 12 Bytes    cout << sizeof(c) << endl;  // 8 Bytes    return 0;}

上述代码输出结果为:
sizeof(a) = 8 Bytes;
sizeof(b) = 12 Bytes;
sizeof(c) = 8 Bytes.
为什么只是改变了结构体中的成员顺序,结果就不一样了?因为编译器内存对齐机制。

1.1.2 内存对齐

1. 内存对齐问题存在于:

理解struct和union等复合结构在内存中的分布。

2. 内存对齐的目的:

  • 平台原因(移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  • 提高内存访问效率
    CPU在访问内存时是按块读取的。块的大小称为memory access granularity(内存存取粒度)。访问已对齐的内存只需1个指令周期;而访问未对齐的内存需2个指令周期,可能需要额外的数据选取、剔除操作,大大降低了效率。
    常见编译器的内存存取粒度如下表,在windows(32)/VC6.0下,n的取值可以为1、2、4、8,默认情况下为8。在linux(32)/GCC下,n的取值只能为1、2、4,默认情况下为4
    表1

3. 内存对齐的定义:

计算机系统对基本类型数据在内存中存放的位置有限制,要求这些数据的首地址是内存存取粒度n(通常为4或8)的倍数,这就是所谓的内存对齐。内存对齐是编译器做的事情。这个值n在不同的CPU平台下、不同的编译器下表现也有所不同。可通过预编译命令`#pragma pack(n)`,n = 1,2,4,8,16.来进行修改。 

4. 内存对齐的规则:

  • 第一条:第1个数据成员置于offset为0的位置,每个数据类型的偏移量必须是min(#pragma pack(n)中的n, 数据自身占用的字节数)的倍数;
  • 第二条:数据成员对齐(局部对齐)后,结构体(联合体)还需进行整体对齐,对齐将按照min(#pragma pack(n)中的n, 数据成员中的最大长度)进行,即结构体(联合体)占用内存应为上述min的倍数。

5. 实例演示:

#include <iostream>using namespace std;# pragma pack(1)    // 设置对齐系数分别为1、2、4、8、16struct Test{    char x1;        // 1 Byte    double x2;      // 8 Bytes    short x3;       // 2 Bytes    float x4;       // 4 Bytes    char x5;        // 1 Byte};int main(){    Test test;    cout << sizeof(test) << endl;    return 0;}

代码输出结果为:
n = 1时,输出16 Bytes;
n = 2时,输出18 Bytes;
n = 4时,输出24 Bytes;
n = 8时,输出32 Bytes;
n = 16时,输出32 Bytes。
分析如下:
注意:按n对齐的意思是,占用地址的偏移量为n的倍数
n = 1时,首先使用规则1进行局部对齐:
x1,1 ≤ 1,按1对齐,占用0;
x2,8 > 1,按1对齐,占用1,2,3,4,5,6,7,8;
x3,2 > 1,按1对齐,占用9,10;
x4,4 > 1,按1对齐,占用11,12,13,14;
x5,1 ≤ 1,按1对齐,占用15;
最后使用规则2进行整体对齐:
x2占用的内存最大,为8 Bytes,8 > 1,整体按照1对齐,16 % 1 = 0。所以在n = 1 时,struct Test占用16 Bytes。如下图:
图1

n = 2时,首先使用规则1进行局部对齐:
x1,1 ≤ 2,按1对齐,占用0;
x2,8 > 2,按2对齐,占用2,3,4,5,6,7,8,9;
x3,2 ≤ 2,按2对齐,占用10,11;
x4,4 > 2,按2对齐,占用,12,13,14,15;
x5,1 ≤ 2,按1对齐,占用16;
最后使用规则2进行整体对齐:
x2占用的内存最大,为8 Bytes,8 > 2,整体按照2对齐,17 % 2 != 0,补齐到18 Bytes。所以在n = 2 时,struct Test占用18 Bytes。如下图:
图2

n = 4时,首先使用规则1进行局部对齐:
x1,1 ≤ 4,按1对齐,占用0;
x2,8 > 4,按4对齐,占用4,5,6,7,8,9,10,11;
x3,2 ≤ 4,按2对齐,占用12,13;
x4,4 ≤ 4,按4对齐,占用16,17,18,19;
x5,1 ≤ 4,按1对齐,占用20;
最后使用规则2进行整体对齐:
x2占用的内存最大,为8 Bytes,8 > 4,整体按照4对齐,21 % 4 != 0,补齐到24字节。所以在n = 4 时,struct Test占用24 Bytes。如下图:
图3

n = 8时,首先使用规则1进行局部对齐:
x1,1 ≤ 8,按1对齐,占用0;
x2,8 ≤ 8,按8对齐,占用8,9,10,11,12,13,14,15;
x3,2 ≤ 8,按2对齐,占用16,17;
x4,4 ≤ 8,按4对齐,占用20,21,22,23;
x5,1 ≤ 8,按1对齐,占用24;
最后使用规则2进行整体对齐:
x2占用的内存最大,为8 Bytes,8 ≤ 8,整体按照8对齐,25 % 8 != 0,补齐到32字节。所以在n = 8 时,struct Test占用32 Bytes。如下图:
图4

n = 16时,首先使用规则1进行局部对齐:
x1,1 ≤ 16,按1对齐,占用0;
x2,8 ≤ 16,按8对齐,占用8,9,10,11,12,13,14,15;
x3,2 ≤ 16,按2对齐,占用16,17;
x4,4 ≤ 16,按4对齐,占用20,21,22,23;
x5,1 ≤ 16,按1对齐,占用24;
最后使用规则2进行整体对齐:
x2占用的内存最大,为8 Bytes,8 ≤ 16,整体按照8对齐,25 % 16 != 0,补齐到32字节。所以在n = 16 时,struct Test占用32 Bytes。如下图:
图5

1.2 Union内存对齐

1.2.1 什么是union

翻译为共用体或者联合体。union的成员变量都是在同一地址存放的,即使用覆盖技术,使成员变量相互覆盖,共同占用同一段内存。
union具有以下特点:

  • 同一个内存段可以用来存放多种不同类型的成员,但是同一时刻只有一个成员起作用;
  • union中起作用的是最后一个存放的成员,在存入一个新成员后,原有的变量就会失去作用;
  • union中所有成员的起始地址相同。

1.2.2 实例

定义如下的union:

#include <iostream>using namespace std;union test{     char mark;     long num;     float score;}a;int main(){     // cout<<a<<endl; // wrong     a.mark = 'b';     cout<<a.mark<<endl; // 输出'b'     cout<<a.num<<endl; // 98 字符'b'的ACSII值     cout<<a.score<<endl; // 输出错误值     a.num = 10;     cout<<a.mark<<endl; // 输出换行('\n'的ASCII值为10)     cout<<a.num<<endl; // 输出10     cout<<a.score<<endl; // 输出错误值     a.score = 10.0;     cout<<a.mark<<endl; // 输出空     cout<<a.num<<endl; // 输出错误值     cout<<a.score<<endl; // 输出10     return 0;}

1.2.3 占用内存

在32位机器上,sizeof(union test)结果为4。其内存结构示意图如下:
图6

对于union所占用的内存大小,需要考虑内存对齐的问题,但是大体来说,union变量所占用的内存长度等于最长的成员的内存长度
与之对比,大体上来说,结构体struct所占用的内存为各个成员的占用的内存之和(当然也需要考虑内存对齐的问题了)。定义以下结构体struct test2:

struct test2{     char mark;     long num;     float score;}b;

其内存结构如下:
图7

二、参考内容:

[1] C、C++内存对齐
http://www.jellythink.com/archives/413
[2] C、C++中union用法总结
http://www.jellythink.com/archives/468
[3] 内存对齐的规则以及作用
http://www.cppblog.com/snailcong/archive/2009/03/16/76705.html

原创粉丝点击