Anteino
Anteino

Reputation: 1136

How to properly use realloc to dynamically size a 2D matrix?

I am trying to dynamically resize a 2D matrix in C. The matrix is sparse in the sense that its rows are not necessarily equally long. For example:

{
    {1, 2},
    {1, 2, 3}
}

I built the piece of code below. It's a silly piece of code but it resembles the actual problem well. The project this will be used in is very big and sharing a 1000 line piece of code would make it unnecessarily harder for yous to help me. The example worked fine. Then I tested it in my project and it made the whole program go haywire. Then I noticed that even the example would sometimes work and sometimes not work. So, obviously, even though the code worked, something is not right.

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

void initializeArray(int **matrix)
{
    matrix = (int **)realloc(matrix, 2 * sizeof(int *));

    for(int i = 0; i < 2; i++)
    {
        matrix[i] = (int *)realloc((matrix)[i], 2 * sizeof(int));
    }
    for(int i = 0; i < 2; i++)
    {
        (matrix)[i][0] = i + 1;
        (matrix)[i][1] = i + 2;
    }
}

int main(void)
{
    int **matrix = (int **)malloc(sizeof(int *));

    initializeArray(matrix);
    for(int i = 0; i < 2; i++)
    {
        for(int j = 0; j < 2; j++)
        {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }

    printf("alive?\n");
}

I have been trying to figure this out for some time now. Any help with this will be much appreciated.

Upvotes: 0

Views: 241

Answers (2)

chux
chux

Reputation: 153368

How to properly use realloc to dynamically size a 2D matrix?

Pass in the old pointer with old and new size info. Return new pointer.

To preserve data and well handle allocation errors is a bit of a challenge. Recall that either/both matrix dimension may independently increase/decrease. Untested code:

// Free allocations
static void *freeMatrix(int **matrix, size_t row) {
  while (row > 0) {
    free(matrix[--row]);
  }
  free(matrix);
  return NULL;
}

This is trickier than first thought.

// On error, free data and return NULL
int **reallocMatrix(int **matrix, size_t old_r, size_t old_c, size_t new_r, size_t new_c) {

  // free reduced rows
  for (size_t r = new_r; r < old_r; r++) {
    free(matrix[r]);
  }

  int **new_m = realloc(matrix, sizeof *matrix * new_r);
  if (new_m == NULL) {
    return freeMatrix(matrix, old_r);  // Oops, out-of-memory, free old one
  }
  matrix = new_m;

  // Re-allocate old rows
  size_t min_r = (new_r < old_r) ? new_r : old_r;
  for (size_t r = 0; r < min_r; r++) {
    int *row = realloc(matrix[r], sizeof matrix[r][0] * new_c);
    if (row == NULL) {
      return freeMatrix(matrix, old_r);
    }
    matrix[r] = row;
    if (old_c < new_c) {
      memset(&matrix[r][old_c], 0, sizeof matrix[r][0] * (new_c - old_c)); // zero new data
    }
  }

  // Allocate new rows
  for (size_t r = old_r; r < new_r; r++) {
    matrix[r] = calloc(new_c, sizeof matrix[r][0]); // allocate & zero new data
    if (matrix[r] == NULL) {
      return freeMatrix(matrix, r);
    }
  }

  return matrix;
}

Sample usage

int main(void) {

  size_t r = 0;
  size_t c = 0;
  int **matrix = NULL;  // No need to allocate anything here
  size_t new_r = 2;
  size_t new_c = 3;

  matrix = resizeMatrix(matrix, r, c, new_r, new_c);
  if (matrix) {
    r = new_r;
    c = new_c;
    for (size_t i = 0; i < r; i++) {
      for (size_t j = 0; j < c; j++) {
        printf("%d ", matrix[i][j]);
      }
      printf("\n");
    }

  }
  freeMatrix(matrix, r);
  printf("Done\n");
}

Upvotes: 2

Anteino
Anteino

Reputation: 1136

I found the solution and I will try to explain it as clear as possible.

I just found out that I was using realloc on an uninitialized array. However, one cannot resize something which has no size yet.

A dynamically allocated 2D matrix in C can be constructed by making a list of pointers, these pointers point to lists of integers, hence the double ** in int **matrix.

**matrix itself is resized in resizeArray by specifying how many rows (2 in this case) are in the matrix: matrix = (int **)realloc(matrix, 2 * sizeof(int)).

Notice I used the word rezized instead of initialized because this reallocation only works because matrix already had a size from when it was initialized in the main(void) function. However, the rows themselves have not (and couldn't have been) initialized by just specifying how many rows **matrix contains.

So, instead of using realloc to resize the rows of the matrix, use malloc.

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

void resizeArray(int ***matrix)
{
    *matrix = (int **)realloc(*matrix, 2 * sizeof(int *));

    for(int i = 0; i < 2; i++)
    {
        (*matrix)[i] = (int *)malloc(2 * sizeof(int));
    }
    for(int i = 0; i < 2; i++)
    {
        (*matrix)[i][0] = i + 1;
        (*matrix)[i][1] = i + 2;
    }
}

int main(void)
{
    int **matrix = (int **)malloc(sizeof(int *));

    resizeArray(&matrix);
    for(int i = 0; i < 2; i++)
    {
        for(int j = 0; j < 2; j++)
        {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }

    printf("alive?\n");
}

Upvotes: 0

Related Questions