1

I've built the Graph class, one constructor and one destructor. My problem is: when i compile this code, i get an error at this line delete [] mat[i];, the error is 'Access violation reading location 0xDDDDDDCD.' and i'm not sure what am i doing wrong. I mean i only delete the memory i dynamically alocated. Later edit: even if i use this instead of my method :

Graph(int nr_noduri) {


this->nr_noduri = nr_noduri;
    mat = new bool* [nr_noduri];
    for (int i = 0; i < nr_noduri; i++) {
        mat[i] = new bool[nr_noduri];
    }
    for (int i = 0; i < nr_noduri; i++) {
        for (int j = 0; j < nr_noduri; j++) {
            mat[i][j] = 0;
        }
    }
}

My code is :

 #include <iostream>
#include <cstdlib>
using namespace std;

class Graph {
    bool** mat;
    int nr_noduri;

public:
    Graph(int nr_noduri) {
        this->nr_noduri = nr_noduri;
        mat = (bool**)calloc(nr_noduri, sizeof(bool*));
        for (int i = 0; i < nr_noduri; i++) {
            mat[i] = (bool*)calloc(nr_noduri, sizeof(bool));
        }
    }

    int size() {
        return nr_noduri;
    }

    void addArc(int v, int w) {
        if (v < nr_noduri && w < nr_noduri) {
            mat[v][w] = true;
        }
        else cout << "Not enough nodes !" << endl;
    }

    bool isArc(int v, int w) {
        return (mat[v][w] == true);

    }

    void print() {
        for (int i = 0; i < nr_noduri; i++) {
            cout << endl;
            for (int j = 0; j < nr_noduri; j++) {
                cout << mat[i][j] << " ";
            }
        }
        cout << endl;
    }

    ~Graph() {
        //cout << nr_noduri;
        for (int i = 0; i < nr_noduri; i++) {
            delete [] mat[i];
        }
        delete[] mat;
    }

    friend void dfs(Graph g, int v, bool vazut[]);
};

void dfs(Graph g, int v, bool vazut[]) {
    int w;
    vazut[v] = true;
    for (w = 0; w < g.size(); w++) {
        if (g.isArc(v, w) && !vazut[w]) {
            cout << v << "->" << w;
            dfs(g, w, vazut);
        }
    }
}


int main() {
    int nr_noduri;
    cin >> nr_noduri;
    Graph v(nr_noduri);
    v.print();
    v.addArc(1, 2);
    v.addArc(2, 5);
    cout << v.isArc(1, 2) << endl;
    v.print();
    bool vazut[100];
    dfs(v, 1, vazut);
    return 0;
}
ballow
  • 81
  • 1
  • 8
  • When you see a highly regular number like `0xDDDDDDCD`, the program is [probably trying to tell you something](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_debug_values). 0xDDDDDDCD is probably 0xDDDDDDDD with an offset, and looking up 0xDDDDDDDD we find the program is likely trying to use memory that had previously been returned. – user4581301 Oct 30 '19 at 17:13
  • 1
    Unrelated (probably): You should familiarize yourself with [the Rule of Three and its friends](https://en.cppreference.com/w/cpp/language/rule_of_three). You can't safely use a class like this without taking them into account. – user4581301 Oct 30 '19 at 17:15
  • `void dfs(Graph g, int v, bool vazut[])` -- You are passing `Graph` by value, and `Graph` does not have correct copy semantics. The code as posted is not going to work until you implement the Rule of 3 (already mentioned). No need to look further. – PaulMcKenzie Oct 30 '19 at 17:20
  • A simple [2 line main program](https://coliru.stacked-crooked.com/a/481ad0921c4667d6) shows the error. – PaulMcKenzie Oct 30 '19 at 17:26

2 Answers2

6

You cannot use delete to deallocate memory that was allocated by calloc (or any of the malloc family of functions). Use new instead to allocate memory.

Graph(int nr_noduri) {
    this->nr_noduri = nr_noduri;
    mat = new bool*[nr_noduri];
    for (int i = 0; i < nr_noduri; i++) {
        mat[i] = new bool[nr_noduri];
    }
}

Better yet, use std::vector<std::vector<bool>> to hold the data.

class Graph {
   std::vector<std::vector<bool>> mat;

   Graph(int nr_noduri) : mat(nr_noduri, std::vector<bool>(nr_noduri) {}

   ...
};
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Does it matter if they're calling C code whether to use new/delete vs. *alloc/free in their code? – Mode77 Oct 30 '19 at 16:37
  • @Mode77, no, it does not. Once memory is allocated, rest of the code can use the memory regardless of how it was allocated. – R Sahu Oct 30 '19 at 16:39
  • @RSahu ...although you might have to placement-new objects into that memory first. – Max Langhof Oct 30 '19 at 16:43
  • even if i do it with new and then i initalize all the matrix with zeros, i still get that error when i deallocate memory, i really dont get it why – ballow Oct 30 '19 at 16:45
  • @ballow, when I commented out the call to `dfs` in `main`, the problem went away. You are doing something in that function that causes undefined behavior. You'll have to step through that code in a debugger to figure out how to resolve it. – R Sahu Oct 30 '19 at 16:58
2

This line:

void dfs(Graph g, int v, bool vazut[])

has the first parameter g passed by value, thus will be a temporary copy of the Graph that is passed in. If Graph does not have correct copy semantics, then the call to dfs will cause undefined behavior.

The Graph contains a member mat that points to dynamically allocated memory. In addition, Graph has a destructor that issues a delete [] on this memory. So when Graph is copied, that mat pointer is also copied. You now have two mat pointers pointing to the same memory. When the temporary goes out of scope, delete [] will be issued on the mat pointer.

That is probably the reason why you are getting runtime issues -- the memory mat was pointing to has already been deleted, and you're deleting it again a second time.

To fix this issue you have two choices:

  1. Pass Graph by reference to dfs instead of by value, or
  2. Fix your Graph class to handle copies correctly.

For item 1:

void dfs(Graph& g, int v, bool vazut[])

that avoids the temporary copy from being made.


However, that to me is just a band-aid solution to avoid having to hit the copy bug (even though you should pass-by-reference anyway).

A better one is to make the Graph class safely copyable, meaning that copies can be made correctly, without a "double deletion" errors you're seeing now.

To do that, add the following functions to complete the rule of 3 to allow copying to be performed correctly:

#include <algorithm>
//…
Graph(const Graph& rhs) : mat(nullptr), nr_noduri(rhs.nr_noduri)
{
    mat = new bool* [rhs.nr_noduri];
    for (int i = 0; i < rhs.nr_noduri; i++) {
        mat[i] = new bool[rhs.nr_noduri];
    }
    for (int i = 0; i < rhs.nr_noduri; i++) {
        for (int j = 0; j < rhs.nr_noduri; j++) {
            mat[i][j] = rhs.mat[i][j];
        }
    }
}

Graph& operator=(const Graph& rhs)
{
    if ( this != &rhs )
    {
        Graph temp(rhs);
        std::swap(temp.mat, mat);
        std::swap(temp.nr_noduri, nr_noduri);
    }
    return *this;
}

Your class was lacking a user-defined copy constructor and assignment operator (which uses the copy / swap idiom).


Another unrelated issue is the way you're building your 2D matrix. You're using one of the worst methods, and that is to allocate pointers, then in a loop, allocate each row.

It not only is a bottleneck in terms of speed (calling the allocator for each row), it is not cache-friendly due to the rows being allocated in different parts of the heap, in addition to heap fragmentation, and if one of those calls to new[] within the loop fails, you have to rollback all of the successful calls to new[] if you don't want to leak memory.

See this solution for a better method of creating a 2D array.

PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45