3

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
  • 2,024
  • 2
  • 15
  • 22
  • 3
    So what is your question? Yes, you can allocate the entire matrix in one go, which is more efficient in some ways. That is what I would do. (Of course, sparse matrices are a whole different issue.) – Jonathan Wood May 01 '15 at 04:27
  • Both the first and third items in your problem-list for the first example are completely avoidable if you size the allocation properly to include the pointer bed. as well as all needed elements. Further, a fixed linear allocation can use VLAs just as efficiently as you're using math, only the compiler does it for you with less chance for error. Finally, as you noted, this isn't a question, and should not be posted as one. If you post a real, related question, you can certainly answer it yourself (and Stack Overflow encourages it when appropriate, especially if the question is interesting). – WhozCraig May 01 '15 at 04:28
  • I'm voting to close this question as off-topic because it belongs to http://codereview.stackexchange.com/ – Ivan Aksamentov - Drop May 01 '15 at 04:29
  • The main issue with matrix is the realloc when you add (or remove) a row or a column. – Ôrel May 01 '15 at 04:46
  • If it is necessary to dynamically allocate memory, then at least one `malloc` is necessary, isn't it? I like your approach with a single `malloc`. That's what I do too. That way, `free`ing is also easy. However, this is a Q&A site, what is your question though? :) – Arun May 01 '15 at 04:47
  • Yes, but the question is not about reallocating them, but allocating them in the better why, avoiding the use of a lot of mallocs. – Sir Jo Black May 01 '15 at 04:47
  • Talking about an idea you had [is fine on SO](http://stackoverflow.com/help/self-answer), but you should still try to fit it into the Q&A format. Perhaps you can rephrase this as a specific question set (e.g. "how can I avoid memory fragmentation while allocating a matrix?"), and then move the answer content to a separately-posted answer, below. – Alex Celeste May 01 '15 at 04:51
  • @Leushenko. thanks for the hint. I try to modify the question! – Sir Jo Black May 01 '15 at 04:54
  • possible duplicate of [How do I correctly set up, access, and free a multidimensional array in C?](http://stackoverflow.com/questions/12462615/how-do-i-correctly-set-up-access-and-free-a-multidimensional-array-in-c) – Lutz Lehmann May 01 '15 at 10:03
  • As written in my answer, the flexible prototype for `int (*m)[c]` is `myfunc(int r, int c, int m[r][c]);`. – Lutz Lehmann May 01 '15 at 14:28

6 Answers6

3

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.

Alex Reynolds
  • 95,983
  • 54
  • 240
  • 345
  • 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 May 01 '15 at 05:58
  • 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. – Sir Jo Black May 01 '15 at 06:02
  • 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. – Alex Reynolds May 01 '15 at 06:15
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;
}
Sir Jo Black
  • 2,024
  • 2
  • 15
  • 22
  • 1
    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. – Lectem May 01 '15 at 06:16
  • 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 May 01 '15 at 06:23
  • @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 May 01 '15 at 08:16
  • @Lectern. I gave out , I put it ... : p – Sir Jo Black May 01 '15 at 11:11
  • @Lectern. Please vote to reopen if you think that this question is useful! – Sir Jo Black May 01 '15 at 12:17
2

A little bit of structure goes a long way here to make a contiguous matrix easier to work with.

struct Matrix
{
    int width;
    int height;
    int* ptr;
};

static struct Matrix matrix_create(int width, int height)
{
    struct Matrix new_matrix;
    new_matrix.width = width;
    new_matrix.height = height;
    new_matrix.ptr = malloc(width * height * sizeof(int));
    return new_matrix;
}

static void matrix_destroy(struct Matrix* m)
{
    free(m->ptr);
}

static int* matrix_row(struct Matrix* m, int row)
{
    return m->ptr + row * m->width;
}

int main()
{
    int r = 0;
    int c = 0;
    struct Matrix m = matrix_create(80, 24);

    for (r=0; r < m.height; ++r)
    {
        int* row = matrix_row(&m, r);
        for (c=0; c < m.width; ++c)
            row[c] = r * m.width + c;
    }
    matrix_destroy(&m);
}

We can also use variable-length structs to just work with struct Matrix* the whole time with a few minor tweaks by allocating the matrix members and its buffer in one go.

  • 1
    Good idea, but the problem of the rows is to help some people who prefer to use the notation m[y][x]. Your solution doesn't solve that issue. In my SW I prefer, almost always, to use the simple way I indicate in my example: y * ncols + x. The structure is a beautiful idea to pack all matrix information! :) Using structures it might be interesting to define some #define. I'm not always happy of the use of function to solve simple operations, in some cases (MCU) they might make heavy the code, and not always the compiler is able to eliminate the calls the use of functions generates. – Sir Jo Black May 01 '15 at 06:17
  • 1
    Yeah, a lot of my coding style places a lot of trust on the optimizer to optimize away things like function calls (including always initializing to zero even when redundant). One of the useful tricks to me is to simply fetch a row pointer from T* row = y * ncols and then just start indexing row[x]. We can do that without involving a function. I find we can avoid the temptation for extra pointers there or additional functions, and somehow it feels intuitive to me there to think about row logic in a similar way it feels straightforward to think about an image in terms of scanlines. –  May 01 '15 at 06:51
1

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
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • 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. – Sir Jo Black May 01 '15 at 09:12
  • 1
    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. – David C. Rankin May 01 '15 at 09:50
  • 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 May 01 '15 at 09:56
  • When I'll will be here again, I'll have the pleasure of seeing your example! – Sir Jo Black May 01 '15 at 09:59
1

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.

Sir Jo Black
  • 2,024
  • 2
  • 15
  • 22
0

From another question array by malloc on function lu decomposition referencing a much earlier question https://stackoverflow.com/a/12462760/3088138 : Since C99 the shortest and most economic matrix allocation is

float (*matrix)[n] = malloc(sizeof(float[m][n]));

which avoids the layered pointer-to-pointer structure.


Per http://c-faq.com/aryptr/ary2dfunc3.html , the way to pass variable length 2D arrays is per

void f2(float *aryp, int nrows, int ncols);

....

f2(*matrix, m, n);

However, you (probably) lose the implicit conversion from 2D addressing to flat addresses, you got to emulate matrix[i][j] by aryp[ncols*i+j].


One can pass the flat 2D array non-destructively, provided in the source code example of the c-faq link as f4 (C99 only, as before). Example, works with recent version of gcc:

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

void print_matrix( int nrows, int ncols, float mat[nrows][ncols]) {
    int i,j;
    for(i=0; i<nrows; i++) {
        for(j=0; j<ncols; j++) printf("%10.4e\t", mat[i][j]);
        puts("");
    }
}

int main() {
    int m=5, n = 10;
    float (*matrix)[n] = malloc(sizeof(float[m][n]));

    int i,j;
    for(i=0; i<m; i++) {
        for(j=0; j<n; j++) matrix[i][j] = i+1.0/(1+j);

    }
    print_matrix(m,n,matrix);
    return 0;
}
Community
  • 1
  • 1
Lutz Lehmann
  • 25,219
  • 2
  • 22
  • 51
  • 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 May 01 '15 at 12:13
  • 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. – Lutz Lehmann May 01 '15 at 12:28
  • 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 May 01 '15 at 12:30
  • 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 ... ! – Sir Jo Black May 01 '15 at 12:35
  • See http://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`. – Lutz Lehmann May 01 '15 at 12:44
  • 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 May 01 '15 at 12:49
  • 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! :) – Sir Jo Black May 01 '15 at 13:05
  • 1
    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 May 01 '15 at 13:05
  • I've added an example for non-structure-destructive parameter passing, it is `f4` in the sample source code of the linked Q&A. – Lutz Lehmann May 01 '15 at 13:19
  • Thanks! :) I'm intersted it seems a good evolution, but I prefer my old method! :) – Sir Jo Black May 01 '15 at 13:21