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;
}