【问题标题】:Why no segfault on member function call?为什么成员函数调用没有段错误?
【发布时间】:2013-10-16 17:01:05
【问题描述】:

我正在寻找有关这段代码的说明。对 A::hello() 的调用有效(我期望一个 segv)。段错误确实发生在对成员 x 的访问上,所以似乎单独的方法解析实际上并没有取消对 bla 的引用?

我在关闭优化的情况下编译,gcc 4.6.3。为什么 bla->hello() 不爆炸?只是想知道发生了什么。谢谢。

class A
{

public:
    int x;

    A() { cout << "constructing a" << endl; }

    void hello()
    {
        cout << "hello a"  << endl;
    }

};

int main()
{
    A * bla;
    bla = NULL;
    bla->hello();    // prints "hello a"
    bla->x = 5;      // segfault
}

【问题讨论】:

  • 未定义的行为是未定义的。
  • 不错的链接@chris。我知道 NULL 取消引用是未定义的,但很高兴知道幕后发生了什么。

标签: c++ pointers null segmentation-fault


【解决方案1】:

您的程序表现出未定义的行为。 “似乎有效”是未定义行为的一种可能表现形式。

特别是,bla-&gt;hello() 调用似乎有效,因为hello() 实际上并没有以任何方式使用this,所以它只是碰巧没有注意到this 不是一个有效的指针。

【讨论】:

    【解决方案2】:

    您正在取消引用NULL 指针,即尝试访问存储在地址NULL 的对象:

    bla = NULL;
    bla->hello();
    bla->x = 5;
    

    这会导致未定义的行为,这意味着任何事情都可能发生,包括将5 分配给成员x 时出现的段错误以及欺骗性“按预期工作” 在调用 hello 方法时效果。

    【讨论】:

      【解决方案3】:

      理论上,这是未定义的行为。实际上,您在调用hello() 时不使用this 指针,它根本不引用您的类,因此可以正常工作并且不会产生内存访问冲突。但是,当您执行bla-&gt;x 时,您试图通过未初始化的bla 指针引用内存,并且它崩溃了。同样,即使在这种情况下也不能保证它会崩溃,这是未定义的行为。

      【讨论】:

      • 是的,因为 hello 是非虚拟的,所以不需要访问 ref 指针来调用它。编译器使用指针的类型来解析特定的 hello 方法,仅此而已。 (甚至有一种模糊的可能性,这种行为是“定义的”。)
      • @HotLicks:好吧,我不会说它是定义的,因为 C++ 标准没有说明该语言应该如何实现。所以从语言标准的角度来看,它可能不是。但从某些 ABI 的角度来看,它也可以很好地定义:)
      【解决方案4】:

      至少在典型实现中,当您通过指针调用非虚拟成员函数时,不会取消引用该指针以找到该函数。

      指针的类型用于确定搜索函数名称的范围(上下文),但这完全发生在编译时。指针指向的内容(包括“无”,在空指针的情况下)与查找函数本身无关。

      编译器找到正确的函数后,通常会将类似a-&gt;b(c); 的调用翻译成大致相当于:b(a, c); -- a 然后成为函数内部this 的值。当函数引用对象中的数据时,this 被取消引用以找到正确的对象,然后通常应用偏移量以在该对象中找到正确的项目。

      如果成员函数从不尝试使用对象的任何成员,this 是空指针这一事实不会影响任何事情。

      另一方面,如果成员函数确实尝试使用对象的成员,它将尝试取消引用 this 来执行此操作,并且如果 this 是 NULL 指针,则不会工作。同样,调用虚函数总是使用对象中的 vtable 指针,因此尝试通过空指针调用虚函数可能会失败(无论虚函数中的代码是否引用对象中的数据与否)。

      正如我一开始所说的,我在这里谈论的是典型的实现。从标准的角度来看,你只是有未定义的行为,这就是它的结束。理论上,实现并不必须按照我所描述的方式工作。然而,实际上,基本上所有相当流行的 C++ 实现(例如 MS VC++、g++、Clang 和 Intel)在这些方面都非常相似。

      【讨论】:

      • 谢谢,我希望能解释一下实践中实际发生的情况,所以答案太棒了。
      【解决方案5】:

      只要成员函数不取消引用 this,调用它通常是“安全的”,但您会处于未定义的行为领域。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-06-15
        • 2018-06-16
        • 2011-12-15
        相关资源
        最近更新 更多