struct结构体的大小,并不是简单地把每个成员的大小加起来,这里涉及到内存对齐的概念。虽然计算机中是以字节编址,但许多类型的长度不止一个字节。为了更快的存取这些类型,一般会把这些类型的初始地址存放到能整除长度的偏移量上。例如某个机器上int长度是4个字节,那么一个int变量的起始偏移量要被4整除,这也就是内存对齐。
struct中字节对齐的一般规则
1. 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2. 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;
3. 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <stdio.h> struct A{ char a; int b; float c; double d; char *e; int *f; short g; }; int main(void) { printf("sizeof(char) = %d\n", sizeof(char)); printf("sizeof(int) = %d\n", sizeof(int)); printf("sizeof(float) = %d\n", sizeof(float)); printf("sizeof(double) = %d\n", sizeof(double)); printf("sizeof(char *) = %d\n", sizeof(char *)); printf("sizeof(int *) = %d\n", sizeof(int *)); printf("sizeof(short) = %d\n", sizeof(short)); printf("%d\n", sizeof(struct A)); return 0; } |
这个程序的输出如下:
1 2 3 4 5 6 7 8 |
sizeof(char) = 1 sizeof(int) = 4 sizeof(float) = 4 sizeof(double) = 8 sizeof(char *) = 4 sizeof(int *) = 4 sizeof(short) = 2 sizeof(struct A) = 40 |
首先A这个结构体里
char a 大小1字节,起始偏移量0
接下来是一个int,大小4字节,如果接着char分配,则起始偏移量为1,不被4整除,因此插入3个字节的填充,int b的起始偏移量是4.
float占用4个字节。
double可用的起始地址是4+4+4=12 ,不能被8整除,因此填入4个字节的空白,double从16开始存放。
char *和int *的大小和int一样,是4个字节。
下面一个short,大小为2,起始地址为16+8+4+4=32
short结束后占用了34个字节,因为结构体中最长的double是8个字节,所以总大小被8整除,在最后面加进去6个字节,最终大小是40字节。
#pragma pack([n|push|pop])
这个预处理指令可以指定对齐的方式。
#pragma pack(n):
n可以取以下这5个数中的任意一个:1、2、4、8、16,可以自定义结构体成员的对齐方式。
#pragma pack(push):
英文单词push是“压”的意思。编译器编译到此处时将保存对齐状态。
#pragma pack(pop):
英文单词pop是”取“的意思。编译器编译到此处时将恢复保存时的对齐状态(在使用该预处理命令要使用#pragma pack(push)保存状态)。