【问题标题】:Why does calling method through null pointer "work" in C++? [duplicate]为什么在 C++ 中通过空指针“工作”调用方法? [复制]
【发布时间】:2012-07-04 11:16:40
【问题描述】:

可能重复:
Calling class method through NULL class pointer

#include <iostream>
using namespace std;
class test
{
    int i;
public:
    test():i(0){ cout << "ctor called" << endl;}
    void show()
    {
        cout<<"show fun called"<<endl;
    }
};

int main(int argc , char *argv[])
{
    test *ptr = NULL;
    ptr->show();
    return 0;
}

显然,不会调用任何 ctor。这是标准吗?或者只是一些编译器优化,因为这个指针没有在 show() 成员函数中使用?

【问题讨论】:

  • 解除对空指针的引用是UB。
  • show() 中添加i = 1; 并尝试运行它。
  • 克里斯,UB的意思是编译器实现?我正在使用 g++ 4.6.3。 Jesse Good,当然,seg 故障,这是毫无疑问的。我想知道编译器是否会为不需要它的成员函数生成没有这个的代码。
  • @bbc:UB 的意思是“未定义的行为”,与“定义的实现”不同。定义的实现意味着至少定义了行为,尽管该定义可能因编译器而异。还; “当然,段错误,毫无疑问。” - 实际上,有很多疑问,因为行为是未定义的。如果它肯定会导致段错误,那么它将被完美地定义,但事实并非如此。任何事情都可能发生,但实际上可能会出现段错误(您希望如此)。
  • 你的代码在大多数编译器下都是有效的[这里没有在 GCC 和 MSVC 上崩溃]。幸运的是,您的 show() 方法符合纯 STATIC 的条件。你没有取消引用指针!指向非多态类的指针上的函数调用不会取消引用指针。它只是用堆栈帧调用方法的地址,其中该指针为零。如果您的代码仅使用全局变量并且从不触及 this 指针 - 或非静态数据成员 - 你没问题。触摸静态数据成员也可以。更好的是,只需将您的方法标记为静态并忘记指针!

标签: c++


【解决方案1】:

调用方法不需要指针。指针的类型是已知的,因此方法的代码是已知的。该方法不使用this,因此它可以正常运行代码。这是未定义的行为,但不检查指针是否为 NULL 会更有效,因此它会运行。

【讨论】:

  • (如何)虚拟方法会影响他观察到的行为?
  • 我预计虚方法调用会失败,因为它需要在 vtable 中查找方法,它会在指针的另一端找到该方法,但指针为 NULL。跨度>
  • 虚拟与 g++ 的段错误,不确定这也取决于实现。编译器会生成没有类对象的 vptr 和 vtblr 吗?猜不是出于效率原因?
  • 经典的非虚方法直接用asm调用。然后堆栈帧将this 指针归零。然而,虚拟方法很可能会出现段错误,因为必须取消引用指针。
【解决方案2】:

如果您查看程序集(对于至少一个编译器),您可以看到它运行的原因(尽管正如许多人指出的那样,它是未定义的行为)。对于这两行:

test *ptr = NULL;
ptr->show();

生成了这个程序集(在我刚刚尝试过的一个编译器中):

00000004: C7 45 FC 00 00 00  mov         dword ptr [ebp-4],0
          00
0000000B: 8B 4D FC           mov         ecx,dword ptr [ebp-4]
0000000E: E8 00 00 00 00     call        ?show@test@@QAEXXZ

它将 NULL (0) 压入堆栈并调用方法,因为方法的地址与实际对象实例无关。

【讨论】:

    【解决方案3】:

    无效,行为未定义,实际结果取决于您的编译器。

    【讨论】:

    • 嗯,它是有效的。 __thiscall 函数不访问this 指针只是一个静态函数。只需添加“静态” - 这是您可以在定义为静态的代码中做的最好的事情 :) 它强制函数使用 __cdecl,因此没有此指针要求。请注意,添加 virtual 将进行更复杂的准备,需要取消引用指针,然后会出现段错误。
    【解决方案4】:

    嗯,首先,它是无效的,因为它会调用未定义的行为。您真的在问为什么编译器允许它,答案是因为那是在实际应用程序中根本不会出现的愚蠢代码,那么为什么要麻烦呢?当指针以静态代码分析无法预料的方式在运行时变为无效时,真正的问题就出现了。

    编译器不是用来牵你的手的,它是用来按照标准编译你的代码的。如果它提供了有用的警告,那就太好了,但那里没有语法或语义上的非法内容,您只是在编写导致未定义行为的代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-01
      • 2012-01-21
      • 1970-01-01
      • 2020-11-19
      • 1970-01-01
      • 2020-11-16
      • 2016-09-25
      • 2019-02-16
      相关资源
      最近更新 更多