How may I create a matrix in C using malloc and avoiding memory problems? How I can use C99 syntax to pass the matrix to a function?

11,056

Solution 1

Use malloc() to allocate a contiguous chunk of memory:

some_datatype_t* matrix = NULL;
matrix = malloc(nrows * ncols * sizeof(some_datatype_t));
if (!matrix) { 
    perror("malloc failed");
    exit(ENOMEM); 
}

Write a function to dereference a cell:

some_datatype_t 
get_value_from_matrix_at_index_ij(some_datatype_t* mtx, 
                                  uint32_t ncols, 
                                  uint32_t i, 
                                  uint32_t j) 
{
    return mtx[i + i * (ncols - 1) + j];
}

Or a setter:

void
set_value_for_matrix_at_index_ij(some_datatype_t** mtx_ptr,
                                 uint32_t ncols, 
                                 uint32_t i, 
                                 uint32_t j,
                                 some_datatype_t val) 
{
    *mtx_ptr[i + i * (ncols - 1) + j] = val;
}

Don't forget to free() your matrix when you're done with it:

free(matrix), matrix = NULL;

Here's an example of a 3x4 matrix:

    0  1  2  3
  ------------
0 | 0  1  2  3
1 | 4  5  6  7
2 | 8  9 10 11

It has 3 rows and 4 columns (ncols = 4).

In linearized form, its cells look like this:

0 1 2 3 4 5 6 7 8 9 10 11

To lookup a cell's contents at some zero-indexed row i and column j, you can calculate the index or address to dereference in constant time:

{1, 2} = matrix[1 + 1*3 + 2] = matrix[6]
{2, 3} = matrix[2 + 2*3 + 3] = matrix[11]
etc.

If you want to hide away some useful attributes into a clean package, you could even wrap a lot of this up into a struct:

typedef struct matrix {
    some_datatype_t* data;
    uint32_t nrows;
    uint32_t ncols;
} matrix_t;

Then you just initialize and pass around a pointer to a matrix_t variable:

matrix_t*
init_matrix(uint32_t nrows, uint32_t ncols) 
{
    matrix_t *m = NULL;
    m = malloc(sizeof(matrix_t));
    if (!m) { /* error */ }
    m->data = NULL;
    m->data = malloc(nrows * ncols * sizeof(some_datatype_t));
    if (!m->data) { /* error */ }
    m->nrows = nrows;
    m->ncols = ncols;
    return m;
}

some_datatype_t 
get_value_from_matrix_at_index_ij(matrix_t* mtx,
                                  uint32_t i, 
                                  uint32_t j) 
{
    return mtx->data[i + i * (mtx->ncols - 1) + j];
}

void
set_value_for_matrix_at_index_ij(matrix_t** mtx_ptr,
                                 uint32_t i, 
                                 uint32_t j,
                                 some_datatype_t val) 
{
    (*mtx_ptr)->data[i + i * ((*mtx_ptr)->ncols - 1) + j] = val;
}

void
delete_matrix(matrix_t** m) 
{
    free((*m)->data), (*m)->data = NULL;
    free(*m), *m = NULL;
}

If you're working with a symmetric square matrix, you can exploit the symmetry and use half the memory. Sometimes, less than half the memory, if storage of the diagonal can be removed (say, for instance, correlation or other symmetric statistical scores).

Mainly, the idea here is to think about how to write an equation that maps between a matrix index pair (i, j) and some contiguous-array index k.

Solution 2

I think a better way to manage a matrix is in the code below. The code below indicates how to use a single malloc to manage a matrix, but if you need to use the matrix style m[y][x] this code shows you how to do that using only two mallocs and not a malloc for each row.

Below is the code where both examples are in the main:

  • The first example fills the matrix elements (y,x) with the value of ( y+1 ) * 100 + ( x+1 ) and uses the formula y * columns + x to point the matrix elements. This involves one malloc.

  • The second example fills the matrix elements (y,x) with the value of (rows-y) * 100 + (columns-x) and uses the style m[y][x] to point the matrix elements. This involves two mallocs and then the use of more bytes of memory.

The code:

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

void print_matrix1(int *m, int r, int c);
void print_matrix2(int **m,int r,int c);

void print_matrix1(int *m,int r,int c)
{
    int y,x;

    for(y=0;y<r;y++) {
        for(x=0;x<c;x++) {
            printf("(%2d,%2d) = %04d; ",y+1,x+1,m[y*c+x]);
        }
    }
}

void print_matrix2(int **m,int r,int c)
{
    int y,x;

    for(y=0;y<r;y++) {
        for(x=0;x<c;x++) {
            printf("(%2d,%2d) = %04d; ",y+1,x+1,m[y][x]);
        }
    }
}

int main(void)
{
    int * matrix_memory;

    int **matrix; /* for example 2 */

    int rows=11,columns=5,x,y;

    matrix_memory = malloc(sizeof(*matrix_memory) * rows * columns);
    if (matrix_memory==NULL)
        return errno;

    /* Example one */
    for(y=0;y<rows;y++)
        for(x=0;x<columns;x++)
            matrix_memory[y*columns+x]=(y+1)*100+(x+1);

    print_matrix1(matrix_memory,rows,columns);
    puts("--------------------------------------------");

    /* Example two */
    matrix=malloc(sizeof(*matrix)*rows);
    if (matrix!=NULL) {
        for(y=0;y<rows;y++)
            matrix[y]=matrix_memory+y*columns;

        /* Enable to print the data of example 1 using matrix[y][x]
        print_matrix2(matrix,rows,columns);
        puts("--------------------------------------------");
        */

        for(y=0;y<rows;y++)
            for(x=0;x<columns;x++)
                matrix[y][x]=(rows-y)*100+(columns-x);

        print_matrix2(matrix,rows,columns);
    }

    /* end and free memory */
    free(matrix_memory);

    if (matrix!=NULL) {
        free(matrix);
        return 0;
    }

    return errno;
}

Solution 3

There are a few subtle points that can make dynamically creating 2D matricies more robust and less likely to provide a chance for inadvertent read from an uninitialized value (undefined behavior). The No. 1 improvement you can make is to allocate the column arrays with calloc such that the memory for each cell is already initialized to zero - 0 at the time of allocation. This allows immediate iteration over the entire matrix without the potential for a read of an uninitialized value. Take a look at the following:

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

int **mtrx_calloc (size_t m, size_t n);                /* initialize elements to 0  */
int **realloc_rows (int **ap, size_t *m, size_t n, size_t newm); /* resize newm x n */
void mtrx_prn (size_t m, size_t n, int **matrix);      /* print matrix with/pad     */
void mtrx_free (size_t m, int **matrix);               /* free memory allocated     */

int main (int argc, char **argv)
{
    /* set initial size from arguments given (default: 3 x 4) */
    size_t m = argc > 2 ? (size_t)atoi (argv[1]) : 3;
    size_t n = argc > 2 ? (size_t)atoi (argv[2]) : 4;

    /* allocate the m x n matrix */
    int **matrix = mtrx_calloc (m, n);

    /* fill with misc values */
    register size_t i = 0, j = 0;
    for (i = 0; i < m; i++)
    {
        for (j = 0; j < n; j++)
            matrix [i][j] = (int)(i + j);
    }

    /* print matrix */
    printf ("\nThe dynamically allocated %zux%zu matrix is:\n\n", m, n);
    mtrx_prn (m, n, matrix);

    /* reallocate matrix - add 4 rows */
    printf ("\nReallocate matrix to %zux%zu:\n\n", m + 4, n);
    size_t oldm = m;
    matrix = realloc_rows (matrix, &m, n, m + 4);

    /* fill new rows with misc values */
    for (i = oldm; i < m; i++)
    {
        for (j = 0; j < n; j++)
            matrix [i][j] = (int)(i + j);
    }

    mtrx_prn (m, n, matrix);

    /* free memory alocated */
    mtrx_free (m, matrix);

    /* just to make it look pretty */
    printf ("\n");

    return 0;
}

/* allocate/initialize mxn matrix */
int **mtrx_calloc (size_t m, size_t n)
{
    register size_t i;
    int **array = calloc (m, sizeof *array);

    if (!array) {   /* validate allocation  */
        fprintf (stderr, "%s() error: memory allocation failed.\n", __func__);
        exit (EXIT_FAILURE);
    }

    for (i = 0; i < m; i++)
    {
        array[i] = calloc (n, sizeof **array);

        if (!array[i]) {   /* validate allocation  */
            fprintf (stderr, "%s() error: memory allocation failed.\n", __func__);
            exit (EXIT_FAILURE);
        }
    }
    return array;
}

/* realloc an array of pointers to int* setting memory to 0. */
int **realloc_rows (int **ap, size_t *m, size_t n, size_t newm)
{
    if (newm <= *m) return ap;
    size_t i = 0;
    int **tmp = realloc (ap, newm * sizeof *ap);
    if (!tmp) {
        fprintf (stderr, "%s() error: memory reallocation failure.\n", __func__);
        // return NULL;
        exit (EXIT_FAILURE);
    }
    ap = tmp;

    for (i = *m; i < newm; i++)
    {
        ap[i] = calloc (n, sizeof **ap);

        if (!ap[i]) {   /* validate allocation  */
            fprintf (stderr, "%s() error: memory allocation failed.\n", __func__);
            exit (EXIT_FAILURE);
        }
    }
    *m = newm;

    return ap;
}

/* print a (m x n) matrix (check pad alloc) */
void mtrx_prn (size_t m, size_t n, int **matrix)
{
    register size_t i, j;

    for (i = 0; i < m; i++)
    {
        char *format = "[ %2d";
        for (j = 0; j < n; j++)
        {
            printf (format, matrix [i][j]);
            format = ", %2d";
        }
        puts(" ]");
    }
}

void mtrx_free (size_t m, int **matrix)
{
    register size_t i;

    for (i = 0; i < m; i++)
    {
        free (matrix [i]);
    }
    free (matrix);
}

**Create 5x4 matrix and reallocate to **

$ ./bin/mtrx_dyn_int 4 5

The dynamically allocated 4x5 matrix is:

[  0,  1,  2,  3,  4 ]
[  1,  2,  3,  4,  5 ]
[  2,  3,  4,  5,  6 ]
[  3,  4,  5,  6,  7 ]

Reallocate matrix to 8x5:

[  0,  1,  2,  3,  4 ]
[  1,  2,  3,  4,  5 ]
[  2,  3,  4,  5,  6 ]
[  3,  4,  5,  6,  7 ]
[  4,  5,  6,  7,  8 ]
[  5,  6,  7,  8,  9 ]
[  6,  7,  8,  9, 10 ]
[  7,  8,  9, 10, 11 ]

Check Memory Errors/Leaks

$ valgrind ./bin/mtrx_dyn_int 4 5
==31604== Memcheck, a memory error detector
==31604== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==31604== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==31604== Command: ./bin/mtrx_dyn_int 4 5
==31604==

The dynamically allocated 4x5 matrix is:

[  0,  1,  2,  3,  4 ]
[  1,  2,  3,  4,  5 ]
[  2,  3,  4,  5,  6 ]
[  3,  4,  5,  6,  7 ]

Reallocate matrix to 8x5:

[  0,  1,  2,  3,  4 ]
[  1,  2,  3,  4,  5 ]
[  2,  3,  4,  5,  6 ]
[  3,  4,  5,  6,  7 ]
[  4,  5,  6,  7,  8 ]
[  5,  6,  7,  8,  9 ]
[  6,  7,  8,  9, 10 ]
[  7,  8,  9, 10, 11 ]

==31604==
==31604== HEAP SUMMARY:
==31604==     in use at exit: 0 bytes in 0 blocks
==31604==   total heap usage: 10 allocs, 10 frees, 256 bytes allocated
==31604==
==31604== All heap blocks were freed -- no leaks are possible
==31604==
==31604== For counts of detected and suppressed errors, rerun with: -v
==31604== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

Matrix Allocated In Single Block - Stride Determines Dimensions

Here is another example that creates a single dimension array that is interpreted as a 2D array by setting a stride defining the number of elements to be considered in each row/col. This approach provides an easier way to handle the underlying array data in a 1D array, but the logic to simulate a 2D array grows more complex to accommodate the underlying 1D array. You are free to remove the label data from the struct that holds the size & stride information, I found it convenient when working with multiple stride settings. Here is the example:

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

#ifndef _STDINT_H
    typedef unsigned char uchar;
    typedef unsigned int  uint;
    typedef unsigned long ulong;
#endif

/** struct mdata defines matrix metadata for size, stride, label and lblsz.
*
*  struct mdata defines metadata for handling array of numbers as 2d array.
*/
typedef struct mdata
{
    int size;
    int stride;
    char *label;
    size_t lblsz;

} mdata;

/* function prototypes */
void mtrx_prnmatrix (int *m, mdata *md);
void mtrx_prnrow (int *m, mdata *md, int v);
void mtrx_prncol (int *m, mdata *md, int v);
void mtrx_showmdata (mdata *md);
long mtrx_getval (int *m, mdata *md, int x, int y);
long mtrx_setval (int *m, mdata *md, int x, int y, int val);
int mtrx_setlable (mdata *md, char *s);
int mtrx_setstride (mdata *md, int stride);
void free_mtrxmd (mdata *md);

int main (int argc, char **argv) {

    /* static for testing, you can allocate this single block */
    int mtrx[] = { 1, 2, 3, 4, 5, 6,
                7, 8, 9, 10, 11, 12,
                13, 14, 15, 16, 17, 18,
                19, 20, 21, 22, 23, 24,
                25, 26, 27, 28, 29, 30,
                31, 32, 33, 34, 35, 36 };

    int sz = 36;
    int stride = 6;
    int vreq = 0;
    mdata *m1d;

    m1d = malloc (sizeof *m1d);
    m1d-> size = sz;
    m1d-> stride = stride;
    m1d-> label = strdup ("m1d (6x6)");
    m1d-> lblsz = strlen (m1d-> label);

    if (argc < 2 ) {
        fprintf (stderr, "Error: insufficient input, usage: %s int (vector [0-5])\n", argv[0]);
        return 1;
    }

    /* show the metadata */
    mtrx_showmdata (m1d);

    /* set the vector request - use strtol for error check */
    vreq = atoi (argv[1]);

    /* print the full matrix */
    mtrx_prnmatrix (mtrx, m1d);

    printf ("\n");

    /* print the requested column vector */
    mtrx_prncol (mtrx, m1d, vreq);

    /* print the requested row vector */
    mtrx_prnrow (mtrx, m1d, vreq);

    /* set a new stride for matrix (set new temp label) */
    mtrx_setstride (m1d, 4);
    mtrx_showmdata (m1d);

    /* set a new label for matrix */
    mtrx_setlable (m1d, "m1d (9x4)");
    mtrx_showmdata (m1d);

    /* print the full updated matrix */
    mtrx_prnmatrix (mtrx, m1d);
    printf ("\n");

    /* set a new stride and label for matrix */
    mtrx_setstride (m1d, 3);
    mtrx_setlable (m1d, "m1d (12x3)");
    mtrx_prnmatrix (mtrx, m1d);
    printf ("\n");

    /* set a new stride and label for matrix */
    mtrx_setstride (m1d, 2);
    mtrx_setlable (m1d, "m1d (18x2)");
    mtrx_prnmatrix (mtrx, m1d);
    printf ("\n");

    /* mtrx_getval test */
    mtrx_showmdata (m1d);
    mtrx_setval (mtrx, m1d, 9, 1, 99);
    int i = 0;
    for (i = 0; i < (m1d-> size / m1d-> stride); i++) {
        printf (" mtrx [%2d,%2d] : %2ld\n", i, 0, mtrx_getval (mtrx, m1d, i, 0));
        printf (" mtrx [%2d,%2d] : %2ld\n", i, 1, mtrx_getval (mtrx, m1d, i, 1));
    }
    printf ("\n");

    /* free data allocated to metadata */
    free_mtrxmd (m1d);

    return 0;
}

/** mtrx_prnmatrix (int *, int, mdata *) print matrix in row x column format.
*
*  mtrx_prnmatrix print matrix in row x column format with metadata label.
*/
void mtrx_prnmatrix (int *m, mdata *md)
{
    int i = 0;

    if (!md) {
        fprintf (stderr, "error: metadata structure not initialized\n");
    }

    printf ("Matrix: %s\n", md->label);
    for (i = 0; i < md-> size; i++)
        if (((i + 1) % md-> stride) == 0)
            if (i == (md->size - 1))
                printf (" %2d ]\n", m[i]);
            else
                printf (" %2d\n", m[i]);
        else
            if (i == 0)
                printf ("[%2d", m[i]);
            else
                printf (" %2d", m[i]);
}

/** mtrx_prnrow (int *, mdata *, int) prints row vector.
*
*  mtrx_prnrow prints matrix row vector based on metadata.
*/
void mtrx_prnrow (int *m, mdata *md, int v)
{
    register int it = v;

    if (!md) {
        fprintf (stderr, "error: metadata structure not initialized\n");
    }
    if (v > md-> size/md-> stride - 1 || v < 0) {
        fprintf (stderr, "error: invalid rvector (%d), valid: 0 < rvector < max (%d)\n",
                v, md-> size/md-> stride);
        return;
    }

    if (md-> label) printf ("Matrix: %s -- row vector: %d\n", md-> label, v);

    for (it = v * md-> stride; it < (v * md-> stride) + md-> stride; it++)
        printf (" %d", m[it]);

    printf ("\n");
}

/** mtrx_prncol (int *, mdata *, int) prints column vector.
*
*  mtrx_prncol prints matrix column vector based on metadata.
*/
void mtrx_prncol (int *m, mdata *md, int v)
{
    register int it = v;

    if (!md) {
        fprintf (stderr, "error: metadata structure not initialized\n");
    }
    if (v > md-> size/md-> stride - 1 || v < 0) {
        fprintf (stderr, "error: invalid vector (%d), valid: 0 < vector < max (%d)\n",
                v, md-> size/md-> stride);
        return;
    }

    if (md-> label) printf ("Matrix: %s -- column vector: %d\n", md-> label, v);

    for (it = v; it < md-> size; it += md-> stride)
        printf (" %d\n", m[it]);
}

/** mtrx_showmdata (mdata *) prints metadata struct.
*
*  mtrx_showmdata prints label, size, stride and lblsz metadata.
*/
void mtrx_showmdata (mdata *md)
{
    printf ("\n label : %s\n size  : %d\n stride: %d\n lblsz : %zd\n\n",
            md-> label, md-> size, md-> stride, md-> lblsz);
}

/** mtrx_getval (int *, mdata *, int, int, int) retrieves value at position x,y).
*
*  mtrx_getval gets the value at the give position within the matrix based on x, y indexes.
*/
long mtrx_getval (int *m, mdata *md, int x, int y)
{
    if (x * y > md-> size) {
        fprintf (stderr, "%s()  error: invalid index, (x * y) > size.\n", __func__);
        return -1;
    }
    if (x > (md-> size / md-> stride - 1)) {
        fprintf (stderr, "%s()  warning: invalid metadata index, (x > %d).\n",
                __func__, md-> size/md-> stride - 1);
    }
    if (y > (md-> stride - 1)) {
        fprintf (stderr, "%s()  warning: invalid metadata index, (y > %d).\n", __func__, md-> stride - 1);
    }
    return m[(x * md-> stride) + y];
}

/** mtrx_setval (int *, mdata *, int, int, int) sets value at position x,y).
*
*  mtrx_setval set the value at the give position within the matrix based on x, y indexes.
*/
long mtrx_setval (int *m, mdata *md, int x, int y, int val)
{
    if (x * y > md-> size) {
        fprintf (stderr, "%s()  error: invalid index, (x * y) > size.\n", __func__);
        return -1;
    }
    if (x > (md-> size / md-> stride - 1)) {
        fprintf (stderr, "%s()  warning: invalid metadata index, (x > %d).\n",
                __func__, md-> size/md-> stride - 1);
    }
    if (y > (md-> stride - 1)) {
        fprintf (stderr, "%s()  warning: invalid metadata index, (y > %d).\n", __func__, md-> stride - 1);
    }
    return m[(x * md-> stride) + y] = val;
}

/** mtrx_setlable (mdata *, char *) sets new label in metadata struct.
*
*  mtrx_setlable sets new label metadata and updates lblsz.
*/
int mtrx_setlable (mdata *md, char *s)
{
    if (!md) {
        fprintf (stderr, "%s()  error: metadata structure not initialized\n", __func__);
        if (!(md = malloc (sizeof (md)))) {
            fprintf (stderr, "%s()  metadata structure allocation failed \n", __func__);
            return 0;
        }
    }

    if (!s) {
        fprintf (stderr, "%s()  error: string not initialized\n", __func__);
        return 0;
    }

    md-> lblsz = strlen (s);

    char *tmp = realloc (md-> label, md-> lblsz + 1);
    if (!tmp) {
        fprintf (stderr, "%s()  error: metadata - label realloc failed.\n", __func__);
        return 0;
    }
    strncpy (tmp, s, md-> lblsz + 1);
    md-> label = tmp;

    return 1;
}

/** mtrx_setstride (mdata *, int) sets new stride in metadata struct.
*
*  mtrx_setstride validates and sets new stride metadata with temp label.
*/
int mtrx_setstride (mdata *md, int stride)
{
    char newlabel[256];
    int newrows = 0;

    if (!md) {
        fprintf (stderr, "%s()  error: metadata structure not initialized\n", __func__);
        md = malloc (sizeof (md));
        if (!md)
            fprintf (stderr, "%s()  metadata structure allocated\n", __func__);
        else {
            fprintf (stderr, "%s()  metadata structure allocation failed \n", __func__);
            return 0;
        }
    }

    if (stride < 1) {
        fprintf (stderr, "%s()  error: invalid (stride < 1) supplied.\n", __func__);
        return 0;
    }

    if (md-> size % stride) {
        fprintf (stderr, "%s()  error: invalid stride (size %% stride != 0)\n", __func__);
        return 0;
    }

    md-> stride = stride;

    newrows = md-> size / stride;
    sprintf (newlabel, "%s -> now (%dx%d)", md->label, newrows, stride);
    mtrx_setlable (md, newlabel);

    return 1;
}

void free_mtrxmd (mdata *md)
{
    if (md-> label) free (md-> label);
    if (md) free (md);
}

Output

$ /bin/mtrx_metadata_new 4

 label : m1d (6x6)
 size  : 36
 stride: 6
 lblsz : 9

Matrix: m1d (6x6)
[ 1  2  3  4  5  6
  7  8  9 10 11 12
 13 14 15 16 17 18
 19 20 21 22 23 24
 25 26 27 28 29 30
 31 32 33 34 35 36 ]

Matrix: m1d (6x6) -- column vector: 4
 5
 11
 17
 23
 29
 35
Matrix: m1d (6x6) -- row vector: 4
 25 26 27 28 29 30

 label : m1d (6x6) -> now (9x4)
 size  : 36
 stride: 4
 lblsz : 22


 label : m1d (9x4)
 size  : 36
 stride: 4
 lblsz : 9

Matrix: m1d (9x4)
[ 1  2  3  4
  5  6  7  8
  9 10 11 12
 13 14 15 16
 17 18 19 20
 21 22 23 24
 25 26 27 28
 29 30 31 32
 33 34 35 36 ]

Matrix: m1d (12x3)
[ 1  2  3
  4  5  6
  7  8  9
 10 11 12
 13 14 15
 16 17 18
 19 20 21
 22 23 24
 25 26 27
 28 29 30
 31 32 33
 34 35 36 ]

Matrix: m1d (18x2)
[ 1  2
  3  4
  5  6
  7  8
  9 10
 11 12
 13 14
 15 16
 17 18
 19 20
 21 22
 23 24
 25 26
 27 28
 29 30
 31 32
 33 34
 35 36 ]


 label : m1d (18x2)
 size  : 36
 stride: 2
 lblsz : 10

 mtrx [ 0, 0] :  1
 mtrx [ 0, 1] :  2
 mtrx [ 1, 0] :  3
 mtrx [ 1, 1] :  4
 mtrx [ 2, 0] :  5
 mtrx [ 2, 1] :  6
 mtrx [ 3, 0] :  7
 mtrx [ 3, 1] :  8
 mtrx [ 4, 0] :  9
 mtrx [ 4, 1] : 10
 mtrx [ 5, 0] : 11
 mtrx [ 5, 1] : 12
 mtrx [ 6, 0] : 13
 mtrx [ 6, 1] : 14
 mtrx [ 7, 0] : 15
 mtrx [ 7, 1] : 16
 mtrx [ 8, 0] : 17
 mtrx [ 8, 1] : 18
 mtrx [ 9, 0] : 19
 mtrx [ 9, 1] : 99
 mtrx [10, 0] : 21
 mtrx [10, 1] : 22
 mtrx [11, 0] : 23
 mtrx [11, 1] : 24
 mtrx [12, 0] : 25
 mtrx [12, 1] : 26
 mtrx [13, 0] : 27
 mtrx [13, 1] : 28
 mtrx [14, 0] : 29
 mtrx [14, 1] : 30
 mtrx [15, 0] : 31
 mtrx [15, 1] : 32
 mtrx [16, 0] : 33
 mtrx [16, 1] : 34
 mtrx [17, 0] : 35
 mtrx [17, 1] : 36

Solution 4

Another very raw way to allocates space for a matrix may be that in the following, as suggested me by a friend in a comment in this question.

This is a risky method that could make it very difficult to debug the code.

It uses a single malloc to allocate all the space to manage the matrix in style m[y][x].

I prefer the simplest way I indicate in my first reply: to use a single array allocated with malloc and to point inside it using m[y*ncols+x]. This simple method use less memory and I think is more fast of other method! (but I've never verified it speed!)

Here other (dangerous) code:

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

void print_matrix2(int **m,int r,int c)
{
    int y,x;

    for(y=0;y<r;y++) {
        for(x=0;x<c;x++) {
            printf("(%2d,%2d) = %04d; ",y+1,x+1,m[y][x]);
        }
    }
}

int main(void)
{
    int **matrix; /* for example 2 */

    int rows=11,columns=5,x,y;

    matrix = malloc(sizeof(*matrix)*rows + sizeof(**matrix) * rows * columns);
    if (matrix==NULL)
        return errno;

    printf("Size of an int %lu\n",sizeof(int));
    puts("Memory allocation");
    printf("matrix:%p\n&matrix[0]:%p &matrix[%d]:%p\n",matrix,&matrix[0],rows-1,&matrix[rows-1]);

    puts("--------------------------------------");

    for(y=0;y<rows;y++) {
        matrix[y]=(int *)((void *)matrix+sizeof(*matrix)*rows)+y*columns;
        printf("matrix[%d]:%p matrix[%d+1]:%p\n",y,matrix[y],y+1,matrix[y]+columns);
    }
    puts("--------------------------------------");

    /* Fill the matrix */
    for(y=0;y<rows;y++)
        for(x=0;x<columns;x++)
            matrix[y][x]=(y+1)*100+(x+1);

    print_matrix2(matrix,rows,columns);


    /* end and free memory */
    free(matrix);
    return 0;
}

This code printout the memory allocation to allow us to verify the memory allocations.

Share:
11,056
Sir Jo Black
Author by

Sir Jo Black

I'm a firmware developer using C/C++ and assembly coding. Now in the 2015, since the end of 2014, I'm "playing" with Atmel 8 bit AVR MCUs such as AT tiny 84/85, atmega 2560/328/168.

Updated on June 07, 2022

Comments

  • Sir Jo Black
    Sir Jo Black almost 2 years

    Have you good indications about use of the malloc function to allocate memory space for matrices?

    In these days I saw that a lot of coders code matrices in a "bad" way when it needs to use malloc to manage them. Am I in error when I think that?

    An example of the "bad" code I mean is the following:

    int main()
    {
        char **row;
        int width=80, height=24, i, j;
    
        row = malloc(height * sizeof(char *));
        for(i = 0; i < width; i++)
            row[i] = malloc(width * sizeof(char));
    
        for(i = 0; i < height; i++)
        {
            for(j = 0; j < width; j++)
            {
                row[i][j] = i*j;
            }
        }
    
        return 0;
    }
    

    In the code above I find at least three problems:

    • It strongly fragments the memory.

    • It uses more memory than necessary

    • It makes not contiguous the memory it uses for the matrix.


    Somebody suggest me the interesting way to use this C99 syntax:

    int (*matrix)[columns] = malloc(sizeof(int[rows][columns]));
    

    Now I need to pass this variable matrix into the following function:

    void print_matrix2(int **m,int r,int c)
    {
        int y,x;
    
        for(y=0;y<r;y++) {
            for(x=0;x<c;x++) {
                printf("(%2d,%2d) = %04d; ",y+1,x+1,m[y][x]);
            }
        }
    }
    

    The only way I've found is to change the prototype:

    void print_matrix2(int (*m)[5],int r,int c);
    

    but I want to avoid the [5] declaration, I want be able to send to my function whatever number of column I want!

    If that is, I feel this C99 improvement is not a definitive solution to the problem and I think the best and simple way to solve the matrices management is to manage them using the classical C language!

  • Sir Jo Black
    Sir Jo Black about 9 years
    Good solution, I see at this formula for a while: i + i * (mtx->ncols - 1) + j .. It's smart ... but is the same off i * ncols + j ... :)
  • Sir Jo Black
    Sir Jo Black about 9 years
    For me this is, for sure, an excellent solution, but, because the calls, might be heavy to execute it on an MCU or without using some kind of compiler optimizations.
  • Alex Reynolds
    Alex Reynolds about 9 years
    I admit I'm not sure what the issues are there, but as far as access time goes, you can't easily beat constant time lookups, and this uses only as much memory as is needed for a 2D matrix, without compression or encoding tricks.
  • Lectem
    Lectem about 9 years
    I like the idea of being able to use the mat[][] notation here but I would have gone even further by using only one malloc(sizeof (int*)*rows+ rows * cols * sizeof(int)) and using the first "row" of pointers to point to the data.
  • Sir Jo Black
    Sir Jo Black about 9 years
    I used the idea you say! I find it correct, I've not indicated it because may be very hard to understand. Basically my purpose is to explain the correct use of the language (we love) to the beginners!!! :) If you want show us your good idea! :)
  • Sir Jo Black
    Sir Jo Black about 9 years
    @Lectem. I have written the code you said above, but I think is dangerous and very hard to debug if errors occur. Then I'll avoid to put it here :p ... but it's very nice!!! :) :p
  • Sir Jo Black
    Sir Jo Black about 9 years
    This allocates a lot of chunks in the for! It should be better all rows are allocated in a contiguous memory area, this avoid memory fragmentation. Moreover a contiguous memory area may be easily saved with a single fwrite or copied/moved with a single function call.
  • David C. Rankin
    David C. Rankin about 9 years
    It is a tradeoff on the complexity of the logic. You are free to create a singe block and then define a stride for the matrix and incorporate that into your code. Generally in that case you will want to create a struct as elements that contains the stride, size, etc.. I have an example I'll add to my answer.
  • Sir Jo Black
    Sir Jo Black about 9 years
    Yes, you may create all the complexity you want/need also creating a base solutions that allocates all the needing memory in one shot! By the way each implementation should have it logic!
  • Sir Jo Black
    Sir Jo Black about 9 years
    When I'll will be here again, I'll have the pleasure of seeing your example!
  • Sir Jo Black
    Sir Jo Black about 9 years
    @Lectern. I gave out , I put it ... : p
  • Sir Jo Black
    Sir Jo Black about 9 years
    How am I permitted to pass this object to a function like the printmatrix2() in my code here!? Have I to specify the dimension? I want a function that may receive all possible conviguration of int matrix without restriction in the definition/prototype of the function, Do I need to pass the dimension when I call the function or is implicit to the "object"?
  • Sir Jo Black
    Sir Jo Black about 9 years
    @Lectern. Please vote to reopen if you think that this question is useful!
  • Lutz Lehmann
    Lutz Lehmann about 9 years
    In C there is almost nothing implicit, everything has to be defined explicitly. As noted in comments in the original thread, passing such flat matrices as return values, and probably also as arguments, is problematic. But this was not the topic of the question.
  • Sir Jo Black
    Sir Jo Black about 9 years
    Yes, but I'm interested in understing this fact, I know C since 25 years, but I admit I don't know C99 specs. I'm asking you the solution!
  • Sir Jo Black
    Sir Jo Black about 9 years
    void print_matrix2(int (*m)[5],int r,int c) ... Declaring this the code runs! But I need to avoid to declare the number of column into the prototype ... !
  • Lutz Lehmann
    Lutz Lehmann about 9 years
    See c-faq.com/aryptr/ary2dfunc3.html, what you want is to use the array type of array4 with the function declaration f2, your last comment is f1b.
  • Sir Jo Black
    Sir Jo Black about 9 years
    My function has this prototype: void print_matrix2(int **m,int r,int c); declaring matrix as: int (*matrix)[columns] = malloc(sizeof(int[rows][columns])); where rows and column are variables: The only way I find to recompile my code is to use the prototype: void print_matrix2(int (*m)[5],int r,int c), but I want not have to declare [5], because it should be change!
  • Sir Jo Black
    Sir Jo Black about 9 years
    I have found the way to receive the pointer and is good, but into the function I've to use m[y*ncols+x] ... :) thanks for the docs! :)
  • Lutz Lehmann
    Lutz Lehmann about 9 years
    Yes, internally, the flat 2D array is one contiguous 1D array, the compiler translates [i][j] into [i*ncols+j]. Thus it is incompatible with a pointer-to-pointer declaration. The ncols information is lost during the function call, you are left with the 1D array. If you don't want this, then using the single-allocation method from your answer is the next-best variant. If you do not want to pass the size informatio explicitely, you need a data structure as in the answer of Ike. If you want to hide the interna, then you need a class-type supporting language, such as C++.
  • Lutz Lehmann
    Lutz Lehmann about 9 years
    I've added an example for non-structure-destructive parameter passing, it is f4 in the sample source code of the linked Q&A.
  • Sir Jo Black
    Sir Jo Black about 9 years
    Thanks! :) I'm intersted it seems a good evolution, but I prefer my old method! :)