Home > database >  realloc() does not copy the data correctly when trying to resize a matrix in c
realloc() does not copy the data correctly when trying to resize a matrix in c

Time:01-13

I'm trying to resize a matrix (double **m) in c, in theory realloc() should copy the data of the old matrix to the new resized one. However, the values in the matrix are not or randomly copied to the new version. The resizing itself works correctly, at least it prints with the right amount of rows and columns.

double **matrix_resize(double **m, int rows, int cols) 
{
    int i;
    double **safe;
    safe = realloc(m, rows * sizeof(double*));
    if (safe == NULL) return NULL;
    m = safe;
    for (i = 0; i < rows;   i) {
        double *safe2 = realloc(m[i], cols * sizeof(double));
        if (safe2 == NULL) {
            free(safe2);
            free(safe);
            return NULL;
        }
        m[i] = safe2;
        free(safe2);
    }
    free(safe);
    return m;
}

I expected the function to return a new matrix with the new amount of rows and columns, also with the old data copied into the new matrix. The amount of rows and columns is correct, however it does not copy the data correctly.

This is the output:

old matrix:

-----0---1---2---
0: | 1 | 1 | 1 |
---------------
1: | 1 | 1 | 1 |
---------------
2: | 1 | 1 | 1 |
---------------

resized matrix:

-----0---1---2---3---4---
0: | 0 | 0 | 1 | 0 | 1 |
-------------------------
1: | 0 | 0 | 0 | 0 | 0 |
-------------------------
2: | 1 | 1 | 1 | 0 | 0 |
-------------------------
3: | 0 | 0 | 1 | 0 | 0 |
-------------------------
4: | 0 | 0 | 1 | 0 | 0 |
-------------------------

CodePudding user response:

As mentioned in comments, the function needs the old and new dimensions to grow or shrink the memory allocations.
Do not free the temporary pointers used directly with realloc (safe and safe2). That is handled by realloc

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

double **free_matrix ( double **fm, int rows) {
    if ( fm) {
        while ( rows) {
            --rows;
            free ( fm[rows]);
        }
        free ( fm);
    }
    return NULL;
}

void matrix_show ( double **m, int rows, int cols) {
    for ( int r = 0; r < rows;   r) {
        for ( int c = 0; c < cols;   c) {
            printf ( "%6.1f ", m[r][c]);
        }
        printf ( "\n");
    }
}

double **matrix_resize ( double **m, int rows, int cols, int newrows, int newcols) {
    int i = 0;
    int temprows = 0;
    double **temp = NULL;
    double **safe = NULL;

    // shrinking - save pointers to free later
    if ( newrows < rows) {
        temprows = rows - newrows;
        if ( NULL != ( temp = malloc ( sizeof *temp * temprows))) {
            for ( int each = 0; each < temprows;   each) {
                temp[each] = m[each   newrows];
            }
        } else {
            fprintf ( stderr, "problem malloc **temp");
            return m;
        }
    }
    if ( NULL == ( safe = realloc ( m, newrows * sizeof *m))) {
        fprintf ( stderr, "problem realloc safe");
        temp = free_matrix ( temp, temprows);
        return m;
    }
    m = safe;
    if ( rows < newrows) {
        int items = newrows - rows;
        for ( int each = 0; each < items;   each) {
            m[each   rows] = NULL;
        }
    }
    for ( i = 0; i < newrows;   i) {
        double *safe2 = realloc ( m[i], newcols * sizeof **m);
        if (safe2 == NULL) {
            fprintf ( stderr, "problem realloc safe2");
            temp = free_matrix ( temp, temprows);
            return m;
        }
        m[i] = safe2;
        if ( i >= rows) {
            // zero new row
            for ( int each = 0; each < newcols;   each) {
                m[i][each] = 0;
            }
        } else {
            // zero new elements in row
            for ( int each = cols; each < newcols;   each) {
                m[i][each] = 0;
            }
        }
    }
    temp = free_matrix ( temp, temprows);
    return m;
}

int main ( void) {
    double **mat = NULL;
    int oldrw = 0;
    int rw = 4;
    int oldcl = 0;
    int cl = 4;

    // 4 x 4
    mat = matrix_resize ( mat, oldrw, oldcl, rw, cl);
    for ( int r = 0; r < rw;   r) {
        for ( int c = 0; c < cl;   c) {
            mat[r][c] = r * cl   c   1;
        }
    }
    matrix_show ( mat, rw, cl);
    printf ( "\n");

    // 6 x 6
    oldrw = rw;
    rw = 6;
    oldcl = cl;
    cl = 6;
    mat = matrix_resize ( mat, oldrw, oldcl, rw, cl);
    matrix_show ( mat, rw, cl);
    printf ( "\n");

    // 7 x 3
    oldrw = rw;
    rw = 7;
    oldcl = cl;
    cl = 3;
    mat = matrix_resize ( mat, oldrw, oldcl, rw, cl);
    matrix_show ( mat, rw, cl);
    printf ( "\n");

    // 2 x 2
    oldrw = rw;
    rw = 2;
    oldcl = cl;
    cl = 2;
    mat = matrix_resize ( mat, oldrw, oldcl, rw, cl);
    matrix_show ( mat, rw, cl);

    mat = free_matrix ( mat, rw);

    return 0;
}

CodePudding user response:

Because of realloc may failed, I recommend to change the size of row and the size of colunm separately. So when realloc fail, you can deal with it properly. I wrote a function that can resize the row of matrix.
If m is NULL, we simply return calloc instead of realloc.
If m is not NULL and the old_rows is larger than new_rows, we store pointers that would be lost by realloc for further free.
If m is not NULL and the old_rows is smaller or equal to new_rows, we just call realloc and initialize the new part of m.

double **matrix_rerow(double **m, int old_rows, int new_rows)
{
    if (!m)
        return calloc(new_rows,sizeof(*m));
    else if(old_rows > new_rows){
        const int len = old_rows - new_rows;
        double **save = malloc(sizeof(*save)*len);
        if (!save)
            return NULL;
        memcpy(save,m new_rows,sizeof(*save)*len);
        m = realloc(m,sizeof(*m)*new_rows);
        if (!m){
            free(save);
            return NULL;
        }
        for(int i = 0; i < len;   i)
            free(save[i]);
        free(save);
        return m;
    }else{
        m = realloc(m,sizeof(*m)*new_rows);
        if (!m)
            return NULL;
        memset(m old_rows,0,sizeof(*m)*(new_rows-old_rows));
        return m;
    }
}

For changing size of column, there is matrix_recol. We use pointer to int instead of int. Because each row call realloc may fail. When it fails, caller can observe row to check how many rows have been modified correctly.

double** matrix_recol(double **m, int *rows, int cols)
{
    const int save = *rows;
    *rows = 0;
    if (!m){
        m = matrix_rerow(NULL, 0, *rows);
        if (!m)
            return NULL;
        for(int i = 0; i < save;   i,(*rows)  ){
            void *ptr = realloc(m[i],sizeof(**m)*cols);
            if (!ptr){
                free(m);
                return NULL;
            }
            m[i] = ptr;
        }
        return m;
    }
    for(int i = 0; i < save;   i,(*rows)  ){
        void *ptr = realloc(m[i],sizeof(**m)*cols);
        if (!ptr)
            return NULL;
        m[i] = ptr;
    }
    return m;
}
  • Related