问题
I'm kinda new to C sorry if my questions is somewhat vague;
I need to use realloc on a 2D array without losing it's previous data, I have this function in my program to do it:
void modifyMatrix(int **iMat, int iRow, int iRow2, int iCol)
{
int i;
iMat = (int**)realloc(iMat, (iRow2)*sizeof(int*));
for(i=iRow; i<iRow2; i++)
{
iMat[i]=NULL;
}
for(i=0; i<iRow2; i++)
{
iMat[i]=(int*)realloc(iMat[i], (iCol)*sizeof(int));
}
}
Where iRow is the original size and iRow 2 & iCol are the new size and are all being captured elsewhere in the program.
Whenever I try to print the matrix I keep getting junk data or memory values on the rows and columns that are added, what am I doing wrong?
Let me know if you need the full code or any other questions to clarify, thanks in advance!
Edit: Below you can see the code I use to create the Matrix
My bad, I think I should've added that the Matrix is already created elsewhere in the program, with this function I'm just trying to modify the dimensions, thanks for the quick response btw!, below you can find the function with which I'm creating the array
void createMatrix(int ***iMat, int iRow, int iCol)
{
int **iRow2 = (int**)calloc(iRow, sizeof(int*));
int i;
for (i=0; i<iRow; i++)
{
iRow2[i] = (int*)calloc(iCol, sizeof(int));
}
*iMat=iRow2;
}
Also, I can only use the array I've already created to do this, I can't create an temp one (which I know would be the easy way to do it)
回答1:
In c the variables are passed by value, because of this the iMat
inside the modifyMatrix()
is not modifying the one in the caller function.
You need to pass the address of iMat
instead
void modifyMatrix(int ***iMat, int iRow, int iRow2, int iCol)
{
int i;
int **safe;
safe = realloc(*iMat, iRow2 * sizeof(int *));
if (safe == NULL)
return;
*iMat = safe;
for (i = 0 ; i < iRow ; i++)
{
int *keep_old_pointer;
keep_old_pointer = realloc(safe[i], iCol * sizeof(int));
if (keep_old_pointer == NULL)
do_something_allocation_failed();
safe[i] = keep_old_pointer;
}
for (int i = iRow ; i < iRow2 ; ++i)
safe[i] = malloc(iCol * sizeof(int));
}
Also, don't assign NULL
to every element and then try to realloc()
because if realloc()
makes sense in this situation then you are overwriting the pointers with NULL
without freeing them.
And don't overwrite the realloc()
ed pointer before checking if the allocation was succesfull, because if it fails you wont be able to free the previous pointer because you would have lost reference to it, causing a memory leak.
回答2:
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.
回答3:
On success, realloc
frees the pointer you pass to it and returns a pointer to the newly allocated memory. You're getting "junk values" because dereferencing a pointer to freed memory is undefined behavior.
It's not pretty, but the way to fix this (as another answer pointed out) is to pass a triple pointer (int***
). That way the function can modify the original value of the pointer. This is how you simulate reference semantics in C, which is a strictly "pass by value" language.
void modifyMatrix(int ***iMat, int iRow, int iRow2, int iCol)
{
int i;
int **newMatrix = realloc(*iMat, iRow2 * sizeof(int*));
if (newMatrix == NULL) {
/* handle realloc error here */
}
else {
*iMat = newMatrix; /* assign pointer to the new memory */
}
for(i=iRow; i<iRow2; i++)
{
(*iMat)[i]=NULL;
}
for(i = 0; i < iRow2; i++)
{
int* newRow = realloc((*iMat)[i], (iCol)*sizeof(int));
if (newRow == NULL) {
/* handle realloc error here */
}
else {
(*iMat)[i] = newRow;
}
}
}
You've also got to add some error checking. If realloc fails and returns a NULL pointer, you've just created a memory leak: you no longer have the previous value of the pointer.
Note that I removed all of your casts. It's bad practice to cast the return of malloc and friends in C.
来源:https://stackoverflow.com/questions/33007171/using-realloc-on-a-2d-array-c