1.字节对齐
每一款不同的处理器,存取内存数据都会有不同的策略,如果是 32 位的 CPU,一般
来讲他在存取内存数据的时候,每次至少存取 4 个字节(即 32 位),也就是按 4 字节对齐
来存取的。换个角度讲:CPU 有这个能力,他能一次存取 4 个字节。
接下来我们可以想到,为了更高效地读取数据,编译器会尽可能地将变量塞进一个 4
字节单元里面,因为这样最省时间。如果变量比较大,4 个字节放不下,则编译器会尽可能
地将变量塞进两个 4 字节单元里面,反正一句话:两个坑能装得下的就绝不用三个坑。这
就是为什么变量的地址要对齐的最根本原因。
所谓地址对齐主要思想:
尽可能提高CPU的运行效率(仅能能的少取读取/写入内存)一个数据如能使用一个单元来存放感觉对不使用两个, 两个能放绝对不用三个
2.普通变量的M值
概念: 一个数据它的大小是固定的(比如整型),如果这个数据他所存放的地址能够被某一个数
所整除(4)那么这个数就成为当前数据的M值。
可以根据具体的系统字长以及数据的大小可以计算得出M值。
int a; // a占用 4 字节, 如果a存放在能被4整除的地址下则是地址对齐, 因此他的M值为4
char b; // b占用 1 字节, 如果b存放在能被1整除的地址下则是地址对齐, 因此他的M值为1
short c; // c占用 2 字节, 如果c存放在能被2整除的地址下则是地址对齐, 因此他的M值为2
double d;// d占用 8 字节, 如果d存放在能被4整除的地址下则是地址对齐, 因此他的M值为4
float e; // e占用 4 字节, 如果e存放在能被4整除的地址下则是地址对齐, 因此他的M值为4
地址分析
a:010FF7E4
b:010FF7DB
c:010FF7CC
d:010FF7BC
e:010FF7B0
注意:如果一个变量的大小超过8 (8/16/32) M值则按8计算即可
3.结构体的M值
结构体的M值
结构体中有多个成员,取决于成员中M值最大的成员。
结构体的地址,必须能被结构体的M值整除(第一个成员的首地址为0)
结构体的尺寸,等于成员中宽度最宽成员的倍数
4.实例分析
#include<stdio.h>
struct student {
int a;
char b;
float c;
double d;
short e;
};
int main() {
struct student std ;
printf("%d\n", sizeof(std));
printf("a:%p\n", &std.a);
printf("b:%p\n", &std.b);
printf("c:%p\n", &std.c);
printf("d:%p\n", &std.d);
printf("e:%p\n", &std.e);
return 0;
}
运行结果:
32
a:0X00B9FC10
b:0X00B9FC14
c:0X00B9FC18
d:0X00B9FC20
e:0X00B9FC28
为什么会出现这样的结果呢我们来分析一下:(1)首先结构体成员最大的M值为8,所以第一个成员a分到8个字节自身需要4个字节剩余4个字节第一个成员的首地址为0现在排列到地址为3的空间(2)第二个结构体成员b自身M值为1现在的地址为4可以整除M故可以接着排列(3)第三个结体成员c自身M值为4当地址为8的时候才可以整除M故地址5 6 7空余(4)第四个结构体成员d的M值为8当地址为16的时候才可以整除M故地址空余12 13 14 15(5)第五个结体成员e的M值为2当地址为24的时候才可以整除M(6)最后的结果要是结构体成员最大的M值的整数倍
让我们在练习一下
struct student {
int a;
char b;
short e;
float c;
double d;
};
答案为24 这个结构体大小你分析对了吗?是不是很简单了其实第三个结构体成员的M值为2地址为6 7就可以放这个成员了
5.总结
结构体大小需要考虑:
(1)自身对齐(就是数据类型所占的字节也是普通变量的M值)
(2)默认对齐(系统字长 32位:4字节 64位:8字节)
(3)有效对齐(自身对齐和默认对齐的最小值)
(4)地址对齐(一定要整除有效地址)
struct stu1
{
char a;
int b;
char c;
char d;
};//结果为12/* char a 自身对齐:1 默认对齐:8 有效对齐:1 地址 0
int b 自身对齐:4 默认对齐:8 有效对齐:4 地址 4 5 6 7
char c 自身对齐:1 默认对齐:8 有效对齐:1 地址 8
char d 自身对齐:1 默认对齐:8 有效对齐:1 地址 9
最后的地址要能整除有效地址,显然10不能整除4,故需要补地址10 11
最终占12个字节 */
struct stu2
{
char a;
short b;
char c;
char d;
};//结果为6/* char a 自身对齐:1 默认对齐:8 有效对齐:1 地址 0
short b 自身对齐:2 默认对齐:8 有效对齐:2 地址 2 3
char c 自身对齐:1 默认对齐:8 有效对齐:1 地址 4
char d 自身对齐:1 默认对齐:8 有效对齐:1 地址 5
最后的地址可以整除有效地址,最终占6个字节 */
struct stu3
{
char a;
double b;
char c;
char d;
};//结果为24/* char a 自身对齐:1 默认对齐:8 有效对齐:1 地址 0
double b 自身对齐:8 默认对齐:8 有效对齐:8 地址 8-15
char c 自身对齐:1 默认对齐:8 有效对齐:1 地址 16
char d 自身对齐:1 默认对齐:8 有效对齐:1 地址 17
最后的地址要能整除有效地址,显然18不能整除8,
故需要补地址18-23,最终占24个字节 */