532628838
圣骑士
圣骑士
  • 注册日期2011-06-10
  • 发帖数98
  • QQ
  • 火币517枚
  • 粉丝77
  • 关注39
阅读:2760回复:0

检测不了的错误现象及解决方法

楼主#
更多 发布于:2011-08-01 16:40
看看下面的例子:
#include <iostream>
using namespace std;
class MyClass{
      public:
             void Foo();
     private:
           int m_data;
};
void MyClass::Foo()
{
     cout<<"MyClass::Foo() is invoked!"<<endl;
}  
  
int main()
{
    MyClass* ptr;
    ptr = new MyClass;
    delete ptr;
    ptr = NULL;
    ptr->Foo();
   return 0;
}
  
编译没有问题,运行也没有问题,结果"MyClass::Foo() is invoked" 能打印出来.
奇怪的是ptr已经被设置成了NULL, 为什么ptr->Foo() 没有任何问题呢?
为什么编译和运行时都不能检测到这个问题呢. 为什么一个空指针还能够调用对象的成员函数呢? 我们要看看汇编代码.
MyClass::Foo() 其实是一个全局函数, 在符号表中的名字是 "_ZN7MyClass2FooEv". 所以调用时并不需要通过对象指针. 不过在调用这个函数时,会传递对象指针进去.
函数的定义:
.globl _ZN7MyClass3FooEv
        .type   _ZN7MyClass3FooEv, @function
_ZN7MyClass3FooEv:
.LFB1442:
        pushl   %ebp
.LCFI4:
        movl    %esp, %ebp
.LCFI5:
        subl    $8, %esp
.LCFI6:
        subl    $8, %esp
        pushl   $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        subl    $12, %esp
        pushl   $.LC0
        pushl   $_ZSt4cout
.LCFI7:
        call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
        addl    $20, %esp
        pushl   %eax
.LCFI8:
        call    _ZNSolsEPFRSoS_E
 addl    $16, %esp
        leave
        ret  
  
函数的调用:
   ptr = NULL;
   ptr->Foo();
   return 0;
对应的汇编
 movl    $0, -4(%ebp)                   ;; ptr = NULL;

 subl    $12, %esp                       ;; 通过堆栈进行参数传递,传入ptr. 不过Foo()没有用到它.
 pushl   -4(%ebp)                        ;;  

 call    _ZN7MyClass3FooEv         ;; ptr->Foo()
 addl    $16, %esp
 movl    $0, %eax                       ;;  return 0
 leave
 ret
  
通过上面的分析,我们明白了为什么这个让人迷惑的语句可以通过编译和运行了.
如果Foo()访问对象的成员,那么肯定是会出错的. 因为Foo()需要使用传入的ptr,而ptr的值是NULL.
比如
void Foo()
{
  cout<<"MyClass::Foo() is invoked!"<<m_data<<endl;

喜欢0 评分0
游客

返回顶部