结构字节对齐
结构偏移
定义了结构类型后,结构中的成员内存布局就确定了。
包括
typedef结构测试
{
int a;
int b;
int c;
}
测试;
int main()
{
测试t;
Test * p = NULL
p = & t;
//b结构测试的偏移量,n1=4
int n1 =(int)&(p-> b)-(int)p;
//地址B的绝对0偏移,n2=4
int N2 =(int)&(((Test *)0)-> b);
printf("n1:%d \n ",n1);
printf("n2:%d \n ",N2);
返回0;
}
内存字节对齐
使用sizeof
运算符计算一个结构占用的空间时,并不是简单地将结构中所有元素占用的空间相加,这就涉及到内存字节对齐的问题。理论上,任何变量都可以从任何地址访问,但实际上,情况并非如此。实际上,访问特定类型的变量只能在特定的地址访问,这就要求变量在空间上按照一定的规则排列,而不是简单的按顺序排列。这是内存对齐。
测试代码:
包括
typedef结构测试
{
char a[5];//5个字节
int b;//4个字节
int c;//4个字节
}
测试;
int main()
{
测试t;
Test * p = NULL
p = & t;
//b n1相对于结构测试的偏移量是8,不是5。
int n1 =(int)&(p-> b)-(int)p;
//绝对0地址b的偏移量。
int N2 =(int)&(((Test *)0)-> b);
printf("n1:%d \n ",n1);
printf("n2:%d \n ",N2);
//sizeof(Test) = 8+4+4 = 16,而不是5+4+4。
printf("sizeof(测试)= %d\n ",sizeof(测试));
返回0;
}
内存对齐原因
有些平台只能在某些地址访问某些类型的数据;
提高访问数据的速度。例如,有些平台总是从偶数地址读取数据。对于一个int变量,如果它是从偶数地址单元存储的,那么读取该变量只需要一个读周期。但是,如果从奇数地址单元存储,则需要2个读取周期来读取变量。
内存对齐原则
原则1:数据成员的对齐规则(默认为最大类型字节)
对于结构的数据成员,第一个数据成员放置在偏移量为0的位置,随后每个数据成员存储在偏移量为数据成员类型大小的整数倍的位置(例如,如果int在32位机器中为4字节,则应该从4的整数倍地址存储)。
原则2:作为构件的结构的对齐规则
如果另一个结构A嵌套在一个结构B中,它仍然与最大成员类型的字节对齐,但结构A的存储起点是A中最大成员的整数倍(struct B
包含struct A
,A有char、int、double
等成员,所以A应该从8的整数倍开始存储。),结构A中成员的对齐规则仍然满足原则1和2。
注意:
1)结构A的大小是这个结构的成员中最大元素的整数倍,这是不够的。
2)而不是将结构A的成员直接移动到结构b中
原则3:完成工作
结构的总大小(即sizeof的结果)必须是其最大成员的整数倍,不足的部分应被填充。
默认情况下,它与结构中最大成员的类型字节大小对齐。当然,也可以通过编程来指定字节对齐的单位
注意:虽然字节对齐的单位可以由pragma pack()指定,但是指定的字节对齐单位只有在结构成员大于或等于pragma pack()指定的字节对齐单位时才有效;否则,它按结构中最大成员的类型字节大小对齐。
案例分析(64位架构平台测试)
结构的数据成员,第一个数据成员放置在偏移量0处,每个数据成员稍后存储在数据成员类型大小的整数倍的偏移量处。但是,如果数据成员类型大小大于字节对齐大小,则偏移量是字节对齐大小的整数倍。
普通结构
1起案件
结构体
{
char a;
int b;
短的
1字节对齐。
当与1个字节对齐时,结构的大小是成员空间大小的总和:
sizeof(A) = 1 + 4 + 2 = 7
用2个字节对齐。
答:1 * 0 = 0
B: 2 * 1 = 2 (int为4字节,大于对齐字节(2字节),按2字节计算)
c: 2 * 3 = 6
}
总而言之:
放置在偏移量为0的位置。因为A是一个大小为1字节的char类型,所以它占用了1个网格。
b放置在偏移量2处。因为B是一个int类型,大小为4个字节,占用4个方块,包含2行,而上面的A只占用1个字节,不足以对齐字节(2个字节),所以不足(用“*”描述)。
c放置在偏移量6处。因为C是短类型,大小为2个字节,所以占用2个方块。
因此
sizeof(A) = 8
与4个字节对齐。
答:1 * 0 = 0
乙:4 * 1 = 4
c: 2 * 4 = 8
尺寸(A) = 12
与8或16字节对齐。
由于结构成员的最大类型字节是4,小于8或16,尽管对齐字节是由pragma pack(xx)指定的,但它仍然与结构成员的最大类型字节或4字节对齐。
2起案件
结构体
{
char a[5];
双b;
短的
用2个字节对齐。
答:1 * 0 = 0
B: 2 * 3 = 6 (double为8字节,大于对齐字节(2字节),按2字节计算)
c: 2 * 7 = 14
结构嵌套结构
1起案件
结构A
{
char e;
短f;
};
结构体
{
int a;
char b;
结构A c;
}
当另一个结构A嵌套在结构B中时,与两个结构中最大的成员对齐,结构B中最大的成员是int,4字节与4字节对齐。但是,A中的最大成员很短,2个字节,A的存储起点必须是2的整数倍。
`答:4 * 0 = 0
乙:1 * 4 = 4`
结构的起始位置:2*3=6,是结构a最大成员的整数倍。
e: 6 + 1*0 = 6
f: 6 + 2*1 = 8
d: 1 * 10 = 10