struct内存对齐

来源:互联网 发布:js调用android和ios 编辑:程序博客网 时间:2024/06/05 02:16

出于速度和空间的考量,编译器在实现过程中均会采用对struct内的变量进行内存对齐的操作,虽然会有一定的空间浪费,却可以减少在读取数据时候的读取操作。

先看下面的例子

struct A{    char a;    int b;};int main(){    cout << "size of A:"<<sizeof(A)<<"bytes" << endl;    return 0;}
输出的结果是8字节。

对于这样的结构体,在内存中有两种存储方式。

      ||  1B  ||  2B  ||  3B  ||  4B  ||  5B  ||  6B  ||  7B  ||  8B  ||

1.  ||   a    ||    ------------b-----------     ||

2.  ||   a    ||                               ||    ------------b-----------     ||
对于第一种存储方案,6、7、8三个字节的存储空间还可以留给后续使用。但注意,当我们要读取b的值的时候,32位的CPU会先读取1-4字节,再读取5-8字节,然后进行拼接得到b的值。而第二种方案,虽然会浪费掉2-4这三个字节,但读取b的值的时候,只需要一次读取操作就可以完成。

了解了内存对齐的基本思想,我又产生了三个新的疑问。一是,内存对齐与结构体内变量声明的顺序是否有关。二是,进行内存对齐的对齐标准是什么,是怎样确定的对齐标准。三是,如果对于64位机器,那么上面的第一种方案无疑是可取的,但实验表明,64位机器上(64位编译器)仍然采取了方案二进行对齐。


对于第一个疑问,看代码:

struct A{    char a,c;    int b;};struct B{    char a;    int b;    char c;};int main(){    cout << "size of A:"<<sizeof(A)<<"bytes" << endl;    cout << "size of B:"<<sizeof(B)<<"bytes" << endl;    return 0;}
输出的结果是A为8字节,B为12字节。这说明,在结构体内声明变量的顺序会影响结构体的内存分配。编译器在处理时,是按顺序进行内存分配和对齐的。


对于疑问二,在别人的博文中(http://blog.csdn.net/kokodudu/article/details/11918219),我看到了一个关于对齐的概念:

这里面有四个概念值:

1)数据类型自身的对齐值:基本数据类型的自身对齐值。

对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。

2)指定对齐值:#pragma pack (value)时的指定对齐值value。
3)结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
4)数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小的那个值。

概念看着比较多,但对于我们实际使用来说,可以进行一个简单不精确的概括。首先,平时写代码时,很少会用到指定对齐值,所以不用关注概念2和概念4 。而对于概念1和概念3,可以简单的理解为,结构体会按照其内部所包含的最长的基本类型成员(包括成员结构体的成员)的长度进行对齐。

下面看一个例子。

struct A{    char a[5];};struct B{    A a;    int b;};int main(){    cout << "size of A:"<<sizeof(A)<<"bytes" << endl;    cout << "size of B:"<<sizeof(B)<<"bytes" << endl;    return 0;}
输出的结果是A是5字节,B是12字节。

分析起来很简单,因为A中最长的基本数据类型是char,所以按1字节进行对齐。B中最长的基本数据类型是int,所以按4字节进行对齐。

但是这个概念里忽略了几个基本数据类型,枚举、指针。下面我们就来测试一下这几个类型的对齐长度。

enum e{};struct A{    e a;};struct B{    int* a;};int main(){    cout << "size of A:"<<sizeof(A)<<"bytes" << endl;    cout << "size of B:"<<sizeof(B)<<"bytes" << endl;    return 0;}
输出结果对于A,结果是4字节。对于B,32位编译器的结果是4字节,64位编译器的结果是8字节。


对于疑问三,实际上这个疑问是有问题的。因为虽然在一个存取周期内CPU把数据读入了CPU之中(假设读入到寄存器RAX中),我们需要对RAX进行移位操作才能使得EAX寄存器中恰好取得int变量b的值。


最后,概括起来,借用《Windows核心编程》里的话来总结结构体进行对齐的根本原因:当CPU访问正确对齐的数据时,它的运行效率最高,当数据大小的数据模数的内存地址是0时,数据是对齐的。


0 0
原创粉丝点击