1、结构体的好处。
需要使用结构这种聚合数据类型来处理基本数据类型难以处理的场景。
2、为什么要内存对齐?
2.1 平台原因:不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2.2 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。因为为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
3、#pragma pack
VS的默认是#pragma pack(8),支持1,2,4,8,16;但是gcc默认是#pragma pack(4),支持1,2,4。
例如:#pragma pack(3)会告警。
4、实例。
环境:Win764位机 + VS2013
int 4字节
char 1字节
float 4字节
double 8字节
4.1 对齐补齐三原则。
原则一:数据成员对齐规则:①struct或union的数据成员,第一个数据成员放在offset为0的地方 ②以后每个数据成员的对齐必须按照:#pragma pack(N)指定的N和这个数据成员自身长度,比较小的那个进行。
原则二:struct或union整体对齐规则:在数据成员完成各自对齐之后,struct或union本身也要对齐。对齐将按照#pragma pack(N)指定的N和struct或union最大数据成员长度中,比较小的那个进行。
原则三:struct或union成员对齐原则:如果一个struct或union里有struct或union成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。
4.2 按一对齐。
#include <iostream>
using namespace std;
int main()
{
//按1对齐
#pragma pack(1)
struct A{
int a;
char b;
short c;
char d;
};
cout << "sizeof(A):\t"<<sizeof(A) << endl;
cin.get();
return 0;
}
运行:
分析:
结构体A 遵循原则一和二。
原则一,成员对齐。
成员a四字节,#pragma pack(1)
,4>1,按1对齐,offset为0,存放区间[0,3];
成员b,一字节,1=1,按1对齐,offset = 4,存放区间[4];
成员c,二字节,2>1,按1对齐,offset = 5,存放区间[5,6];
成员d,一字节,1=1,按1对齐,offset = 6,存放区间[7];
原则二,struct整体对齐。
整体存放在[0,7]中,共八字节;对齐 pack(1),8是1的倍数,就是八字节。
示图:
4.3 按二对齐。
#include <iostream>
using namespace std;
int main()
{
//按2对齐
#pragma pack(2)
struct B{
int a;
char b;
short c;
char d;
};
cout << "sizeof(B):\t"<<sizeof(B) << endl;
cin.get();
return 0;
}
运行:
分析:
结构体B 遵循原则一和二。
原则一,成员对齐。
成员a四字节,#pragma pack(2)
,4>2,按2对齐,offset为0,存放区间[0,3];
成员b,一字节,1<2,按1对齐,offset = 4,存放区间[4];
成员c,二字节,2=2,按2对齐,offset = 5,此时的offset应该被对齐数2整除,由5变6,存放区间[6,7];
成员d,一字节,1<2,按1对齐,offset = 8,存放区间[8];
原则二,struct整体对齐。
整体存放在[0,8]中,共九个字节;对齐 pack(2),应该被2整除,就是十个字节。
示图:
4.4 按四对齐。
#include <iostream>
using namespace std;
int main()
{
//按4对齐
#pragma pack(4)
struct C{
int a;
char b;
short c;
char d;
};
cout << "sizeof(C):\t"<<sizeof(C) << endl;
cin.get();
return 0;
}
运行:
分析:
结构体C 遵循原则一和二。
原则一,成员对齐。
成员a四字节,#pragma pack(4)
,4=4,按4对齐,offset为0,存放区间[0,3];
成员b,一字节,1<4,按1对齐,offset = 4,存放区间[4];
成员c,二字节,2<4,按2对齐,offset = 5,此时的offset应该被对齐数2整除,由5变6,存放区间[6,7];
成员d,一字节,1<4,按1对齐,offset = 8,存放区间[8];
原则二,struct整体对齐。
整体存放在[0,8]中,共九个字节;对齐 pack(4),应该被4整除,就是十二个字节。
示图:
4.5 按八对齐。
#include <iostream>
using namespace std;
int main()
{
//按8对齐
#pragma pack(8)
struct D
{
int a[2];
short b[3];
double c;
};
cout << "sizeof(D):\t" <<sizeof(D) << endl;
cin.get();
return 0;
}
运行:
分析:
结构体D 遵循原则一和二。
成员a单体四字节,#pragma pack(8)
,4<8,按4对齐,offset = 0,存放区间[0,7];
成员b单体二字节,2<8, 按2对齐,offset = 8,存储区间[8,13];
成员c八字节,8 = 8,,按8对齐,offset = 14,此时的offset应该被对齐数8整除,由14变16,存储区间[16,23];
原则二,struct整体对齐。
整体存放在[0,23]中,共24个字节;对齐 pack(8),可以被8整除,就是24个字节。
示图:
4.5 嵌套结构体按四对齐。
#include <iostream>
using namespace std;
int main()
{
//嵌套结构体。
#pragma pack(4)
struct E
{
char g[2];
short h;
struct F
{
int a;
double b;
float c;
};
F f;
};
cout << "sizeof(E):\t"<<sizeof(E) << endl;
cin.get();
return 0;
}
运行:
分析:
嵌套结构,顺序计算。
①、原则一、二。
成员g单体一字节,#pragma pack(4)
,4>1,按1对齐,offset = 0,存放区间[0,1];
成员h二字节,2<4,按2对齐,offset = 2,存放区间[2,3];
②、此时,应用原则三。
offset=4,结构体内部最大元素为double(8字节),offset应该被8整除,所以offset变为8,但是原则一,#pragma pack(4)
,4<8,所以还是从4开始存放接下来的struct F(f)
③、结构体F 遵循原则一和二。
成员a四字节,#pragma pack(4)
,4=4,按4对齐,offset = 4,存放区间[4,7];
成员b八字节,8>4,按4对齐,offset = 8,存储区间[8,15];
成员c四字节,4=4,按4对齐,offset = 16,存储区间[16,19];
④、原则二,struct整体对齐。
整体存放在[0,19]中,共20个字节;对齐 pack(4),可以被4整除,就是20个字节。
示图:
4.6 嵌套结构体按八对齐。
#include <iostream>
using namespace std;
int main()
{
#pragma pack(8)
struct G
{
int a;
char b[3];
short c[3];
double d[2];
struct H
{
int e;
float f;
double g;
};
H h;
};
cout << "sizeof(G):\t" << sizeof(G) << endl;
cin.get();
return 0;
}
运行:
分析:
嵌套结构,顺序计算。
①、原则一、二。
成员a四字节,#pragma pack(8)
,4<8,按4对齐,offset为0,存放区间[0,3];
成员b单体一字节,1<8,按1对齐,offset = 4,存放区间[4,6];
成员c单体二字节,2<8,按2对齐,offset = 7,此时的offset应该被对齐数2整除,由7变8,存放区间[8,13];
成员d单体八字节,8=8,按8对齐,offset = 14,此时的offset应该被对齐数2整除,由14变16,存放区间[16,31];
②、原则三:offset = 32,最大成员为double,32%8==0,那么H的成员从32开始存。
成员e四字节,4<8,按4对齐,offset = 32,存放区间[32,35];
成员f四字节,4<8,按4对齐,offset = 36,存放区间[36,39];
成员g八字节,8=8,按8对齐,offset = 40,存放区间[40,47];
③、原则二:struct整体对齐。
整体存放在[0,47]中,共48个字节;对齐 pack(8),可以被8整除,就是48个字节。
示图:
5、总结:
5.1 Pragma Pack(N);
只影响的是数据在内存中的排列方式。
5.2 cpu可以访问任何pack的数据,只是效率有高低之分。
5.3 最好把大小相似的数据元素(如整型、浮点型)放到开头;把奇数大小的数据(如字符串、缓冲区)放到后面。
参考:参考
入门萌新,浅知拙见,若有斧正,不胜感激。^ - ^