结构体字节对齐之嵌套结构体

  • 搜狐畅游2020游戏研发笔试题目:以下输出的结果是???
#include <iostream>
#pragma pack(2)
struct S1
{
    S1() { f = 0; s = 0; i = 0; c = 0; }
    float f;
    short s;
    int i;
    char c;
};
 
#pragma pack(push)
#pragma pack(16)
struct S2
{
    S2() { d = 0; c = 0; i = 0; }
    double d;
    S1 s1;
    char c;
    int i;
};
#pragma pack(pop)
 
int main()
{
    std::cout << sizeof(S2) << std::endl;
}
  • 结构体对齐的规则大家都清楚,着重说一下结构体中嵌套结构体的情况:以我的测试看来,结构体内的结构体不是展开另算,如有错误,欢迎批评改正

#pragma pack(n) 表示的是设置n字节对齐
1.对于结构的各个成员,第一个成员位于偏移为0的位置,以后的每个数据成员的偏移量(相对于首地址)必须是
min(这个数据成员的自身长度,n)的倍数
2.在所有的数据成员完成各自对齐之后,结构或联合体本身也要进行对齐,也就是总大小必须是
min(长度最长的基本数据成员,n)的倍数
基本数据成员:char,short int,int,float,double等

  • S2大小为32,S1的大小为12,计算方式如下
#pragma pack(2)
struct S1
{
    S1() { f = 0; s = 0; i = 0; c = 0; }
    float f;//4字节【规则1偏移量0】
    short s;//2字节【规则1,偏移量2,是min(2,2)=2的倍数】
    int i;//4字节【规则1,偏移量6,是min(4,2)=2的倍数】
    char c;//1+1字节【规则1,偏移量10,是min(1,2)的倍数】
};//【规则2,总字节大小4+4+2+1=11字节,不是min(4,2)=2的倍数,在最后一个数据成员处补1字节,以对齐】->总大小12字节
#pragma pack(16)
struct S2
{
    S2() { d = 0; c = 0; i = 0; }
    double d;//8字节
    S1 s1;//S1没有展开,按12字节不变,按规则1以1字节对齐,相当于char ss[12]
    char c;//1字节【规则1,偏移量20,满足】+3字节
    int i;//4字节【规则1,偏移量21,不是min(4,16)=4的整数倍,在上一个数据成员后补3字节,使偏移量为24】+4字节
};//【规则2,总大小8+12+(1+3)+4=28字节,不是min(8,16)=8的倍数,在最后一个数据成员处补4字节,使总大小为32以对齐】->总大小32字节
  • 之所以这题将S1展开算也是32,是因为S1上的设置的是按2字节对齐#pragma pack(2),若设置的按1字节对齐#pragma pack(1),展开算依然是32,但是实际大小却是24
    在这里插入图片描述
  • 测试用例如下
#pragma pack(1) 
class Demo
{
	short int a;
	char ss[3];
};
#pragma pack(push)
#pragma pack(4)
struct test
{
	char ss[3];
	Demo dd;
	int a;
};
  • 根据结构体对齐规则可知,Demo的大小:
#pragma pack(1) 
class Demo
{
	short int a;//2字节【规则1偏移量为0】
	char ss[3];//3字节【规则1偏移量为2,是min(1,1)=1的整数倍】
};
//【规则2:总大小5字节,是min(1,2)=1的倍数】->Demo大小:5字节
  • 计算test的大小时,对于嵌套的结构体大小,若按照展开计算
方式1:直接展开
#pragma pack(4)
struct test
{
	char ss[3];//3字节->3+1字节
	class Demo
{
	short int a;//2字节【规则1,偏移量要是min(2,4)=2的倍数,所以要在前一个数据成员后补1字节】
	char ss[3];//3字节->3+3字节【规则1满足】
};
	int a;//4【规则1,偏移量4+2+3=9,不是4的整数倍,补到12】
};
//【规则2:总字节大小16,是4的整数倍】->test大小16
方式2:先算test本身,再算Demo
#pragma pack(4)
struct test
{
	char ss[3];//3+1字节
	int a;//4字节
};//->8字节
//单独算Demo
#pragma pack(4)
class Demo
{
	short int a;//2字节
	char ss[3];//3+1字节
};//->6字节
//test总大小14字节
  • 展开计算test大小时,两种方式计算的大小分别为16,14,然而实际的test大小是12
    在这里插入图片描述
  • 这个12字节是这样计算来的
#pragma pack(4)
struct test
{
	char ss[3];//3字节
	Demo dd;//Demo没有展开,按5字节不变,按规则1以1字节对齐,相当于char ss[5]
	int a;//4字节,偏移量8,满足规则1
};
//总大小3+5+4=12字节,是min(4,4)=4的倍数,满足规则2
  • 如果将Demo上的#pragma pack(1)改为#pragma pack(2),Demo的大小变为6字节,test的大小就是16字节
  #pragma pack(4)
struct test
{
	char ss[3];//3字节
	Demo dd;//Demo没有展开,按6字节不变,按规则1以1字节对齐,相当于char ss[5]
	int a;//4字节,偏移量9,不满足规则1,要在Demo后补3字节以使偏移量为12,成为4的倍数
};//总大小:3+(6+3)+4=16字节
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值