学习C++反汇编-构造函数

来源:互联网 发布:c语言库函数大全 编辑:程序博客网 时间:2024/06/05 10:23

1.局部对象下的构造函数

C++源代码:

#include<iostream>using namespace std;class CNumber{public:int num;CNumber(){num = 1;}};int main(){CNumber Number;return 0;}
生成的汇编代码:

int main(){00C81750  push        ebp  00C81751  mov         ebp,esp  00C81753  sub         esp,0D0h  00C81759  push        ebx  00C8175A  push        esi  00C8175B  push        edi  00C8175C  lea         edi,[ebp-0D0h]  00C81762  mov         ecx,34h  00C81767  mov         eax,0CCCCCCCCh  00C8176C  rep stos    dword ptr es:[edi]  00C8176E  mov         eax,dword ptr ds:[00C88004h]  00C81773  xor         eax,ebp  00C81775  mov         dword ptr [ebp-4],eax  CNumber Number;00C81778  lea         ecx,[Number]  取得对象首地址传入ecx中作为参数  00C8177B  call        CNumber::CNumber (0C81348h)  调用构造函数return 0;00C81780  xor         eax,eax  }#include<iostream>using namespace std;class CNumber{public:int num;CNumber()00C816A0  push        ebp  00C816A1  mov         ebp,esp  00C816A3  sub         esp,0CCh  00C816A9  push        ebx  00C816AA  push        esi  00C816AB  push        edi  00C816AC  push        ecx  00C816AD  lea         edi,[ebp-0CCh]  00C816B3  mov         ecx,33h  00C816B8  mov         eax,0CCCCCCCCh  00C816BD  rep stos    dword ptr es:[edi]  00C816BF  pop         ecx  还原ecx,ecx中保存对象的首地址00C816C0  mov         dword ptr [this],ecx  传递给this指针{num = 1;00C816C3  mov         eax,dword ptr [this]  eax中保存了对象的首地址00C816C6  mov         dword ptr [eax],1  将数据成员设置为1}00C816CC  mov         eax,dword ptr [this]  将this指针存入eax中作为返回值00C816CF  pop         edi  00C816D0  pop         esi  00C816D1  pop         ebx  00C816D2  mov         esp,ebp  00C816D4  pop         ebp  00C816D5  ret
在进入对象的作用域时编译器会产生调用构造函数的代码。由于构造函数属于成员函数,因此在调用过程中同样需要传递this指针。构造函数调用结束后,会将this指针作为返回值。
2.堆对象下的构造函数

C++源代码:

#include<iostream>using namespace std;class CNumber{public:int num;CNumber(){num = 1;}};int main(){CNumber *Number=0;Number=new CNumber;Number->num=2;printf("%d \r\n",Number->num);return 0;}//用结构体定义了一个实体,那么要引用里面的成员就用.操作符//用结构体定义的是一个结构指针,那么要引用里面的成员就用->操作符 
生成的汇编代码:
int main(){002519C3  call        532519C8  002519C8  push        esi  002519C9  push        edi  002519CA  lea         edi,[ebp-0F4h]  002519D0  mov         ecx,3Ah  002519D5  mov         eax,0CCCCCCCCh  002519DA  rep stos    dword ptr es:[edi]  002519DC  mov         eax,dword ptr ds:[0025B004h]  002519E1  xor         eax,ebp  002519E3  push        eax  002519E4  lea         eax,[ebp-0Ch]  002519E7  mov         dword ptr fs:[00000000h],eax  CNumber *Number = 0;002519ED  mov         dword ptr [Number],0  指针初始化为0 Number = new CNumber;002519F4  push        4  压入类的大小用于堆内存的申请002519F6  call        operator new (02512C1h)  002519FB  add         esp,4  002519FE  mov         dword ptr [ebp-0ECh],eax  使用临时变量保存new返回值00251A04  mov         dword ptr [ebp-4],0  保存申请堆空间的次数00251A0B  cmp         dword ptr [ebp-0ECh],0  检测堆内存是否申请成功00251A12  je          main+77h (0251A27h)  失败则跳过构造函数00251A14  mov         ecx,dword ptr [ebp-0ECh]  申请成功将对象首地址传入ecx中00251A1A  call        CNumber::CNumber (02513FCh)  调用构造函数00251A1F  mov         dword ptr [ebp-0F4h],eax  00251A25  jmp         main+81h (0251A31h)  00251A27  mov         dword ptr [ebp-0F4h],0 申请堆空间失败设置指针值为NULL00251A31  mov         eax,dword ptr [ebp-0F4h]  00251A37  mov         dword ptr [ebp-0E0h],eax  00251A3D  mov         dword ptr [ebp-4],0FFFFFFFFh  00251A44  mov         ecx,dword ptr [ebp-0E0h]  00251A4A  mov         dword ptr [Number],ecx  Number->num = 2;00251A4D  mov         eax,dword ptr [Number]  eax得到this指针Number->num = 2;00251A50  mov         dword ptr [eax],2  为成员变量赋值printf("%d \r\n", Number->num);00251A56  mov         eax,dword ptr [Number]  00251A59  mov         ecx,dword ptr [eax]  00251A5B  push        ecx  00251A5C  push        258B30h  00251A61  call        _printf (02513C5h)  00251A66  add         esp,8  return 0;00251A69  xor         eax,eax  }
在使用new申请了堆空间以后需要调用构造函数以完成对象的数据成员初始化过程。如果堆空间申请失败则会避开构造函数的调用。因此需要编译器检查堆空间的申请结果产生一个双分支结构。

3.参数对象下的构造函数

C++源代码:

#include<iostream>using namespace std;class CMyString{public:char * m_pString;//无参构造函数 CMyString(){m_pString = 0;}//拷贝构造函数 CMyString(CMyString& obj){int nLen = strlen(obj.m_pString);this->m_pString = new char[nLen + sizeof(char)];strcpy(this->m_pString, obj.m_pString);}//析构函数 ~CMyString(){if (m_pString != NULL){delete[] m_pString;m_pString = NULL;}}//设置字符串的成员函数 void setString(char * pString){int nLen = strlen(pString);if (m_pString != NULL){delete[] m_pString;m_pString = NULL;}m_pString = new char[nLen + sizeof(char)];strcpy(m_pString, pString);}};//参数是对象类型会触发拷贝构造函数 void Show(CMyString MyString){printf(MyString.m_pString);}int main(){CMyString MyString;MyString.setString("Hello");Show(MyString);return 0;}
生成的汇编代码:

int main(){00115460  push        ebp  00115461  mov         ebp,esp  00115463  push        0FFFFFFFFh  00115465  push        116318h  0011546A  mov         eax,dword ptr fs:[00000000h]  00115470  push        eax  00115471  sub         esp,0ECh  00115477  push        ebx  00115478  push        esi  00115479  push        edi  0011547A  lea         edi,[ebp-0F8h]  00115480  mov         ecx,3Bh  00115485  mov         eax,0CCCCCCCCh  0011548A  rep stos    dword ptr es:[edi]  0011548C  mov         eax,dword ptr ds:[0011B004h]  00115491  xor         eax,ebp  00115493  mov         dword ptr [ebp-10h],eax  00115496  push        eax  00115497  lea         eax,[ebp-0Ch]  0011549A  mov         dword ptr fs:[00000000h],eax  CMyString MyString;001154A0  push        4  001154A2  lea         ecx,[MyString]  001154A5  call        CMyString::__autoclassinit2 (0111424h)  001154AA  lea         ecx,[MyString]  001154AD  call        CMyString::CMyString (0111433h)  调用无参构造函数001154B2  mov         dword ptr [ebp-4],0  MyString.setString("Hello");001154B9  push        118B30h  001154BE  lea         ecx,[MyString]  001154C1  call        CMyString::setString (011141Ah)  调用成员函数Show(MyString);001154C6  push        ecx  001154C7  mov         ecx,esp  获取参数对象的地址保存到ecx中001154C9  mov         dword ptr [ebp-0E4h],esp  001154CF  lea         eax,[MyString]  获取对象的地址保存到eax中 001154D2  push        eax  将MyString的地址作为参数调用拷贝构造函数001154D3  call        CMyString::CMyString (0111429h)  001154D8  mov         dword ptr [ebp-0F8h],eax  保存拷贝构造函数返回的this指针001154DE  call        Show (011141Fh)  001154E3  add         esp,4  return 0;001154E6  mov         dword ptr [ebp-0F0h],0  001154F0  mov         dword ptr [ebp-4],0FFFFFFFFh  001154F7  lea         ecx,[MyString]  001154FA  call        CMyString::~CMyString (011140Bh)  001154FF  mov         eax,dword ptr [ebp-0F0h]  }
在执行Show之前先进入到CMyString的拷贝构造函数中。由于使用了深拷贝的方式,对对象中的数据成员所指向的堆空间数据也进行了数据复制,因此当参数对象被销毁时,释放的堆空间数据是拷贝对象所制作的数据副本,对源对象没有任何影响。

4.返回对象下的构造函数

C++源代码:

#include<iostream>using namespace std;class CMyString{public:char * m_pString;//无参构造函数 CMyString(){m_pString = 0;}//拷贝构造函数 CMyString(CMyString& obj){int nLen = strlen(obj.m_pString);this->m_pString = new char[nLen + sizeof(char)];strcpy(this->m_pString, obj.m_pString);}//析构函数 ~CMyString(){if (m_pString != NULL){delete[] m_pString;m_pString = NULL;}}//设置字符串的成员函数 void setString(char * pString){int nLen = strlen(pString);if (m_pString != NULL){delete[] m_pString;m_pString = NULL;}m_pString = new char[nLen + sizeof(char)];strcpy(m_pString, pString);}};CMyString GetMyString(){CMyString MyString;MyString.setString("World");return MyString;}int main(){CMyString MyString=GetMyString();}
生成的汇编代码:

int main(){01351C90  push        ebp  01351C91  mov         ebp,esp  01351C93  sub         esp,0D0h  01351C99  push        ebx  01351C9A  push        esi  01351C9B  push        edi  01351C9C  lea         edi,[ebp-0D0h]  01351CA2  mov         ecx,34h  01351CA7  mov         eax,0CCCCCCCCh  01351CAC  rep stos    dword ptr es:[edi]  01351CAE  mov         eax,dword ptr ds:[0135B004h]  01351CB3  xor         eax,ebp  01351CB5  mov         dword ptr [ebp-4],eax  CMyString MyString = GetMyString();01351CB8  push        4  01351CBA  lea         ecx,[MyString]  01351CBD  call        CMyString::__autoclassinit2 (01351212h)  01351CC2  lea         eax,[MyString]  01351CC5  push        eax  01351CC6  call        GetMyString (0135120Dh)  01351CCB  add         esp,4  }CMyString GetMyString(){013519C0  push        ebp  013519C1  mov         ebp,esp  013519C3  push        0FFFFFFFFh  013519C5  push        1356568h  013519CA  mov         eax,dword ptr fs:[00000000h]  013519D0  push        eax  013519D1  sub         esp,0DCh  013519D7  push        ebx  013519D8  push        esi  013519D9  push        edi  013519DA  lea         edi,[ebp-0E8h]  013519E0  mov         ecx,37h  013519E5  mov         eax,0CCCCCCCCh  013519EA  rep stos    dword ptr es:[edi]  013519EC  mov         eax,dword ptr ds:[0135B004h]  013519F1  xor         eax,ebp  013519F3  mov         dword ptr [ebp-10h],eax  013519F6  push        eax  013519F7  lea         eax,[ebp-0Ch]  013519FA  mov         dword ptr fs:[00000000h],eax  01351A00  mov         dword ptr [ebp-0E4h],0  CMyString MyString;01351A0A  push        4  01351A0C  lea         ecx,[MyString]  01351A0F  call        CMyString::__autoclassinit2 (01351212h)  01351A14  lea         ecx,[MyString]  01351A17  call        CMyString::CMyString (013513CFh)  01351A1C  mov         dword ptr [ebp-4],0  MyString.setString("World");01351A23  push        1358B30h  01351A28  lea         ecx,[MyString]  01351A2B  call        CMyString::setString (01351136h)  return MyString;01351A30  lea         eax,[MyString] 获取局部变量的首地址 01351A33  push        eax  将对象的地址作为参数01351A34  mov         ecx,dword ptr [ebp+8]  01351A37  call        CMyString::CMyString (01351276h) 调用隐含的参数对象的拷贝构造函数,以局部对象的地址作为参数  01351A3C  mov         ecx,dword ptr [ebp-0E4h]  01351A42  or          ecx,1  01351A45  mov         dword ptr [ebp-0E4h],ecx  01351A4B  mov         dword ptr [ebp-4],0FFFFFFFFh  01351A52  lea         ecx,[MyString]  01351A55  call        CMyString::~CMyString (01351069h)  01351A5A  mov         eax,dword ptr [ebp+8]  将参数作为返回值}
虽然编译器会对返回值为对象类型的函数进行调整,修改其参数与返回值,但是它留下了一个与返回指针类型不同的特征,那就是在函数中使用拷贝构造函数。返回值和参数为对象指针类型的函数,不会使用以参数为目标的拷贝构造函数,而是直接使用指针保存对象首地址。
0 0
原创粉丝点击