好多笔试中,会考结构体的sizeof是多少,这就涉及到了字节对齐问题。(VC或GCC编译器,默认按4字节对齐)
什么叫字节对齐?
就是数据在内存中存放的方式,它存放的地址需要是它长度的整数倍。比如单字节放在什么地址都可以,双字节数据只能存放在偶地址上,4字节数只能存放在是4的倍数的地址上。
注意:它与CPU访问的方式无关,32位机器就是按照4字节来访问的,因为它的数据总线是32位,不会以数据在内存中的对齐方式而变化。
一、对于标准数据类型,它的地址是他长度的整数倍就行了。
char:1字节对齐(有符号无符号一样)
short:2
int:4
long:4
float:4
double:8
二、对于非标准类型,对齐方式如下:
1、数组:按照基本数据类型对齐,只要第一个数据对齐了,后面的自然就对齐了。
2、联合体:按照元素中最大的长度对齐。
3、结构体:结构中每个数据都要对齐。
其中,因为结构体对齐最复杂,所以也最常见。
下面举几个结构体例子:
1、struct st1
{
char x1;
int x2;
char x3[10];
};
它占几个字节呢?正确答案是:20
分析如下:char本来占1个字节,因为4字节对齐,所以需要在x1后空出3个字节,接下来才是x2.x3本来占10个字节,但由于最后两个字节后面也需要空出2个字节,所以x3占12个字节。这样4+4+12=20个字节。
2、如下两个类似的结构体,成员相同,只是顺序不同,则所占字节不同。
struct A
{
char a;
int b;
short:c;
};
struct B
{
int b;
char a;
short c;
};
正确答案:A占12个字节,B占8个字节。
分析:A:a(1个字节)之后是b(4个字节),需要在a之后空着3个字节。4+4+4=12.
B:b先占4个字节,紧接着a占1个字节,然后c占2个字节,那么只需要在a之后空1个字节就可以存放c。所以4+2+2=8.
当然,可以不采取默认的对齐方式,可以手工修改任意字节对齐,这个只是修改编译器的对齐方式,只是改变了编译出来的数据的排列方式而已,CPU依旧是按照4字节访问的。
人工修改字节对齐方式有2种方式:
1、#pragma pack(n)与#pragma pack() n字节对齐
如:
#pragma pack (2) /*指定按2字节对齐*/
struct C
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
sizeof(struct C)值就变为8。
2、__attribute((aligned (n)))与__attribute__((packed))
使用如下:
struct A
{
char a;
int b;
char a[10];
}__attrubute__ ((aligned(1)));
1字节对齐,则sizeof(A)=15。
struct A
{
char a;
int b;
char a[10];
}__attrubute__ ((packed));
取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,是GCC特有的语法。
问:什么时候需要手动设置对齐方式呢?
答:实际工程中,当在不同CPU下,设计通信协议时,或编写硬件驱动程序时(需要用结构体来描述寄存器结构),需要手动设置。即使看起来就自然对齐的也要手动对齐,以免不同的编译器生成的代码不一样。