简体   繁体   中英

Understanding segmentation faults in references when popping from queue

I've got the following function:

std::vector<Node> BFS(Graph g, Node source, Node target) {
    std::queue<std::vector<Node> > q;
    std::vector<Node> path = {source};
    q.push(path);
    while (!q.empty()) {
        std::vector<Node> cur_path = q.front();
        q.pop();
        Node cur_node = cur_path[cur_path.size() - 1];
        if (cur_node.data == target.data) {
            return cur_path;
        }
        // for (int i = 0; i < g.edge_map[cur_path].size(); i++) {
        //     std::vector<Node> new_path = cur_path;
        //     new_path.push_back(g.edge_map[cur_path][i]);
        //     q.push(new_path);
        // }
    }
}

Now, I'm not claiming this accomplishes a search at the moment, I just want to know why it causes a segmentation fault. I know it's because I've got that q.pop(); bit, but why? Is this because vectors are inherently references? I can include more of my code if needed, just trying to encapsulate the issue at hand.

EDIT: here's the main() driver:

int main(int argc, char **argv) {
    // double normals[][3] = {
    //    #include "normals.txt"
    // };
    Graph g;
    Node n0 {0, "red"};
    Node n1 {1, "yellow"};
    Node n2 {2, "black"};
    Node n3 {3, "black"};
    Node n4 {4, "red"};
    Node n5 {5, "yellow"};
    Node n6 {6, "black"};
    Node n7 {7, "black"};
    Node n8 {8, "red"};
    Node n9 {9, "yellow"};
    Node n10 {10, "black"};
    Node n11 {11, "black"};

    g.add_neighbor(n0, n1, true);
    g.add_neighbor(n0, n2, true);
    g.add_neighbor(n0, n3, true);
    g.add_neighbor(n2, n4, true);
    g.add_neighbor(n4, n5, true);
    g.add_neighbor(n4, n6, true);
    g.add_neighbor(n4, n7, true);
    g.add_neighbor(n6, n8, true);
    g.add_neighbor(n8, n9, true);
    g.add_neighbor(n8, n10, true);
    g.add_neighbor(n8, n11, true);
    BFS(g, n0, n11);
    return 0;
}

which gives:

graph. yup
Segmentation fault (core dumped)

FWIW, my graph has an edge_map member which is a map with Node structs as keys, and vectors of Node structs as values to represent a graph adjacency list. The constructor outputs the 'graph. yup' bit, please let me know if you need more information

EDIT 2: I'm just going to include the rest of it for completeness, sorry for verbosity but I'm trying to understand why this is happening.

graph.h:

#include <iostream>
#include <map>
#include <vector>

struct Node {
    int data;
    std::string color;
};

inline bool operator<(const Node & left, const Node & right) {
    return left.data < right.data;
}

class Graph {
public:

    std::map< Node, std::vector<Node> > edge_map;

    Graph();

    void add_neighbor(Node cur_node, Node new_node, bool both_ways=false);

    void remove_neighbor(Node cur_node, Node del_node, bool both_ways=false);

    void print_neighbors(Node cur_node);

    virtual ~Graph();
};

graph.cpp:

#include <iostream>
#include <vector>
#include <algorithm>
#include "graph.h"
#include <map>

Graph::Graph() {
    std::cout << "graph. yup" << std::endl;
}


void Graph::add_neighbor(Node cur_node, Node new_node, bool both_ways) {
    edge_map[cur_node].push_back(new_node);
    if (both_ways) {
        edge_map[new_node].push_back(cur_node);
    }
}

void Graph::remove_neighbor(Node cur_node, Node del_node, bool both_ways) {
    // TODO: convert vector values in map of <Node, vector<Node> > into maps, so this function can be easy and have sensible complexity
    std::cout << "Not implemented yet." << std::endl;
    // if (both_ways) {
    //     edge_map[del_node].remove(cur_node);
    // }
    //this->edge_map[cur_node].erase(std::remove(this->edge_map[cur_node].begin(), this->edge_map[cur_node].end(), del_node), this->edge_map[cur_node].end());
}

void Graph::print_neighbors(Node cur_node) {
    for (int i = 0; i < edge_map[cur_node].size(); i++) {
        std::cout << edge_map[cur_node][i].data << " " << edge_map[cur_node][i].color << std::endl;
    }
}

Graph::~Graph() {
    std::cout << "ded graph :(" << std::endl;
}

any general tips are also appreciated, I'm green as hell.

One big, glaring error:

Your BFS function does not return a value for all control paths, even though it's supposed to return a std::vector<Node> . Thus your program has undefined behavior. The only path where BFS returns is in the conditional statement in the while loop:

 if (cur_node.data == target.data) {
            return cur_path;

However no return is done if the cur_node.data is never equal to target.data during the running of the outer while loop, thus triggering the undefined behavior (in your case, a segmentation fault).

Running your program here, using the triggering output statement, "I'm in trouble!!" shows you are causing undefined behavior. Note that the compiler used is Visual Studio 2015.

But to show the ramifications of undefined behavior, note that gcc as shown here will just crash without printing the message. To have it print the message, the buffer has to be flushed with std::endl to see the "trouble!!" string as seen here .

Given all of that, since you have to return something, you have to decide what to return in the case of the node not being found. You could return a std::vector<Node>() , but how would the caller to BFS determine if this node is a node that exists or not? I think you need to straighten out the ground rules for BFS as to what the output of that function should denote.

Also:

Node cur_node = cur_path[cur_path.size() - 1];

could simply be:

Node cur_node = cur_path.back();

(I have not enough reputation to add a comment)

As far as I understand what might be happening:

  • q.pop invokes destructor of std::vector

  • ~std::vector destroys each Node

  • Maybe the Node destructor does something illegal?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM