HI all,
I want to create a 2D dynamic array and then initialize all elements
with 0.
Ignoring the initialization for a second (because you didn't specify the
element type), I think that the following enables the most flexible
usage, if your rows are all full:
#include <stdlib.h>
type *
get_type2d(size_t rows, size_t cols)
{
return (0u < cols && (size_t)-1 / sizeof(type) / cols >= rows)
? malloc(rows * cols * sizeof(type))
: 0;
}
You would index the allocated array like this:
{
type *arr;
arr = get_type2d(nrows, ncols);
if (0 != arr) {
/* Store ... in array[row][col], conceptually. */
arr[row * ncols + col] = ...;
free(arr);
}
}
This can be generalized for N dimensional arrays. The following function
takes "long unsigned" arguments instead of "size_t", because "size_t"
may be promoted by the default argument promotions, and that doesn't
play very well with va_arg().
#include <stdlib.h>
#include <stdarg.h>
/*
Usage:
get_ndim(el_size, dim_1, dim_2, ..., dim_n, 0LU);
Allocate memory for dim_1 * dim_2 * ... * dim_n elements, each being
of size "el_size". All arguments must be of type "long unsigned". The
variable argument list must be terminated with 0LU.
get_ndim(el_size, 0LU)
allocates a zero-dimensional array (= space for a single element).
The caller is responsible for not passing 0LU as el_size.
The function returns a pointer returned by malloc() (which can be 0),
or 0 if the wanted size (in bytes) cannot be represented in size_t.
*/
void *
get_ndim(long unsigned el_size, ...)
{
size_t max_el,
num_el;
va_list ap;
long unsigned next;
max_el = (size_t)-1 / el_size;
num_el = 1u;
va_start(ap, el_size);
while (0LU != (next = va_arg(ap, long unsigned))
&& max_el / next >= num_el) {
num_el *= next;
}
va_end(ap);
return 0LU == next ? malloc(num_el * el_size) : 0;
}
The access pattern implements the Horner scheme.
http://en.wikipedia.org/wiki/Horner_scheme
An example for the Horner scheme with auto arrays might be:
{
type arr[4][5][6]; /* 120 */
arr[1][2][3] = ...; /* [1 * (5*6) + 2 * (6) + 3], [45] */
}
-->
{
type arr[4 * (5 * (6))]; /* 120 */
arr[(1 * 5 + 2) * 6 + 3] = ...; /* [45] */
}
Thus
/*
If arr denotes the non-null return value of
get_ndim(el_size, dim_1, ... dim_n, 0LU)
that is, arr is conceptually
array[dim_1][dim_2] ... [dim_n]
then the conceptual element with valid subscripts
array[i_1][i_2] ... [i_n]
can be accessed with
arr[get_idx(i_1, dim_2, i_2, ..., dim_n, i_n, 0LU)]
For zero-dimensional arrays, the following is valid:
get_ndim(el_size, 0LU)[get_idx(0LU, 0LU)]
*/
size_t
get_idx(long unsigned outermost_idx, ...)
{
size_t sum;
va_list ap;
long unsigned next_size;
sum = outermost_idx;
va_start(ap, outermost_idx);
while (0LU != (next_size = va_arg(ap, long unsigned))) {
sum = sum * next_size + va_arg(ap, long unsigned);
}
return sum;
}
Usage:
int main(void)
{
double *mtr;
/* allocate 100 4x4 matrices */
mtr = get_ndim(sizeof *mtr, 100LU, 4LU, 4LU, 0LU);
if (0 != mtr) {
/* Set the 59th matrix to 0.0 -- starting at &matrix[58][0][0]. */
size_t base,
idx;
base = get_idx(58LU, 4LU, 0LU, 4LU, 0LU, 0LU);
for (idx = 0u; idx < 16u; ++idx) {
mtr[base + idx] = 0.0;
}
free(mtr);
}
return 0;
}
Cheers,
lacos