Here is the implementation I normally use on my code:
#define DYNARRAY_GROW(_cell_type, _array, _need, _to_add) do { \
if (_array##_len + (_need) >= (_to_add)) { \
_array##_cap += (_to_add); \
_cell_type *_aux = realloc(_array, \
_array##_cap * sizeof _array[0]); \
if (!_aux) { \
/* error checking code ... */ \
ERR("Error growing array " #_array "\n"); \
} else { \
_array = _aux; \
} \
} \
} while (0)
later, I define an array by using the following variables:
MyCellType *A = NULL; /* pointer to cell type */
size_t A_len = 0, /* no elements, empty array */
A_cap = 0;
#define ARRAY_A_GROW_INCREMENT (20) /* NUMBER OF ELEMENTS \
* TO GROW IN CASE OF NEED */
...
/* ASSUME I NEED TO GROW THE ARRAY FOR 3 ELEMENTS */
DYNARRAY_GROW(MyType, A, 3, ARRAY_A_GROW_INCREMENT);
A[A_len++] = first_element;
A[A_len++] = second_element;
A[A_len++] = third_element;
/* if A has need to grow, the number of new cells given to the array
* is not 3, but the value of ARRAY_A_GROW_INCREMENT (in this case 20)
* so you save calls to realloc() */
There's a drawback, although (but this also applies to any kind of array you
can have in this manner):
- in the reallocation, it is possible that a new memory segment is completely allocated, and all the array contents to be moved to another place in memory. This makes that any other pointer reference that points into the array to be invalid from the reallocation time onwards... and it has been source of some errors I have learned the bad way. Always think that the elements can move, so be careful with pointers that happen to point to elements inside the array (this includes the pointers that are themselves inside the array)
- If you test your code with a C++ compiler (let's say you use GTest or similar) then you need to cast the
realloc()
call return value, as the
void *
is not assignment compatible with any other type in that language, as C is. So, in that case, substitute the call to realloc to show as:
_cell_type *_aux = (_cell_type *) realloc(...