问题:
在一本书上看到了内存对齐,脑袋突然转不过来,看了作者解释了一大通,还是没能体会到,无奈~~~
解决:
当然是寻找广大网友,看了网友的解释,我才理解了作者文字中的意思,但是对于刚看到这句话时候的我,真的反应不过来。
引用:(以下是引用别人对于内存规则的讲解,个人觉得较好理解)
1.对于结构的各个成员,第一个成员位于偏移为0的位置,以后的每个数据成员的偏移量必须是 min(#pragma pack()指定的数,这个数据成员的自身长度)的倍数
2.在所有的数据成员完成各自对齐之后,结构或联合体本身也要进行对齐,对齐将按照 #pragram pack 指定的数值和结构或者联合体最大数据成员长度中比较小的那个 也就是 min(#pragram pack() , 长度最长的数据成员);
#pragram pack(n) 表示的是设置n字节对齐,vc6默认的是8,这个是重点,不然接下来没法比较
举个栗子
struct Test{
char c1;
short s;
char c2;
int i;
};
如果我说他的大小为(1+2+1+4=8),你信吗?当然,如果这样我就不会纠结作者写的东西了。先根据第一条内存对齐规则,第一个变量c1相对偏移地址为0,第二个变量为short型,按照规则min(8,2)=2,所以偏移量为2的倍数,即要在第一个变量后补一个字节,第二个变量的起始地址为2,接下来又是一个char型,min(8,1)=1,刚好第三个变量在地址4,为1的倍数,不用进行补字节操作,第四个为int型,min(8,4)=4,所以偏移量应该为4的倍数,所以需要在第三个变量补三个字节。
然后再根据第二条规则,寻找结构体中最大成员长度的变量与指定数值长度进行比较,找出较小的那个,即min(8,int)=(8,4)=4;所以结构体的大小应该为4的倍数,这里刚刚好,所以不用补齐字节
最后的结果是0x|00|0xxx|0000 (x表示补的字节数)所以最后的结果为12,也许有人跟我有一样的疑问,倍数为什么都是从最小的开始算,其实再想一下,计算机的内存有限,本来这种内存对齐方式就是一种空间换时间的方式,牺牲了内存来取得访问时间的减少,如果往大倍数加,那还不如不对齐,也没必要,每个变量占的空间本来就那么多,再给它多开辟几个空间,它依然只占用那么一点空间。
避免内存对齐的影响
一个简单的方法是重新排布结构体中的变量,还是上面那个结构体
struct Test{
char c1;
char c2;
short s;
int i;
};
这样,每个成员都对齐在其自然边界上,从而避免了编译器自动补齐,这是技巧之一,除此还可以使用#pragma pack()来改变编译器的默认对齐规则
使用指令#pragma pack(n),编译器将按照n个字节对齐
该指令还有一个条件,虽然指定了按照n个字节对齐,但是并不是每个成员都按照那个字节对齐,每个成员是按自己的方式进行对齐。
对齐规则:
每个成员按其类型的对齐参数(通常是这个类型的大小)和指定的对齐参数(这里是n字节)中较小的一个对齐,即min(n,sizeof(item)).并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节。
使用指令#pragma pack(),编译器将取消自定义字节对齐方式
再来一个栗子
#pragma pack(8)
struct Test0{
char c1;
int i;
};
struct Test1{
char c2;
Test0 d;
int k;
};
#pragma pack()
先对Test0分析
Test0中char型变量为1个字节,按照对齐规则,选取min(1,8)=1,所以c按1字节对齐,int型为4个字节,min(8,4)=4,按4字节对齐,所以char型,即变量c后应该补三个字节.最终整个大小为8个字节,刚好是8的倍数(对齐后的长度是成员中最大的对齐参数的整数倍),不用进行字节补齐,0xxx|0000,占8个字节
Test1
分析Test1前要先了解结构体型变量在结构体中的对齐规则:
对于结构体来说,他的默认对齐方式就是它的所有成员使用的对齐参数最大的一个,很明显在Test0中最大的就是int型,占4个字节,以它作用结构体的自然边界,所以现在是这种情况 0xxx|0xxx|0000 ,第一个竖线前的四个字节是Test1中c2占用字节,因为Test0结构体中的c1按照4字节对齐,所以c1应该地址4的地方,c2后面就应该补3三个字节,同样道理,c1后也应该补三个字节,使Test0中i变量地址为4的倍数(基于对齐规则,如果看不懂,建议再看一下前面的对齐规则),所以i变量在地址8处,为4的倍数,此时占用了12个字节。所以变量k刚好在其自然边界上,不用再进行字节补齐。所以最终结果为16个字节 0xxx|0xxx|0000|0000
总结
对于结构体,编译器会自动进行成员变量的对齐,
以提高运算效率。缺省情况下,编译器为结构体的每个成员按其自然对界(natural alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。
自然对界(natural alignment)即默认对齐方式,是指按结构体的成员中 size 最大的成员对齐。