为什么重命名我的变量会阻止segfault

Why does renaming my variable prevent a segfault?

本文关键字:segfault 变量 重命名 我的 为什么      更新时间:2023-10-16

我实现了一个单一的深度优先搜索算法来遍历我的图,将迭代器指定到一个起始节点。

文件摘要:

  • GraphIterGraph::iterator类型定义
  • Graph扩展map<string, Node>
  • start->second.edges()返回set<string>

如果start->second.edges()大小0,则此代码会导致分割故障:

(为了简洁起见,我已经截断了不相关的部分,包括递归调用。)


错误代码

void Graph::dfs(GraphIter start)
{
    cout << "EDGES SIZE: " << start->second.edges().size() << endl;
    for (set<string>::iterator it = start->second.edges().begin();
          it != start->second.edges().end(); ++it)
    {
        GraphIter iter = this->find(*it);     // <--- SEGMENTATION FAULT
    }
}

现在看看当我把start->second.edges()拉入一个局部变量时会发生什么:不再有segfault!

以下是不会生成segfault的代码:


良好规范

void Graph::dfs(GraphIter start)
{
    set<string> edges = start->second.edges();       // <--- MAGIC TRICK
    cout << "EDGES SIZE: " << edges.size() << endl;
    for (set<string>::iterator it = edges.begin();
          it != edges.end(); ++it)
    {
        GraphIter iter = this->find(*it);
    }
}

因此,不同之处在于,在好的代码中,当字符串集的大小(来自edges()方法)为0时,在第二种情况下永远不会进入for循环。但在第一种情况下,for循环仍然至少执行一次,直到它意识到它不能取消引用it变量。

为什么这些不同?他们不是在访问记忆的相同部分吗?

因为edges()按值返回一个set,所以start->second.edges().begin()start->second.edges().end()将迭代器返回到不同的容器,因为每次调用edges()都会返回一个新的set

通过使用命名变量创建单个副本,可以确保迭代器都来自同一个容器,并且可以有效地从begin()迭代器到end()

可能是start->second.edges()按值返回std::set<std::string>。这将导致循环中的迭代器不兼容,并导致未定义的行为。

你的"魔术"

set<string> edges = start->second.edges();

确保在同一个容器"edges"上进行迭代。

您可以通过引用返回Node::edges()来修复它:

const std::set<std::string>& edges() const { .... }