结构体内存对齐
2013-08-31 01:07
106 查看
计算方法
(1.1) 结构体中的第一个元素的地址作为base地址,可以认为其地址的大小为0,比如下面的AA类型的结构体中的int id这个元素的地址,就可以认为是0开始的。int id占据了0,1,2,3这四个字节。
(1.2) 接下来按照结构体中元素被定义的顺序,依次计算每个元素的地址,每个都必须是自己大小的整数倍。
比如AA中的double weight,地址在int id之后,所以4是它的起始地址。在win 32 机器中,double的大小为8,所以double需要前面一个元素的内存对齐,也就是说,需要在Int id 之后且在double weight之前的位置闲置4,5,6,7这四个字节不用,然后double接着占据8,9,10,11,12,13,14,15这八个字节。这就是padding内存填充。
比如AA中的float height,地址在double weight之后,所以16是它的起始地址。在win 32机器中,float的大小为4,所以float height其实位置合适。
(1.3) 进行步骤2直到所有元素都算完。
此时此刻,还要检查结构体的所有元素的大小之后是否是最大元素的大小的整数倍,如果不是,还需要在末尾增加差值进行内存填充,使得总大小为最大元素的大小的整数倍。
比如,对于上面进行的第2步,已经算出了元素大小之和为
4(int) + 4(int对double填充) + 8(double) + 0(double无需对float填充) + 4(float) = 20.
20还需要再加4才能使得结构体总大小能够是double weight大小的整数倍。
所以最后结果为
4(int) + 4(int对double填充) + 8(double) + 0(double无需对float填充) + 4(float) + 4(收尾填充) = 24.
计算原理
(2.1) 上述的1.1和1.2是一种编译优化,是为了更快,想想也是每次4个字节4个字节地取数据比每次1个字节1个字节地取要快一些。
(2.2) 为什么这样就能更快呢?因为大多数计算机体系统结构对于访问内存的指令是有条件要求的,比如在win 32平台中,访问4字节的指令所访问的内存地址应该是4的整数倍,访问2字节的指令所访问的内存地址应该是2字节的整数倍,这就是对齐(Alignment)。为了对齐,需要做填充(padding)
(2.3)结构体的基地址,也就是结构体中的第一个成员的地址,会从满足对齐要求的地址实现,这个过程由编译器完成。比如如果第一个元素为int,那么基地址必然是4的倍数。所以计算结构体大小的时候,你不需要考虑基地址是否需要你来填充实现对齐,它必然是对齐的。所以上面说“ 结构体中的第一个元素的地址作为base地址,可以认为其地址的大小为0”。
(2.4 )上述的1.3是看上去是没用的,在只有1个结构体变量的时候确实是没有必要的。但是当多个结构体依次排列的时候,这可以使得下一个结构体的起始位置满足1.1和1.2的条件,同时也满足上面2.3所说的,基地址必然是对齐的。
(2.5) 注意上面说的依次计算。其内含是,编译机是按照结构体变量定义的顺序,一边读元素,一边分配空间的。所以我们计算大小时也得这么来。所以,如果有两个相同的结构体,成员内容相同,但是成员定义的顺序不同,那么这两个结构体计算大小可能是不同的。
下面来看看完整的测试程序。
计算的过程如下:
(1.1) 结构体中的第一个元素的地址作为base地址,可以认为其地址的大小为0,比如下面的AA类型的结构体中的int id这个元素的地址,就可以认为是0开始的。int id占据了0,1,2,3这四个字节。
(1.2) 接下来按照结构体中元素被定义的顺序,依次计算每个元素的地址,每个都必须是自己大小的整数倍。
比如AA中的double weight,地址在int id之后,所以4是它的起始地址。在win 32 机器中,double的大小为8,所以double需要前面一个元素的内存对齐,也就是说,需要在Int id 之后且在double weight之前的位置闲置4,5,6,7这四个字节不用,然后double接着占据8,9,10,11,12,13,14,15这八个字节。这就是padding内存填充。
比如AA中的float height,地址在double weight之后,所以16是它的起始地址。在win 32机器中,float的大小为4,所以float height其实位置合适。
(1.3) 进行步骤2直到所有元素都算完。
此时此刻,还要检查结构体的所有元素的大小之后是否是最大元素的大小的整数倍,如果不是,还需要在末尾增加差值进行内存填充,使得总大小为最大元素的大小的整数倍。
比如,对于上面进行的第2步,已经算出了元素大小之和为
4(int) + 4(int对double填充) + 8(double) + 0(double无需对float填充) + 4(float) = 20.
20还需要再加4才能使得结构体总大小能够是double weight大小的整数倍。
所以最后结果为
4(int) + 4(int对double填充) + 8(double) + 0(double无需对float填充) + 4(float) + 4(收尾填充) = 24.
typedef struct aa { int id; double weight; float height; } AA;
计算原理
(2.1) 上述的1.1和1.2是一种编译优化,是为了更快,想想也是每次4个字节4个字节地取数据比每次1个字节1个字节地取要快一些。
(2.2) 为什么这样就能更快呢?因为大多数计算机体系统结构对于访问内存的指令是有条件要求的,比如在win 32平台中,访问4字节的指令所访问的内存地址应该是4的整数倍,访问2字节的指令所访问的内存地址应该是2字节的整数倍,这就是对齐(Alignment)。为了对齐,需要做填充(padding)
(2.3)结构体的基地址,也就是结构体中的第一个成员的地址,会从满足对齐要求的地址实现,这个过程由编译器完成。比如如果第一个元素为int,那么基地址必然是4的倍数。所以计算结构体大小的时候,你不需要考虑基地址是否需要你来填充实现对齐,它必然是对齐的。所以上面说“ 结构体中的第一个元素的地址作为base地址,可以认为其地址的大小为0”。
(2.4 )上述的1.3是看上去是没用的,在只有1个结构体变量的时候确实是没有必要的。但是当多个结构体依次排列的时候,这可以使得下一个结构体的起始位置满足1.1和1.2的条件,同时也满足上面2.3所说的,基地址必然是对齐的。
(2.5) 注意上面说的依次计算。其内含是,编译机是按照结构体变量定义的顺序,一边读元素,一边分配空间的。所以我们计算大小时也得这么来。所以,如果有两个相同的结构体,成员内容相同,但是成员定义的顺序不同,那么这两个结构体计算大小可能是不同的。
下面来看看完整的测试程序。
// structure_alignment.cpp : Defines the entry point for the console application.在win 32下用vs 6.0验证结果如下:
#include "stdafx.h"
#include <iostream>
typedef struct aa { int id; double weight; float height; } AA;
typedef struct aa1
{
double weight;
float height;
int id;
short gender;
} AA1;
typedef struct aa2
{
short gender;
int id;
float height;
double weight;
} AA2;
typedef struct aa3
{
int id;
short gender;
float height;
double weight;
} AA3;
int main(int argc, char* argv[])
{
printf("Hello World!\n");
BB b;
AA a;
std::cout<<"size of short: "<<sizeof(short)<<" ,size of int: "<<sizeof(int)<<" , size of float: "<<sizeof(float)<<" ,size of double: "<<sizeof(double)<<std::endl;
std::cout<<"size of AA: "<<sizeof(AA)<<std::endl;
std::cout<<"size of AA1: "<<sizeof(AA1)<<std::endl;
std::cout<<"size of AA2: "<<sizeof(AA2)<<std::endl;
std::cout<<"size of AA3: "<<sizeof(AA3)<<std::endl;
return 0;
}
计算的过程如下:
typedef struct aa { int id; //4(自身)+4(填充) double weight; //8(自身)+0(填充) float height; //4(自身)+4(填充) } AA; typedef struct aa1 { double weight; //8(自身)+0(填充) float height; //4(自身)+0(填充) int id; //4(自身)+0(填充) short gender; //2(自身)+6(填充) } AA1; typedef struct aa2 { short gender; //2(自身)+2(填充) int id; //4(自身)+0(填充) float height; //4(自身)+4(填充) double weight; //8(自身)+0(填充) } AA2; typedef struct aa3 { int id; //4(自身)+0(填充) short gender; //2(自身)+2(填充) float height; //4(自身)+4(填充) double weight; //8(自身)+0(填充) } AA3;