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

时间:2022-09-06 15:32:29
#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.

我输出为“TESTING NULL POINTER”。任何人都可以解释为什么这个程序打印输出而不是崩溃。我在Dev C ++和aCC编译器上检查了它们都给出了相同的结果。

6 个解决方案

#1


26  

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.

你没有使用A的任何成员变量 - 该函数完全独立于A实例,因此生成的代码恰好不包含解除引用0的任何内容。这仍然是未定义的行为 - 它可能恰好适用于某些编译器。未定义的行为意味着“任何事情都可能发生” - 包括程序恰好像程序员所期望的那样工作。

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.

如果你是make mprint virtual你可能会崩溃 - 或者如果编译器发现它确实不需要vtable,你可能得不到它。

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

如果将成员变量添加到A并打印出来,则会发生崩溃。

#2


7  

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

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

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.

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

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!

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

#3


5  

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

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

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

所以它只是把它称为普通函数,并将空指针作为参数传递给你,你从来没有以任何方式实际使用它。这就解释了为什么它不会崩溃,但编译器可以*地做任何事情

#4


2  

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

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

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.

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

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.

在这种情况下,此实例为NULL但这没有任何区别,因为在callee方法中没有使用成员变量。尝试更改代码,以便在mprint()方法中打印成员变量的值,然后就会崩溃。

#5


1  

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

这两个成语在Google Chrome的javascript VM V8中用于表示31位整数

#6


0  

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.

会员职能怎么样?成员函数未分配新闻总是有一个所有成员函数的副本。默认情况下,成员变量被添加到每个成员函数,该成员函数是指向对象本身的指针。当没有对象存在时,对象指针为空值。这很重要,因为你没有以任何方式访问它。如果在方法中使用任何成员变量的this指针,则会遇到问题。这是因为在空指针的情况下,成员变量无效。

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

在MFC中,我们有CWnd的GetSafeHwnd()方法。这与原理相同。

#1


26  

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.

你没有使用A的任何成员变量 - 该函数完全独立于A实例,因此生成的代码恰好不包含解除引用0的任何内容。这仍然是未定义的行为 - 它可能恰好适用于某些编译器。未定义的行为意味着“任何事情都可能发生” - 包括程序恰好像程序员所期望的那样工作。

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.

如果你是make mprint virtual你可能会崩溃 - 或者如果编译器发现它确实不需要vtable,你可能得不到它。

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

如果将成员变量添加到A并打印出来,则会发生崩溃。

#2


7  

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

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

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.

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

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!

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

#3


5  

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

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

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

所以它只是把它称为普通函数,并将空指针作为参数传递给你,你从来没有以任何方式实际使用它。这就解释了为什么它不会崩溃,但编译器可以*地做任何事情

#4


2  

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

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

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.

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

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.

在这种情况下,此实例为NULL但这没有任何区别,因为在callee方法中没有使用成员变量。尝试更改代码,以便在mprint()方法中打印成员变量的值,然后就会崩溃。

#5


1  

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

这两个成语在Google Chrome的javascript VM V8中用于表示31位整数

#6


0  

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.

会员职能怎么样?成员函数未分配新闻总是有一个所有成员函数的副本。默认情况下,成员变量被添加到每个成员函数,该成员函数是指向对象本身的指针。当没有对象存在时,对象指针为空值。这很重要,因为你没有以任何方式访问它。如果在方法中使用任何成员变量的this指针,则会遇到问题。这是因为在空指针的情况下,成员变量无效。

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

在MFC中,我们有CWnd的GetSafeHwnd()方法。这与原理相同。