字节对齐学习笔记
在观看嵌入式学习视频时有一道关于地址对齐的题目,发现自己有很多不清楚的地方,而且这一部分有很多案例需要分情况讨论,所以单独写一篇文章来总结一下。
1.总体规则
在进行结构体的地址对齐时,总是以结构体中最大的数据类型为标准进行对齐,数组只看其数据类型,不看具体大小。
注意:指针的大小要看操作系统是32位还是64位。
举个栗子:引自结构体嵌套对齐
struct S1 {
char a;// 0
double b;// 8-15
short c[2];//16-19,因为double的8比short的2大,引用规则一
//共24
};
结构体S1中,最大的数据类型为double,所以整体要以8byte进行对齐。
2.结构体嵌套
结构体S2中嵌套结构体(或联合体)S1,则按照S1的最大元素类型和S2中各元素类型取较大者对齐
struct S2 {
struct S1 a;// , 0-23
int b;//24-27
char c;//28
// 最后按S1的最大类型double的8字节对齐,因为double比S2的int和char字节数都大,最后
//整个S2按照8字节对齐
};
结构体S2中,由于嵌套了结构体S1,而两者中最大的数据类型仍是S1中的double,所以S2也是以8byte进行对齐。
3.如何对齐
S2中的b数据类型为int,是4byte,占据24-27,c的数据类型为char,是1byte,加上b的4byte小于8byte,所以c的地址直接接在b的后面,而不是从32开始。
如果c的数据类型大于4byte,即接在b后面会无法对齐,此时c的地址会从32开始。
4.强制转换
可以使用#pragma pack()
进行数据对齐的强制转换,#pragma pack(4)
即转化成4位对齐
5.嵌套 + 位域——内存对齐
下面这个栗子引用自:有关联合体嵌套结构体 、位域(位段)、小端存放
在结构体划分位段时,会受到自身变量类型的影响。如果该类型剩余空间不足以继续分配位段,则会自动对齐至下一个内存单元中,进行分配。
错误代码:
union MyUnion
{
unsigned short num; // 16 位
struct
{
unsigned char al : 6; // 低 6位
unsigned char ah : 6; // 中 6位
unsigned char hh : 4; // 高 6位
};
};
int main()
{
MyUnion un;
un.num = 0x111; // 0000 0001 0001 0001
int n = un.hh; //error: 使用了未初始化的局部变量“un”
return 0;
}
al首先占据了num低字节位的前6位,在ah想要继续往下占据的时候,由于它们是char类型,只有8位,减去al的6位,此时只剩下2位,不够分配给ah,所以将会进行对齐,导致ah占据num高字节位的前8位,而不是与al地址连续。即:
unsigned char al : 6;
低 6位=0000 0001 0001 0001
unsigned char ah : 6;
高 6位=0000 0001 0001 0001
正确改法:
将 unsigned char;
类型换成 unsigned short;
,使之在内存对齐时,使用2字节对齐。