0

I'm aware that there are several similar questions to this one already, but I've read through them and none of them seem to cover my specific problem...

I'm trying to dereference a public pointer from a struct, but it's throwing a SIGSEV Segmentation fault, even though the pointer does point to a value

It prints 1 & 2 but does not get any further. If I change it to reference the pointer rather than the value, the error moves to wherever I reference the value, so I know the issue is with dereferencing, not the rest of the line.

If you need to see any more of my code in order to find the issue, just ask. I just didn't want to make you trawl through hundreds of lines unnecesarily...

#include <vector>
#include "Entities.cpp"

struct Chunk {
    public:
        std::vector<BaseEntity>* environment; // this is what I'm trying to access

        Chunk() {
            environment = new std::vector<BaseEntity>(1); // this is where init

            environment->push_back(Grass()); // adding something to it works fine
        }

        ~Chunk() {
            delete(environment);
        }
};

class World {
    public:
        Chunk* loaded[3][3];

        World() {
            for (int x = 0; x < 3; x++)
                for (int y = 0; y < 3; y++) {
                    loaded[x][y] = new Chunk();
                }
        }

        ~World() {
            for (int x = 0; x < 3; x++)
                for (int y = 0; y < 3; y++) {
                    delete(loaded[x][y]);
                }
        }
};

And here's the code that accesses it (in another class & file)

void render(SDL_Renderer* gRenderer, Atlas* atlas, World* world, int SCREEN_WIDTH, int SCREEN_HEIGHT) {
        printf("1");
        for (int x = 0; x < 3; x++)
            for (int y = 0; y < 3; y++) {
                printf("2");
                std::vector<BaseEntity> env =
                        *(world->loaded[x][y]->environment); // here is where the error occurs
                printf("3");

                printf('0' + env.size() + "");
                printf("a");

                for (const auto& a : env) {
                    printf("b");
                    drawTexture(gRenderer, atlas, a.atlasID,
                            SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
                }
                printf("4");
            }
        printf("5");

    }

This is the code that calls it:

int main(int argv, char** args) {
    SDL_Init( SDL_INIT_VIDEO );

    //The window we'll be rendering to
    SDL_Window* gWindow = SDL_CreateWindow(
         title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );

    if (gWindow == NULL) {
        printf("window error");
    }
    
    //Create renderer for window
    SDL_Renderer* gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED );
    if (gRenderer == NULL) {
        printf("renderer error");
    }

    bool quit = false; // quit flag

    //Event handler
    SDL_Event e;

    //nice little rendering class
    Renderer* renderer = new Renderer();

    //World
    World* world = new World();

    //Texture Atlas
    Atlas* atlas = new Atlas(SDL_GetWindowSurface( gWindow), gRenderer);


    //While application is running
        while( !quit )
        {
            //Handle events on queue
            while( SDL_PollEvent( &e ) != 0 )
            {

                switch(e.type) {
                    case SDL_QUIT: //User requests quit
                        quit = true;
                        break;
                }
                
            }

            //Clear screen
            //SDL_SetRenderDrawColor( gRenderer, 0, 0xFF, 0, 0xFF );
            SDL_RenderClear( gRenderer );

            renderer->render(gRenderer, atlas, world, SCREEN_WIDTH, SCREEN_HEIGHT);

            //Update the surface
            SDL_RenderPresent(gRenderer);
            //SDL_UpdateWindowSurface( gWindow );

        }

        delete (world, renderer, atlas);

        return 0;
}
Skye
  • 31
  • 6
  • Where does `World *world` come from? How is it initialized? – Olaf Dietsche Aug 24 '21 at 14:43
  • I'll add it to the question – Skye Aug 24 '21 at 14:49
  • _"I just didn't want to make you trawl through hundreds of lines unnecesarily..."_ if hundreds of lines of code would be unnecessary, it's very possible that a [mre] would be appropriate. It's a good skill to develop, too. Keep removing code until your problem becomes obvious or you have a _very small_ program that exhibits your problem. – Drew Dormann Aug 24 '21 at 14:54
  • 2
    `delete (world, renderer, atlas);` this probably doesn't do what you want. `delete` is not a function call here. World and Chunk violate the rule of 3. – Mat Aug 24 '21 at 15:01

2 Answers2

0

I don't see an immediate problem with your code (except copying the vector is obviously inefficient), but I do see a problem with your printf debugging.

  1. You should use fprintf(stderr, ...) for debug printfs, so as to avoid stdio buffering, or call fflush(NULL).
    The fact that 3 is not printed does not necessarily mean that printf("3") is unreached.
  2. You should avoid printf debugging altogether, and learn to use a real debugger (GDB or LLDB) instead.
  3. If you run your program with Address Sanitizer (-fsanitize=address); it will likely point you straight at the bug.

Finally, when writing in C++, avoid C-style arrays, and especially multi-level arrays. Use vectors instead.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • My console isn't buffered anyway. I used printf to debug intially, but I quickly moved on to GDB when printf didn't clear up the issue. Is `-fsanitize=address` a compiler option or a debugger option? – Skye Aug 24 '21 at 15:04
  • fsanitize doesn't seem to be available for my compiler (MinGW) – Skye Aug 24 '21 at 15:16
  • @Skye "`-fsanitize` ..." -- in that case, you'll need to create a http://stackoverflow.com/help/mcve before someone can help you. Developing on a more capable platform, would be another obvious choice. – Employed Russian Aug 24 '21 at 15:27
0

This allocates a std::vector with one element of BaseEntity

environment = new std::vector<BaseEntity>(1); // this is where init

then another element of Grass is appended

environment->push_back(Grass()); // adding something to it works fine

but maybe this is the first problem, since the vector only holds BaseEntitys, see What is object slicing?


This accesses the first element of the given vector, and this is not the appended Grass but the default constructed BaseEntity object from new std::vector<BaseEntity>(1)

std::vector<BaseEntity> env = *(world->loaded[x][y]->environment); // here is where the error occurs

Accessing the pointer shouldn't be a problem, because it seems to be initialized properly. But maybe copying the BaseEntity is the real problem here. I would look into the (copy) constructor of BaseEntity (and maybe Grass) and see, if there is some problematic code.

Olaf Dietsche
  • 72,253
  • 8
  • 102
  • 198