Search code examples
cdynamic-arrayspush-back

Troubles with inserting elements in dynamic array in C


I have a dynamic array with this struct:

typedef struct vector_struct {
   size_t e_sz;
   char e_type;

   #define V_INT 1
   #define V_DOUBLE 2
   #define V_CHAR 3
   #define V_FLOAT 4


unsigned no_e;
unsigned cur_cap;
void* e_array;

}* vector_type;

where no_e is the size, cur_cap the capacity, e_sz is the size of the elements in the array and e_array is the void pointer.

I had to complete a push_back function that has to work for the 4 differetns types defined above.

void v_push_back(vector_type v, void* new_val){

if( v->no_e >= v->cur_cap ){

    /*** reallocate a larger array ***/
    v->cur_cap += (v->cur_cap) ? v->cur_cap : 2;

    v->e_array = realloc(v->e_array, v->cur_cap*(v->e_sz))

}
/*** copy new_val in the array at index v->no_e ***/

/*** TO BE DONE START ***/

if(v->e_type == 1)
    memcpy(((int*)v->e_array) + v->no_e*(v->e_sz), new_val, v->e_sz);
else if(v->e_type == 2)
    memcpy(((double*)v->e_array) + v->no_e*(v->e_sz), new_val, v->e_sz);
else if(v->e_type == 3)
    memcpy(((char*)v->e_array) + v->no_e*(v->e_sz), new_val, v->e_sz);
else if(v->e_type == 4)
    memcpy(((float*)v->e_array) + v->no_e*(v->e_sz), new_val, v->e_sz);

/*** TO BE DONE END ***/

(v->no_e)++;
} 

This function works for the char, but it appears that it's not inserting either int or double. I can't find out the error in the code.


Solution

  • ... + v->no_e*(v->e_sz) is wrong. You already cast to the appropriate type, so if you do + on an array of items, you get pointer arithmetic for that specific type. It is sufficient just to do ... + v->no_e because the item size is implicitly there based on the pointed-at type. This is why 1 byte chars work but nothing else.

    Pointer arithmetic instead of array indexing is usually harder to read. I'd rewrite the code like this:

    void* item;
    switch(v->e_type)
    {
       case V_INT:     item = &( (int*)    v->e_array )[v->no_e]; break;
       case V_DOUBLE:  item = &( (double*) v->e_array )[v->no_e]; break;
       case V_CHAR:    item = &( (char*)   v->e_array )[v->no_e]; break;
       case V_FLOAT:   item = &( (float*)  v->e_array )[v->no_e]; break;
    }
    memcpy(item, new_val, v->e_sz);
    

    Note how a few spaces here and there turns repetitive code much more readable and minimizes the chance for typo-related bugs. Also note that this code is taking advantage of [] having higher operator precedence than &.

    The pointer arithmetic alternative is actually pretty OK too in this specific case. Many might find this more readable:

    void* item;
    switch(v->e_type)
    {
       case V_INT:     item = (int*)    v->e_array + v->no_e; break;
       case V_DOUBLE:  item = (double*) v->e_array + v->no_e; break;
       case V_CHAR:    item = (char*)   v->e_array + v->no_e; break;
       case V_FLOAT:   item = (float*)  v->e_array + v->no_e; break;
    }
    memcpy(item, new_val, v->e_sz);