上一篇使用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结构体:
测试结构体:
结论:对于结构体变量,要按照结构体最大的基本数据类型进行对齐。结构体变量按顺序分配变量,分配完一个成员后,如果刚好占了对齐字节大小,则下一个继续分配,如果占不够对齐字节大小,则看下一个成员的大小是否可以填在它空出的字节处,如果可以,则跟着存储,否则空出剩余字节,重新分配新的字节,保证对齐。
位域分析
- Zh_t包含两个int位域,w是5位,w1是4位, 可以看到该结构体大小是4个字节。并且w和w1是连续存储,字节区块都是0.
- char5位和int4位,大小仍是4字节,依旧连续存储。
- char2位和char4位,大小是1个字节,连续存储。
- w-char2位和w1-char4位,w2-char3位,大小是2个字节,w和w1是连续存储,但是由于char占用1个字节存储,w和w1占了6位,剩余2位,w2没法全部填满到这一字节,因此空出2位,存放到下一个字节。
结论:结构体里的位域按照最大的类型分配内存,如果没有超出最大类型的字节数,则永远连续分配;如果发现内存未分配的剩余的位数不够新成员的位数,则空出剩余位数,重新分配。