介绍三个概念:自身对齐值、指定对齐值、有效对齐值。
自身对齐值:数据类型本身的对齐值,例如char类型的自身对齐值是1,short类型是2;
指定对齐值:编译器或程序员指定的对齐值,32位单片机的指定对齐值默认是4;
有效对齐值:自身对齐值和指定对齐值中较小的那个。
对齐有两个规则:
1 基本类型变量起始地址要按一定规则对齐.
char 类型,其起始地址要1字节边界上,即其地址能被1整除(即任意地址即可)
short类型,其起始地址要2字节边界上,即其地址能被2整除(0x0002,0x0004,0x0006.....,地址的末位能被2整除)
int 类型,其起始地址要4字节边界上,即其地址能被4整除(0x0004,0x0008,0x00012,0x0016......,地址的末位能被4整除)
long类型,其起始地址要4字节边界上,即其地址能被4整除(0x0002,0x0004,0x0006......,地址的末位能被4整除)
float类型,其起始地址要4字节边界上,即其地址能被4整除(0x0002,0x0004,0x0006......,地址的末位能被4整除)
double类型,其起始地址要8字节边界上,即其地址能被8整除 (0x0008,0x0016,0x0024......,地址的末位能被8整除)
2 结构体地址对齐需要兼顾下面三个方面
1 若结构体中最大占用空间的成员为int类型,则整个结构体的起始地址应以4字节为边界;2 且结构内成员的偏移量也要参照1中的int类型,满足相应倍数进行对齐;3 且结构总尺寸也要对齐. 要为最大尺寸的成员int类型的整数倍,,如果不是则要在结构最后补齐成整数倍例
举例:
struct A{
char a;
char b;
char c;
char d;
};
0x0000 | 0x00001 | 0x0002 | 0x0003 |
a | b | c | d |
结构体A占4个字节, (0x0000~0x003总内存大小为4,为char类型大小的倍数)
假如结构体A起始地址是0x0000,
成员a的自身对齐值1,指定对齐值4,所以有效对齐值是1,地址0x0000是1的整数倍,故a存放起始地址是0x0000,占一个字节;
成员b的自身对齐值1,指定对齐值4,所以有效对齐值是1,地址0x0001是1的整数倍,故b存放起始地址是0x0001,占一个字节;
成员c的自身对齐值1,指定对齐值4,所以有效对齐值是1,地址0x0002是1的整数倍,故c存放起始地址是0x0002,占一个字节;
成员d的自身对齐值1,指定对齐值4,所以有效对齐值是1,地址0x0003是1的整数倍,故d存放起始地址是0x0003,占一个字节;
此时结构体A的有效对齐值是其最大数据成员的自身对齐值,它的成员都是char类型,故结构体A的有效对齐值是1.
struct B{
char a;
short b;
char c;
char d;
};
0x0000 | 0x00001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 |
a | x | b | b | c | d |
结构体B占6个字节(0x0000~0x0005总内存为6,为short 类型的3倍)
struct C{
char a;
int b;
char c;
char d;
};
0x0000 | 0x00001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 | 0x0009 | 0x000A | 0x000B |
a | x | x
| x | b | b | b | b | c | d | x | x |
x为补齐的数据,结构体C占12个字节 (0~B 占12字节) ,结构体C的成员占据10个字节,而结构体C的有效对齐值是其成员b的自身对齐值4,10不是4的倍数,故还需补齐两个字节,此时结构体C占据12个字节,是4的倍数
成员a的自身对齐值1,指定对齐值4,所以有效对齐值是1,地址0x0000是1的整数倍,故a存放起始地址是0x0000,占一个字节;
成员b的自身对齐值4,指定对齐值4,所以有效对齐值是4,地址0x0004是4的整数倍,故b存放起始地址是0x0004,占四个字节;
成员c的自身对齐值1,指定对齐值4,所以有效对齐值是1,地址0x0008是1的整数倍,故c存放起始地址是0x0008,占一个字节;
成员d的自身对齐值1,指定对齐值4,所以有效对齐值是1,地址0x0009是1的整数倍,故d存放起始地址是0x0009,占一个字节;
struct D{
char a;
double b;
char c;
char d;
};
0x0000 | 0x00001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 | 0x0009 | 0x000A | 0x000B | 0x000C | 0x000D | 0x000E | 0x000F |
a | x | x | x | b | b | b | b | b | b | b | b | c | d | Y | Y |
结构体D占16个字节,8的倍数
注意:由于结构体D的起始地址由a开始存储,且按4字节的指定对齐值对齐,所以b从0x0004开始存储。
若没有指定按4字节对齐,起始地址需要按照从0x0008开始且总尺寸大小为8的倍数,D结构体的实际占据0~0x0011,总共为17;不为8的倍数,补齐至24,则sizeof(D)= 8+8+2+6, =24;
0x0000 | 0x00001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 | 0x0008 | 0x0009 | 0x000A | 0x000B | 0x000C | 0x000D | 0x000E | 0x000F |
a | x | x | x | x | x
| x
| x | b
| b | b | b | b | b | b | b |
0x0010 | 0x00011 | 0x0012 | 0x0013 | 0x0014 | 0x0015 | 0x0016 | 0x0017 | 0x0018(十进制24) | 0x0009 | 0x000A | 0x000B | 0x000C | 0x000D | 0x000E | 0x000F |
c | d
| x | x | x | x | x | x | x |
|