【问题标题】:C++ linked list reverse traversal memory corruptionC++链表反向遍历内存损坏
【发布时间】:2020-11-14 01:39:20
【问题描述】:

我正在学习 C++。作为映射项目的一部分,我试图(反向)通过节点类中的父地址指针属性从特定节点到起始节点的链表遍历。我试图弄清楚为什么父母的地址值一直被破坏,这会导致错误并阻止遍历。

我提取了下面项目的基本组件,以及一些帮助代码,可以轻松地重新创建错误。

#include <iostream>
#include <stdio.h>
#include <vector>
using std::vector;
using std::cout;
using std::endl;

class Node
{
    public:
        int idx;
        Node * addr = nullptr;
        Node * parent_addr = nullptr;
    
        Node (int init_idx, Node * init_parent) {
            idx = init_idx;
            parent_addr = init_parent;
        }
};

void PrintNode(Node & n) {
    cout << "idx: " << n.idx << " addr: " << n.addr << " parent_addr: " << n.parent_addr << endl;
}

void PrintNodeList (vector<Node> & path) {
    for (Node n : path) {PrintNode(n);}
}

void CreateNodeList (const int node_count, vector<Node> & node_list) {
    for (int i=0; i<=(node_count - 1); i++) {
        if (i==0) {
            //  first node parent is null
            node_list.push_back(Node(i, nullptr));
        } else {
            // subsequent nodes parent is previous node
            node_list.push_back(Node(i, &node_list[i-1]));
        }
        // store address of node to clearly follow forward and reverse traversals
        node_list[i].addr = &node_list[i];
    }
    cout << "Node List..." << endl;
    PrintNodeList(node_list);
}

void TraverseNodeList(Node * current_node) {
    cout << endl << "Traversing node list in reverse..." << endl;
    while (current_node != nullptr) {
        PrintNode(*current_node);
        current_node = current_node->parent_addr;
    }
    cout << endl << "Completed reverse traversal." << endl;
}

int main() {
    // generate list of nodes
    const int node_count = 5;
    vector<Node> node_list;
    CreateNodeList (node_count, node_list);
        
    // reverse traverse the list of nodes
    const int last_node = node_count - 1;
    TraverseNodeList(&node_list[last_node]);
    
    return 0;
}

Here is a sample node list (node index, address of the node, address of node's parent):

Here is a traversal corrupting at the start node:

Here is a traversal corrupting before the start node:

实际的 CreateNodeList 函数:

此代码是从 A* 搜索项目中提取的。随着地图的遍历,正在检查的节点(current_node)的邻居被更新(父属性被设置并且它们被标记为已访问)并通过以下方式推送到std::vector&lt;node *&gt; node_list:

for (auto neighbor : current_node->neighbors) {
    neighbor->parent = current_node; 
    node_list.push_back(neighbor); 
    neighbor->visited = true;
}

【问题讨论】:

  • 你怎么知道是parent_addr 被破坏了,而不是addr?如果您想保持this-&gt;addr 等于this,那么缺少复制构造函数是有问题的。 (为什么你甚至存储addr而不是获取对象的地址?)
  • 在定义node_list 和第一次调用node_list.push_back 之间的一段时间内调用node_list.reserve(5) 会有所不同吗?
  • 重新检查push_back 的文档,然后仔细检查CreateNodeList 看是否能发现问题。
  • Vorian,SamV 实际上给出了正确的答案,所以我删除了我的。建议你接受前者。

标签: c++ memory corruption


【解决方案1】:
node_list.push_back(Node(i, &node_list[i-1]));

指向向量中前一个元素的指针被传递给新Node元素的构造函数。

这立即变成未定义的行为。 std::vectors 的一个基本属性是,当 std::vector 重新分配时,所有现有的指向向量的指针和迭代器都将失效。

在构造这个新的Node 之后,它会作为参数传递给push_back,这可能会导致向量内容的重新分配,从而使这个和所有以前指向向量的指针无效。

如果需要处理指向向量中元素的指针,则必须进行额外的工作以确保向量永远不会重新分配。这超出了这个问题的范围,而且会相当麻烦,所以这里最好的办法可能是重新考虑链表的整体设计,使其不使用std::vectors,也许std::list 将是一个替代方案(但是因为std::list 本身就是一个链表,所以这有点可疑)。在任何情况下,崩溃的原因都是未定义的行为,因为引用指向重新分配的向量中的值的指针,使指向向量中的值的所有指针无效。

【讨论】:

  • 很好,Sam,向量的其他元素的地址 within 向量绝对是个坏主意。在这种情况下,重新分配可能是致命的。顺便说一句,将索引(而不是指针)存储到向量中可以解决这个问题,因为索引在使用时会变成地址,而不是在创建时。
  • 山姆,感谢您的回复。我知道(现在)std::vector 在此示例中并不理想,因为重新分配问题。但是,std::vector 和节点类的结构(除了 ->addr,我添加用于调试)是课程的组成部分。我在问题中添加了对实际问题的更多解释。
  • 我不清楚与std::vector 的工作方式相关的新信息是如何工作的。新代码引用了一些以neighborsparentvisited 作为其成员的类。他们都不是Node 的成员,如原始问题所示。如果这里的目标是创建一个向量,之后保持不变,那么只需使用reserve() 保留向量的大小,然后push_back 使用相同数量的值保证向量不会重新分配。有关更多信息,请参阅 C++ 教科书。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-01
  • 2015-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-01
相关资源
最近更新 更多