C语言内存对齐详解(1)

1、什么是字节对齐,为何要对齐?编程

    现代计算机中内存空间都是按照byte划分的,从理论上讲彷佛对任何类型的变量的访问能够从任何地址开始,但实际状况是在访问特定类型变量的时候常常在特定的内存地址访问,这就须要各类类型数据按照必定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。架构

    对齐的做用和缘由:各个硬件平台对存储空间的处理上有很大的不一样。一些平台对某些特定类型的数据只能从某些特定地址开始存取。好比有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其余平台可能没有这种状况,可是最多见的是若是不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。好比有些平台每次读都是从偶地址开始,若是一个int型(假设为32位系统)若是存放在偶地址开始的地方,那 么一个读周期就能够读出这32bit,而若是存放在奇地址开始的地方,就须要2个读周期,并对两次读出的结果的高低字节进行拼凑才能获得该32bit数据。显然在读取效率上降低不少。ide

2、请看下面的结构:测试

struct struct1 
{ 
   double dda; 
   char cda; 
   int ida; 
}; 

sizeof(struct1) = ?
错误的求法:spa

sizeof(struct1)=sizeof(double)+sizeof(char)+sizeof(int)=13code

可是当你在VC上运行以下测试代码:blog

#include<stdio.h>

struct mystruct
{
    double dda;
    char cda;
    int ida;
};

int main()
{
    struct mystruct ss;
    printf("%d\n",sizeof(ss));
    return 0;
}
C代码

运行结果为:16内存


其实,这是VC对变量存储的一个特殊处理。为了提升CPU的存储速度,VC对一些变量的起始地址作了“对齐”处理。在默认状况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。下面列出经常使用类型的对齐方式(vc6.0,32位系统)ci

类型            对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)it

char              偏移量必须为sizeof(char)即1的倍数

int               偏移量必须为sizeof(int)即4的倍数

float             偏移量必须为sizeof(float)即4的倍数

double            偏移量必须为sizeof(double)即8的倍数

Short             偏移量必须为sizeof(short)即2的倍数


各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充。同时VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,因此在为最后一个成员变量申请空间后,还会根据须要自动填充空缺的字节。

如今来分析VC是怎样来存放结构的:

struct struct1 
{ 
   double dda; 
   char cda; 
   int ida; 
}; 

第一个成员dda分配空间,其起始地址跟结构的起始地址相同(恰好偏移量0恰好为sizeof(double)的倍数),该成员变量占用sizeof(double)=8个字节;接下来为第二个成员cda分配空间,这时下一个能够分配的地址对于结构的起始地址的偏移量为8,是sizeof(char)的倍数,因此把cda存放在偏移量为8的地方知足对齐方式,该成员变量占用 sizeof(char)=1个字节;接下来为第三个成员ida分配空间,这时下一个能够分配的地址对于结构的起始地址的偏移量为9,不是sizeof (int)=4的倍数,为了知足对齐方式对偏移量的约束问题,VC自动填充3个字节(这三个字节没有放什么东西),这时下一个能够分配的地址对于结构的起始地址的偏移量为12,恰好是sizeof(int)=4的倍数,因此把ida存放在偏移量为12的地方,该成员变量占用sizeof(int)=4个字节;这时整个结构的成员变量已经都分配了空间,总的占用的空间大小为:8+1+3+4=16,恰好为结构的字节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数,因此没有空缺的字节须要填充。因此整个结构的大小为:sizeof(struct1)=8+1+ 3+4=16,其中有3个字节是VC自动填充的,没有听任何有意义的东西。

下面再举个例子,交换一下上面的struct1的成员变量的位置,使它变成下面的状况:

struct mystruct2
{
    char cda;
    double dda;
    int ida;
};

在VC环境下,运行结果为:24

struct mystruct2
{
    char cda;    //偏移量为0,知足对齐方式,cda占用1个字节;
    double dda;  //下一个可用的地址的偏移量为1,不是sizeof(double)=8 
                 //的倍数,须要补足7个字节才能使偏移量变为8(知足对齐 
                 //方式),所以VC自动填充7个字节,dda存放在偏移量为8 
                 //的地址上,它占用8个字节。 

    int ida;     //下一个可用的地址的偏移量为16,是sizeof(int)=4的倍 
                 //数,知足int的对齐方式,因此不须要VC自动填充,type存 
                 //放在偏移量为16的地址上,它占用4个字节。
   
   //全部成员变量都分配了空间,空间总的大小为1+7+8+4=20,不是结构 
   //的节边界数(即结构中占用最大空间的类型所占用的字节数sizeof 
   //(double)=8)的倍数,因此须要填充4个字节,以知足结构的大小为 
   //sizeof(double)=8的倍数。
};

因此该结构总的大小为:sizeof(struct2)为1+7+8+4+4=24。其中总的有7+4=11个字节是VC自动填充的,没有听任何有意义的东西。

相关文章
相关标签/搜索