0

I am currently working on an assignment to implement Conways Game of Life in C++. At the end of excution, I get the following message: free(): double free detected in tcache 2 Aborted (core dumped). I think, this is due to the line delete[] pf and the variable updatedField. At least the problem does not occur, when I set the pointer to NULL. But at the same time, memory leakage occurs (according to using valgrind). I compile the program using g++ -o main main.cpp. I am working on a Linux Distribution.

Here is my code:

#include <iostream>

using namespace std;

const uint8_t ALIVE = 1;
const uint8_t DEAD = 0;

class Field
{
    int width;
    int height;
    uint8_t *pf;

public:
    int numberCellsAlive;
    Field(int width_, int height_) : width(width_), height(height_)
    {
        pf = new uint8_t[width * height];
    }

    ~Field()
    {

        pf = NULL; //This solves the problem, but leads to memory leakage
        delete[] pf;
    }

    uint8_t get(uint32_t x, uint32_t y)
    {
        return pf[x + width * y];
    }

    void set(uint32_t x, uint32_t y, uint8_t state)
    {
        pf[x + width * y] = state;
    }

    int size()
    {
        return width;
    }
};

int getRandomNumber(int size)
{
    int randomNumber = (rand() % size);
    return randomNumber;
}

int getNumberOfInitialAliveCellsFromPercentage(int size, int percentage)
{

    return (size * size) * percentage / 100;
}

void fill(Field &field)
{
    int size = field.size();
    for (int y = 0; y < size; y++)
    {
        for (int x = 0; x < size; x++)
        {
            if (field.get(x, y) == ALIVE)
            {
                cout << " 0 ";
            }
            else
            {

                cout << " . ";
            }

            if (x == (size - 1))
            {

                cout << endl;
            }
        }
    }
    cout << endl;
}

Field init(Field &field, int percentage)
{
    int size = field.size();
    for (int y = 0; y < size; y++)
    {
        for (int x = 0; x < size; x++)
        {
            field.set(x, y, DEAD);
        }
    }

    field.numberCellsAlive = getNumberOfInitialAliveCellsFromPercentage(size, percentage);
    int randomX = getRandomNumber(size);
    int randomY = getRandomNumber(size);
    int f = 0;
    srand((unsigned)time(0));
    while (f < field.numberCellsAlive)
    {
        randomX = getRandomNumber(size);
        randomY = getRandomNumber(size);

        if (field.get(randomX, randomY) == DEAD)
        {
            f++;
            field.set(randomX, randomY, ALIVE);
        }
    }

    if (field.numberCellsAlive > 0)
    {
        fill(field);
    }
    return field;
}

// Checks neighbours if cell dies or stays alive.
Field checkNeighbours(Field &field)
{
    int countLifes = 0;
    int size = field.size();
    Field updatedField = Field(size, size);

    for (int y = 0; y < size; y++)
    {
        for (int x = 0; x < size; x++)
        {
            int neighbourCount = 0;

            //vertical
            if (y < size - 1)
            {
                if (field.get(x, y + 1) == ALIVE)
                {
                    neighbourCount++;
                }
            }
            if (y > 0)
            {
                if (field.get(x, y - 1) == ALIVE)
                {
                    neighbourCount++;
                }
            }
            //horizontal
            if (x > 0)
            {
                if (field.get(x - 1, y) == ALIVE)
                {
                    neighbourCount++;
                }
            }
            if (x < size - 1)
            {
                if (field.get(x + 1, y) == ALIVE)
                {
                    neighbourCount++;
                }
            }
            //Diagonal upper left
            if (y > 0 && x > 0)
            {
                if (field.get(x - 1, y - 1) == ALIVE)
                {
                    neighbourCount++;
                }
            }
            //Diagonal bottom right
            if (x < size - 1 && y < size - 1)
            {
                if (field.get(x + 1, y + 1) == ALIVE)
                {
                    neighbourCount++;
                }
            }
            // Diagonal bottom left
            if (y < size - 1 && x > 0)
            {
                if (field.get(x - 1, y + 1) == ALIVE)
                {
                    neighbourCount++;
                }
            }
            // Diagonal upper right
            if (y > 0 && x < size - 1)
            {
                if (field.get(x + 1, y - 1) == ALIVE)
                {
                    neighbourCount++;
                }
            }

            if (field.get(x, y) == ALIVE)
            {

                if (neighbourCount == 2 || neighbourCount == 3)
                {
                    countLifes++;
                    updatedField.set(x, y, ALIVE);
                }
                else
                {
                    updatedField.set(x, y, DEAD);
                }
            }
            else
            {
                if (neighbourCount == 3)
                {
                    countLifes++;
                    updatedField.set(x, y, ALIVE);
                }
                else
                {
                    updatedField.set(x, y, DEAD);
                }
            }
        }
    }
    updatedField.numberCellsAlive = countLifes;
    return updatedField;
}

int main(int argc, char *argv[])
{

    if (argc < 4)
    {
        cout << "Illegal number of arguments" << endl
             << "Please enter exactly 3 arguments following this pattern: " << endl
             << "./main SIZE_OF_BOARD NUMBER_OF_GENERATIONS PERCENTAGE_OF_CELLS_ALIVE " << endl;

        return 1;
    }

    //Clear console
    cout << "\033[2J\033[1;1H";

    int gen;
    int size;
    int percentage;

    sscanf(argv[1], "%d", &size);
    sscanf(argv[2], "%d", &gen);
    sscanf(argv[3], "%d", &percentage);

    Field field = Field(size, size);

    field = init(field, percentage);

    for (int i = 2; i <= gen; i++)
    {
        if ((!field.numberCellsAlive) > 0)
        {

            cout << "Game is over before time because no cells are alive. " << endl;
        }

        cin.get();
        cout << "\033[2J\033[1;1H";

        field = checkNeighbours(field);

        cout << i << " Generation " << endl;

        fill(field);
    }

    cin.get();
}

If somebody could point me in the right direction of how to solve this, I'd be very grateful!.

student20
  • 11
  • 2
  • 1
    You'll be glad to hear you don't need anyone's help to figure this out, just a tool you already have: your debugger! This is exactly what a debugger is for. It runs your program, one line at a time, and shows you what's happening. Knowing how to use a debugger is a required skill for every C++ developer, no exceptions. With your debugger's help you should be able to quickly find all bugs in this and all future programs you write, without having to ask anyone for help. Have you tried using your debugger, already? If not, why not? What did your debugger show you? – Sam Varshavchik Aug 09 '20 at 16:30
  • 1
    Why are you using manual memory management? Why don't you use containers or (at least) smart pointers? – Jesper Juhl Aug 09 '20 at 16:30
  • Pro tip: add checks in both `get()` and `set()` that verify that both `x` and `y` are valid, and are not out of range. Then, use your debugger to set a breakpoint on the failing check, and then investigate why they are out of range. The bug in the shown code will then become very apparent. – Sam Varshavchik Aug 09 '20 at 16:33
  • 1
    You want to read [What is The Rule of Three?](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) and [Rule-of-Three becomes Rule-of-Five with C++11?](https://stackoverflow.com/questions/4782757/rule-of-three-becomes-rule-of-five-with-c11). – Blastfurnace Aug 09 '20 at 16:36

0 Answers0