深入理解内存对齐

来源:互联网 发布:multisim12数据库下载 编辑:程序博客网 时间:2024/05/22 03:19



内存对齐”应该是编译器的“管辖范围”。编译器为程序中的每个“数据单元”安排在适当的位置上。但是C语言的一个特点就是太灵活,太强大,它允许你干预“内存对齐”。如果你想了解更加底层的秘密,“内存对齐”对你就不应该再透明了。


对齐原因


1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。


对齐规则


每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。
规则:
1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行
2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行
3、结合1、2可推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。

#pragma pack(n)


编译器中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。

n:可选参数;指定packing的数值,以字节为单位;默认数值是8,合法的数值分别是1、2、4、8、16。


代码示例

使用默认##pragma pack(n)

#include <iostream>using namespace::std;//#pragma pack (n)class Csize{        public:                int x;                char y;                void print();};void Csize::print(){         cout << sizeof(Csize) << endl;}int main(int argc,char *argv[]){        Csize a;        a.print();        return 0;}

此时输出结果为8,即类Csize的长度为8。当程序未设定#pragma pack (n)时,默认n=8,即编译器执行8字节对齐。而类中最大数据元素为int型,占4个字节,此时执行较小的,也就是4字节对齐方式。类Csize中其他类型,如本例中的char类型只有1个字节,在执行4字节对齐的情况下,后面两个字节自动补齐(1+3),不添加任意数据。所以类Csize长度为4+(1+3)=8。

使用#pragma pack (2)

#include <iostream>#pragma pack(2)using namespace::std;class Csize{        public:                int x;                char y;                void print();};void Csize::print(){         cout << sizeof(Csize) << endl;}int main(int argc,char *argv[]){        Csize a;        a.print();        return 0;}

此时输出结果为6。由于#pragma pack (2),设定n=2,编译器执行2字节对齐。此时int型被拆分成两个部分,均为2字节,而char型本身为1字节,但是自动补全1个字节。所以2+2+(1+1)=6,即类Csize的长度为6。

注意:#progma pack (n)中,n只能取合法的计算机数值,即2^n。否则会出现警告,如:warning: alignment must be a small power of two, not 3 [-Wpragmas]


0 0