空指针对象问题

时间:2009-11-08 12:20:02

标签: c++

嗨,有人可以告诉为什么在Linux和Windows中会出现同样的问题:

#include <iostream>
using namespace std;

class A
{
private:
   int _dmember;

public:
   void func()
   {
     cout<<"Inside A!! "<<endl;
     cout<<_dmember; // crash when reach here.
   }
};

int main ()

{

    A* a= NULL;

    a->func(); // prints "Inside A!!!" 

    return 1;
}

有人能说出为什么会发生这种奇怪的事情吗?我的意思是 ,     a-&gt; func()不应该进入func(),...?     这是不可动摇的行为,

为什么会出现上述情感?

编辑:当然,* = null是故意的!!所以对于所有回答“这是未定义的行为”或“你永远不应该尝试在NULL指针上调用函数!!”的人来说,来吧....这就是重点。有些人正确解释了这种行为。

11 个答案:

答案 0 :(得分:12)

这是未定义的行为。您绝不能在空指针上调用函数。

有了这个,让我们回答一下我认为你问的问题:为什么我们还要进入函数的中途?

当您调用UB时,编译器可以自由地执行任何操作,因此允许它发出仍然有效的代码。这就是在这种特殊情况下在一些(很多?)系统上发生的事情。

您能够成功调用空指针上的函数的原因是您的编译器不会将函数存储在对象中。相反,上面的代码有点像这样解释:

class A {
    int _dmember;
};

void A::func(A *this) {
    cout << "Inside A!!" << endl;
    cout << this->_dmember << endl;
}

int main() {
    A *a = ...;
    A::func(a);
}

所以,你看到没有什么能阻止你在空指针上调用函数;它只是调用函数体,this指针设置为null。但是一旦函数尝试通过访问类中的字段来取消引用this指针,操作系统就会介入并杀死程序以进行非法内存访问(在Linux上称为分段错误,在Windows上称为访问冲突)。

Nitpicker的角落:虚拟功能是另一回事。

答案 1 :(得分:7)

未定义的行为,因为您正在访问NULL指针:

A* a= NULL;
a->func(); // is not defined by the language

请注意,即使func()未尝试访问成员变量,行为仍未定义。例如,以下代码可以无错误地运行,但它不正确:

   func()
   {
     cout<<"Inside A!! "<<endl;
   }

编辑:在我充分尊重的情况下,C ++并不糟糕!

您需要的是智能指针,而不是原始指针。正如我的教授总是说,如果你不知道你在C / C ++中做了什么,最好不要这样做!

使用boost::scoped_ptr,享受异常安全,自动内存管理,零开销和NULL检查:

struct test
{
    int var;
    void fun()
    {
        std::cout << var;
    }
};

int main()
{
    boost::scoped_ptr<test> p(NULL);
    p->fun(); // Assertion will fail, Happy debugging :)
}

答案 2 :(得分:1)

取消引用空指针是未定义的行为。 一切都可能发生,所以不要这样做。 在解除引用之前,您必须检查指针是否有效。此指针不能为null,因此您不会避免未定义的行为。

答案 3 :(得分:0)

大多数编译器只是将指针传递给类作为第一个参数(this指针)。如果你不继续取消引用这个指针那么你实际上不会导致崩溃。你的这个指针,在函数内,将只是NULL。

正如AraK所指出的那样,这是一种不确定的行为,所以你的里程数会有所不同......

答案 4 :(得分:0)

你不应该为你的指针分配内存吗?我只是想知道调用NULL指针函数的意图是什么?它应该立即崩溃。它不会在你不调用成员_dmember的行上崩溃,但是当你调用它时你的函数崩溃导致对象根本就没有被分配。 _dmember指向未定义的内存...这就是它崩溃的原因

答案 5 :(得分:0)

它是一个空指针,你根本无法定义如果我们在其上调用一个函数会发生什么。

答案 6 :(得分:0)

任何指针变量都应该指向某个对象。

您的声明A * a = NULL; 不指向任何地方,因此不会产生应有的结果。

你可以尝试这个

A * a = NULL;
A b;
a=&b;
a->func();

这将产生输出。

答案 7 :(得分:0)

由于您的类中没有虚函数,因此在此处考虑生成代表此类型的C代码会更容易。大约:

#include <stdio.h>


typedef struct
{
  int d_;
} A;


FILE* print_a_to(A* a, FILE* dest)
{
  return fprintf(dest, "Inside A!! \n") < 0 ||
         fprintf(dest, "%d", a->d_) < 0 ?
         NULL :
         dest;
}


int main(int argc, char* argv[])
{
  A* a = NULL;
  return NULL == print_a_to(a, stdout) ?
         -1 :
         0;
}

查看函数print_a_to的第二行;看到指针a的解除引用?根据函数main的第一行,您将NULL作为指针a的值传递。在这里取消引用空指针等同于在类上调用需要通过空指针访问其成员变量的成员函数。

答案 8 :(得分:0)

如果我不清楚, 我不是故意在下面做的:

A* a=NULL;    
a->f(); 

我写这段代码只是为了检查它为什么会起作用,当然我很失望而且我失望的原因是我在Redhat-Linux中调试非常大的程序,通过日志文件概念(意思是 - 打印输入,退出)从功能到日志,包括打印重要值)。 并且,在我登录日志的路上,我希望如果我在函数调用的特定STACK上,我希望至少操作这些函数的实例是活着的,但是当我发现并且失望它不应该是,因为它使我失去理智通过日志文件调试更加困难。

答案 9 :(得分:0)

我希望您完全按照所见的描述症状。我试过Windows和Linux。 Linux发出段错误,Windows显示错误对话框。

0x0左右的地址区域受Windows和Linux保护。在此内存区域中读取和写入将导致操作系统抛出异常。您的应用程序可以捕获异常。大多数应用程序没有,OS默认处理例程是打印一些错误消息并终止程序。

有人可能会问为什么消息“”里面的A !! “在终止之前打印。答案是在后端,C ++编译器将类方法转换为过程调用。此步骤不涉及指针解引用。您可以认为结果如下所示:

void A_func(A* a)
{
     cout<<"Inside A!! "<<endl;
     cout<<a->_dmember; // crash when reach here.
}

A * a = NULL;    A_func(一);

NULL指针的取消引用发生在第二个语句中。所以第一个声明执行得很好。

答案 10 :(得分:0)

重点是 - &gt;类对象上的运算符(没有vtable)不是指针的取消引用

a->foo()

的简写
A::foo(a)

第一个参数转换为this指针。当你尝试通过引用成员变量来解决'this'时,事情会变坏。