IMPORTANT NOTE UP FRONT
If this is meant to be compiled as C code, make sure your compiler is actually compiling it as C, not C++, otherwise the code I've written below won't work.
If this is meant to be compiled as C++, use new
instead of malloc
(or better yet, use vectors)
If this is meant to be compiled as either C or C++, then create an abstraction layer and use malloc
for the C implementation and new
for the C++ implementation.
Now that we've gotten that out of the way...
Let's start from the other end. Suppose you declare a 3x3 matrix as
double m[3][3];
Remember that unless it is the operand of the sizeof
or unary &
operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T
" will be converted ("decay") to an expression of type "pointer to T
", and the value of the expression will be the address of the first element of the array.
So, the type of the expression m[i][j]
is double
(duh). The type of the expression m[i]
is "3-element array of double
", or double [3]
; unless it is the operand of the sizeof
or unary &
operators, it will decay to an expression of type "pointer to double
", or double *
.
Now for the tricky bit: the type of the expression m
is "3-element array of 3-element array of double
", or double [3][3]
; unless it is the operand of the sizeof
or unary &
operators, it will decay to an expression of type "pointer to 3-element array of double
", or double (*)[3]
, not double **
. So if you want to allocate a 3x3 matrix dynamically, you want the type of m
to be double (*)[3]
, not double **
:
double (*m)[3] = malloc( 3 * sizeof *m );
The type of the expression *m
is "3-element array of double
", so sizeof *m == sizeof (double [3]) == 3 * sizeof (double)
.
When you allocated memory as
double **m = malloc( 3 * 3 * sizeof (double));
you allocated a single-dimensioned array of pointers to double
, but you set aside the wrong amount of memory, and each element of the array contained an indeterminate pointer value, which is why writing to m[0][0]
threw an exception. You would have to allocate such an array in 2 stages:
double **m = malloc( 3 * sizeof *m ); // allocates space for 3 pointers to double
for ( int i = 0; i < 3; i++ )
m[i] = malloc( 3 * sizeof *m[i]); // allocates space for 3 doubles
If you need the memory to be contiguous, use the first method. If the memory doesn't have to be contiguous, you can use the second method.
Generalizing this to NxM matrices:
If you know the number of rows and columns at compile time and the memory must be contiguous, use the following:
T (*m)[COLS] = malloc( ROWS * sizeof *m );
If you don't know the number of rows and columns at compile time and you are using a C99 compiler or a C 2011 compiler that supports variable-length arrays, use the following:
size_t rows, cols;
rows = get_rows();
cols = get_cols();
T (*m)[cols] = malloc( rows * sizeof *m );
If you don't know the number of rows and columns at compile time and variable-length arrays aren't available, or the matrix is so large that you cannot allocate it as a single, contiguous block, use the following:
T **m = malloc( rows * sizeof *m );
if ( m )
{
for ( size_t i = 0; i < rows; i++ )
{
m[i] = malloc( cols * sizeof *m[i] );
}
}