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