是否可能从引用中产生分段错误?

时间:2015-12-21 18:24:58

标签: c++

假设以下代码:

Foo & foo = getFoo();
foo.attr; // 100% safe access?

如果foo是指针,我会检查它是否为NULL,但是因为它是引用,所以这种检查是不必要的。我想知道的是,是否有可能弄乱对象的引用,以致它会使其属性不安全。

我尝试了一些示例,例如尝试将NULL转换为Foo对象,但是我遇到了编译错误。我只是想确保上面的代码总是安全的,并且我不应该知道内部C++黑魔法。

根据本杰明的回答,我可以制作一个示例代码,我从引用中得到segmentation fault,因此它回答了我的问题。我会粘贴我的代码,以防有人对未来感兴趣:

#include <iostream>
using namespace std;

class B
{
    public:
    int x;
    B() {x = 5;}
};
class A
{
    public:
    void f()
    {
        b = *(B*)NULL;
    }
    B & getB()
    {
        return b;
    }

    B b;
};

int main()
{
    A a;
    a.f();

    cout << a.getB().x << endl;
    return 0;
}

3 个答案:

答案 0 :(得分:28)

是的,这是可能的。

Foo& Fr = *(Foo*)nullptr;

从技术上讲,这是取消引用该指针的未定义行为。但它很可能不会导致任何可观察到的错误。这可能会:

Fr.attr = 10;

然而,正如Jonathan Wakely在评论中指出的那样,你没有理由检查这样的案例。如果函数返回无效引用,则该函数被破坏,需要修复。假设引用有效,您的使用代码不会被破坏。但是,正如David Schwartz的回答中所提到的,完全合法的代码中有效引用 无效(尽管不为空)。但是你没办法检查这个。您只需知道它可能发生的情况,然后停止使用该引用。

答案 1 :(得分:23)

引用必须在引用该引用时引用有效对象。这是一个C ++标准要求,违反它的任何代码都是UB,可以做任何事情。

然而,在参考坐下之后销毁参考引用的对象是完全合法的。此时,访问引用是非法的。例如:

std::vector<int> j;
j.push_back(3);
int& k = j.front(); // legal, object exists now
j.clear();         // legal, object may be destroyed while reference exists
k++;              // illegal, destruction of object invalidates reference

这意味着返回引用的函数必须始终返回一个在返回时有效的引用。这就是在空向量上调用front为UB的原因 - 引用必须在其就位时有效。但是,通常会有条件随后使该引用无效,如果您打算尝试存储引用并稍后访问它,则需要了解这些条件是什么。

通常,您应该假设存储返回的引用并在以后访问它是不安全的,除非您知道引用仍然有效。例如,std::vector会仔细解释在什么条件下对容器的引用可能无效,并且包括对push_back的后续调用。所以这被打破了:

std::vector<int> j;
j.push_back(3);
int &first = j.front();
j.push_back(4);
int &second = j.back();
if (first == second) // illegal, references into container are invalidated by push_back

但这很好:

std::vector<int> j;
j.push_back(3);
j.push_back(4);
int &first = j.front();
int &second = j.back();
if (first == second) // legal, references into container stay valid

答案 2 :(得分:17)

可以引用不良内存。所以foo.attr; // 100% safe access?的答案是否定的。请考虑以下示例:

int &something() {
    int i = 5, &j = i;
    return j; // Return reference to local variable. This is destroyed at end of scope. 
}

int main() {
    int &k = something(); // Equivalent to getFoo()
    std::cout << k << endl; // Using this reference is undefined behavior. 
    return 0;
}

Live example.

引用k未指向合法内存。但这仍然会编译。然而,在程序员没有犯错的情况下,情况并非如此。在这种情况下,函数something()写得不正确,需要修复。没有办法或理由检查这一点。如果一个函数返回一个错误的引用,那么你唯一可以(而且应该)做的就是修复有问题的函数。