To work with dynamically allocated 2d arrays, correct pointer types should be used. (int **) is a pointer to a pointer, pointing to the first element of an array of pointers which themselves point to disparate allocations. The result of this sort of code is a jagged array, but not a 2d array. The allocated memory is not guaranteed to be contiguous (as array allocations must be):
size_t num_rows = 3;
size_t num_cols = 5;
int **jagged_arr = malloc(sizeof *jagged_arr * num_rows);
for (size_t i = 0; i < num_rows; i++) {
jagged_arr[i] = malloc(sizeof *jagged_arr[i] * num_cols);
}
One possibility is to simply allocate storage for a 1d array, and calculate offsets into this array from 2d array indices. This works fine, but the result is not a 2d array:
size_t num_elems = num_rows * num_cols;
int *simulated_2d_arr = malloc(sizeof *simulated_2d_arr * num_elems);
This can't be indexed as a 2d array, but the 1d index can be calculated from the number of columns and the 2d array indices:
for (size_t i = 0; i < num_rows; i++) {
for (size_t j = 0; j < num_cols; j++) {
simulated_2d_arr[i * num_cols + j] = i * num_cols + j;
}
}
Both of these approaches have their uses, but they suffer from a disadvantage in that the resulting arrays can't be passed to functions which are meant to work with 2d arrays. That is, consider a function to print a 2d array, such as:
void print_2d_arr(size_t rows, size_t cols, int arr[][cols])
{
for (size_t i = 0; i < rows; i++) {
for (size_t j = 0; j < cols; j++) {
printf("%5d", arr[i][j]);
}
putchar('\n');
}
}
This function will work for something like:
int real_2d_arr[2][3] = { { 1, 2, 3 },
{ 4, 5, 6 } };
But it will not work for the earlier jagged_arr:
expected ‘int (*)[(sizetype)(cols)]’ but argument is of type ‘int **’
or for simulated_2d_arr:
expected ‘int (*)[(sizetype)(cols)]’ but argument is of type ‘int *’
The correct type to use when dynamically allocating 2d arrays is seen in the above error messages. For a 2d array of ints, that would be int (*)[]. This is the type that a 2d array decays to in most expressions, including function calls. So, to dynamically allocate a 2d array of ints, this would work:
size_t num_rows = 3;
size_t num_cols = 5;
int (*array_2d)[num_cols] = malloc(sizeof *array_2d * num_rows);
This allocates space for num_rows arrays of num_cols ints. Note that this does not create a VLA, but the VLA type is used. Of course, VLAs were introduced back in C99, but were made optional in C11 (though still widely supported).
As for the dynamic type part of your question, one option would be to create an enum to hold type identifiers, and pass one of these enumeration constants to whatever functions need them. These functions will need to accept (void *) arguments, which will be appropriately converted based on the type enumeration constant. This is a little more involved, but here is an example program. Note that the print_array() function works for both the dynamically allocated arrays, and for a statically sized array. Also note that there is no need for triple, or even double indirection!
#include <stdio.h>
#include <stdlib.h>
enum Type { CHAR,
INT,
FLOAT,
DOUBLE };
void * get_array(enum Type type, size_t rows, size_t cols);
void init_array(enum Type type, size_t rows, size_t cols, void *arr);
void print_array(enum Type type, size_t rows, size_t cols, void *arr);
int main(void)
{
char (*arr_char)[5] = get_array(CHAR, 4, 5);
int (*arr_int)[5] = get_array(INT, 4, 5);
double (*arr_double)[5] = get_array(DOUBLE, 4, 5);
int arr_static[][3] = { { 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 } };
if (arr_char) { // check for null pointer
init_array(CHAR, 4, 5, arr_char);
puts("4x5 array of char");
print_array(CHAR, 4, 5, arr_char);
putchar('\n');
}
if (arr_int) { // check for null pointer
init_array(INT, 4, 5, arr_int);
puts("4x5 array of int");
print_array(INT, 4, 5, arr_int);
putchar('\n');
}
if (arr_double) { // check for null pointer
init_array(DOUBLE, 4, 5, arr_double);
puts("4x5 array of double");
print_array(DOUBLE, 4, 5, arr_double);
putchar('\n');
}
puts("Statically sized 3x3 array of int");
print_array(INT, 3, 3, arr_static);
putchar('\n');
/* Cleanup */
free(arr_char);
free(arr_int);
free(arr_double);
return 0;
}
/* Returns null pointer on allocation failure */
void *get_array(enum Type type, size_t rows, size_t cols)
{
size_t array_sz = 0;
void *ret = NULL;
switch (type) {
case CHAR:
array_sz = sizeof (char) * rows * cols;
break;
case INT:
array_sz = sizeof (int) * rows * cols;
break;
case FLOAT:
array_sz = sizeof (float) * rows * cols;
break;
case DOUBLE:
array_sz = sizeof (double) * rows * cols;
break;
default:
fprintf(stderr, "Unrecognized type in get_array()");
}
if (array_sz) {
ret = malloc(array_sz);
}
return ret;
}
void init_array(enum Type type, size_t rows, size_t cols, void *arr)
{
for (size_t i = 0; i < rows; i++) {
for (size_t j = 0; j < cols; j++) {
int offset = i * cols + j;
switch (type) {
case CHAR:
{
char (*array_char)[cols] = arr;
array_char[i][j] = 'a' + offset;
break;
}
case INT:
{
int (*array_int)[cols] = arr;
array_int[i][j] = 0 + offset;
break;
}
case FLOAT:
{
float (*array_float)[cols] = arr;
array_float[i][j] = 0.0 + offset;
break;
}
case DOUBLE:
{
double (*array_double)[cols] = arr;
array_double[i][j] = 0.0 + offset;
break;
}
default:
fprintf(stderr, "Unrecognized type in get_array()");
}
}
}
}
void print_array(enum Type type, size_t rows, size_t cols, void *arr)
{
for (size_t i = 0; i < rows; i++) {
for (size_t j = 0; j < cols; j++) {
switch (type) {
case CHAR:
{
char (*array_char)[cols] = arr;
printf("%3c", array_char[i][j]);
break;
}
case INT:
{
int (*array_int)[cols] = arr;
printf("%5d", array_int[i][j]);
break;
}
case FLOAT:
{
float (*array_float)[cols] = arr;
printf("%8.2f", array_float[i][j]);
break;
}
case DOUBLE:
{
double (*array_double)[cols] = arr;
printf("%8.2f", array_double[i][j]);
break;
}
default:
fprintf(stderr, "Unrecognized type in get_array()");
}
}
putchar('\n');
}
}
Program output:
4x5 array of char
a b c d e
f g h i j
k l m n o
p q r s t
4x5 array of int
0 1 2 3 4
5 6 7 8 9
10 11 12 13 14
15 16 17 18 19
4x5 array of double
0.00 1.00 2.00 3.00 4.00
5.00 6.00 7.00 8.00 9.00
10.00 11.00 12.00 13.00 14.00
15.00 16.00 17.00 18.00 19.00
Statically sized 3x3 array of int
1 2 3
4 5 6
7 8 9
*(int*)? - hassan arafat