(1)对于基本数据类型,如果一个变量占用n个字节,则该变量在内存中的起始地址
必须是n的整数倍,即:存放起始地址 % n = 0。比如,int型变量占用4个字节,则int a; 变量a在内存中的起始地址必须是4的整数倍。
(2)对于结构体类型,那么结构体的起始地址是其最宽数据类型成员的整数倍。比如下面的结构体:
struct Stu{
int a;
char b;
double c;
}
//定义结构体变量
struct Stu st;
对于结构体变量st,其成员变量占用字节数最多的变量类型是double
,占用8个字节,所以变量st在内存中的起始地址是8的整数倍。
为了提高程序的执行效率。
具体来说就是访问内存速度更快。实际内存读取时,内存单元每n个一组,一次读一组。
实质上是空间换时间。
这涉及到了有没有杂注: #pragma pack(n)
的问题,可以使用#pragma pack(n)
来添加杂注,改变结构体成员的对齐方式。VS中默认杂注为8,可以使用
#pragma pack(show)
来查看默认的杂注,在程序中添加上面的这行代码,然后生成解决方案
,就可以查看默认的杂注。如下:
warning C4810: 杂注 pack(show) 的值 == 8
当然,也可以手动设置,设置方式为:
#pragma pack(n)
其中n用来设定变量以n字节对齐方式,可以设定的值包括:1、2、4、8,VS中默认是8
若需要取消强制对齐方式,可用命令
#pragma pack()
不过需要注意的是:#pragma pack(n)无法影响结构体开始位置,只能影响结构体中成员的对齐方式
1.从偏移为0的位置开始存储;
2.如果没有定义杂注#pragma pack(n)
3.如果定义了#pragma pack(n)
3.1 sizeof的最终结果必然是min(n,结构体内部最大成员)的整数倍,不够补齐;
3.2 结构体内部各个成员的首地址必然是min(n,自身大小)的整数倍。
下面结合实例分析,来了解字节对齐。
说明:我使用win10,VS编译器。int占4个字节,char占1个字节,double占8个字节
首先,不使用杂注的情况。
#include<stdio.h>
//sizeof(S1)=24,这个是容易理解的
struct S1{ //没有杂注
int i; //0-3
char j; //4-7
int a; //8-15
double b; //16-23
};
//sizeof(S2)=24,如果按照单个成员变量来分析,结构体占用是20个字节,
//但是因为结构体的起始地址是最宽成员类型的整数倍,即是8的整数倍,所以sizeof(S2)=24
struct S2{ //没有杂注
int i; //0-3
char j; //4-7
double b; //8-15
int a; //16-19
};
int main() {
struct S1 s1;
struct S2 s2;
printf("%d\n", sizeof(s1));
printf("%d\n", sizeof(s2));
return 0;
}
分析:
(1)对于结构体S1,int i
占用4个字节,并且存放的起始地址是4的整数倍,由于刚开始是从0
开始的,自动满足,存储位置为0-3
。char j
占用1个字节,如果没有字节对齐的话,变量j
存放在位置4
就行了,但是因为字节对齐,int a
,也占用4个字节,且存放的起始位置是4的整数倍,所以变量a
必须从位置8
开始存放。所以变量j
占用位置为4-7
。同理,int a
存放位置为8-15
,double b
存放的位置为16-23
。此时整个结构体的大小是24,正好是8的整数倍,符合结构体的对齐原则,所以sizeof(S1)=24
。
(2)对于结构体S2,参照结构体S1的分析,int i
存放在0-3
,char j
存放在4-7
,double b
存放在8-15
,int a
存放在16-19
。此时结构体大小为20个字节,但是由于结构体对齐原则(结构体的起始地址是最宽成员类型的整数倍),也就是起始位置必须是8的整数倍,所以sizeof(S2)=24
,而不是等于20。
执行结果:
代码:
#include<stdio.h>
struct S1{ //有杂注,n=1
int i; //0-3
char j; //4
int a; //5-8
double b; //9-16
};
struct S2{ //有杂注,n=1
int i; //0-3
char j; //4
double b; //5-12
int a; //13-16
};
int main() {
struct S1 s1;
struct S2 s2;
printf("%d\n", sizeof(s1));
printf("%d\n", sizeof(s2));
return 0;
}
分析:
(1)对于结构体S1,int i
占用4个字节,并且存放的起始地址是4的整数倍,由于刚开始是从0
开始的,自动满足,存储位置为0-3
。char j
占用1个字节,由字节对齐具体原则3.2,char j
的首地址是min(1,1)
的整数倍,也就是1
的整数倍,所以存放位置为4
。同理int a
的首地址也是1的整数倍,存放位置为5-8
;double b
的首地址也是1
的整数倍,存放位置为9-16
。此时结构体占用的空间是17,由字节对齐具体原则3.1,结构体的占用空间必须是min(1,8)
的整数倍,也就是1
的整数倍,所以17
是符合的。
(2)对于结构体S2,参照结构体S1的分析。
结果:
代码:
#include<stdio.h>
struct S1{ //有杂注,n=2
int i; //0-3
char j; //4-5
int a; //6-9
double b; //10-17
};
struct S2{ //有杂注,n=2
int i; //0-3
char j; //4-5
double b; //6-13
int a; //14-17
};
int main() {
struct S1 s1;
struct S2 s2;
printf("%d\n", sizeof(s1));
printf("%d\n", sizeof(s2));
return 0;
}
分析:
(1)对于结构体S1,int i
占用4个字节,并且存放的起始地址是4的整数倍,由于刚开始是从0
开始的,自动满足,存储位置为0-3
。char j
占用1个字节,由字节对齐具体原则3.2,char j
的首地址是min(1,2)
的整数倍,也就是1
的整数倍,所以应该存放位置为4
。但是int a
的首地址是min(4,2)
的整数倍,也就是2
的整数倍,所以char j
的存放位置为4-5
。int a
的存放位置为6-9
,double b
的存放位置为10-17
。此时结构体的大小为18,也符合字节对齐具体原则3.1。所以sizeof(S1)=18
。
(2)对于结构体S2,参照结构体S1的分析。
结果: