为什么在C++中通过空指针调用成员函数时程序不会崩溃?

发布于 2024-10-26 03:05:55 字数 312 浏览 2 评论 0原文

#include "iostream"
using namespace std;
class A
{
public:
    void mprint()
    {
        cout<<"\n TESTING NULL POINTER";
    }
};

int main()
{
    A *a = NULL;
    a->mprint();
    return 0;
}

我得到的输出为“测试空指针”。谁能解释一下为什么这个程序打印输出而不是崩溃。我在 Dev C++ 和 aCC 编译器上检查过,都给出了相同的结果。

#include "iostream"
using namespace std;
class A
{
public:
    void mprint()
    {
        cout<<"\n TESTING NULL POINTER";
    }
};

int main()
{
    A *a = NULL;
    a->mprint();
    return 0;
}

I am getting output as "TESTING NULL POINTER". Can anyone please explain why this program is printing the output instead of crashing. I checked it on Dev C++ and aCC compiler both gave same result.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

苄①跕圉湢 2024-11-02 03:05:55

您没有使用 A 的任何成员变量 - 该函数完全独立于 A 实例,因此生成的代码恰好不包含任何取消引用 0 的内容。仍然是未定义的行为 - 它可能恰好在某些编译器上工作。未定义的行为意味着“任何事情都可能发生”——包括程序恰好按照程序员的预期工作。

例如,如果您将 mprint 设为虚拟,您可能会崩溃 - 或者如果编译器发现它实际上并不需要 vtable,您可能不会崩溃。

如果你向 A 添加一个成员变量并打印它,你将会崩溃。

You're not using any member variables of A - the function is completely independent of the A instance, and therefore the generated code happens to not contain anything that dereferences 0. This is still undefined behavior - it just may happen to work on some compilers. Undefined behavior means "anything can happen" - including that the program happens to work as the programmer expected.

If you e.g. make mprint virtual you may get a crash - or you may not get one if the compiler sees that it doesn't really need a vtable.

If you add a member variable to A and print this, you will get a crash.

当爱已成负担 2024-11-02 03:05:55

根据 C++ 规范,该程序具有未定义的行为,因为您在空接收器上调用成员函数。

不过,这种方法有效的原因是,非虚拟成员函数通常被实现为常规函数,这些函数将“this”指针作为隐式的第一个参数。因此,如果您在空指针上调用成员函数,只要不使用 this 指针,您的程序就不会崩溃。当然,你不能依赖这个;有效的 C++ 编译器可能会导致崩溃。

然而,虚拟函数则不同,因为实际被调用的函数需要在运行时解析。这通常涉及对接收者的虚拟函数表进行内省。因此,如果您尝试在空指针上调用虚拟成员函数,即使该函数不访问该函数,它仍然会导致崩溃。如果你好奇的话就试试这个吧!

According to the C++ spec, This program has undefined behavior because you're invoking a member function on a null receiver.

The reason that this works, though, is that non virtual member functions are typically implemented as regular functions that take the "this" pointer as an implicit first argument. Consequently, if you call a member function on a null pointer, as long as you don't use the this pointer, your program will not crash. Of course, you cannot rely n this; a valid C++ compiler could cause this to crash.

However, virtual functions are a different story because the function that actually gets called needs to be resolved at runtime. This usually involves introspecting on the receiver's virtual function table. Thus if you try calling a virtual member function on a null pointer, even if te function doesn't access this, it will still cause a crash. Try this out if you're curious!

请恋爱 2024-11-02 03:05:55

使用指向对象的空指针调用成员函数的结果在 C++ 中是未定义的行为,因此它可以执行任何操作。

在这种情况下,很可能是因为它重写了您的函数,因为它是这样的

void mprint(A* this);

,并且您的调用是这样的

mprint(0);

所以它只是像普通函数一样调用它,并将空指针作为参数传递,而您永远不会以任何方式实际使用它。这解释了为什么它不会崩溃,但编译器可以自由地做任何事情

The results of calling a member function using a null pointer to an object is undefined behavour in c++ so it can do anything.

In this case it's likely because it's rewritten your function as it it was like this

void mprint(A* this);

and your call like this

mprint(0);

So it's just called it as if it was an ordinary function and passed the null pointer as a parameter which you never then actually use in any way. That explains why it doesn't crash, but the compiler is free to do pretty much anything

随遇而安 2024-11-02 03:05:55

简单答案:因为 mprint() 没有使用类的任何成员变量

详细答案:当调用类的方法时,类实例被传递给被调用函数(通常作为第一个参数,但是,在某些调用约定(例如 __thiscall)中,这是在寄存器中传递的)。该类实例用于访问被调用方方法中使用的所有成员变量。

在本例中,该实例为 NULL,但这没有任何区别,因为被调用方方法中没有使用任何成员变量。尝试更改您的代码,以便在 mprint() 方法中打印成员变量的值,您将会崩溃。

Simple Answer: Because mprint() is not using any of the member variables of the class

Detailed Answer: When a method of a class is called, class instance is passed on to the callee function (normally as the first argument, however, in some calling conventions such as __thiscall, this is passed in a register). This class instance is used to access all the member variables that are used in the callee method.

In this case, this instance is NULL but this doesnt make any difference since no member variables are being used in the callee method. Try changing your code such that you print the value of a member variable in mprint() method and you will get the crash.

凡间太子 2024-11-02 03:05:55

能够在无效指针上调用非虚拟成员函数甚至可以对与指针本身中的对象关联的信息进行编码。例如:

#include <iostream>

class MagicInteger {

  public:

   static MagicInteger* fromInt (int x) {
     return reinterpret_cast<MagicInteger*>(x);
   }

   int getValue() {
     return static_cast<int>(reinterpret_cast<intptr_t>(this));
   }       

  private:
   // forbid messing around
   MagicInteger ();  
   MagicInteger (MagicInteger&);
   MagicInteger& operator=(const MagicInteger&);
};

int main (void) {
  MagicInteger* i = MagicInteger::fromInt(6);
  std::cout << "Value is " << i->getValue() << std::endl;
  return 0;
}

这也可以用来实现标记指针,即包含元数据的指针有关受指点者的信息。

这两个惯用法用于 Google Chrome 的 javascript VM V8表示31位整数

Being able of invoking non-virtual member functions on non-valid pointers even enables encoding the information associated to an object in the pointer itself. For example:

#include <iostream>

class MagicInteger {

  public:

   static MagicInteger* fromInt (int x) {
     return reinterpret_cast<MagicInteger*>(x);
   }

   int getValue() {
     return static_cast<int>(reinterpret_cast<intptr_t>(this));
   }       

  private:
   // forbid messing around
   MagicInteger ();  
   MagicInteger (MagicInteger&);
   MagicInteger& operator=(const MagicInteger&);
};

int main (void) {
  MagicInteger* i = MagicInteger::fromInt(6);
  std::cout << "Value is " << i->getValue() << std::endl;
  return 0;
}

This can also be used to implement tagged pointers, that is, pointers that contain meta-information about the pointee.

These two idioms are used in Google Chrome's javascript VM V8 to represent 31-bit integers

时常饿 2024-11-02 03:05:55

这是完全合法的通话。

让我们了解它是如何工作的

当一个新对象被创建时,它的成员变量也会被创建。

那么成员函数呢?成员函数没有分配消息,所有成员函数总是有一份副本。默认情况下,每个成员函数都会添加一个成员变量,即指向对象本身的 this 指针。
当不存在对象时,对象指针为空值。这并不重要,因为您没有以任何方式访问它。如果在方法中使用任何成员变量的 this 指针,就会遇到问题。这是因为成员变量在空指针的情况下无效。

在MFC中,我们有CWnd的GetSafeHwnd()方法。这遵循同样的原理。

This is completely legal call.

lets understand how it works

when a new object is creates its member variables are created.

What about member functions? Member function are not allocated news there is always one copy of all member function. By default a member variable is added to every member function that is this pointer which is pointing to the object itself.
When there is no object present that is object pointer is null value. It doenst matter because you are not accesssing it any way. You will get in problems if you use this pointer of any of the member variable in the method. This is because member variable are not valid in case of null pointer.

in MFC we have GetSafeHwnd() method for CWnd. This works on same principle.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文