Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memory allocation/reallocation question

I just finished working out a memory allocation problem with the current program I'm writing, but I am not happy with what I had to do to fix it.

In my program, I was building up an array of structs, reallocating space for the array every time I wanted to add a struct to it. Here is a generic version of my struct and the function that would add a struct to the array:

typedef struct Example {
    const char* name;
    int (*func)(int, int);
    int bool_switch;
}

int add_struct_to_array( Example **example_array, int *ex_array_size, int name, int (*func)(int, int), int bool_switch)
{
    // first, make a new struct
    Example *new_example = (Example *) calloc( 1, sizeof( Example ) );
    if( new_example != NULL ) {
        new_example->name = name;
        new_example->func = func;
        new_example->bool_switch = bool_switch;
        ( *ex_array_size )++;
    } else {
        printf( "Errror allocating %s\n", name );
        exit( -1 );
    }

    // now, realloc the array of structs and add the new member to it
    Example **temp_example_array = ( Example** )realloc( example_array, ( *ex_array_size ) * sizeof( Example* ) );
    if( temp_example_array != NULL ) {
        example_array = temp_example_array;
        example_array[ ( *ex_array_size ) - 1 ] = new_example;
    } else {
        printf( "Reallocation failed\n" )
        exit( -1 );
    }
    return 0;
}

And here is where I would call the functions (notice how I'm initially allocating the array of structs, cuz that's where the problem was)

#include "example_struct.h"

int main( int argc, char **argv )
{
    int ex_array_size = 0;
    Example **example_array = ( Example** )calloc( 0, sizeof( Example* ) );

    add_struct_to_array( example_array, &ex_array_size, "name", &function, 1 );
    ...
    ...
    add_struct_to_array( example_array, &ex_array_size, "other_name", &other_func, 0 );

    /* Do stuff here */

    example_array_free( example_array );

    return 0;
}

In my ignorance, I apparently thought that allocating the array with size 0 would be ok, since it was initially empty, and I could add structs to it after that. Obviously, this did not work, I would get runtime errors about error for object 0x100100080: pointer being reallocated was not allocated. The example_array was at address 0x100100080 and the first struct I would allocate for would be at address 0x100100090, and after a few reallocations the example_array would run out of room.

So, finally, to my question. I solved this problem by allocating more space for my example_array than I would need, but that seems very inelegant. Is there a better way to do this?

**EDIT**

Ok, so from the looks of most of the responses, I shouldn't be using pointers to pointers. So, I am trying it a slightly different way, mixing pmg and crypto's responses. Here is my code now:

/* example_struct.h */
int add_struct_to_array( Example *example_array, int *ex_array_size, int name, int (*func)(int, int), int bool_switch)
{
    Example temp_example_array = realloc( example_array, ( ( *ex_array_size ) + 1 ) * sizeof( Example ) );

    if( temp_example_array != NULL ) {
        example_array = temp_example_array;
        Example new_example;
        new_example.name = name;
        new_example.func = func;
        new_example.bool_switch = bool_switch;
        example_array[ ( *ex_array_size ) ] = new_example;
        ++( *ex_array_size );
    } else {
        fprintf( stderr, "Error reallocating for %s", name );
        exit( -1 );
    }
    return 0;
}



/* main.c */
...
...
#include "example_struct.h"
int main( int argc, char **argv )
{
    int ex_array_size = 0;
    Example *example_array = NULL;

    add_struct_to_array( example_array, &ex_array_size, "name", &func, 1 );
    add_struct_to_array( ... );
    ...
    add_struct_to_array( example_array, &ex_array_size, "other name", &other_func, 0 );

    example_free( example_array );
}

Everything compiles and realloc's alright, but I have trouble accessing the structs within the array.

/* main.c */
...
...
#include "example_struct.h"
int main( int argc, char **argv )
{
    int ex_array_size = 0;
    Example *example_array = NULL;

    add_struct_to_array( example_array, &ex_array_size, "name", &func, 1 );
    add_struct_to_array( ... );
    ...
    add_struct_to_array( example_array, &ex_array_size, "other name", &other_func, 0 );


    printf( "%s\n", example_array[0].name ) /* Segfault */


    example_free( example_array );
}

Thanks again for all your help.


1 Answers

realloc takes NULL as the pointer value very fine ... and does a malloc in that case

*p = NULL;
new = realloc(p, 42); /* same as new = malloc(42); */
if (!new) { /* error */ }
p = new;

So, forget about calloc (you will overwrite the zeroes right after, anyway), initialize your pointers to NULL and realloc at will.

int main(void) {
    Example *example_array = NULL;
    add_struct_to_array(&example_array, &ex_array_size, "name", function, 1);
    /* ... */
    free(example_array);
}
like image 96
pmg Avatar answered Dec 19 '25 12:12

pmg