When you are passing an array of pointers to a function to realloc
, you basically have 2 choices; (1) pass the address of the array to the function (i.e. &array
) as the parameter, meaning your function definition will be reallocfoo (int ***array, size_t* size)
or (2) assign the return of the function in the calling routine. (e.g. array = reallocfoo (array, &size);
)
Since you have already been given answers for (1), let's look at how you would implement and use (2). Note: there is no need to make your function the type
of the array, it is just returning a memory address, so making use of a generic void
pointer is fine. For example:
void *xrealloc2 (void **memptr, size_t *n)
{
void *tmp = realloc (memptr, *n * 2 * sizeof tmp);
if (!tmp) {
fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
return NULL;
}
memptr = tmp;
memset (memptr + *n, 0, *n * sizeof tmp);
*n *= 2;
return memptr;
}
Also note since you are reallocating an array of pointers, there is no need to pass the type size (a pointer is a pointer is a pointer -- in all the cases we care about here). Putting this to work, since you are not passing the address of your array, you will need to assign the return to complete the reallocation. (much as you did in your code above) e.g.:
if (ridx == rmax) /* if realloc is needed */
ia = xrealloc2 ((void **)ia, &rmax);
Note: the current number of pointers (rmax
) is passed as a pointer so its value can be updated to twice current in the reallocation function. (so when you run out next time, you can realloc based on the correct updated current number). Putting all the pieces together, you get a short example that just forces reallocation twice. Additionally, the original allocation is placed in a function as well to keep the main body of code tidy and the return checks for the allocation in the function. (you can decide how you handle memory exhaustion -- NULL
return or exit
, examples of both are shown in the two functions)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define RMAX 2
#define COLS 5
void *xcalloc (size_t n, size_t s);
void *xrealloc2 (void **memptr, size_t *n);
int main (void) {
int **ia = NULL;
size_t rmax = RMAX;
size_t rows = 0;
size_t ridx = 0, cidx = 0;
srand (2275311); /* arbitrary repeatable seed */
ia = xcalloc (RMAX, sizeof *ia);
/* intentionally force reallocation */
while (ridx < 3 * RMAX) {
ia[ridx] = xcalloc (COLS, sizeof **ia);
for (cidx = 0; cidx < COLS; cidx++)
ia[ridx][cidx] = rand () % 1000 + 1;
ridx++;
if (ridx == rmax)
ia = xrealloc2 ((void **)ia, &rmax);
}
rows = ridx;
printf ("\n the reallocated 2D array elements are:\n\n");
for (ridx = 0; ridx < rows; ridx++) {
for (cidx = 0; cidx < COLS; cidx++)
printf (" %4d", ia[ridx][cidx]);
putchar ('\n');
}
putchar ('\n');
for (ridx = 0; ridx < rows; ridx++)
free (ia[ridx]);
free (ia);
return 0;
}
/** xcalloc allocates memory using calloc and validates the return.
* xcalloc allocates memory and reports an error if the value is
* null, returning a memory address only if the value is nonzero
* freeing the caller of validating within the body of code.
*/
void *xcalloc (size_t n, size_t s)
{
register void *memptr = calloc (n, s);
if (memptr == 0)
{
fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
exit (EXIT_FAILURE);
}
return memptr;
}
/* realloc array of pointers ('memptr') to twice current
* number of pointer ('*nptrs'). Note: 'nptrs' is a pointer
* to the current number so that its updated value is preserved.
* no pointer size is required as it is known (simply the size
* of a pointer
*/
void *xrealloc2 (void **memptr, size_t *n)
{
void *tmp = realloc (memptr, *n * 2 * sizeof tmp);
#ifdef DEBUG
printf ("\n reallocating %zu to %zu\n", *n, *n * 2);
#endif
if (!tmp) {
fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
return NULL;
}
memptr = tmp;
memset (memptr + *n, 0, *n * sizeof tmp);
*n *= 2;
return memptr;
}
After you compile the code and run it, it will confirm that instead of just the maximum 2
rows (pointers) originally allocated, reallocation occurs twice increasing that number to 8
(e.g. 2->4->8
) so all 6 rows of integers assigned are properly allocated:
the reallocated 2D array elements are:
155 573 760 410 956
553 271 624 625 934
259 291 811 161 185
756 211 16 6 449
124 869 353 210 317
310 181 897 866 831
If you have any questions, let me know. Don't forget, always run any code that allocates or reallocated through valgrind
(or similar memory checker) to insure your memory use is correct and that your free all memory you allocate.
==29608== Memcheck, a memory error detector
==29608== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==29608== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==29608== Command: ./bin/realloc2d
==29608==
the reallocated 2D array elements are:
<snip>
==29608==
==29608== HEAP SUMMARY:
==29608== in use at exit: 0 bytes in 0 blocks
==29608== total heap usage: 9 allocs, 9 frees, 232 bytes allocated
==29608==
==29608== All heap blocks were freed -- no leaks are possible
==29608==
==29608== For counts of detected and suppressed errors, rerun with: -v
==29608== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
You can compile the code above with the -DDEBUG
flag to have it print to stdout
each time it reallocates and provide the current count of pointers allocated. Good luck.