realloc fails in visual studio, but work using gcc (ntdll.dll)

1

I'm using Visual Studio 2017 and I have this code in C. When I run in VS it fails, but when I compile with gcc it runs perfectly. The error is this:

Exception thrown at 0x779BE643 (ntdll.dll) in GestaoFinanceira.exe: 0xC0000005: Access violation reading location 0x00000009.

This is my code.

I'm using this struct type:

struct budget {
    struct tm time;
    float valor;
};

struct geral {
    struct budget *b;
    int nelem_budget;
};

In main I call a function that will read from a file and save the values is the struct g->b[] as the number of elements in g->nelem_budget

int main() {
    struct geral *g = (struct geral *)malloc(sizeof(struct geral));

    readFileBudget(g);
    // just to check if the read was ok
    showBudget(g->b, g->nelem_budget);

    addBudget(g);
    // to check again if the item added is ok
    showBudget(g->b, g->nelem_budget);

    system("Pause");
    return 0;
}

Here is when the error appens:

void addBudget(struct geral *g) {
    int month, year;
    float value;

    // in the future, this values are to be an user input
    month = 5;
    year = 2015;
    value = 5000;

    printf("Month: %d, Year: %d, Value: %f\n", month, year, value);

    g->b[g->nelem_budget].time.tm_mon = month;
    g->b[g->nelem_budget].time.tm_year = year;
    g->b[g->nelem_budget].valor = value;

    g->nelem_budget++;

    struct budget *tmp = NULL;
    tmp = g->b;
    g->b = realloc(g->b, g->nelem_budget * sizeof(struct budget));
    if (g->b == NULL) {          //reallocated pointer ptr1
        free(tmp);
        printf("error-addBudget->realloc");
        exit(EXIT_FAILURE);
    }

    // just for debug
    printf("month: %d, year: %d, value: %f\n", g->b[g->nelem_budget - 1].time.tm_mon, g->b[g->nelem_budget - 1].time.tm_year, g->b[g->nelem_budget - 1].valor);
}

I have an empty value at the end of the g->b array, that's why I first add the values, and then I call the realloc.

c
visual-studio
gcc
malloc
realloc
asked on Stack Overflow Mar 21, 2018 by Ricardo Barreto • edited Mar 21, 2018 by Pablo

1 Answer

1

You need to reallocate first, then add a new element, not the other way round.

I don't like this

struct budget *tmp = NULL;
    tmp = g->b;
    g->b = realloc(g->b, g->nelem_budget * sizeof(struct budget));
    if (g->b == NULL) {          //reallocated pointer ptr1
        free(tmp);
        printf("error-addBudget->realloc");
        exit(EXIT_FAILURE);
    }

for a couple of reasons:

realloc should be called like this:

struct budget *tmp;

tmp = realloc(g->b, (g->nelem_budget + 1) * sizeof *tmp);

if(tmp == NULL)
{
    // error handling
    return 0; // return error code
}

g->b = tmp;
g->nelem_budget++;

Don't do exit in a function other than main. Instead of closing the program, make addBudget return 1 on success and 0 on failure. The caller of addBudget should decide what to do in case of failure, not addBudget. If you for example put these functions in a library, your program would automatically end on error, and that's sometimes something that you don't want to happen.

So, the function should look like this:

int addBudget(struct geral *g) {
    if(g == NULL)
        return 0;

    int month, year;
    float value;

    // in the future, this values are to be an user input
    month = 5;
    year = 2015;
    value = 5000;

    // reallocate first

    struct budget *tmp = NULL;
    tmp = realloc(g->b, (g->nelem_budget + 1) * sizeof *tmp);

    if(tmp == NULL)
    {
        fprintf(stderr, "error-addBudget->realloc\n");
        return 0;
    }

    g->b = tmp;

    // add new budget after reallocation
    printf("Month: %d, Year: %d, Value: %f\n", month, year, value);

    g->b[g->nelem_budget].time.tm_mon = month;
    g->b[g->nelem_budget].time.tm_year = year;
    g->b[g->nelem_budget].valor = value;

    g->nelem_budget++;

    // just for debug
    printf("month: %d, year: %d, value: %f\n", g->b[g->nelem_budget - 1].time.tm_mon, g->b[g->nelem_budget - 1].time.tm_year, g->b[g->nelem_budget - 1].valor);

    return 1;
}

and then in main:

int main() {
    // using calloc to initialize
    // everything with 0
    struct geral *g = calloc(1, sizeof *g);

    if(g == NULL)
    {
        fprintf(stderr, "Not engough memory\n");
        return 1;
    }

    // you should also check of readFileBudget
    // fails
    readFileBudget(g);

    // just to check if the read was ok
    showBudget(g->b, g->nelem_budget);

    if(addBudget(g) == 0)
    {
        free(g->b);
        free(g);
        return 1;
    }

    // to check again if the item added is ok
    showBudget(g->b, g->nelem_budget);


    // don't forget to free the memory
    free(g->b);
    free(g);

    system("Pause");
    return 0;
}

Also see: do not cast malloc

And error messages should be printed to stderr, not stdout.

answered on Stack Overflow Mar 21, 2018 by Pablo

User contributions licensed under CC BY-SA 3.0