最近在看之前项目上的代码,发现了#pragma pack(n)这个预处理语句,就看不太懂,自己的C语言基础还停留在大学学的那半学期的水平,一通扒拉,发现了这么一个知识点,记录一下。
那这个预处理语句的作用是什么呢,其实就是要求编译器对结构体进行分配内存时按照我们要求的规则进行对齐并分配内存。那这是什么意思呢,这就要从结构体的默认对齐规则开始说起。假设我们要定义了一个这样两个结构体:
struct aa {
char a;
int b;
double c;
}
struct bb {
char a;
double b;
int c;
}
结构体aa和bb他们占用内存一样吗,如果一样的话他们占用的内存大小是13(1+4+8)吗?其实,他们占用的内存空间即不一样而且还都不是13。这是为什么呢,其实这就是结构体默认的对其导致的,结构体的对其的规则总结起来有三条
1.第一个成员变量对齐到结构体初始位置,即0偏移处
2.从第二个成员变量开始,对齐到该变量对齐数的整数倍数处
3.结构体的最终大小必须是成员变量中最大对齐数的整数倍
按照这三条规则我们分别来看结构体aa和结构体bb分别分配多少内存,首先来看aa的情况,根据规则1,a从0开始偏移,因为是char类型,占一个字节。再看吧,根据规则2,对其到该变量的整数倍,又因为数据类型是int,占4个字节,所以从第5个字节开始放,同理,因为c是double,占8个字节,所以从第九个字节开始放。所以结构体aa的内存分配情况如下图所示,总共占用了16个字节:
接着我们看一下结构体bb,根据规则1,规则2,a,b,c的对其方式如下,但是根据规则3
结构体的最终最终大小要为最大对齐数的整数倍(3*8=24),所以,我们要补上最后的四个字节。
说到这里,结构体对齐规则就介绍完成了,接下来,说会前面的pragma pack(n),他的作用就是忽视掉这些默认规则,按照开发人员的自己的意愿来对其,其中这个n指定的就是对的字节数,这里的n必须是2的阶乘,他要配合#pragma pack(push)和#pragma pack(pop),直接看代码:
#include <stdio.h>
struct aa {
char a;
int b;
double c;
} A;
struct bb {
char a;
double b;
int c;
} B;
#pragma pack(push)
#pragma pack(1)
struct cc {
char a;
int b;
double c;
} C;
#pragma pack(2)
struct dd {
char a;
double b;
int c;
} D;
#pragma pack(pop)
int main()
{
printf("A大小为:%d \n", sizeof(A));
printf("B大小为:%d \n", sizeof(B));
printf("C大小为:%d \n", sizeof(C));
printf("D大小为:%d \n", sizeof(D));
}
?代码的输出结果为:
A大小为:16
B大小为:24
C大小为:13
D大小为:14
按 <RETURN> 来关闭窗口...
在上面的代码中,最先定义的两个结构使用的默认的对其规则,我们进行了验证,从#pragma pack(push)这里开始,我们分别使用了,字节数是1和2的对齐规则进行结构内存分配,所以,C和D内存分配情况分别是13(1+4+8)和14(2+4+8)最后#pragma pack(pop) 恢复默认的对齐规则,这里#pragma pack(push)和#pragma pack(pop)需要配合使用。
|