为什么在成员函数调用上没有段错误

Why no segfault on member function call?

本文关键字:段错误 错误 成员 函数调用 为什么      更新时间:2023-10-16

我正在寻找对这段代码的澄清。对 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
}

程序表现出未定义的行为。"似乎有效"是未定义行为的一种可能表现。

特别是,bla->hello()调用似乎有效,因为hello()实际上并没有以任何方式使用this,所以它恰好没有注意到this不是有效的指针。

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

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

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

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

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

编译器找到正确的函数后,通常会将像 a->b(c); 这样的调用转换为大致等效的内容:b(a, c); -- 然后a成为函数内 this 的值。当函数引用对象中的数据时,this被取消引用以查找正确的对象,然后通常应用偏移量以在该对象中查找正确的项。

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

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

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

只要成员函数不取消引用this,调用它通常是"安全的",但你处于未定义的行为状态。

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