结构体对齐(Struct Alignment):是计算机编程中的一个概念,通常用于描述编译器如何安排结构体的成员在内存中的存储方式。
在很多计算机体系结构中,访问未对齐的数据可能会导致性能下降,甚至是程序崩溃。为了优化内存访问,编译器会对结构体进行对齐,即按照某种规则将结构体的成员排列在内存中,以便于有效地访问。这种对齐通常是根据硬件平台的要求来进行的。
具体对齐值的设置以及对齐值的用处我们在实际例子中进行阐述。
举个例子,设置如下结构体:
typedef struct _INFO {char szValue[6];float fValue;double dbValue;short sValue;int nValue;char cValue; }Info,*Info;
定义完结构体后使用sizeof()计算该结构体的大小:
int main() { printf("sizeof Info:%d", sizeof(Info));system("pause");}
这个结构体的大小是如何计算而来的呢?
这个时候就需要引出一个概念
成员偏移量:
结构体成员偏移量(Struct Member Offset)指的是结构体中每个成员相对于结构体起始地址的偏移量。这个偏移量表示了该成员在内存中的存储位置。
那么现在我上面的结构体进行初始化,根据内存中的内容进行计算。
Info info = { "wolve",12.5f,100.5,0xAAAA,0xBBBBBBBB,'c' };
打个断点,进行内存查看。
内存的内容如下(以32位的格式进行排列)
实际的内容在c(ascii = 63)处结束;
77 6f 6c 76 65 00 cc cc 00 00 48 41 cc cc cc cc 00 00 00 00 00 20 59 40 aa aa cc cc bb bb bb bb 63
发现目前结构体的存储内容所需要的实际内存只有33字节,并且编译器在某些地方会自动补入cc字符进行填充。这个结果其实就是由结构体的成员偏移量引起的。
成员偏移量的计算公式
member offset % min(ZpValue,sizeof(member type)) == 0;
member offset:表示结构体成员偏移量;
ZpValue:表示对齐值;
对齐值可以在Visual Studio中进行设置。
MVSC默认对齐是8
min():取后面数列最小值;
sizeof(member type):成员类型大小;
min(ZpValue, sizeof(member type))表示 ZpValue 和成员类型大小中的较小值。
% : 表示取模运算符,即取偏移量除以最小对齐值和成员类型大小之间的较小值的余数。
==0:表示取模的结果必须为 0,即偏移量必须能够整除最小对齐值和成员类型大小之间的较小值。
介绍完公式后继续以上述内存中的内容为例子进行演示:
内存:
77 6f 6c 76 65 00 cc cc 00 00 48 41 cc cc cc cc 00 00 00 00 00 20 59 40 aa aa cc cc bb bb bb bb 63
结构体:
typedef struct _INFO { //格式:成员偏移量char szValue[6]; //0float fValue; //理论偏移量:6;实际偏移量:member offset % min(8,4) == 0; 在计算的时候先将理论偏移量带入(member offse) => 6 % 4 == 0? 显然6除以4取余不为0,那么这个时候就持续在理论偏移量上+1,直到偏移量为8的时候,余为0,这个时候float类型成员实际成员偏移量为8(在上述内存的内容中已经体现);接着在该基础上继续计算后续几个类型的成员偏移量。double dbValue; //理论偏移量:8+4=12,实际偏移量:16 因为16 % min(8,8) == 0(所以double在第17字节开始存储)short sValue; //理论偏移量:16+8=24 ,实际偏移量:24 因为 24 % min(8,2) == 0(所以short在第15字节位置开始存储)int nValue; //理论偏移量:24+2=26,实际偏移量:28 因为 28 % min(8,4) == 0(所以int成员在第28字节位置开始存储)char cValue; //理论偏移量:28+4=32,实际偏移量:32 因为 32 % min(8,1) == 0(所以int成员在第28字节位置开始存储) }Info,*Info; 最后实际偏移32 + 1 (char成员大小) == 33 (得到实际存储成员所用的大小) 偏移量:在该数据前面存储着多大的数据
但是为什么sizeof()函数在计算结构体大小的时候得到的值是40呢?
原因在计算整个结构体大小的时候还需要满足另外一个公式:
Struct size % Max(Member type) == 0 这个表达式描述了结构体的大小必须是结构体中最大成员的大小的倍数。 Struct size 表示结构体的总大小。Max(Member type) 表示结构体中成员类型的最大大小。 因此,这个表达式确保了结构体的大小能够容纳结构体中最大的成员,并且结构体的布局能够在内存中正确对齐,从而避免了浪费内存空间和可能导致的对齐问题。
这个时候我们接着带入计算
Struct size = 初步等于33(也就是刚才计算出来的结构体实际存储空间大小) Max(Member type) = 8 (该结构体中成员最大类型宽度) 33 % 8 == 0 ? 很显然33除以8余数不为0,那么还是一样持续+1,指导Struct Size结构体大小为40时可以整除8; 所以sizeof()计算出该结构体大小为40。
最后,该结构体实际在内存中存储为:
77 6f 6c 76 65 00 cc cc 00 00 48 41 cc cc cc cc 00 00 00 00 00 20 59 40 aa aa cc cc bb bb bb bb 63 cc cc cc cc cc cc cc cc