1

I am trying to reallocate both elements of a 2D-array and also manipulate the values in the 2D-array within the same function. But I just can't seem to get it to work. On compiling the code doesn't show any errors, but when running it will only ever print the amount of lines I specified as the initial_size at the beginning of the main function.

So it seems that the inner realloc is working fine, as it always prints an entire row once it gets started. But the outer realloc is not working, as it will only print a couple of rows.

Note: The real code takes input of unknown size, from getchar() / scanf(). That is why the realloc functions are inside loops in the function.

Simplified version of the function, with error handling omitted:

void func(int ***A) {
    int i, j;
    int len = 2;
    int len2 = 2;
    
    for (i = 0; i < 10; i++) {
        // Check to see if a realloc is needed.
        if (i >= len) {
            len *= 2;
            int **tmp = realloc(*A, len * sizeof(int*));
            *A = tmp;
            printf("Len1 = %d\n", len);
        }
        len2 = 2;
    
        for (j = 0; j < 20; j++) {
            // Check to see if a realloc is needed.
            if (j >= len2) {
                len2 *= 2;
                int *new_row = realloc((*A)[i], len2 * sizeof(int));
    
                (*A)[i] = new_row;
            }
            // Assign value.
            (*A)[i][j] = i * j;
        }
    }
}

int main() {
    int i, j;
    int initial_size = 2;
    int **A;
 
    // Malloc with a starting size of 2 (could be any size).
    A = malloc(initial_size * sizeof(int*));
    for (i = 0; i < initial_size; i++) {
        A[i] = malloc(initial_size * sizeof(int));
    }

    // Call function.
    func(&A);

    // Print the results, row by row.
    for (i = 0; i < 10; i++) {
        for (j = 0; j < 20; j++) {
            printf("%d, ", A[i][j]);
        }
        printf("\n");
    }
    return 0;
}

I have been stuck for a while now, so any help is greatly appreciated :)

chqrlie
  • 131,814
  • 10
  • 121
  • 189
AstonKey
  • 71
  • 5
  • Consider using 2D arrays instead: [Correctly allocating multi-dimensional arrays](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays). – Lundin Oct 27 '20 at 07:28
  • Why do you call `realloc(*A, len * sizeof(int*))` from within a loop? Overall this code seems needlessly complicated. 1. calculate the new size(s), 2. realloc, 3. done. – Lundin Oct 27 '20 at 07:30
  • Realloc is in a loop because the real code is processing input of unknown size. So it reallocs and puts the data into the array in the same loop. – AstonKey Oct 27 '20 at 08:01
  • @AstonKey: you can accept an answer by clicking on the grey checkmark below its score. – chqrlie May 30 '21 at 19:32

2 Answers2

0

When you realloc in the outer loop, you increase the numbers of pointers from two (those created in main) to four.

The value of the two pointers from main are copied to the new memory area.

The value of the two new pointers are indeterminate and that's your problem. You never initialize them by allocating memory - yet you use them in the other loop.

Further, it seems that you realloc the inner array dimensions individually but you only track one length (i.e. len2). That's also a problem.

And the line len2 = 2 between the loops are wrong as you loose information about previously allocated memory.

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • Thanks!! So how would I go about fixing this bug? – AstonKey Oct 27 '20 at 08:20
  • @AstonKey You need to initialize them by allocating memory for the two new pointers - just like you did in `main` with the first two pointers. However, also read the part about tracking the size on the "inner" arrays... – Support Ukraine Oct 27 '20 at 08:27
  • @AstonKey Re the size of the "inner" array. As a first approach I'll recommend that you keep all "inner" arrays the same size. In other words - when reallocating in the second loop, do the reallocation on **all** inner arrays. – Support Ukraine Oct 27 '20 at 08:33
  • Forgive me as I am quite new at c (and programming in general), but doesn't the realloc function allocate the specified memory, and return new pointers? Thanks again for the help :) – AstonKey Oct 27 '20 at 08:52
  • @AstonKey The realloc in your first loop allocate memory for new pointer (2 new pointers the first time). But these pointers doesn't **point** to any memory. You need to add code for that. – Support Ukraine Oct 27 '20 at 09:52
0

There are multiple problems in your code:

  • you reallocate the pointer array but do not initialize the pointers in the newly allocated portion
  • you only reallocate the first int array pointed to by the pointer array
  • you do not initialize the newly allocated area in this int array.

Note that 2D-matrices are more idiomatic in C as arrays of arrays, but it is difficult to manipulate such arrays when the outer dimension is not fixed at compile time.

You should use a reallocation function with specified dimensions for the current and target sizes and deal with reallocating and freeing the blocks appropriately. Also avoid triple pointers: just return the potentially reallocated outer array.

#include <stdio.h>
#include <stdlib.h>

int **func(int **A, int rows, int cols, int new_rows, int new_cols) {
    int i, j;

    /* free the rows that are discarded */
    if (new_rows < rows) {
        for (i = new_rows; i < rows; i++) {
            free(A[i]);
        }
    }
    /* free the outer array if new rows is zero */
    if (new_rows == 0) {
        free(A);
        return NULL;
    }
    /* reallocate the outer array if required */
    if (new_rows != rows) {
        A = realloc(A, sizeof(*A) * new_rows);
    }
    /* reallocate the existing rows */
    if (new_cols != cols) {
        for (i = 0; i < rows; i++) {
            A[i] = realloc(A[i], sizeof(int) * new_cols);
            if (new_cols > cols) {
                for (j = cols; j < new_cols; j++)
                    A[i][j] = 0;
            }
        }
    }
    /* allocate the new rows (initialized to 0) */
    for (i = rows; i < new_rows; i++) {
        A[i] = calloc(sizeof(int), new_cols);
    }
    return A;
}

int main() {
    int **A = NULL;

    /* reallocate to 10x20 */
    A = func(A, 0, 0, 10, 20);

    // Print the results, row by row.
    for (i = 0; i < 10; i++) {
        for (j = 0; j < 20; j++) {
            printf("%d, ", A[i][j]);
        }
        printf("\n");
    }
    /* reallocate to 0x0 (free the matrix) */
    func(A, 10, 20, 0, 0);
    return 0;
}

Note that the above code does not check for memory allocation failure. To deal with this possibility, it would be much simpler to allocate a new array and copy the current values, then free the previous matrix if no allocation error occurred. It would also be more efficient to allocate all rows as a single block of memory. Below is an example of this approach:

#include <stdio.h>
#include <stdlib.h>

int **func(int **A, int rows, int cols, int new_rows, int new_cols) {
    int i, j;
    int **B = NULL;

    if (new_rows == rows && new_cols == cols)
        return A;

    if (new_rows != 0 && new_cols != 0) {
        /* allocate the new matrix */
        B = malloc(sizeof(*B) * new_rows);
        if (B == NULL)
            return NULL;
        B[0] = calloc(sizeof(int) * new_cols, new_rows);
        if (B[0] == NULL) {
            free(B);
            return NULL;
        }
        /* initialize the row pointers */
        for (i = 1; i < new_rows; i++) {
            B[i] = B[i - 1] + new_cols;
        }
        /* copy the current data */
        for (i = 0; i < new_rows && i < rows; i++) {
            for (j = 0; j < new_cols && j < cols; i++) {
                B[i][j] = A[i][j];
            }
        }
    }

    /* free the previous matrix */
    if (A != NULL) {
        free(A[0]);
        free(A);
    }
    return B;
}

int main() {
    int **A = NULL;
    int i, j;

    /* reallocate to 10x20 */
    A = func(A, 0, 0, 10, 20);
    if (A == NULL) {
        printf("Matrix reallocation failed\n");
        return 1;
    }

    // Print the results, row by row.
    for (i = 0; i < 10; i++) {
        for (j = 0; j < 20; j++) {
            printf("%d, ", A[i][j]);
        }
        printf("\n");
    }
    /* reallocate to 0x0 (free the matrix) */
    func(A, 10, 20, 0, 0);
    return 0;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Thanks alot!! How would you go about this if you did not know the dimensions (10x20) at runtime? For example reading from an input until EOF is reached? Thanks again!! – AstonKey Oct 27 '20 at 10:11
  • @AstonKey: you can read the input one line at a time, you can determine the number of columns from the first line and insert an extra row for each new line, then you can set the value as you parse the line with `strtol()` – chqrlie Oct 27 '20 at 14:53