在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
有下面的一个简单的类:
class CNullPointCall { public: static void Test1(); void Test2(); void Test3(int iTest); void Test4(); private: static int m_iStatic; int m_iTest; }; int CNullPointCall::m_iStatic = 0; void CNullPointCall::Test1() { cout << m_iStatic << endl; } void CNullPointCall::Test2() { cout << "Very Cool!" << endl; } void CNullPointCall::Test3(int iTest) { cout << iTest << endl; } void CNullPointCall::Test4() { cout << m_iTest << endl; } 那么下面的代码都正确吗?都会输出什么?
CNullPointCall *pNull = NULL; // 没错,就是给指针赋值为空 pNull->Test1(); // call 1 pNull->Test2(); // call 2 pNull->Test3(13); // call 3 pNull->Test4(); // call 4 你肯定会很奇怪我为什么这么问。一个值为NULL的指针怎么可以用来调用类的成员函数呢?!可是实事却很让人吃惊:除了call 4那行代码以外,其余3个类成员函数的调用都是成功的,都能正确的输出结果,而且包含这3行代码的程序能非常好的运行。
void CNullPointCall::Test4(CNullPointCall *this) { cout << this->m_iTest << endl; } 而把call 4那行代码转换成了下面的形式: CNullPointCall::Test4(pNull); 所以会在通过this指针访问m_iTest的时候造成程序的崩溃。
CNullPointCall *pNull = NULL; 0041171E mov dword ptr [pNull],0 pNull->Test1(); 00411725 call CNullPointCall::Test1 (411069h) pNull->Test2(); 0041172A mov ecx,dword ptr [pNull] 0041172D call CNullPointCall::Test2 (4111E0h) pNull->Test3(13); 00411732 push 0Dh 00411734 mov ecx,dword ptr [pNull] 00411737 call CNullPointCall::Test3 (41105Ah) pNull->Test4(); 0041173C mov ecx,dword ptr [pNull] 0041173F call CNullPointCall::Test4 (411032h) 通过比较静态函数Test1()和其他3个非静态函数调用所生成的的汇编代码可以看出:非静态函数调用之前都会把指向对象的指针pNull(也就是 this指针)放到ecx寄存器中(mov ecx,dword ptr [pNull])。这就是this指针的特殊之处。看call 3那行C++代码的汇编代码就可以看到this指针跟一般的函数参数的区别:一般的函数参数是直接压入栈中(push 0Dh),而this指针却被放到了ecx寄存器中。在类的非成员函数中如果要用到类的成员变量,就可以通过访问ecx寄存器来得到指向对象的this指 针,然后再通过this指针加上成员变量的偏移量来找到相应的成员变量。
class CTest { public: void SetValue(); private: int m_iValue1; int m_iValue2; }; void CTest::SetValue() { m_iValue1 = 13; m_iValue2 = 13; } 用如下的代码调用成员函数: CTest test; test.SetValue(); 上面的C++代码的汇编代码为: CTest test; test.SetValue(); 004117DC lea ecx,[test] 004117DF call CTest::SetValue (4111CCh) 同样的,首先把指向对象的指针放到ecx寄存器中;然后调用类CTest的成员函数SetValue()。地址4111CCh那里存放的其实就是一个转跳指令,转跳到成员函数SetValue()内部。 004111CC jmp CTest::SetValue (411750h) 而411750h才是类CTest的成员函数SetValue()的地址。
void CTest::SetValue() { 00411750 push ebp 00411751 mov ebp,esp 00411753 sub esp,0CCh 00411759 push ebx 0041175A push esi 0041175B push edi 0041175C push ecx // 1 0041175D lea edi,[ebp-0CCh] 00411763 mov ecx,33h 00411768 mov eax,0CCCCCCCCh 0041176D rep stos dword ptr es:[edi] 0041176F pop ecx // 2 00411770 mov dword ptr [ebp-8],ecx // 3 m_iValue1 = 13; 00411773 mov eax,dword ptr [this] // 4 00411776 mov dword ptr [eax],0Dh // 5 m_iValue2 = 13; 0041177C mov eax,dword ptr [this] // 6 0041177F mov dword ptr [eax+4],0Dh // 7 } 00411786 pop edi 00411787 pop esi 00411788 pop ebx 00411789 mov esp,ebp 0041178B pop ebp 0041178C ret 下面对上面的汇编代码中的重点行进行分析: |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论