使用.debug_info调试信息查看结构体、位域变量内存分配

结构体与位域内存分配的.debug_info分析
本文通过.debug_info调试信息深入分析结构体和位域的内存分配规则,探讨不同成员类型如何影响内存对齐,以及位域如何连续存储和填充内存。

       上一篇使用readelf查看了.debug_info调试信息,现在我们对它进行分析。首先将调试信息保存到文档中:
readelf -wi test > out.txt

结构体分析

       首先,我在结构体里定义了一个char型和一个double型,编译连接后查看调试信息,然后改变结构体中成员类型,查看内存分配方式。
1. 在调试信息中找到main函数,可以看到main函数前面的<1>表示他是第一级。DW_AT_frame_base表示栈指针的位置。DW_AT_low_pc和DW_AT_high_pc表示这段程序的开始和结束位置。紧接着往下看,可以看到一个块,就是<2>处。再往下就可以看到快里面有我定义的一个结构体:blabla。DW_OP_plus_ fbreg:-32表示栈指针减去32就可以得到该变量的位置。
这里写图片描述
2. 根据DW_AT_type即变量的类型:0x1353在调试信息里面找。如图:
这里写图片描述

可以看到,这个结构体大小是16个字节。第一个成员名字是a,它的内存从结构体开始处分配,在调试信息里找它的类型,可以看到它是char型,大小是一个字节:
这里写图片描述
第二个成员b,它的内存从结构体的第8个字节开始处分配,查看它的类型是double 型,8个字节:
这里写图片描述
说明分配内存时为了对齐,虽然char类型只占1个字节,但是一个double类型8个字节,没法补到剩余的7个字节去,所以空出了7个字节。
3. 改变结构体成员定义顺序,先定义double,后定义char,结果一样:
这里写图片描述
4. 接着我将结构体成员类型改为char 和 int,查看结果:

这里写图片描述
a是char,b是int
这里写图片描述
同样为了对齐将char后面的三个字节空了出来。
5. 将结构体成员类型定义为int 和double:
这里写图片描述
int后面的4个字节空出。
6. 再重新定义成员为:char,int,double。发现并不是将char和int后面的都空出来,而是将char后面空出三个字节,相当于补成和int一样的4字节,然后二者合起来正好8字节对齐。
这里写图片描述
7. char,char,double:发现第二个char紧接着第一个char,然后又空出6个字节,和double对齐。
这里写图片描述
8. char,double,char:此时发现由于两个char没有在一起定义,因此第一个char占用一个字节,后面7个字节都空着,为了和double对齐,第二个char同理。这两种结构体看起来大小一样,但实际上这种定义方式比上一种要占用更大的空间,
这里写图片描述
9. 接着我在这个结构体里定义了另一个结构体e,e结构体包含3个double成员,查看结果:
c是char型,e是结构体,大小是24字节,char按照double对齐。

这里写图片描述
10. 将e结构体定义为double,int,测试发现char按照double对齐:
e结构体:
这里写图片描述
测试结构体:
这里写图片描述
结论:对于结构体变量,要按照结构体最大的基本数据类型进行对齐。结构体变量按顺序分配变量,分配完一个成员后,如果刚好占了对齐字节大小,则下一个继续分配,如果占不够对齐字节大小,则看下一个成员的大小是否可以填在它空出的字节处,如果可以,则跟着存储,否则空出剩余字节,重新分配新的字节,保证对齐。

位域分析

  1. Zh_t包含两个int位域,w是5位,w1是4位, 可以看到该结构体大小是4个字节。并且w和w1是连续存储,字节区块都是0.
    这里写图片描述
  2. char5位和int4位,大小仍是4字节,依旧连续存储。
    这里写图片描述
  3. char2位和char4位,大小是1个字节,连续存储。
    这里写图片描述
  4. w-char2位和w1-char4位,w2-char3位,大小是2个字节,w和w1是连续存储,但是由于char占用1个字节存储,w和w1占了6位,剩余2位,w2没法全部填满到这一字节,因此空出2位,存放到下一个字节。
    这里写图片描述
    结论:结构体里的位域按照最大的类型分配内存,如果没有超出最大类型的字节数,则永远连续分配;如果发现内存未分配的剩余的位数不够新成员的位数,则空出剩余位数,重新分配。
void Key_Pro(void) { u8 Key_num; for(Key_num = 0; Key_num < KEY_NUM; Key_num++) { if(GPIO_ReadInputDataBit(KEy[Key_num].KeyPort, KEy[Key_num].KeyPin) == 0) { KEy[Key_num].KeyAge++; if(KEy[Key_num].KeyAge > KEYGATE) { KEy[Key_num].KeyPress = 1; } if(KEy[Key_num].KeyAge > LONGAGE) { KEy[Key_num].KeyAge = LONGAGE + 1; ContinuePress(); } if(SysInfo.AlarmClockOk) { SysInfo.AlarmClockOk = 0; SysFlag.UpOver = 0; //return; } } else//警报时,执行这里 { if((KEy[Num_Power].KeyPress) && (KEy[Num_Alm].KeyPress) && (!SysFlag.First_Press)) { KEy[Num_Power].KeyPress = 0; KEy[Num_Alm].KeyPress = 0; SysInfo.AlarmClockOk = 0; SysFlag.UpOver = 0; SysData.Sys_State = SET_STATE; SysData.Set_State = SET_ALM_CLOCK_HOUR; SysData.SET_Value = SysInfo.AlarmClockInfo.hour; ResetAlarmClockTime(); SysFlag.DispSetEn = 0; Delayms(200); return; } else if((KEy[Key_num].KeyPress) && (KEy[Key_num].KeyProcessed == 0) && (!SysFlag.First_Press)) { if(SysInfo.AlarmClockOk) { SysInfo.AlarmClockOk = 0; SysFlag.UpOver = 0; return; } if(SysFlag.BiBiBi) { SysFlag.BiBiBi = 0; SysFlag.UpOver = 0; ALM_OUT_EN(DISABLE); SysData.Wait = 33; //10*150=1500ms KEy[Key_num].KeyPress = 0; KEy[Key_num].KeyAge = 0; } else { KEy[Key_num].fun(SHORT_PRESS); } KEy[Key_num].KeyProcessed = 1; } if((KEy[Num_Up].KeyPress == 0) && (KEy[Num_Down].KeyPress == 0) && (KEy[Num_Alm].KeyPress == 0) && (!SysData.Cal_Mode)) { KeyHold = 0; KeyOffset = 1; SysData.WaitReleaseKey++; if(SysData.WaitReleaseKey > 20 * KEY_NUM) //18*50=1000ms { SysData.WaitReleaseKey = 0; SysFlag.FlickEn = 1; if((SysData.Sys_State == SET_STATE) && (SysData.Set_State == SET_ALM_Type)) { switch(SysData.SET_Value) { case 1: Set_Buzz_Pluse(BUZZ_0_LEVEL); break; case 2: Set_Buzz_Pluse(BUZZ_1_LEVEL); break; case 3: Set_Buzz_Pluse(BUZZ_2_LEVEL); break; } } } } if((KEy[Num_Alm].KeyPress == 1) && (SysData.Cal_Mode) && (Key_num == Num_Alm) && (SysFlag.Cal_En == 1)) { SysFlag.Cal_En = 0; // SysData.Cal_DB_Point++; // if(SysData.Cal_DB_Point > POINT_40DB) // { // SysData.Cal_DB_Point = POINT_100DB; // } SysData.Cal_Step = 0; SysFlag.Cal_Finish = 0; // SysFlag.Cal_DB_Finish = 0; SysFlag.FlickEn = 0; } KEy[Key_num].KeyAge = 0; KEy[Key_num].KeyPress = 0; KEy[Key_num].KeyProcessed = 0; if(Key_num == 0) { SysFlag.First_Press = 0x00;//Power key first press } } if((KEy[Key_num].KeyPress) && (KEy[Key_num].KeyProcessed == 0) && (KEy[Key_num].KeyAge > LONGAGE) && (!SysFlag.First_Press)) { KEy[Key_num].KeyAge = 0; KEy[Key_num].KeyProcessed = 1; KEy[Key_num].fun(LONG_PRESS); } if((SysFlag.First_Press) && (KEy[Num_Down].KeyPress) && (KEy[Num_Up].KeyPress)) { Enter_Cal(); } if((SysFlag.First_Press) && (KEy[Num_Alm].KeyPress)) { Clear_Rec(); KEy[Num_Alm].KeyPress = 0; SysFlag.First_Press = 0; } } }
06-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值