结构体大小的怪问题

昨天施有个问题问我,就是一个分配了3个WORD和2个DWORD的结构体,计算长度是不是14而是16,我研究了一下,找到原因所在,花了点时间把分析写成邮件发送给他,后来一想,既然已经写出来了,何不发到我的CSDN 的blog上?于是就有了这篇拙文

WORD的字长是2,DWORD的字长是4,3个WORD和2个DWORD总共应该是14字节,但是在众多编译器上(gcc,bcc32和cl )均是16字节,于是写了个测试代码来简单分析一下。

我的测试代码如下:
#include        <iostream>
#include        <windows.h>
using namespace std;

struct  STR1
{
        WORD x;
        WORD c;
        WORD v;
//        WORD z; //暂时先屏蔽此代码
        DWORD b;
        DWORD n;

};

int main()
{

        STR1 s1;

        s1.x = 1;  //1语句
        s1.c = 2;  //2语句
        s1.v = 3;  //3语句
        s1.b = 4;  //4语句
        s1.n = 5;  //5语句
// s1.z = 6;  //6语句 //也先暂时屏蔽


        cout << sizeof(s1) << endl; //7语句
        cin.get();
}

编译执行后,显示STR1的尺寸是16,ok,保存生成的二进制文件,用IDA反编译此文件,分析结果如下:
.text:00401294 ; int __cdecl main(int argc,const char **argv,const char *envp)  ;这个就是主函数了
.text:00401294 _main           proc near               ; DATA XREF: .data:0043804Co
.text:00401294
.text:00401294 var_10          = word ptr -10h   ;开始在栈中分配空间,注意这里开始分配结构体空间
.text:00401294 var_E           = word ptr -0Eh
.text:00401294 var_C           = word ptr -0Ch
.text:00401294 var_8           = dword ptr -8
.text:00401294 var_4           = dword ptr -4
.text:00401294 argc            = dword ptr  8
.text:00401294 argv            = dword ptr  0Ch
.text:00401294 envp            = dword ptr  10h
.text:00401294
.text:00401294                 push    ebp   
.text:00401295                 mov     ebp, esp    ;开辟新的栈空间,调用函数,保存现场
.text:00401297                 add     esp, 0FFFFFFF0h   ;需要用的内存大小
.text:0040129A                 mov     [ebp+var_10], 1   ;对应C源码中的1语句
.text:004012A0                 mov     [ebp+var_E], 2   ;后面类推
.text:004012A6                 mov     [ebp+var_C], 3
.text:004012AC                 mov     [ebp+var_8], 4
.text:004012B3                 mov     [ebp+var_4], 5
.text:004012BA                 push    offset sub_401308  ;第7语句
.text:004012BF                 push    10h
.text:004012C1                 push    offset unk_445AA0
.text:004012C6                 call    @$beql$qrx5_GUIDt1 ; operator==(_GUID &,_GUID &)
.text:004012CB                 add     esp, 8
.text:004012CE                 push    eax
.text:004012CF                 call    sub_4012FC
.text:004012D4                 add     esp, 8
.text:004012D7                 push    offset unk_445A18
.text:004012DC                 call    sub_401334
.text:004012E1                 pop     ecx
.text:004012E2                 xor     eax, eax
.text:004012E4                 mov     esp, ebp
.text:004012E6                 pop     ebp
.text:004012E7                 retn
.text:004012E7 _main           endp


这里要注意的是0040129A 开始执行的代码,注意分到栈空间是-10, -e, -c, -8, -4, 对应的结构体的数据类型并不完全对应,各相应分配为2,2, 4, 4, 4 字节数,共计16字节,假设分配时栈空间从0012FF7C 开始,则执行1语句后,栈结果如下:
0012FF7C > 00440001 <<结构体的开始,分配了2字节
0012FF80 > 00000001
0012FF84 > 00000100
0012FF88 > 00000001
0012FF8C   0012FFB8
执行2语句后
0012FF7C > 00020001 <<结构体分配两字节,2被分配
0012FF80 > 00000001
0012FF84 > 00000100
0012FF88 > 00000001
0012FF8C   0012FFB8
执行3语句后
0012FF7C > 00020001
0012FF80 > 00000003 <<又被分配两字节,3被分配,注意下步操作将分配DWORD类型数据
0012FF84 > 00000100
0012FF88 > 00000001
0012FF8C   0012FFB8
执行4语句后
0012FF7C > 00020001
0012FF80 > 00000003
0012FF84 > 00000004 <<4被分配到这里了,而不是0012FF82,因为这里分配的是DWORD类型
0012FF88 > 00000001
0012FF8C   0012FFB8
执行5语句后  
0012FF7C > 00020001
0012FF80 > 00000003
0012FF84 > 00000004
0012FF88 > 00000005 <<5被分配
0012FF8C   0012FFB8

这里当执行第三步命令后,由于下个指令是分配DWORD,也就是4字节立即数的时候,将会分配到下一个正好被32位数整除的内存地址中(这样CPU计算寻址会更方便,因为现在的CPU都是32位字长指令的,但也会对我们造成一些困扰),也就是说 0012FF80 地址的后半部分并没有使用,但却占用了空间,虽然是一些浪费,但是intel认为这样换来的计算寻址会更迅速。

再多一步:
这回将源码中结构体中屏蔽的代码解除屏蔽(也将main函数中的屏蔽解除掉),然后在编译运行,这时结果仍然是16,再观察
执行6语句后  
0012FF7C > 00020001
0012FF80 > 00060003 <<6被分配在这里
0012FF84 > 00000004
0012FF88 > 00000005 
0012FF8C   0012FFB8

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值