C++ 2 Dimensional Array with Variable Size Rows

2D array with different size for each line in C

Arrays of arrays of different size is possible in C. Simply they are not 2D arrays but arrays or pointers. You will find more about the difference in that answer from C tag FAQ.

@vahero has shown how you could dynamically allocate that, but it is also possible with static or automatic storage:

char row0[] = "TT";      // size 3 because of the terminating null...
char row1[] = "TTTTT";
char row2[] = "TTT";
char row3[] = "TTTT";
char* array[] = { row0, row1, row2, row3};

Or without additional variable identifiers:

char *arr[] = { 
(char[]){ 'T', 'T', 0 },
(char[]){ 'T', 'T', 'T', 'T', 'T', 0 },
(char[]){ 'T', 'T', 'T', 0 },
(char[]){ 'T', 'T', 'T', 'T', 0 },
};

You can then use is as usual:

for (int i=0; i<sizeof(arr)/sizeof(arr[0]); i++) {
for(int j=0; arr[i][j] != '\0'; j++) {
printf("%c ", arr[i][j]);
}
printf("\n");
}

C 2 dimensional array with variable size rows

Using a struct and compound literals can be done on the stack only.

typedef struct
{
size_t size ;
int* a ;

} jag_array ;

jag_array m[] = { { 3 , ( int[] ){ 1,2,3 } } ,
6 , ( int[] ){ 1,2,3,4,5,6 } ,
4 ,( int[] ){ 1,2,3,4 } } ;

This has limitations. When you copy the struct the arrays themselves are not copied.

Separate functions and macros could help handling this, but it is not that pretty.

Need a two-dimensional array of variable size in a struct

You can't do it with double indexing (cell[x][y]) in C, there's no way to express that the number of bytes to jump for each row is dynamic.

So, the best (in my opinion) way to do is it to just do the indexing manually, using a one-dimensional array.

Put a plain:

Cell *cell;

in the struct (keeping width and height) and then index like so:

set_cell(Grid *g, unsigned int x, unsigned int y, Cell value)
{
g->cell[y * g->width + x] = value;
}

it's not unlikely that the compiler will inline this, and it's going to be pretty tight. Probably faster than the jagged array" approach which uses much more memory and another layer of indirection.

Allocation is simple:

Grid initGrid(unsigned int width, unsigned int height)
{
Grid g;
g.width = width;
g.height = height;
g.cell = malloc(width * height * sizeof *g.cell);
// add assert or error here, can't return NULL for value type
return g;
}

if you wanted to heap-allocate Grid too, you could co-allocate it with its elements.

And yes, you need to free() the allocation when you're done with it, in order to not leak memory. Strictly speaking on modern systems the OS will free all resources when the program ends anyway, but it's good form to free anyway:

void destroyGrid(Grid g)
{
free(g.cell);
}

Passing a multidimensional array of variable size

The easiest way is (for C99 and later)

void printArry(int a, int b, int arr[a][b]){
/* what goes here? */
}

But, there are other ways around

void printArry(int a, int b, int arr[][b]){
/* what goes here? */
}

or

void printArry(int a, int b, int (*arr)[b]){
/* what goes here? */
}

Compiler will adjust the first two to the third syntax. So, semantically all three are identical.

And a little bit confusing which will work only as function prototype:

void printArry(int a, int b, int arr[*][*]);

What is the best way to make 2 dimensional array in C

This

int **arr = (int **)malloc(sizeof(int *) * 3);

is not a declaration or allocation of a two-dimensional array

Here a one-dimensional array with the element type int * is created. And then each element of the one-dimensional array in turn points to an allocated one dimensional array with the element type int.

This declaration of a two-dimensional array

    const int row = 3;
const int col = 4;

int arr[row][col] = {
{1,2,3,4},
{3,4,5,6},
{5,6,7,8}
};

is incorrect. Variable length arrays (and you declared a variable length array) may not be initialized in declaration.

You could write instead

    enum { row = 3, col = 4 };

int arr[row][col] = {
{1,2,3,4},
{3,4,5,6},
{5,6,7,8}
};

When such an array is passed to a function it is implicitly converted to pointer to its first element of the type int ( * )[col].

You could pass it to a function that has a parameter of the type of a variable length array the following way

void    my_func( size_t row, size_t col, int arr[row][col] )
{
printf("test2: %d", arr[0][1]);
}

Or if to place the definition of the enumeration before the function declaration

    enum { row = 3, col = 4 };

then the function could be also declared like

void    my_func( int arr[][col], size_t row )
{
printf("test2: %d", arr[0][1]);
}

Here is a demonstrative program that shows three different approaches. The first one when an array is defined with compile-time constants for array sizes. The second one when a variable length array is created. And the third one when a one-dimensional array of pointer to one-dimensional arrays are allocated dynamically.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum { row = 3, col = 4 };

void output1( int a[][col], size_t row )
{
for ( size_t i = 0; i < row; i++ )
{
for ( size_t j = 0; j < col; j++ )
{
printf( "%d ", a[i][j] );
}
putchar( '\n' );
}
}

void output2( size_t row, size_t col, int a[row][col] )
{
for ( size_t i = 0; i < row; i++ )
{
for ( size_t j = 0; j < col; j++ )
{
printf( "%d ", a[i][j] );
}
putchar( '\n' );
}
}

void output3( int **a, size_t row, size_t col )
{
for ( size_t i = 0; i < row; i++ )
{
for ( size_t j = 0; j < col; j++ )
{
printf( "%d ", a[i][j] );
}
putchar( '\n' );
}
}

int main(void)
{
int arr1[row][col] =
{
{1,2,3,4},
{3,4,5,6},
{5,6,7,8}
};

output1( arr1, row );
putchar( '\n' );

const size_t row = 3, col = 4;

int arr2[row][col];

memcpy( arr2, arr1, row * col * sizeof( int ) );

output2( row, col, arr2 );
putchar( '\n' );

int **arr3 = malloc( row * sizeof( int * ) );

for ( size_t i = 0; i < row; i++ )
{
arr3[i] = malloc( col * sizeof( int ) );
memcpy( arr3[i], arr1[i], col * sizeof( int ) );
}

output3( arr3, row, col );
putchar( '\n' );

for ( size_t i = 0; i < row; i++ )
{
free( arr3[i] );
}

free( arr3 );
}

The program output is

1 2 3 4 
3 4 5 6
5 6 7 8

1 2 3 4
3 4 5 6
5 6 7 8

1 2 3 4
3 4 5 6
5 6 7 8

Pay attention to that the function output2 can be used with the array arr1 the same way as it is used with the array arr2.



Related Topics



Leave a reply



Submit