How may I create a matrix in C using malloc and av

2020-07-23 05:52发布

问题:

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!

回答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.



回答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.



回答3:

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


回答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.



回答5:

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


回答6:

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