分享

【程序员一枚】C++基础之内存对齐

 pgj555 2014-04-29

今天又遇到一个恶搞问题,内存对齐

为什么会出现内存对齐?

效率问题,对于结构体,访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

为什么要考虑内存对齐?

跨平台,跨语言,跨编译器的时候因为内存对齐的原因可能造成设置的数据和获取的数据不同。

举几个简单的例子,假如您在32位操作系统下将上面的结构体直接以取地址的方式把该结构体写入到一个文件中,再在64位操作系统下读取,直接将内存付给这个结构体,就会出现问题。

再比如我使用上面的结构体编译一个C++程序,直接将该结构体对应的内存发送给一个Java程序,Java程序在读取这段数据的时候就麻烦了。

所以为了避免内存对齐,我建议自己定义数据,显式的将结构体中的数据按照既定的规则写到数据流中。虽然这样可以避免内存对齐,但是大家也应该了解一下内存对齐。

内存对齐和编译器有关。

编译器有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma
pack(n)n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。

VS2008的对齐系数默认为8

举例说明:

struct 
Test

{

    char  a;

    short  b;

    int c;

};

int main()

{

    Test 
    test;

    test.a = 0x12;

    test.b = 0x1234;

    test.c = 0x12345678;

    int size = sizeof(Test);

   Test *point = &test;

   return 0;

}

运行之后得到:

size8

内存排列方式为:

 

 
 

因为我的电脑的CPUintel的,所以在内存中小端存储,也就是你看到的c是按照byte倒叙存储的(CPU大小端问题将会在以后的章节中详细说明)

但是在中间有一byte的空白内存,这就是编译器内存对齐造成的。

而如果数据结构是这样的(ac的顺序变了):

struct 
Test

{

    char a;

    int c;

    short b;

};

运行之后的结果是:

size的值为12

内存的排列方式为:

 
 

 

内存对齐的规则:

VS2008默认#pragma pack(n)中的n=8

struct 
Test

{

char a; n=8 char=1 , 对齐起始地址为1的倍数,所以a的起始内存地址为0,内存区间[0,0]

short b; n=8 short=2, 对齐起始地址为2的倍数,所以b的起始内存地址为2,内存区间[2,3]

int c; n=8 int=4, 对齐起始地址为4的倍数,所以b的起始内存地址为4,内存区间[4,7]

};

#pragma 
pack(1)

struct 
Test

{

char a; n=1 char=1 ,对齐起始地址为1的倍数,所以a的起始内存地址为0,内存区间[0,0]

short b; n=1 short=2,对齐起始地址为1的倍数,所以b的起始内存地址为1,内存区间[1,2]

int c; n=1 int=4, 对齐起始地址为1的倍数,所以b的起始内存地址为3,内存区间[3,6]

};

还存在一种情况:

VS2008默认#pragma pack(n)中的n=8

struct 
Test

{

double e;

char f;

};

int size =sizeof(Test); size的值为16   (注:补齐,保证最终大小是8的倍数)

#pragma 
pack(4)

struct 
Test

{

double e;

char f;

};

int size =sizeof(Test); size的值为12  (注:补齐,保证最终大小是4的倍数)

上面两种情况的内存排列是一致的,但是大小是不一样的。

据说GCCVS2008是不一样的,没有测试

下一次有时间试一下。

对于内存对齐的问题,我的建议就是显式组织数据,尤其对于网络数据传输和读写文件,不要直接取地址。

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多