Unfortunately, you cannot use variable-length arrays in a struct
definition; you're going to have to rely on dynamic memory allocation.
There are several ways to do this, but which you use depends on a few things.
Does the array need to be part of the struct
instance, contiguous with number
?
+---+
| | number
+---+
| | array[0][0]
+---+
| | array[0][1]
+---+
...
+---+
| | array[0][C-1]
+---+
| | array[1][0]
+---+
| | array[1][1]
+---+
...
This would be the case if you ever needed to serialize the contents of the struct
instance (writing to a binary file, sending over a network, etc.). Of course, you'd need some way of saving the number of rows and columns as well.
AFAIK, the only way to do this is with a flexible array member; this will have to be a 1D array, onto which you can map a pointer to a c
-element array if you want to use 2D array subscripting. Quick and dirty example:
#include <stdio.h>
#include <stdlib.h>
#include "dumper.h"
struct args {
int number;
int array[];
};
int main( int argc, char **argv )
{
if ( argc < 3 )
{
fprintf( stderr, "USAGE: %s rows columns\n", argv[0] );
return EXIT_FAILURE;
}
size_t r = strtoul( argv[1], NULL, 0 );
size_t c = strtoul( argv[2], NULL, 0 );
/**
* Allocate enough space for the struct instance plus
* an r x c array of int. sizeof (int) * c * r would
* also work, this just makes it clear that I'm allocating
* enough space for r instances of c-element arrays.
*/
struct args *a = malloc( sizeof *a + sizeof (int [c]) * r );
if ( a )
{
a->number = r;
/**
* a->array is a 1D array - however, we can map a
* pointer to a c-element array onto a->array,
* allowing us to use regular 2D array subscripting
* to access array elements.
*/
int (*map)[c] = (int (*)[c]) a->array;
for ( size_t i = 0; i < r; i++ )
for ( size_t j = 0; j < c; j++ )
map[i][j] = c * i + j; // rather than a->array[c * i + j]
/**
* Dumper is a utility I wrote to display the addresses
* and contents of multiple objects.
*/
char *names[] = { "a", "a->array", "map" };
void *addrs[] = { a, a->array, map };
size_t sizes[] = { sizeof *a + sizeof (int [c]) * r,
sizeof *map * r, sizeof *map * r };
dumper( names, addrs, sizes, 3, stdout );
/**
* Display the value of the various struct members
*/
printf( "a->number = %d\n", a->number );
for ( size_t i = 0; i < r; i++ )
for ( size_t j = 0; j < c; j++ )
printf( "a->array[%zu][%zu] = %d\n", i, j, map[i][j] );
free( a );
}
return 0;
}
Here's some output with a 2 x 3 array:
$ ./flex 2 3
Item Address 00 01 02 03
---- ------- -- -- -- --
a 0x60000269d120 02 00 00 00 ....
0x60000269d124 00 00 00 00 ....
0x60000269d128 01 00 00 00 ....
0x60000269d12c 02 00 00 00 ....
0x60000269d130 03 00 00 00 ....
0x60000269d134 04 00 00 00 ....
0x60000269d138 05 00 00 00 ....
a->array 0x60000269d124 00 00 00 00 ....
0x60000269d128 01 00 00 00 ....
0x60000269d12c 02 00 00 00 ....
0x60000269d130 03 00 00 00 ....
0x60000269d134 04 00 00 00 ....
0x60000269d138 05 00 00 00 ....
map 0x60000269d124 00 00 00 00 ....
0x60000269d128 01 00 00 00 ....
0x60000269d12c 02 00 00 00 ....
0x60000269d130 03 00 00 00 ....
0x60000269d134 04 00 00 00 ....
0x60000269d138 05 00 00 00 ....
a->number = 2
a->array[0][0] = 0
a->array[0][1] = 1
a->array[0][2] = 2
a->array[1][0] = 3
a->array[1][1] = 4
a->array[1][2] = 5
You can see the array contents are contiguous with the number
member in the struct instance.
You don't have to use the map
pointer if you don't want to - you can compute the index as
a->array[c * i + j] = ...;
I just think it's easier and more clear to use 2D subscripting.
Can the array exist in memory that's not contiguous with the struct
instance, but the array itself still needs to be contiguous?
+---+
| | number
+---+ +---+
| | array -------> | | array[0][0]
+---+ +---+
| | array[0][1]
+---+
...
+---+
| | array[0][C-1]
+---+
| | array[1][0]
+---+
| | array[1][1]
+---+
In this case, the definition of the struct
would be
struct args {
int number;
int *array; // points to an array instance
};
We still treat the array
member as a 1D array:
struct args s;
s.array = malloc( sizeof (int [c]) * r );
And like the example above, we can create a pointer to a c
-element array to allow us to use 2D array subscripting:
int (*map)[c] = ( int (*)[c] ) s.array;
for ( size_t i = 0; i < r; i++ )
for ( size_t j = 0; j < c; j++ )
map[i][j] = c * i + j;
or again you can just use
a.array[c * i + j] = c * i + j;
Complete example:
#include <stdio.h>
#include <stdlib.h>
#include "dumper.h"
struct args {
int number;
int *array;
};
int main( int argc, char **argv )
{
if ( argc < 3 )
{
fprintf( stderr, "USAGE: %s rows columns\n", argv[0] );
return EXIT_FAILURE;
}
size_t r = strtoul( argv[1], NULL, 0 );
size_t c = strtoul( argv[2], NULL, 0 );
/**
* Create an automatic struct instance
*/
struct args a;
a.number = r;
/**
* Allocate space for array; we want enough space
* to hold r instances of c-element arrays.
*/
a.array = malloc( sizeof (int [c]) * r );
if ( a.array )
{
int (*map)[c] = (int (*)[c]) a.array;
for ( size_t i = 0; i < r; i++ )
for ( size_t j = 0; j < c; j++ )
map[i][j] = c * i + j;
char *names[] = { "a", "a.array", "map" };
void *addrs[] = { &a, a.array, map };
size_t sizes[] = { sizeof a, sizeof *map * r, sizeof *map * r };
dumper( names, addrs, sizes, 3, stdout );
printf( "a.number = %d\n", a.number );
for ( size_t i = 0; i < r; i++ )
for ( size_t j = 0; j < c; j++ )
printf( "a.array[%zu][%zu] = %d\n", i, j, map[i][j] );
free( a.array );
}
return 0;
}
with output:
$ ./flex2 3 4
Item Address 00 01 02 03
---- ------- -- -- -- --
a 0x16b2e37c0 03 00 00 00 ....
0x16b2e37c4 01 00 00 00 ....
0x16b2e37c8 40 c2 d3 01 @...
0x16b2e37cc 00 60 00 00 .`..
a.array 0x600001d3c240 00 00 00 00 ....
0x600001d3c244 01 00 00 00 ....
0x600001d3c248 02 00 00 00 ....
0x600001d3c24c 03 00 00 00 ....
0x600001d3c250 04 00 00 00 ....
0x600001d3c254 05 00 00 00 ....
0x600001d3c258 06 00 00 00 ....
0x600001d3c25c 07 00 00 00 ....
0x600001d3c260 08 00 00 00 ....
0x600001d3c264 09 00 00 00 ....
0x600001d3c268 0a 00 00 00 ....
0x600001d3c26c 0b 00 00 00 ....
map 0x600001d3c240 00 00 00 00 ....
0x600001d3c244 01 00 00 00 ....
0x600001d3c248 02 00 00 00 ....
0x600001d3c24c 03 00 00 00 ....
0x600001d3c250 04 00 00 00 ....
0x600001d3c254 05 00 00 00 ....
0x600001d3c258 06 00 00 00 ....
0x600001d3c25c 07 00 00 00 ....
0x600001d3c260 08 00 00 00 ....
0x600001d3c264 09 00 00 00 ....
0x600001d3c268 0a 00 00 00 ....
0x600001d3c26c 0b 00 00 00 ....
a.number = 3
a.array[0][0] = 0
a.array[0][1] = 1
a.array[0][2] = 2
a.array[0][3] = 3
a.array[1][0] = 4
a.array[1][1] = 5
a.array[1][2] = 6
a.array[1][3] = 7
a.array[2][0] = 8
a.array[2][1] = 9
a.array[2][2] = 10
a.array[2][3] = 11
As you can see, the contents of the array are not stored in the struct
instance itself - instead, we store the address of the first element of the array in the array
member (0x600001d3c240
). Note that there are four bytes of padding between number
and array
.
If you don't need the array to be contiguous you can allocate the rows individually, such as:
+---+
| | number
+---+ +---+ +---+
| | array ------> | | array[0] ------> | | array[0][0]
+---+ +---+ +---+
| | array[1] ----+ | | array[0][1]
+---+ | +---+
... | ...
|
| +---+
+-> | | array[1][0]
+---+
| | array[1][1]
+---+
...
In that case, your struct
definition would be
struct args {
int number;
int **array;
};
and you'd use a two-step allocation method:
struct args a;
/**
* Allocate enough memory to store r pointers to int
*/
a.array = malloc( sizeof *a.array * r );
/**
* For each a.array[i], allocate space for c ints
*/
for ( size_t i = 0; i < r; i++ )
{
a.array[i] = malloc( sizeof *a.array[i] * c );
}
for ( size_t i = 0; i < r; i++ )
for ( size_t j = 0; j < c; j++ )
a.array[i][j] = c * i + j;
The advantage of this method is you can use regular 2D array subscripting on a.array
; you don't need to map a pointer to an array onto it.
Complete example:
#include <stdio.h>
#include <stdlib.h>
#include "dumper.h"
struct args {
int number;
int **array;
};
int main( int argc, char **argv )
{
if ( argc < 3 )
{
fprintf( stderr, "USAGE: %s rows columns\n", argv[0] );
return EXIT_FAILURE;
}
size_t r = strtoul( argv[1], NULL, 0 );
size_t c = strtoul( argv[2], NULL, 0 );
/**
* Create an automatic struct instance
*/
struct args a;
a.number = r;
/**
* Allocate space for r pointers to int
*/
a.array = malloc( sizeof *a.array * r );
if ( a.array )
{
/**
* For each a.array[i], allocate space for
* c ints
*/
for ( size_t i = 0; i < r; i++ )
a.array[i] = malloc( sizeof *a.array[i] * c );
for ( size_t i = 0; i < r; i++ )
for ( size_t j = 0; j < c; j++ )
a.array[i][j] = c * i + j;
char *names[] = { "a", "a.array", "a.array[0]", "a.array[1]" };
void *addrs[] = { &a, a.array, a.array[0], a.array[1] };
size_t sizes[] = { sizeof a, sizeof a.array[0] * r, sizeof a.array[0][0] * c, sizeof a.array[0][0] * c };
dumper( names, addrs, sizes, 4, stdout );
printf( "a.number = %d\n", a.number );
for ( size_t i = 0; i < r; i++ )
for ( size_t j = 0; j < c; j++ )
printf( "a.array[%zu][%zu] = %d\n", i, j, a.array[i][j] );
for ( size_t i = 0; i < r; i++ )
free( a.array[i] );
free( a.array );
}
return 0;
}
with output:
$ ./flex3 3 4
Item Address 00 01 02 03
---- ------- -- -- -- --
a 0x16fc177a0 03 00 00 00 ....
0x16fc177a4 00 00 00 00 ....
0x16fc177a8 20 91 97 03 ....
0x16fc177ac 00 60 00 00 .`..
a.array 0x600003979120 20 c0 b7 03 ....
0x600003979124 00 60 00 00 .`..
0x600003979128 30 c0 b7 03 0...
0x60000397912c 00 60 00 00 .`..
0x600003979130 40 c0 b7 03 @...
0x600003979134 00 60 00 00 .`..
a.array[0] 0x600003b7c020 00 00 00 00 ....
0x600003b7c024 01 00 00 00 ....
0x600003b7c028 02 00 00 00 ....
0x600003b7c02c 03 00 00 00 ....
a.array[1] 0x600003b7c030 04 00 00 00 ....
0x600003b7c034 05 00 00 00 ....
0x600003b7c038 06 00 00 00 ....
0x600003b7c03c 07 00 00 00 ....
a.number = 3
a.array[0][0] = 0
a.array[0][1] = 1
a.array[0][2] = 2
a.array[0][3] = 3
a.array[1][0] = 4
a.array[1][1] = 5
a.array[1][2] = 6
a.array[1][3] = 7
a.array[2][0] = 8
a.array[2][1] = 9
a.array[2][2] = 10
a.array[2][3] = 11
In this case, a.array
stores the address of the first element of the array of pointers (0x600003979120
), and each a.array[i]
stores the address of a 4-element array of int
.
Which method you use depends on your needs. All have their strengths and weaknesses.