计算机内存是以字节(Byte)为单位划分的,理论上CPU可以访问任意编号的字节,但实际情况并非如此。cpu一次能读取多少内存要看数据总线是多少位,如果是16位,则一次只能读取2个字节,如果是32位,则可以读取4个字节,并且cpu不能跨内存区间访问。 假设有一个整型的数据a的首地址不是4的倍数,不妨设为0X00FFFFF3,则该整型数据存储在地址范围为0X00FFFFF3~0X00FFFFF6的存储空间中,而CPU每次只能对4的倍数内存地址进行读取,因此想读取a的数据,CPU要分别在0X00FFFFF0和0X00FFFFF4进行两次内存读取,而且还要对两次读取的数据进行处理才能得到a的数据。 加入采取对齐的方式,将数据a的首地址放到0X00FFFFF4,只需一次读取一次就可以得到数据a,所以说,字节对齐的根本原因其实在于cpu读取内存的效率问题,对齐以后,cpu读取内存的效率会更快。但采取对齐以后0X00FFFFF3这个地址就会浪费掉,对齐就是安排对象在内存中的位置,以使效率最大化,但这是以牺牲空间为代价的,这就需要根据实际情况去选择。
结构体对齐的条件根据CPU、操作系统,还有编译器而决定的,一般情况下,32位下是按照4个字节来对齐,64位下则是按照8个字节来对。
结构体对齐规则
- 结构体成员,除了第一个是始终放在最开始的地方,其它数据成员的地址必须是它本身大小或对齐参数两者中较小的一个的倍数。(对齐参数可以是通过编译命令
#pragma pack(n) 来指定,或者是系统默认的对齐参数) - 结构体嵌套,即结构体作为成员,则结构体成员要从其内部最大元素大小的的整数倍地址开始存储。(struct a里存有char,int,struct b,b里有char,int,double等,那b应该从8的整数倍开始存储)
#include <stdio.h>
typedef struct
{
char s1;
double s2;
char s3;
}a;
int main(char argc, char *argv[])
{
printf("%ld\n", sizeof(a));
return 0;
}
ubuntu64位下编译运行得到结果:24 ubuntu32位下编译运行得到结果:16 64位下,s1占1字节,后面填充7字节,s2占8个字节,s3占1字节,后面填充7字节,共占24字节。 32位下,s1占1字节,后面填充3字节,s2占8个字节,s3占1字节,后面填充3字节,共占16字节。
#include <stdio.h>
typedef struct
{
char s1;
double s2;
char s3;
}a;
typedef struct
{
char t1;
a t2;
double t3;
}b;
int main(char argc, char *argv[])
{
printf("%ld\n", sizeof(b));
return 0;
}
ubuntu64位下编译运行得到结果:40 ubuntu32位下编译运行得到结果:28 64位下,t1占1字节,后面填充7字节,t2中s1占1个字节,后面填充7个字节,t2中s2占8个字节,t2中s3占1个字节,后面填充7个字节,t3占8个字节,共占40字节。 32位下,t1占1字节,后面填充3字节,t2中s1占1个字节,后面填充3个字节,t2中s2占8个字节,t2中s3占1个字节,后面填充3个字节,t3占8个字节,共占24字节。
|