【问题标题】:Problem in understanding pointer to an Object set to NULL理解指向设置为 NULL 的对象的指针的问题
【发布时间】:2011-07-28 07:55:01
【问题描述】:

谁能解释一下把指向对象的指针设为NULL是什么意思?

当我们将ptr赋给一个对象为NULL时,我们怎么还能访问成员函数呢?

class A
{
public:
    void print()
    {
        cout<<"Hello";
    }
}

int main()
{
    A * a;
    A* b = NULL;

    a->print();
    b->print();
}

编辑: 程序会崩溃吗? http://ideone.com/DudZ6

【问题讨论】:

    标签: c++ class


    【解决方案1】:

    指针用于访问该对象的成员。如果您的函数不需要访问任何成员并且不是虚拟的,则它们实际上不使用该对象。严格来说,代码仍然无效,但不会崩溃。

    如果你用打印“this”指针替换内容,它仍然可以工作,因为你没有取消引用它。

    struct X { 
        void print() { 
            cout << "this pointer is " << this << endl; 
        } 
    };
    
    int main() {
        X *x;
        x->print(); // will print whatever was on the stack, ie, random garbage
        X *t = NULL;
        t->print(); // will print a null pointer
    }
    

    如果您添加一个成员变量并打印它,或者将函数设为虚拟函数(因此必须通过对象本身查找它),它将崩溃。

    struct X { 
        virtual void print() { 
            cout << "Hello World" << endl; 
        } 
    };
    
    struct X2 { 
        int y;
        void print() { 
            cout << "y is " << y << endl; 
        } 
    };
    
    int main() {
        X *x = NULL;
        x->print(); // will crash on null pointer dereference
        X2 *x2 = NULL;
        x2->print(); // will crash on null pointer dereference
    }
    

    【讨论】:

    • 其实不是。该答案仅描述了在一个特定实现中发生的情况。行为未定义,其他实现可能会做一些不同的事情。
    • @dascandy 你能否为你关于这个指针的第二点添加一个例子。谢谢你的回复。时间限制结束后将其标记为已接受。
    • @James,同意,添加“通常”、“非常经常”、“几乎总是”、“无法想象其他任何内容”之一应该更准确。
    • 从问题“我们如何仍然可以访问成员函数?”意味着在某些特定平台上的经验观察......这个答案确实解释了代码如何执行 if 编译器基本上忽略指针的值,直到访问成员数据或虚拟调度指针。但是,“严格来说是无效的,但它不会崩溃”的承诺太多......即使在该特定平台上观察到非崩溃行为,标准中也没有任何内容保证它会做同样的事情,即使重新执行相同的可执行文件。
    • @Als 这是很久以前的事了,所以它可能已经改变了,但是 GreenHills 编译器曾经在它上面崩溃过。而且您并没有真正声称取消引用空指针不是未定义的行为,是吗?这是第 1.9 节中使用的未定义行为的例子。
    【解决方案2】:

    你不能。任何取消引用指针的尝试(任何应用 *-&gt; 运算符)是未定义的行为,可能会 任何事物。当然,大多数情况下,只有在 代码实际上需要来自对象的数据(包括内部数据,如 vptr);否则,代码似乎可以工作。但这只是其中之一 可能的未定义行为;我实际上使用了一个编译器 你的小程序会崩溃。


    根据 C++ 标准

    1.9 程序执行
    4:

    本国际标准中将某些其他操作描述为未定义(例如,取消引用空指针的效果)。 [注意:本国际标准对包含未定义行为的程序的行为没有要求。 ]

    严格来说,行为是未定义的,正如许多人在 cmets 部分指出的那样,仅仅因为它在特定平台上工作并不使其合法。

    因此根据 C++ 标准,代码 sn-p 的行为是未定义行为。

    【讨论】:

    • 我在一个环境中工作过,我在地址 0x0 有一个对象。空指针绝对不会在那里崩溃。
    • @James Kanze:+1,为了完整起见,我冒昧地将标准引用添加到您的答案中,我希望这很好,如果不能随意恢复它。
    • @dascandy 正如已经指出的那样,这是未定义的行为。我曾在几个环境中工作过,通过空指针写入会修改操作系统中的关键数据。但是任何 C 或 C++ 对象都不能位于同一地址;如果系统将物理地址 0 用于 C 或 C++,则它必须使用其他位模式作为空指针。
    【解决方案3】:

    你不能,

       A* b = NULL;
     b->print();
    

    这是未定义的 bahvior,可能会导致您的程序因 segmentation fault 而崩溃(取决于您的 print 方法的定义)。

    一般来说,给指针赋值NULL是为了给指针一个值,意思是“这个指针没有设置,不要使用它”。拿这个代码:

    void function() {
       int* p;
       int* q = NULL:
       ... 
    }
    

    函数调用时,pq在栈上分配;由于编译器的工作方式,p 未分配,这意味着它获取在调用函数时在堆栈上找到的值;这是一个随机值,通常与您的程序没有太大关系。

    现在,p 有一个值这一事实可能会让你认为这个值是“正确的”,所以你使用它;但是,它不是(因为它是随机的,由于没有初始化指针),所以你也会遇到崩溃或某种意外行为。

    q 的情况下,将NULL 分配给它的事实可以让您知道它没有可用的价值。所以,你不要引用它:

     if (q != NULL) {
         q->....
     }
    

    上面的成语不适用于p,所以你无法判断是否可以。

    最后:总是初始化你的变量;在指针的情况下,如果您没有要分配的值,请分配 NULL,这样您就知道不应取消引用指针。

    【讨论】:

    • @Als 这取决于实现。这是未定义的行为,我使用了会崩溃的编译器。一些编译器让你侥幸逃脱的事实并不意味着它是合法的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-09
    相关资源
    最近更新 更多