简体   繁体   中英

C realloc error: malloc(): invalid size (unsorted)

I am currently making a sdl2(/graphical) version of the game of life in C, so when the screen is resized I need to update the grid. When the screen is resized I don't want it to just change the size of the tiles, I want it to actually create more tiles. So I reallocate the cells list, which contains all of the cells states but for some odd reason that doesn't work.

bool updateCells(int w, int h) {
    size_t x, y, 
          oldGridCol, 
          oldGridRow;

    oldGridCol = gridCol;
    oldGridRow = gridRow;

    gridRow = w / gridSizeW;
    gridCol = h / gridSizeH;

    cells = (cellState_t *)realloc(cells, (gridRow * gridCol) * sizeof(cellState_t));
    if(cells == NULL) {
        SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Memory allocation failed!");
        return false;
    }

    for(y = 0; y < oldGridRow; y++) {
        for(x = 0; x < oldGridCol; x++) {
            writeCell(y, x, *(cells + (x + (y * oldGridCol))));
        }
    }

    return false;
}

When this function is called the realloc function returns this:

malloc(): invalid size (unsorted)
Aborted

Thank you in advance!

I have made a minimal reproducible example of this program, which doesn't use SDL2, just plain C. Now for some reason this example works.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>

typedef enum cellState {
    dead,
    live,
    potentialDead,
    potentialLive
} cellState_t;

size_t          gridRow,
            gridCol,
            gridSizeW,
            gridSizeH;

cellState_t     *cells;

bool            quitLoop;

bool initCells(void);
bool updateCells(int w, int h);
void writeCell(size_t row, size_t col, cellState_t state);
cellState_t readCell(size_t row, size_t col);

void die(const char *f, const size_t l, const char *fmt, ...);

int main(int argc, char *args[]) {
    int w, h;

    quitLoop = false;

    gridSizeW = 25;
    gridSizeH = 25;


    // Let's assume that the window size is 640 by 480
    gridRow = 640 / gridSizeW;
    gridCol = 480 / gridSizeH;

    if(!initCells())
        die(__FILE__, __LINE__, "Failed to initialize cells!");

    writeCell(1, 2, live);
    writeCell(2, 3, live);
    writeCell(3, 3, live);
    writeCell(3, 2, live);
    writeCell(3, 1, live);

    while(!quitLoop) {
        updateCells(640, 480);

        printf("%d\n", readCell(1, 2));
    }

    return EXIT_SUCCESS;
}

bool initCells(void) {
    cells = calloc((gridRow * gridCol), sizeof(cellState_t));
    if(cells == NULL) {
        return false;
    }

    return true;
}

bool updateCells(int w, int h) {
    size_t x, y, 
          oldGridCol, 
          oldGridRow;

    oldGridCol = gridCol;
    oldGridRow = gridRow;

    gridRow = w / gridSizeW;
    gridCol = h / gridSizeH;

    cells = (cellState_t *)realloc(cells, (gridRow * gridCol) * sizeof(cellState_t));
    if(cells == NULL) {
        return false;
    }

    for(y = 0; y < oldGridRow; y++) {
        for(x = 0; x < oldGridCol; x++) {
            writeCell(y, x, *(cells + (x + (y * oldGridCol))));
        }
    }

    return false;
}

void writeCell(size_t row, size_t col, cellState_t state) {
    *(cells + (col + (row * gridCol))) = state;
}

cellState_t readCell(size_t row, size_t col) {
    return *(cells + (col + (row * gridCol)));
}

void die(const char *f, const size_t l, const char *fmt, ...) {
    va_list vargs;

    va_start(vargs, fmt);

    fprintf(stderr, "error from file %s on line %ld: ", f, l);
    //SDL_LogMessageV(SDL_LOG_CATEGORY_ERROR, SDL_LOG_PRIORITY_CRITICAL, fmt, vargs);

    fputc('\n', stderr);

    va_end(vargs);

    exit(EXIT_FAILURE);
}

Maybe it's the window size variable that affects the outcome, or something like that.

But the full code doesn't work, here's the full code:

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

#define WINDOW_NAME "sdl-life"
#define WINDOWW     640
#define WINDOWH     480

typedef enum cellState {
    dead,
    live,
    potentialDead,
    potentialLive
} cellState_t;

SDL_Window      *gWindow;
SDL_Renderer        *gRenderer;
SDL_Texture     *gLiveCellTexture,
            *gGrid;

size_t          gridRow,
            gridCol,
            gridSizeW,
            gridSizeH;

cellState_t     *cells;

bool            quitLoop;

bool initSdl(void);
void closeSdl(void);
SDL_Texture *loadTexture(const char *path);
bool loadMedia(void);

bool initCells(void);
bool updateCells(int w, int h);
void writeCell(size_t row, size_t col, cellState_t state);
cellState_t readCell(size_t row, size_t col);

void displayCell(cellState_t status, SDL_Rect location);
void displayAllCells(void);

void die(const char *f, const size_t l, const char *fmt, ...);

int main(int argc, char *args[]) {
    SDL_Event event;
    int w, h;

    quitLoop = false;

    if(!initSdl())
        die(__FILE__, __LINE__, "Failed to initialize SDL!");

    if(!loadMedia())
        die(__FILE__, __LINE__, "Failed to load media!");

    SDL_GetWindowSize(gWindow, &w, &h);

    gridSizeW = 25;
    gridSizeH = 25;

    gridRow = w / gridSizeW;
    gridCol = h / gridSizeH;

    if(!initCells())
        die(__FILE__, __LINE__, "Failed to initialize cells!");

    writeCell(1, 2, live);
    writeCell(2, 3, live);
    writeCell(3, 3, live);
    writeCell(3, 2, live);
    writeCell(3, 1, live);

    SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0x00, 0x00);

    while(!quitLoop) {
        while(SDL_PollEvent(&event)) {
            if(event.type == SDL_QUIT)
                quitLoop = true;
        }

        SDL_RenderClear(gRenderer);

        SDL_GetWindowSize(gWindow, &w, &h);
        updateCells(w, h);

        displayAllCells();

        SDL_RenderPresent(gRenderer);
    }

    closeSdl();

    return EXIT_SUCCESS;
}

bool initSdl(void) {
    SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, "The initialization process has begun");
    if(SDL_Init(SDL_INIT_VIDEO) < 0) {
        SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to initialize SDL: %s", SDL_GetError());
        return false;
    }

    if(!IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG) {
        SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to initialize SDL_image: %s", IMG_GetError());
        return false;
    }

    if(TTF_Init() == -1) {
        SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to initialize SDL_ttf: %s", TTF_GetError());
        return false;
    }

    gWindow = SDL_CreateWindow(WINDOW_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOWW, WINDOWH, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
    if(gWindow == NULL) {
        SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to create the window: %s", SDL_GetError());
        return false;
    }

    gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if(gRenderer == NULL) {
        SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to create the renderer: %s", SDL_GetError());
        return false;
    }

    SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, "The initialization has finished");

    return true;
}

void closeSdl(void) {
    SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, "SDL is shutting DOWN!");

    free(cells);

    SDL_DestroyTexture(gLiveCellTexture);
    SDL_DestroyTexture(gGrid);
    SDL_DestroyRenderer(gRenderer);
    SDL_DestroyWindow(gWindow);

    IMG_Quit();
    TTF_Quit();
    SDL_Quit();
}

SDL_Texture *loadTexture(const char *path) {
    SDL_Texture *newTexture;
    SDL_Surface *loadedSurface;

    loadedSurface = IMG_Load(path);
    if(loadedSurface == NULL) {
        SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to load surface: %s", IMG_GetError());
        return NULL;
    }

    SDL_SetColorKey(loadedSurface, SDL_TRUE, SDL_MapRGB(loadedSurface->format, 0x0, 0xff, 0xff));

    newTexture = SDL_CreateTextureFromSurface(gRenderer, loadedSurface);
    if(newTexture == NULL) {
        SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to convert surface to texture: %s", SDL_GetError());
        return NULL;
    }

    SDL_FreeSurface(loadedSurface);

    return(newTexture);
}

bool loadMedia(void) {
    gLiveCellTexture = loadTexture("livecell.png");
    if(gLiveCellTexture == NULL) {
        SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to load surface: %s", IMG_GetError());
        return false;
    }

    gGrid = loadTexture("grid.png");
    if(gGrid == NULL) {
        SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to load surface: %s", IMG_GetError());
        return false;
    }

    return true;
}

bool initCells(void) {
    cells = calloc((gridRow * gridCol), sizeof(cellState_t));
    if(cells == NULL) {
        SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Memory allocation failed!");
        return false;
    }

    return true;
}

bool updateCells(int w, int h) {
    size_t x, y, 
          oldGridCol, 
          oldGridRow;

    oldGridCol = gridCol;
    oldGridRow = gridRow;

    gridRow = w / gridSizeW;
    gridCol = h / gridSizeH;

    cells = (cellState_t *)realloc(cells, (gridRow * gridCol) * sizeof(cellState_t));
    if(cells == NULL) {
        SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Memory reallocation failed!");
        return false;
    }

    for(y = 0; y < oldGridRow; y++) {
        for(x = 0; x < oldGridCol; x++) {
            writeCell(y, x, *(cells + (x + (y * oldGridCol))));
        }
    }

    return false;
}

void writeCell(size_t row, size_t col, cellState_t state) {
    *(cells + (col + (row * gridCol))) = state;
}

cellState_t readCell(size_t row, size_t col) {
    return *(cells + (col + (row * gridCol)));
}

void displayCell(cellState_t status, SDL_Rect location) {
    SDL_RenderCopy(gRenderer, gGrid, NULL, &location);

    if(status == live) {
        SDL_RenderCopy(gRenderer, gLiveCellTexture, NULL, &location);
    }
}

void displayAllCells(void) {
    size_t x, y;
    SDL_Rect location;

    location.w = gridSizeW;
    location.h = gridSizeH;
    location.x = 0;
    location.y = 0;

    for(y = 0; y < gridRow; y++) {
        for(x = 0; x < gridCol; x++) {
            displayCell(readCell(y, x), location);

            location.x += location.w;
        }

        location.y += location.h;
        location.x = 0;
    }
}

void die(const char *f, const size_t l, const char *fmt, ...) {
    va_list vargs;

    va_start(vargs, fmt);

    fprintf(stderr, "error from file %s on line %ld: ", f, l);
    SDL_LogMessageV(SDL_LOG_CATEGORY_ERROR, SDL_LOG_PRIORITY_CRITICAL, fmt, vargs);

    va_end(vargs);

    closeSdl();

    exit(EXIT_FAILURE);
}

My guess is that your problem occurs when you try to realloc cells to

gridRow * gridCol

since w / gridSizeW seems to be the amount of elements you want. Try:

cells = (cellState_t *)realloc(cells, (gridRow * gridCol) * sizeof(cellState_t));

Let's analize your code. The grid is a 2D-array named cells, which you allocate dynamically basing on its dimensions which are kept in gridSizeW and gridSizeH.

Ideally to address a cell you use its coordinates which would go from (1 to gridSizeW; 1 to gridSizeH) or instead, more commonly, from (0 to gridSizeW-1; 0 to gridSizeH-1).

Your routine to get the value of a cell is:

cellState_t readCell(size_t row, size_t col) {
    return *(cells + (col + (row * gridCol)));
}

which implements the idea of taking the requested row number, multiply it for the width, and adding the column number. This is correct to turn a 2D address to a 1D one.

The problem is that your in-memory representation of the grid has nothing (or little) to do with the dimensions of the screen. If you have a grid 25 x 25 (you assign these as initial width and height), you must allocate 25*25 cells , no matter what SDL_GetWindowSize() returns to you.

Basically, you should always use gridSizeW and gridSizeH for coping with the cell pointer. You use instead gridRow and gridCol . Only when you update the screen, you perform a "scaling" of a row+column coordinate of then grid to an X+Y coordinate of the screen. So your readcell routine, assuming a 0-based numbering, should be:

cellState_t readCell(size_t row, size_t col) {
    return *(cells + (col + (row * gridSizeW)));
                    // gridSizeW -------^
}

The variables gridRow and gridCol, which are the ratio of w/gridwidth and h/gridheight as you correctly wrote:

SDL_GetWindowSize(gWindow, &w, &h);    // w+h: screen dimensions

gridSizeW = 25;   // grid dimensions
gridSizeH = 25;

gridRow = w / gridSizeW;  // screenwidth=250? then a cell is 10 pixels wide
gridCol = h / gridSizeH;

are to be used only when interfacing to SDL.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM