昨天做腾讯的笔试题,有两道是内存对齐问题,之前也看过,不过理解不够深刻,今天看了下前人总结,也小小的总结一下.
首先说说为什么要有内存对齐
1.内存对齐原因:
(1):平台原因:不是所有的硬件平台都能访问任意地址的的任意数据的,某些硬件只能在某些地址取出某些特定类型的数据(百科).
(2):性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问.
PS:每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n), n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。默认:vc==8, gcc==4
2.对齐规则:
(1)数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照 #pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
(2)结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
(3)结合1、2推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。
http://www.cppblog.com/cc/archive/2006/08/01/10765.html 这里面有内存对齐的形象展示.
下面是我在VC 6.0上的实验测试:
#include<stdio.h>
#pragma pack (4) /*结构体中的每个成员的起始地址都会是2的整数倍*/
struct X1
{
char a;
int b;
__int64 i;
}x1;
struct X2
{
char a;
__int64 i;
int b;
}x2;
struct X3
{
__int64 i;
char a;
int b;
}x3;
void test_pragma_pack_4()
{
printf("sizeof(x1) = %d, &a = %x, &b = %x, &i = %x\n", sizeof(x1), &(x1.a), &(x1.b), &(x1.i));
printf("sizeof(x2) = %d, &a = %x, &i = %x, &b = %x\n", sizeof(x2), &(x2.a), &(x2.i), &(x2.b));
printf("sizeof(x3) = %d, &i = %x, &a = %x, &b = %x\n", sizeof(x3), &(x3.i), &(x3.a), &(x3.b));
}
int main()
{
test_pragma_pack_4();
return 0;
}
输出:
sizeof(x1) = 16, &a = 427e78, &b = 427e7c, &i = 427e80
sizeof(x2) = 16, &a = 427e88, &i = 427e8c, &b = 427e94
sizeof(x3) = 16, &i = 427e98, &a = 427ea0, &b = 427ea4
Press any key to continue
分析:
1:由于x1.a是char类型占1个字节,放在了地址为0x427e78处,而x1.b是int类型,占4个字节,因此以4个字节方式对齐,x1.b的地址为0x427e7c=0x427e78 + 0x000003, 而x1.i是__int64类型,占8个字节,因此用8与设置的对齐模数4比较:8>4,因此x1.b与x1.i之间以4字节对齐,x1.i的地址为0x427e80 = 0x427e7c + 0x000003. sizeof(x1) = 16
2:由于x2.a是char类型占1个字节,放在了地址0x427e88处,而x2.i是__int64类型,占8个字节,8>4,0x427e88 + 0x000003 = 0x427e8c
,而x2.b是int类型占4个字节,i占8个字节是4的整数倍,因此可直接对齐,x2.b的地址为0x427e94.
3:由于x3.i是__int64类型,占8个字节,地址为0x427e98,而x3.a是char类型,占1个字节,8是1的整数倍,已经对齐,x3.a的地址为0x427ea0, x3.b是int类型,占4个字节,之前共占9个字节,不是4的整数倍,因此需要做对齐处理,x3.b的地址为0x427ea0 + 0x000003= 0x427ea4.
以上是我今天总结,有错误的地方希望多指正,正在学习中...