2

I would like the user to define the size of the 2d array in the main, I currently have this struct:

#define SIZE 2
struct args
{
  int number;
  int array[SIZE][SIZE];
}

This works fine, however, I would like to remove the #define SIZE command, and let SIZE be a value that the user defines, so in the main function I want to get from keyboard as:

int SIZE;
printf("enter array size");
scanf("%d", &SIZE);

Can you help me?

bedirates
  • 67
  • 4

5 Answers5

4

You can allocate the array dynamically and, optionally, define a macro or access function to access the elements using a 2-dimensional index. E.g.:

struct args
{
  int number;
  int *array;
};
#define index(i,j) ((i)*SIZE + (j)) 
...

struct args A;
unsigned SIZE;
printf("enter array size");
scanf("%u", &SIZE);  // Should add error check here
A.array = malloc(SIZE*SIZE*sizeof(int));  // Allocate memory for the array
...
A.array[index(2,3)] = 4;
printf("Value at (2,3)=%d\n", A.array[index(2,3)]);
...
free(A.array);  // Free the dynamically allocated memory
nielsen
  • 5,641
  • 10
  • 27
1

Since you require a runtime sized 2D array in both dimensions, you will not be able to utilize 2D array syntax, e.g. a[row][col]. You will have to allocate a "flat" array SIZE*SIZE in size, then use a function call GetAt(Row,Col) and SetAt(Row, Col, Value) and do the pointer arithmetic (simply index = (row * SIZE) + col;) then use that 1 dimensional index in your flat array.

Remember to free the dynamically allocated array!

franji1
  • 3,088
  • 2
  • 23
  • 43
1

If you want to be able to use the 2D syntax for your array dynamically allocated, you should declare your array like this :

struct args
{
  int number;
  int **array;
};

Then you allocate the first dimension of your array :

struct args arr;
arr.array = malloc(SIZE * sizeof(int*));

then the second dimension

for (int i = 0; i < SIZE; i++) {
    arr.array[i] = malloc(SIZE * sizeof(int));
}

Here is a complete working example

struct args
{
  int number;
  int **array;
};


int main() {

  struct args arr;

  unsigned size;
  printf("enter array size");
  scanf("%u", &size);
  arr.array = (int**)malloc(size * sizeof(int*)); 

  for (int i = 0; i < size; i++) {
    arr.array[i] = (int*)malloc(size * sizeof(int));
  }

  arr.array[2][3] = 4;
  printf("Value at (2,3)=%d\n", arr.array[2][3]);

  return 0;
}
M. Yousfi
  • 578
  • 5
  • 24
1

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.

John Bode
  • 119,563
  • 19
  • 122
  • 198
0

If your compiler supports the variable length arrays (VLA), you may try the following option:

#include <stdio.h>

struct args
{
  int number;
  int *array;
};

void dumpStruct(struct args *toDump)
{
  for (int i = 0; i < toDump->number; ++i) {
    printf("%d\n", toDump->array[i]);
  }
}

int main(int argc, char *argv[])
{
  int SIZE;
  printf("Enter array size: ");
  scanf("%d", &SIZE);

  int varArray[SIZE][SIZE];
  struct args varArrayStruct = {
    .number = SIZE,
    .array = (int*) varArray
  };

  for (int i = 0; i < varArrayStruct.number; ++i) {
    varArrayStruct.array[i] = i + 1;
  }

  dumpStruct(&varArrayStruct);
}

Here your array is allocated on stack (so keep your eye on its size) and freed automatically upon your program exits. So you don't need to manage the heap.

The disadvantage is that you have to manually calculate the linear index of an item from the given column and row. It is impossible in C to declare an incomplete two-dimensional array so its elements could be referenced using the [i][j] notation.