0

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)

gabevt
  • 33
  • 1
  • 7
  • 1
    You don't need to cast `(int **) realloc ...`. – Iharob Al Asimi Oct 08 '15 at 05:17
  • Why would there *not* be junk data in the new rows and columns? – user253751 Oct 08 '15 at 05:20
  • I'm filling the new spaces with a cycle, I've isolated the problem to this part of the code but I can't find it, oddly enough if I just add 1 column and 1 row it works perfectly or I make the matrix smaller it works perfectly, but when I try to add more than 1 row or column I get the error – gabevt Oct 08 '15 at 05:36
  • 1
    This is not a 2D array, it is a lookup table. Just because pointers allow the `[ ]` syntax, it doesn't turn them into arrays. – Lundin Oct 08 '15 at 06:20
  • Probably just another duplicate (to for example: http://stackoverflow.com/q/29776167/694576) – alk Oct 08 '15 at 06:59

3 Answers3

0

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.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • 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!, I added the code with which I create the matrix – gabevt Oct 08 '15 at 05:23
  • 2
    if `iRow2>iRow` the later `safe[i]` will be uninitialized and passed to `realloc`. There is a memory leak if `iRow2 – mch Oct 08 '15 at 07:45
0

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.

Community
  • 1
  • 1
PC Luddite
  • 5,883
  • 6
  • 23
  • 39
  • every `iMat[i]` should be `(*iMat)[i]`. the commented out loop has one purpose: it initializes the pointer which will be passed to `realloc`, otherwise you would pass an uninitialized pointer to it. – mch Oct 08 '15 at 07:43
  • Should I be calling the function as modifyMatrix(&iMat, iRow, iRow2, iCol); ? Haven't really used triple pointer that much – gabevt Oct 09 '15 at 04:49
  • Got it working but one quesiton, should I free *newRow and **newMatrix? – gabevt Oct 09 '15 at 05:08
  • Nvm, Just figured that those arrays will get deleted as soon as the function ends – gabevt Oct 09 '15 at 05:11
  • @gabevt Yes, you should use `modifyMatrix(&iMat, iRow, iRow2, iCol)`. Also, you only have to free all memory that you allocated with `malloc` and friends. If you didn't use `malloc`, `calloc`, `realloc` or similar to allocate the array, then no, you don't have to free them. – PC Luddite Oct 09 '15 at 05:48
0

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.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85