数据内存对齐

内存对齐的作用:

  1. 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

  2. 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问只需要依次访问。

不同平台数据类型占用字节表:

数据类型 32位 64位 备注
char 1 1
short 2 2
int 4 4
long 4 8 32位于64位不同
float 4 4
char* 4 8 其他类型指针也是如此
long long 8 8
double 8 8
long double 10/12 10/16 有效字节10字节,为了内存对齐实际分配:32位12字节,64位16字节

结构体(struct)

结构体(struct)各成员各自拥有自己的内存,各自使用互不干涉,同时存在的,遵循内存对齐原则。

struct内存对齐规则:

  1. 结构体变量的起始地址能够被其最宽的成员大小整除
  2. 结构体每个成员相对于起始地址的偏移能够被其自身大小整除,如果不能则在前一个成员后面补充字节
  3. 结构体总体大小能够被最宽的成员的大小整除,如不能则在后面补充字节
  4. 程序中有#pragma pack(n)预编译指令,所有成员对齐以n字节为准(即偏移量是n的整数倍),不再考虑当前类型以及最大结构体类型。

实例:

//64位
struct C
{
    double t;	//8
    char b;		//1
    int a;		//4
    short c;	//2
};

t占8个字节,是b的1个字节的整数倍,不需要补充字节;

b占1个字节,不是a的4个字节的整数倍,所以b后面需要加3个补充字节;

a占4个字节,是c的2个字节的整数倍,不需要补充字节;

c占2个字节,字节数为5+1+3+4+2=18;

总字节数要为double类型所占8字节的整数倍;所以c后面增加6个补充字节。

所以sizeof(C)=24;


实例:

#pragma pack(1)

struct fun
{
    int i;		//4
    double d;	//8
    char c; 	//1
}

因为程序中有#pragma pack(1)预编译命令,所谓所占字节为1字节的整数倍。

总字节数为4+8+1=13

sizeof(fun)=13;


联合体(union)

联合体(union)各自成员共用一块内存空间,并且同时只有一个成员可以得到这这块内存的使用权(对该内存的读写),各变量共用一个内存首地址。

union内存对齐规则:

  1. 找到占用字节最多的成员
  2. union的字节数必须是占用字节最多的成员的的字节的倍数,而且需要能够容纳其他的成员。

实例:

typedef union
{
    long i;		//8
    int k[5];	//4*5
    char c;		//1
}D;

long为占字节最多的数据类型,占8个字节;所以该union字节数为8的整数倍;成员k占字节数为4*5=20。所以该union占字节数为24个字节。


更复杂的实例:

struct BU
{
    int number;             //4字节
    union UBffer
    {
        char buffer[13];    //填充3字节,该成员占16字节空间
        int number;
    }ubuf;
    int aa;                 //占4字节空间,当前偏移量已补齐为20
    double dou;             //占8字节空间
}bu;

sizeof(BU) = 4 + 13 + 3(补齐) + 4 + 8 = 32,分析方法类似,在计算aa的偏移量时,我们可以肯定的是一定是int类型的整数倍,由于不作任何缓冲补齐的情况下,number + buffer = 17字节,为了符合规则1,需要填充3个字节。

结构体BU稍微变换下aa和dou成员顺序,则结果就大不相同:

struct BC
{
    int number;             //4字节
    union UBffer
    {
        char buffer[13];    //填充7字节,该成员占20字节空间
        int number;
    }ubuf;
    double dou;             //占8字节空间,当前偏移量已补齐为24
    int aa;                 //占4字节空间,当前占用空间36字节,最大double类型,还需要根据规则2补齐
}bu;

此时sizeof(BC) = 4 + 13 + 7(规则1补齐) + 8 + 4 + 4(规则2补齐) = 40 (8的整数倍)


__attribute__ ((__packed__)) 关键字,该关键字的作用:可以让结构体,按照紧凑排列的方式,占用内存。

#include <stdio.h>
#include <iostream>
 
using namespace std;
 
struct test1 
{
    char c;
    int i;
};
 
struct __attribute__ ((__packed__)) test2 
{
    char c;
    int i;
};
 
int main()
{
    cout << "size of test1:" << sizeof(struct test1) << endl;
    cout << "size of test2:" << sizeof(struct test2) << endl;
}

运行结果:
sizeof (test1) = 8
sizeof (test2) = 5


参考:

sizeof

  1. 数组

int a[5] = {1,2,3,4,5};

sizeof(a) = 20	//数组a占用的内存空间
sizeof(*a) = 4	//指向的int类型
  1. 指针

char *p = "abcdefghijk";

sizeof(p) = 4	//指针占用4个字节的空间(32位机)
sizeof(*p) = 1	//指向的char类型