Dynamic allocation of a 2D matrix in C

109 Views Asked by At

I'm a beginner with C and I'm trying to learn how to work with dynamic allocation of 2D matrixes. Can someone tell me why it gives me problems? (In the main function, I used 10 and 11 as test sizes.)

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

void insert_values(int **arr, int dim, int size);

void resize(int ***arr, int dim) {
    if (*arr == NULL) { 
        *arr = (int **)malloc(dim * sizeof(int *));
        if (!*arr) return; 
    } 
    else { 
        *arr = (int **)realloc(*arr, dim * sizeof(int *));
        if (*arr == NULL) return; 
    }

    for (int i = 0; i < dim; i++) { 
        if ((*arr)[i] == NULL) { 
            (*arr)[i] = (int *)malloc(dim * sizeof(int));

            if (!(*arr)[i]) return; 

            break;
        }
        else { //se esiste la posizione allora lo rialloco
            (*arr)[i] = (int *)realloc(**arr, dim * sizeof(int));
 
            if ((*arr)[i] == NULL) return;
        }
    }
}
    
void insert_values(int **arr, int dim, int size) {
    if (size > dim) {
        resize(&(arr), size);
    }

    int x;
    int y;

    for(x = 0; x < size; x++) {
        for (y = 0; y < size; y++) {
            arr[x][y] = x + y;
            printf("%d |", arr[x][y]);
        }

        printf("\n");
    }
}

void print_values(int **arr, int size) {
    for (int i = 0; i < size; i++) {
        for (int j = 0; j < size; j++) {
            printf("%d |", *(arr+i)[j]);
        }

        printf("\n");
    }
}

int main(int argc, char *argv[]) {
    int **x =  (int**)malloc(10 * sizeof(*x));

    if (!x) { return 0; }

    for (int i = 0; i < 10; i++) {
        x[i] = (int*)malloc(10 * sizeof(int));
    }

    insert_values(x, 10, 11);
    print_values(x, 11);

    for (int i = 0; i < 10; i++) {
        free(x[i]);
    }

    free(x);
}
0 |1 |2 |3 |4 |5 |6 |7 |8 |9 |10 |
1 |2 |3 |4 |5 |6 |7 |8 |9 |10 |11 |
2 |3 |4 |5 |6 |7 |8 |9 |10 |11 |12 |
3 |4 |5 |6 |7 |8 |9 |10 |11 |12 |13 |
4 |5 |6 |7 |8 |9 |10 |11 |12 |13 |14 |
5 |6 |7 |8 |9 |10 |11 |12 |13 |14 |15 |
6 |7 |8 |9 |10 |11 |12 |13 |14 |15 |16 |
7 |8 |9 |10 |11 |12 |13 |14 |15 |16 |17 |
8 |9 |10 |11 |12 |13 |14 |15 |16 |17 |18 |
9 |10 |11 |12 |13 |14 |15 |16 |17 |18 |19 |
10 |11 |12 |13 |14 |15 |16 |17 |18 |19 |20 |
-533986992 |-533955728 |

This is the output that during insert_values works but in print_values it gives me a segmentation fault.

3

There are 3 best solutions below

0
Chris On

Issues with your code have been identified, and it has been suggested that you create an actual Matrix type to represent your matrix, rather than having to pass around triple pointers. It may help you to see how this approach would affect your code.

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

typedef struct { 
    size_t dim;
    int **data;
} Matrix;

typedef int (*matrix_cell_initializer_func)(size_t row, size_t col);

// Interface for working with matrices
Matrix *new_matrix(size_t dim);
void free_matrix(Matrix *m);
void init_matrix(Matrix *m, matrix_cell_initializer_func f);
void print_matrix(Matrix *m);
Matrix *resize_matrix(Matrix *m, size_t new_dim);

int init(size_t row, size_t col);

int main(void) {
    Matrix *m = new_matrix(5);

    resize_matrix(m, 6);
    init_matrix(m, init);
    print_matrix(m);
    free_matrix(m);
    free(m);
}

// Implementations of matrix functions.

Matrix *new_matrix(size_t dim) {
    Matrix *m = malloc(sizeof(Matrix));
    if (!m) return NULL;

    m->data = malloc(sizeof(int *) * dim);
    if (!m->data) {
        free(m);
        return NULL;
    }

    for (size_t i = 0; i < dim; i++) {
        m->data[i] = malloc(sizeof(int) * dim);
        if (!(m->data[i])) {
            for (size_t j = 0; j <= i; j++) {
                free(m->data[j]);
            }

            free(m);
            return NULL;
        }
    }

    m->dim = dim;

    return m;
}

void free_matrix(Matrix *m) {
    if (!m) return;

    for (size_t i = 0; i < m->dim; i++) {
        if (m->data[i]) free(m->data[i]);
    } 
}

void init_matrix(Matrix *m, matrix_cell_initializer_func f) {
    for (size_t i = 0; i < m->dim; i++) {
        for (size_t j = 0; j < m->dim; j++) {
            m->data[i][j] = f(i, j);
        }
    }
}

void print_matrix(Matrix *m) {
    for (size_t i = 0; i < m->dim; i++) {
        for (size_t j = 0; j < m->dim; j++) {
            printf(" %8d ", m->data[i][j]);
        }

        puts("\n");
    }
}

int init(size_t row, size_t col) {
    return row * col + row; 
}

Matrix *resize_matrix(Matrix *m, size_t new_dim) {
    if (new_dim < m->dim) return NULL;
    if (new_dim == m->dim) return m;

    int **new_data = realloc(m->data, sizeof(int *) * new_dim);
    if (!new_data) return NULL;

    m->data = new_data;
    for (size_t i = 0; i < new_dim; i++) {
        int *new_row = realloc(m->data[i], sizeof(int) * new_dim);
        if (!new_row) return NULL;

        m->data[i] = new_row;
    }

    m->dim = new_dim;

    return m;
}
0
chux - Reinstate Monica On

resize() thoughts

Other code may need work too.

  • void resize() needs to know both the new and old dimensions. @Ian Abbott

  • A general resize() would work with an increasing and decreasing re-sizing.

  • The result of *alloc() does not need a cast. Remove to reduce clutter.

  • Within resize(), consider dereferencing the address of the passed in pointer for clearer code.

  • Tolerate dimensions with size 0.

  • Sizing to the de-reference pointer is less error prone, easier to review and maintain than sizing to the type.


Here is an untested candidate resize() re-write to illustrate these ideas.
Simplified out-of-memory handling. Adjust as desired.

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

void resize(int ***arr, size_t old_rows, size_t old_columns, size_t new_rows,
    size_t new_columns) {
  // For clearer code, de-reference the pointer into a local variable.
  int **arr2 = *arr;

  // If shrinking rows, free excess rows.
  if (new_rows < old_rows) { 
    for (size_t r = new_rows; r < old_rows; r++) {
      free(arr2[r]);
    }
    old_rows = new_rows;
  }
  // Reallocate rows
  arr2 = realloc(arr2, sizeof arr2[0] * new_rows);
  if (arr2 == NULL && new_rows > 0) {
    fprintf(stderr, "Allocation failure\n");
    exit(EXIT_FAILURE);
  }
  // Reallocate old row for the new column width.
  for (size_t r = 0; r < old_rows; r++) {
    arr2[r] = realloc(arr2[r], sizeof arr2[r][0] * new_columns);
    if (arr2[r] == NULL && new_columns > 0) {
      fprintf(stderr, "Allocation failure\n");
      exit(EXIT_FAILURE);
    }
    // Zero new columns
    for (size_t c = old_columns; c < new_columns; c++) {
      arr2[r][c] = 0;
    }
  }
  // Assign new rows
  for (size_t r = old_rows; r < new_rows; r++) {
    arr2[r] = calloc(new_columns, sizeof arr2[r][0]);
    if (arr2[r] == NULL && new_columns > 0) {
      fprintf(stderr, "Allocation failure\n");
      exit(EXIT_FAILURE);
    }
  }

  *arr = arr2;
}
0
Jayaprakash Nevara On

Here is the working code.

Made the following changes to make it work:

  1. redefined the resize function to take original size as an input This will help initialize the additional array pointers due to resizing to NULL. Added to code initialize this additional allocated pointers to NULL in this function

  2. From function insert_values called resize function with the additional parameter (original size)

  3. replaced the *(arr + i)[j] in printf statement in print_values function to arr[i][j]

Blockquote

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

void insert_values(int** arr, int dim, int size);

void resize(int*** arr, int dim, int original_size) {
    if (*arr == NULL) {
        *arr = (int**)malloc(dim * sizeof(int*));
        if (!*arr) return;
    }
    else {
        *arr = (int**)realloc(*arr, dim * sizeof(int*));
        if (*arr == NULL) return;

        for (int i = original_size; i < dim; i++)
            (*arr)[i] = NULL;
    }

    for (int i = 0; i < dim; i++) {
        if ((*arr)[i] == NULL) {
            (*arr)[i] = (int*)malloc(dim * sizeof(int));

            if (!(*arr)[i]) return;

            break;
        }
        else { //se esiste la posizione allora lo rialloco
            (*arr)[i] = (int*)realloc((*arr)[i], dim * sizeof(int));

            if ((*arr)[i] == NULL) return;
        }
    }
}


void insert_values(int** arr, int dim, int size) {
    if (size > dim) {
        resize(&(arr), size, dim);
    }

    int x;
    int y;

    for (x = 0; x < size; x++) {
        for (y = 0; y < size; y++) {
            arr[x][y] = x + y;
            printf("%d |", arr[x][y]);
        }

        printf("\n");
    }
}

void print_values(int** arr, int size) {
    for (int i = 0; i < size; i++) {
        for (int j = 0; j < size; j++) {
            printf("%d |", arr[i][j]);
        }

        printf("\n");
    }
}

int main(int argc, char* argv[]) {
    int** x = (int**)malloc(10 * sizeof(*x));

    if (!x) { return 0; }

    for (int i = 0; i < 10; i++) {
        x[i] = (int*)malloc(10 * sizeof(int));
    }

    insert_values(x, 10, 11);
    print_values(x, 11);

    for (int i = 0; i < 10; i++) {
        free(x[i]);
    }

    free(x);
}

Blockquote