-1

[Editor's note: I have edited the title to try to make this useful to others in the future. To give the answerers credit, this was just a "why does this not work?" question when they answered it!]

The following code crashes at the top -> ... line with a segmentation fault, regardless of what Node* is trying to be pushed onto the vector children. Does anyone know what might be causing this?

struct Node
{
    string data;
    struct Node* parent;
    vector<struct Node*> children;
};

struct Node* push(string word, struct Node* top)
{
    Node* temp = (struct Node*) malloc(sizeof(struct Node));
    temp -> data = word;
    temp -> parent = top;
    return temp;
}

int main()
{
    Node* top = push("blah", NULL);
    Node* temp = NULL;
    top -> children.push_back(temp);
}
Brooks Moses
  • 9,267
  • 2
  • 33
  • 57
vanchagreen
  • 372
  • 2
  • 9
  • 1
    what is the type of `top` and is it pointing to something? – Tony The Lion Jan 08 '12 at 00:04
  • 3
    Please show a complete example where top is initialised. – Gary Jan 08 '12 at 00:05
  • You are allowed to push a NULL pointer into your vector, but your `top` needs to point to a valid memory region, else bad things happen. – Tony The Lion Jan 08 '12 at 00:05
  • 2
    And where is the `top` in your code except mentioned once in `main` even without declaration? Post some real code... –  Jan 08 '12 at 00:05
  • Unlike C, it's not required to type `struct` before `Node*` in C++. You can just type `Node*` and the compiler will know you're talking about the structure `Node`. – In silico Jan 08 '12 at 00:06
  • Apparently the compiler assumes something else, otherwise it wouldn't have crashed. What's your **real** code? – littleadv Jan 08 '12 at 00:10
  • @In silico : I didn't know that, thank you. I added an initialization for top as well as a "push" function. – vanchagreen Jan 08 '12 at 00:12

5 Answers5

5

The problem is that malloc will not call constructors. And you're counting on the vector children to be constructed.

Replace:

Node* temp = (struct Node*) malloc(sizeof(struct Node));

With:

Node* temp = new Node;

malloc (from C) and new (from C++) will both allocate the memory you need, but only new will call the required constructors, as C doesn't use them. If you're not positive that you need malloc, use new.

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
  • 6
    @vanchagreen: It sounds like you come from a C background. Note that C++ works very differently from C, to a point where they are practically considered to be very different languages. I recommend picking up [a C++ book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). You might not need to start with an intro book, but it certainly doesn't hurt. – In silico Jan 08 '12 at 00:21
1

You don't use malloc on objects, you should use new instead. malloc is a C function that just allocates a chunk of memory, while the C++ operator std::new also takes care of the object initialization and construction - step that you've missed here and that causes you troubles (for example the constructor for temp->children has never been called in your case).

As a rule of thumb: if you're writing C++ code you should use the C++ operators std::new and std::delete for dynamic memory allocations and releases, not the C functions.

littleadv
  • 20,100
  • 2
  • 36
  • 50
  • I downvoted you because I mistakenly thought you were wrong -- in large part because you didn't explain why they were different (in the initial version of the answer, which was just the first sentence). Then I did a bit more looking around, and realized I was wrong and you were right, and cancelled the downvote -- although I didn't upvote because the initial version was still a poor answer. Now that you've added a good clear explanation, I've upvoted. – Brooks Moses Jan 08 '12 at 00:30
  • There is no `std::new` or `std::delete`. `new` and `delete` aren't even functions (nor are the operators); they are language constructs. – Kerrek SB Jan 08 '12 at 00:46
1

Your problem is that your children vector isn't correctly initialized. You should use Node* temp = new Node; instead of malloc to call the constructor for Node, which calls the constructor of children, thus correctly initializing the vector

Grizzly
  • 19,595
  • 4
  • 60
  • 78
1

As others have commented, it looks like you have come from C and are in need of a good C++ book. C++ is not just "C with classes"!

Your push function looks very much like it should be a constructor. A constructor is called by new after it has allocated the required memory and performs the necessary initialisation. The compiler will generate one for you, if you don't provide one (it will also provide a copy constructor and an assignment operator (see What is The Rule of Three?).

Since you called malloc() and not new, the synthesised default constructor was not called and therefore the children vector was not initialised, which caused your access violation.

Here I demonstrate how you may implement a default constructor (and disable the other two), to initiate each of the three data members of your class (or struct):

#include <string>
#include <vector>
using std::vector;
using std::string;

class Node
{
public:
    Node(const string& word, Node* top)
        :data(word)
        ,parent(top)
        ,children()
    {
        if (top != NULL)
        {
            top->children.push_back(this);
        }
    }

    virtual ~Node()
    {
        std::vector<Node*>::iterator i = std::find(children.begin(), children.end(), this);
        if (i != children.end())
        {
            parent->children.erase(i);
        }
        // TODO: You may wish to destory children at this point
    }

private:
    Node(const Node& disable);
    Node& operator =(const Node& disable);

private:
    string data;
    Node* parent;
    vector<Node*> children;
};

int main()
{
    Node top("blah", NULL);
    Node child("foo", &top);
}

I also implemented a destructor, which removes a node from its parent's children on destruction.

Community
  • 1
  • 1
johnsyweb
  • 136,902
  • 23
  • 188
  • 247
0

malloc() just allocates an empty block of memory, you should use the new() operator which initializes all member objects;

Node* temp = new Node();
Joachim Isaksson
  • 176,943
  • 25
  • 281
  • 294