Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1097116
  • 博文数量: 242
  • 博客积分: 10209
  • 博客等级: 上将
  • 技术积分: 3028
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-12 09:27
文章分类

全部博文(242)

文章存档

2014年(1)

2013年(1)

2010年(51)

2009年(65)

2008年(124)

我的朋友

分类: C/C++

2009-02-16 20:56:29

一、内存对齐的原因
大部分的参考资料都是如是说的:
1
、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2
、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

二、对齐规则

自然条件下:在x86
    * A char (one byte) will be 1-byte aligned.
    * A short (two bytes) will be 2-byte aligned.
    * An int (four bytes) will be 4-byte aligned.
    * A float (four bytes) will be 4-byte aligned.
    * A double (eight bytes) will be 8-byte aligned on Windows and 4-byte aligned on Linux.

默认条件下编译器就按上述规则对齐。程序员可以通过预编译命令#pragma pack(n)n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的对齐系数

规则:
1
、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
2
、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
3
、结合12颗推断:当#pragma packn值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。


三、如何避免内存对齐的影响

    
那么,能不能既达到提高性能的目的,又能节约一点空间呢?有一点小技巧可以使用。比如我们可以将上面的结构改成:

struct bar
{
     char c1;
     char c2;
     short s;
     int i;
};
    
这样一来,每个成员都对齐在其自然边界上,从而避免了编译器自动对齐。在这个例子中,sizeof(bar) == 8

    
这个技巧有一个重要的作用,尤其是这个结构作为API的一部分提供给第三方开发使用的时候。第三方开发者可能将编译器的默认对齐选项改变,从而造成这个结构在你的发行的DLL中使用某种对齐方式,而在第三方开发者哪里却使用另外一种对齐方式。这将会导致重大问题。

 

四、如何使用c/c++中的对齐选项

  
   vc6中的编译选项有 /Zp[1|2|4|8|16] /Zp1表示以1字节边界对齐,相应的,/Zpn表示以n字节边界对齐。n字节边界对齐的意思是说,一个成员的地址必须安排在成员的尺寸的整数倍地址上 或者是n的整数倍地址上,取它们中的最小值。也就是:
     min ( sizeof ( member ),   n)
    
实际上,1字节边界对齐也就表示了结构成员之间没有空洞。
     /Zpn
选项是应用于整个工程的,影响所有的参与编译的结构。
    
要使用这个选项,可以在vc6中打开工程属性页,c/c++页,选择Code Generation分类,在Struct member alignment可以选择。

    
要专门针对某些结构定义使用对齐选项,可以使用#pragma pack编译指令。指令语法如下:
#pragma pack( [ show ] | [ push | pop ] [, identifier ] , n   )
    
意义和/Zpn选项相同。比如:

#pragma pack(1)
struct foo_pack
{
     char c1;
     short s;
     char c2;
     int i;
};
#pragma pack()

 

五、栈内存对齐

    我们可以观察到,在vc6中栈的对齐方式不受结构成员对齐选项的影响。(本来就是两码事)。它总是保持对齐,而且对齐在4字节边界上。

    验证代码

    #include

    struct foo
    {
        char c1;
        short s;
        char c2;
        int i;
    };

    struct bar
    {
        char c1;
        char c2;
        short s;
        int i;
    };

    #pragma pack(1)
    struct foo_pack
    {
        char c1;
        short s;
        char c2;
        int i;
    };
    #pragma pack()


    int main(int argc, char* argv[])
    {
        char c1;
        short s;
        char c2;
        int i;

    struct foo a;
    struct bar b;
    struct foo_pack p;

    printf("stack c1 %p, s %p, c2 %p, i %p\n",
        (unsigned int)(void*)&c1 - (unsigned int)(void*)&i,
        (unsigned int)(void*)&s - (unsigned int)(void*)&i,
        (unsigned int)(void*)&c2 - (unsigned int)(void*)&i,
        (unsigned int)(void*)&i - (unsigned int)(void*)&i);

    printf("struct foo c1 %p, s %p, c2 %p, i %p\n",
        (unsigned int)(void*)&a.c1 - (unsigned int)(void*)&a,
        (unsigned int)(void*)&a.s - (unsigned int)(void*)&a,
        (unsigned int)(void*)&a.c2 - (unsigned int)(void*)&a,
        (unsigned int)(void*)&a.i - (unsigned int)(void*)&a);

    printf("struct bar c1 %p, c2 %p, s %p, i %p\n",
        (unsigned int)(void*)&b.c1 - (unsigned int)(void*)&b,
        (unsigned int)(void*)&b.c2 - (unsigned int)(void*)&b,
        (unsigned int)(void*)&b.s - (unsigned int)(void*)&b,
        (unsigned int)(void*)&b.i - (unsigned int)(void*)&b);

printf("struct foo_pack c1 %p, s %p, c2 %p, i %p\n",
        (unsigned int)(void*)&p.c1 - (unsigned int)(void*)&p,
        (unsigned int)(void*)&p.s - (unsigned int)(void*)&p,
        (unsigned int)(void*)&p.c2 - (unsigned int)(void*)&p,
        (unsigned int)(void*)&p.i - (unsigned int)(void*)&p);

    printf("sizeof foo is %d\n", sizeof(foo));
    printf("sizeof bar is %d\n", sizeof(bar));
    printf("sizeof foo_pack is %d\n", sizeof(foo_pack));
       return 0;

    }运行结果如下:

stack c1 11, s 8, c2 7, i 0

struct foo c1 0, s 2, c2 4, i 8

struct bar c1 0, c2 1, s 2, i 4

struct foo_pack c1 0, s 1, c2 3, i 4

其中栈内存分布情况较为特殊,示意图如下:

阅读(1304) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~