Why am I getting a Segmentation Fault when adding a unrelated line of code?

Viewed 106

I am trying to make a maze generator program in C++, I am using a custom data structure for handling the grid, I know that is not the best since we have built-in data structures, but I am trying to learn.

I am trying to find a way to save the maze in an istream, that being either an ifstream, std::cout, or a string stream, the function to make that happen works fine, I am able to save mazes.

At the moment, I have implemented a way to add obstacles, which the algorithm should avoid when generating the maze, and I am able to generate different types of maze patterns, for example, the one that I am having trouble with:

int main()
{
    srand(time(NULL));

    const Coord size(11, 9);
    Maze maze({size.x, size.y});

    maze.setObstacle({0, 0});
    maze.setObstacle({0, 1});
    maze.setObstacle({0, 4});
    maze.setObstacle({0, 5});
    maze.setObstacle({0, 6});
    maze.setObstacle({0, 9});
    maze.setObstacle({0, 10});
    maze.setObstacle({1, 0});
    maze.setObstacle({1, 5});
    maze.setObstacle({1, 10});

    int cnt = 1;
    for(int y = 4; y < size.y; y++)
    {
        for(int x = 0; x < cnt; x++)
        {
            maze.setObstacle({y, x});
            maze.setObstacle({y, size.x - 1 - x});
        }
        cnt++;
    }

    std::ofstream out("maze.txt");
    maze.output(std::cout, OutputType::Maze);
    out.close();
    
    return 0;
}

This piece of code, if you follow all the indices will produce a heart-shaped maze. The problem is if I add a line that opens a fstream to output the maze to it, even if I don't use the fstream I get an error:

Thread 1 received signal SIGSEGV, Segmentation fault.
0x00000000004040f0 in Grid<Cell>::operator= (this=this@entry=0x68fc00, grid=...) at include/Grid.inl:53
53      Grid<T>& Grid<T>::operator=(const Grid<T>& grid)

The function that is referring to is this:

template <typename T>
Grid<T>& Grid<T>::operator=(const Grid<T>& grid)
{
    if(this == &grid)
        return *this;

    for(int i = 0; i < size.y; i++)
    {
        delete[] data[i];
    }
    delete[] data;

    this->size = grid.size;
    data = new T*[size.y];
    for(int i = 0; i < size.y; i++)
    {
        data[i] = new T[size.x];
    }

    for(int i = 0; i < size.y; i++)
    {
        for(int j = 0; j < size.x; j++)
        {
            this->data[i][j] = grid.data[i][j];
        }
    }
    return *this;
}

This segmentation fault is only present when trying to shape the maze into a heart, for example, I remove the for loop that shapes the bottom of the heart, everything works fine, I can open the file, can even write to it.

The place where the assignment operator is called that throws the Segmentation Fault is in the constructor of the maze, where I call it like this:

Maze::Maze(Coord size)
{
    this->size = size;
    grid = Grid<Cell>(size);
    maze = Grid<uint8_t>({size.x * 2 + 1, size.y * 2 + 1});
    ...
}

Why do I get this error only when I open a file, even If I don't use it?

At the moment I don't know which parts of the code you need to look at to help me, If you need anything else to figure something out, I will add that part of the code.

You can also find the code on GitHub, it doesn't have any dependencies and you can just compile it with the included makefile, make sure to add the obj directory in the main dir, and also make sure to get the bug_fix branch.

I have just tried to compile and use it on a VM that runs Ubuntu, everything works as expected without the Segmentation fault popping up, the VM is hosted on the same machine.

Additional information:

  • Compiler: g++ (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
  • Compiler flags: -g -O2 -Wall -std=c++17
  • Windows: Windows 10 Pro 20H2
  • CPU: AMD Ryzen 7 5800X 8-Core Processor 3.80 GHz (recently updgraded)
  • MB: AsRock B450 Pro4 with bios update
  • After I made the update to the bios and changed the processor, I didn't reinstall windows, everything works fine, I had a bug with the windows time, and now a game crashes, but I have stress tested the CPU and GPU and everything runs normally, without crashes. Could it be from the upgrade?

Sorry for the long post and for the fact that I couldn't include a minimal reproducible example. Hope someone could share some wisdom with me.

1 Answers

Well, I have figured out how to solve the issue, as @molbdnilo pointed out, the index management was bad, I was using a T** to store all the data, when I could have gotten away with T* and using the "y * W + x" formula, which is how I fixed it.

I now store all the data in a one dimensional array and handle all the index operations with the given formula, now all the indexes are always in (x, y) order and never have to be flipped, which was seeming to cause the issues since I lost track of most of the order.

In case someone stumbles on this thread and needs the same answer, here is the new grid class:

Grid.h

#pragma once

#include "Cell.h"

template <typename T>
class Grid
{
private:
    T* data;
    Coord size;
public:
    Grid();
    Grid(Coord size);
    T& operator()(int x, int y);
    T& operator()(const Coord& c);
    Grid& operator=(const Grid& grid);
    Coord getSize();
    ~Grid();
};

#include "Grid.inl"

Grid.inl

template <typename T>
Grid<T>::Grid()
{
    data = nullptr;
}

template <typename T>
Grid<T>::Grid(Coord size)
{
    this->size = size;
    data = new T[size.x * size.y];
}

template <typename T>
T& Grid<T>::operator()(int x, int y)
{
    assert(x < size.x && y < size.y && x >= 0 && y >= 0);
    return data[y * size.x + x];
}

template <typename T>
T& Grid<T>::operator()(const Coord& c)
{
    assert(c.x < size.x && c.y < size.y && c.x >= 0 && c.y >= 0);
    return data[c.y * size.x + c.x];
}

template <typename T>
Grid<T>& Grid<T>::operator=(const Grid<T>& grid)
{
    if(this == &grid)
        return *this;

    delete[] data;

    this->size = grid.size;
    data = new T[size.x * size.y];

    for(int i = 0; i < size.x * size.y; i++)
    {
        this->data[i] = grid.data[i];
    }
    return *this;
}

template <typename T>
Coord Grid<T>::getSize()
{
    return size;
}

template <typename T>
Grid<T>::~Grid()
{
    delete[] data;
}

Thanks for all the answers and help.

Related