If you work with matrices, I recommend you get into the habit of using a struct for it.
I've posted my own preferred one here several times already, but here goes again!
First, I keep the actual matrix data in a separate structure. The matrices refer to this structure (I call this the owner), and it records the number of matrices referring to it. As long as you destroy each matrix after you no longer need it, the owners get destroyed then too, and you don't need to worry about the cases where two of those matrices referred to the same data; it all gets handled correctly with very little code.
The actual matrix has both row and column strides; this means the matrix elements do not need to be consecutive in memory either way, as long as their addresses are regular. You can make one that is compatible with standard C two-dimensional arrays, but you can also transpose a matrix without copying any data.
Code:
struct owner {
long refcount; /* Number of matrices using this data */
double data[];
};
typedef struct matrix matrix;
struct matrix {
int rows;
int cols;
long rowstride;
long colstride;
struct owner *owner;
double *origin;
};
#define MATRIX_NONE { 0, 0, 0L, 0L, NULL, NULL }
To access the element at row r, column c, in matrix m (with m declared as matrix m;), you use
m.origin[r*m.rowstride + c*m.colstride]
If m is a pointer to a matrix, matrix *m;, you use
m->origin[r*m->rowstride + c*m->colstride]
In both cases 0 ≤ r < rows, 0 ≤ c < cols.
I don't mind posting my functions for creating and managing such structures, but I guess those are really not that useful for beginner programmers.
Instead, you can just supply your functions with a pointer to the initial element (I call this origin), number of rows (rows), number of columns (cols), the number of positions between elements on the same column but successive rows (rowstride), and the number of positions between elements on the same row but successive columns (colstride). For example,
Code:
int do_something(int *const origin,
const int rows, const int cols,
const int rowstride, const int colstride)
{
/* element[r][c] == origin[ r * rowstride + c * colstride ],
* and 0 <= r && 0 <= c && r < rows && c < cols.
*/
return 0;
}
but note that if you intend the code to work for large matrices, rowstride and colstride should be of type long (or ssize_t), as int is not large enough to hold array sizes on all architectures.
In essence, colstride is the number of elements to go forward in the array (negative if backwards) to get to the next column on the same row.
rowstride is the number of elements to go forward in the array (negative if backwards) to get to the next row on the same column.
Because C uses row-major order, for normal 2D arrays rowstride is the number of elements on each row (array width), and colstride is 1.
Here is an example of how to use the above:
Code:
int matrix[9][7];
do_something((int *)matrix, 9, 7, 7, 1);
Or, if you wish to only work on the 3-row, 2-column submatrix starting at the second row, third column,
Code:
do_something((int *)(&matrix[1][2]), 3, 2, 7, 1);
Or, if you wanted to work on the transpose of the submatrix,
Code:
do_something((int *)(&matrix[1][2]), 2, 3, 1, 7);
The options are, well, not limitless, but huge! For a last example, this works on the one-column, 7-row matrix composed of the diagonal elements in the matrix:
Code:
do_something((int *)matrix, 7, 1, 8, 0);
That is, colstride is zero because there is just one column, and rowstride is large enough to move one row down and one column right, i.e. number of columns in the array + 1.
The only downside of having both colstride and rowstride is a slight overhead in element access; instead of one multiplication, we do two. In most situations it is not really even measurable, and in the rest, the benefits/versatility outweigh that small cost.