什么是内存对齐?
内存对齐可以用一句话来概括:
“数据项只能存储在地址是数据项大小的整数倍的内存位置上”。例如int类型占用4个字节,地址只能在0,4,8等位置上。
不同类型的数据在内存中按照一定的规则排列,而不一定是顺序的一个接一个的排列,这就是所谓的内存对齐。如下Test1和Test2所占的内存空间是不同的。
struct Test1
{
char c1;
short s;
char c2;
int i;
}:
struct Test2
{
char c1;
char c2;
short s;
int i;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
为什么需要内存对齐?
CPU对内存的读取不是连续的,而是分成块读取的,块的大小只能是1、2、4、8、16..字节;当读取操作的数据未对齐,则需要两次总线周期来访问内存,因此性能会大打折扣;某些硬件平台只能从规定的相对地址处读取特定类型的数据,否则产生硬件异常。
内存对齐目的:是为了对齐数据以提高存储器的性能,加快内存的读取速度,提高寻址效率。
#pragma pack用于指定内存对齐方式
#pragma pack能够改变编译器的默认对齐方式
1
2
struct占用的内存大小的计算方法:
A,第一个成员起始于0偏移处。
B,每个成员按其类型大小和pack参数中较小的一个进行对齐(偏移地址必须能被对齐参数整除;结构体成员的大小取其内部长度最大的数据成员作为其大小)。
C,结构体总长度必须为所有对齐参数的整数倍。编译器在默认情况下按照4字节对齐。
#pragma pack(4)
struct Test1
{ //对齐参数 偏移地址 大小
char c1; // 1 0 1 (1 < 4 所以对齐参数是1)
short s; // 2 2 2 (2 < 4 所以对齐参数是2;偏移地址必须能被对齐参数整除,所以偏移地址是2)
char c2; // 1 4 1 (1 < 4 所以对齐参数是1;偏移地址必须能被对齐参数整除,所以偏移地址是4)
int i; // 4 8 4 (4 = 4 所以对齐参数是4;偏移地址必须能被对齐参数整除,所以偏移地址是8)
};
// 所以结构体总的内存大小为:8 + 4 = 12
// 验算:12是1,2,4的整数倍,满足:结构体总长度必须为所有对齐参数的整数倍。
#pragma pack()
#pragma pack(4)
struct Test2
{ // 对齐参数 偏移地址 大小
char c1; // 1 0 1
char c2; // 1 1 1
short s; // 2 2 2
int i; // 4 4 4
};
// 所以结构体总的内存大小为:4 + 4 = 8
// 验算:8是1,2,4的整数倍,满足:结构体总长度必须为所有对齐参数的整数倍。
#pragma pack()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
实例:结构体大小计算
练习一:
#pragma pack(1)
struct Test1
{ // 对齐参数 偏移地址 大小
char c1; // 1 0 1
short s; // 1 1 2
char c2; // 1 3 1
int i; // 1 4 4
}:
#pragma pack()
#pragma pack(1)
struct Test2
{ // 对齐参数 偏移地址 大小
char c1; // 1 0 1
char c2; // 1 1 1
short s; // 1 2 2
int i; // 1 4 4
};
#pragma pack()
sizeof(struct Test1) = 8
sizeof(struct Test2) = 8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
练习二:
#include <stdio.h>
#pragma pack(2)
struct Test1
{ // 对齐参数 偏移地址 大小
char c1; // 1 0 1
short s; // 2 2 2
char c2; // 1 4 1
int i; // 2 6 4
};
// 所以结构体总的内存大小为:6 + 4 = 10
// 验算:10是1,2的整数倍,满足:结构体总长度必须为所有对齐参数的整数倍。
#pragma pack()
#pragma pack(4)
struct Test2
{ // 对齐参数 偏移地址 大小
char c1; // 1 0 1
char c2; // 1 1 1
short s; // 2 2 2
int i; // 4 4 4
};
// 所以结构体总的内存大小为:4 + 4 = 8
// 验算:8是1,2,4的整数倍,满足:结构体总长度必须为所有对齐参数的整数倍。
#pragma pack()
int main()
{
printf("sizeof(Test1) = %d\n", sizeof(struct Test1));
printf("sizeof(Test2) = %d\n", sizeof(struct Test2));
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
练习三:
#include <stdio.h>
#pragma pack(8)
struct S1
{ // 对齐参数 偏移地址 大小
short a; // 2 0 2
long b; // 4 4 4 8
};
struct S2 // ( 结构体成员的大小取其内部长度最大的数据成员作为其大小 )
{ // 对齐参数 偏移地址 大小
char c; // 1 0 1
struct S1 d; // 4 4 8
double e; // 8 (4) 16 (12) 8 24
};
// 结构体成员的大小取其内部长度最大的数据成员4; 4 < 8 所以对齐参数是4。上面已算出struct S1 d的大小为8.
// 注意:gcc不支持8字节对齐,#pragma pack(8) ; 所以结果应该是12 + 8 = 20
#pragma pack()
int main()
{
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
内存对齐之结构体中添加位段:
struct B
{
char c1:6;
char c2:1;
int in:16;
};
// sizeof (b) = 4
1
2
3
4
5
6
7
标记 c1,c2,in 分别需要 6位 1位 16位 一个字节8位
补充说明:
struct packed_struct
{
unsigned intf1 :1;
unsigned intf2 :1;
unsigned intf3 :1;
unsigned inttype :4;
unsigned intindex :7;
};
// sizeof(structpackd_struct) = 4
1
2
3
4
5
6
7
8
9
当位段出现在结构定义中时, 至少会占用等同于unsigned int类型的空间,当所有的位段之和超出,分配另一个unsignedint空间。unsigned char或者其他类型不必考虑。
/**************************枚举类型***************************/
struct A
{
enum a
{
INDEX1,
INDEX2,
INDEX3,
};
} a ;
// sizeof(a) = 0 枚举是类型不是变量不分配变量
1
2
3
4
5
6
7
8
9
10
/**************************联合体****************************/
union B
{
short s1;
char c2;
short s2;
int in;
} b ;
union B
{
short s1:6;
char c2:1;
short s2:15;
int in:16;
} b ;
// 两个联合体 sizeof(b) 均为4
---------------------
作者:码农u号
来源:CSDN
原文:https://blog.csdn.net/qq_29545231/article/details/77447672
版权声明:本文为博主原创文章,转载请附上博文链接!