浅谈C语言中的内存对齐

来源:互联网 发布:lol末日人工智能投票 编辑:程序博客网 时间:2024/05/24 06:32

先看一下下面两段代码:

1:#include

int main(){

      struct st{

             short a;

             int b,c;

             char d;

      };

      printf("%d",sizeof(struct st));

      return 0;:

}

 

2:#include

int main(){

      struct st{

             int b,c;

             short a;

             char d;

      };

      printf("%d",sizeof(struct st));

      return 0;

}

可以看出,上面两段代码之间唯一的区别就是第四行和第五行调换了位置。代码的结果都是输出目标结构体所占的内存空间。我们都知道在不同系统中,同一数据类型所占字节数也不同。实验所用系统默认为short2,int4,char1。那么,上面两段代码的运行结果是什么呢?第一段代码结果为16,第二段结果为12。

两个结构体均由两个int,一个char和一个short组成,但结果并不是简单内存相加所得的11。而且仅仅调换结构体内部的代码顺序后,结构体的大小也随之改变。这正是由于C语言自然启用的内存对齐造成的。

当我们设置一个变量后,系统便会为他分配内存空间。Int就给4个,char就给一个,看起来很合理。但实际上,再分配内存空间时,系统还要考虑到寻址速度。系统的取值有一个特点,即是对一个变量的取值一次只会从该变量的内存大小的整数倍内存地址处开始取值,一次无法取完就两次。比如一个int类型的数据,起始地址为2,占用了2、3、4、5。那么当系统对他寻址时每次查找4个单位内存。第一次就会从0开始取值,取值为0、1、2、3的内容,0和1的内容并非想要的就会舍去,然后保存下2和3的内容。第二次取值会取4、5、6、7的内容,同样只保留4和5的内容。然后在两次取值后再把内容合并得到你想要的int内容。

上面可以看出,在没有内存对齐时,由于特殊的寻址方法,取值需要两次。然而如果一开始就把这个int数据保存在0位或者4位(其他像8、12、16等4的整数倍处也可),便可以在第一次取值时,就取到整个int的内容。内存对齐便是如此原理。既不会降低取值速度,也不影响快速寻址。但事情没有十全十美的。正如我们刚刚所看到的,在结构体中,由于内存对齐的存在,原本大小为11的结构体,变成了12和16。

为什么会这样子呢?因为一个结构体在系统中所占内存为连续的。而一个结构体中未免不会出现多种数据类型。当多个数据类型在同一结构体中都需要内存对齐时,在每个数据类型中间就未免会出现一些空的内存位。但由于这些空内存位处于结构体之中,就也会被包含到结构体的内存占用当中。

再看一下上面两段代码:

第一段的顺序是short int int char

Short所占2个单位内存,即可以假设为0和1,第一个int占四个,由于内存对齐,他只能从4开始,也就是4567。第二个int就是8、9、10、11。最后char则是占12。到这为止,该结构体所占内存仅为13,但为何系统反馈结果为16呢?那是因为结构体本身也要存在对齐。结构体的长度必须为结构体中最长的数据类型长度的整数倍。第一个结构体中最长的数据类型是int,长度为4,所以该结构体长度需凑到4的整数倍,即为16。

依照上面的方法,也可以推算出结构体二的长度为12。

几乎同样的结构体,却仅因代码顺序内存大小相差25%。可以见得合理利用内存对齐是多么重要,代码顺序也是会在很大程度上影响程序质量的。

有时,我们可能不需要如此快的运行速度,而相对的更需要较小的占用内存空间。这时,我们可以自己定义内存对齐的对齐值(如何设置自己百度︿( ̄︶ ̄)︿方法有很多,这里就不在细讲)。通过干涉内存对齐,便可以以牺牲速度为代价,获得更小的内存占用。