C进阶之:内存对齐

什么是内存对齐? 
内存对齐可以用一句话来概括: 
“数据项只能存储在地址是数据项大小的整数倍的内存位置上”。例如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 
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/phenixyf/article/details/85534693