How can I implement a generic, dynamically growing array in C?

My requirements:

  • The data structure must contain an arbitrary number of elements (on the order of hundreds of thousands).
  • The data structure must use memory efficiently. In this case, it means adding additional hundreds of thousands of items when needed.
  • The data structure must contain elements of an unknown but uniform type.

The way I expected to be able to build it was to define a structure representing an array. The structure contained the length of the array and the number of elements in the array. It also contained a pointer to an array of void pointers that yielded the actual data.

Diagram showing the conceptual layout of a data structure

The pointer was used as memory was equal to malloc () 'd and realloc ()' d as the array grew. The array was not specified because the type of the elements was unknown. The user has to create and initialize elements, pass them to the array implementation, and keep track of what type was used.

In memory, the structure will exist either on the stack or on the heap (user choice), the elements themselves will be scattered throughout the heap, and the array of pointers to elements will exist on the heap.

The problem is that when I went for this, I needed to assign a pointer to an empty space in the array of pointers (for example, when adding an element), and I couldn't do that because void pointers cannot (the compiler says: "incomplete type "void" is not assigned ").

Is there any other approach I should be using?


Nguai's code helped me get it right! See him below in his answer, and here is the implementation I wrote based on his code:

typedef struct {
    void **head;
    size_t used_size;
    size_t free_size;
    size_t current_size;
    size_t size_increment;
} GrowingArray;

GrowingArray createEmptyGrowingArray(int initial_size, int size_increment) {
    GrowingArray empty_growing_array;
    empty_growing_array.head = malloc(initial_size * sizeof(void *));
    empty_growing_array.used_size = 0;
    empty_growing_array.free_size = initial_size;
    empty_growing_array.current_size = initial_size;
    empty_growing_array.size_increment = size_increment;

    return empty_growing_array;
}

GrowingArray appendToGrowingArray(GrowingArray growing_array, void *new_element) {

    void *new_head_of_array;

    if (growing_array.free_size == 0) {
        new_head_of_array = realloc(growing_array.head, (growing_array.current_size + growing_array.size_increment) * sizeof(void*));
        if (new_head_of_array == NULL) {
            printf("Reallocation failure.\n");
        }

        growing_array.free_size = growing_array.size_increment;
        growing_array.current_size += growing_array.size_increment;
        growing_array.head = new_head_of_array;
    }

    growing_array.head[growing_array.used_size++] = new_element;
    growing_array.free_size--;

    return growing_array;
}

void finalizeGrowingArrayMemory(GrowingArray growing_array) {
    growing_array.head = realloc(growing_array.head, growing_array.current_size * sizeof(void *));
}

void freeGrowingArray(GrowingArray growing_array) {
    free(growing_array.head);
}

int main(int argc, char* argv[]) {
    GrowingArray test_array = createEmptyGrowingArray(5, 1);

    int *test_integer = (int *)malloc(1 * sizeof(int));
    *test_integer = 4;

    int *another_integer = (int *)malloc(1 * sizeof(int));
    *another_integer = 6;

    int *yet_another_integer = (int *)malloc(sizeof(int));
    *yet_another_integer = 9;

    test_array = appendToGrowingArray(test_array, test_integer);
    test_array = appendToGrowingArray(test_array, another_integer);
    test_array = appendToGrowingArray(test_array, yet_another_integer);
    finalizeGrowingArrayMemory(test_array);

    printf("%x,", *(int *)test_array.head[0]);
    printf("%x,", *(int *)test_array.head[1]);
    printf("%x\n", *(int *)test_array.head[2]);

    freeGrowingArray(test_array);

    printf("Going to free %llx\n", (long long int)test_integer);
    free(test_integer);

    printf("Going to free %llx\n", (long long int)another_integer);
    free(another_integer);

    printf("Going to free %llx\n", (long long int)yet_another_integer);
    free(yet_another_integer);

    return 0;
}

      

0


source to share


2 answers


Here's some code that tries to give you an idea of ​​what you've described. This is not a solution. It's on a scale of 2, so you can stretch that.



 #include <stdio.h>
 #include <stdlib.h>

 const size_t stIncrement = 2;

 typedef struct
 {
    size_t space_left;
    size_t size;
    void **vptrX;
 }T;

 T t;

 void
 vStoreData( void *data )
 {
    void *vptrTemp;
    size_t stMaxLength;

   if( t.space_left == 0 )
   {
       stMaxLength = t.size + stIncrement;
       vptrTemp = realloc(t.vptrX, stMaxLength * sizeof(void *) );
       if( vptrTemp == NULL ){
          printf( "Failed realloc");
         exit(1);
       }
       t.space_left = stIncrement;
       t.vptrX = vptrTemp;
   }

   t.vptrX[t.size++] = data;
   t.space_left--;
}

//This will make the memory efficient.
void
vFinalizeMemory()
{
   t.vptrX = realloc(t.vptrX, t.size * sizeof(void *));
}

int
main(void)
{
   int i;
   char c;
   float d;
   char *cp = "How are you";
   i = 10;
   c ='O';

   d = 40.12345;

   t.vptrX = malloc(stIncrement*sizeof(void *));
   t.size = 0;
   t.space_left = 2;

   vStoreData( &i );

   vStoreData( &c );

   vStoreData( cp );

   vStoreData( &d );

   vStoreData( &c );

   vStoreData( cp );
   vStoreData( &d );

   vFinalizeMemory();
   printf( "%d\n", *((int *)t.vptrX[0]) );
   printf( "%c\n", *((char *)t.vptrX[1] ));
   printf( "%s\n", (char *)t.vptrX[2] );
   printf( "%f\n", *((float*)t.vptrX[3] ));
   printf( "%c\n", *((char *)t.vptrX[4] ));
   printf( "%s\n", (char *)t.vptrX[5] );
   printf( "%f\n", *((float*)t.vptrX[6] ));

   return 0; 
}

      

0


source


You want a pointer to an array of void pointers, not a void pointer;

void ** array_of_pointers_to_actual_elements ;

      



Good luck!

+1


source







All Articles