如何在C中创建一个用户定义大小的结构体二维数组。

huangapple go评论63阅读模式
英文:

How to create a user defined sized 2d array of struct in C

问题

Sure, here's the translated code part:

我希望用户在主函数中定义2D数组的大小,我目前有这个结构体:

struct args
{
  int number;
  int array[SIZE][SIZE];
}

这个代码可以正常工作,但是我想删除#define SIZE命令,让用户定义SIZE的值,所以在主函数中,我希望从键盘获取大小,如下所示:

int SIZE;
printf("请输入数组大小:");
scanf("%d", &SIZE);

Is there anything else you need help with?

英文:

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?

答案1

得分: 4

你可以动态分配数组,并可选择定义一个宏或访问函数,以使用二维索引访问元素。例如:

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

struct args A;
unsigned SIZE;
printf("输入数组大小");
scanf("%u", &SIZE);  // 此处应添加错误检查
A.array = malloc(SIZE*SIZE*sizeof(int));  // 为数组分配内存
...
A.array[index(2,3)] = 4;
printf("在(2,3)处的值=%d\n", A.array[index(2,3)]);
...
free(A.array);  // 释放动态分配的内存
英文:

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

答案2

得分: 1

由于您需要在两个维度中使用运行时大小的二维数组,您将无法使用二维数组语法,例如 a[row][col]。您将需要分配一个大小为 SIZE*SIZE 的“平面”数组,然后使用函数调用 GetAt(Row,Col) 和 SetAt(Row, Col, Value),并进行指针算术(简单地 index = (row * SIZE) + col;),然后在您的平面数组中使用这个一维索引。

请记得释放动态分配的数组!

英文:

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!

答案3

得分: 1

如果您想能够动态分配数组并使用2D语法,您应该这样声明数组:

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

然后,您可以分配数组的第一维:

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

接着分配第二维:

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

以下是一个完整的工作示例:

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;
}
英文:

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 &lt; 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(&quot;enter array size&quot;);
  scanf(&quot;%u&quot;, &amp;size);
  arr.array = (int**)malloc(size * sizeof(int*)); 

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

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

  return 0;
}

答案4

得分: 1

你可以看到,在第一个例子中,数组的内容与结构体实例中的number成员是连续存储的。在第二个例子中,数组的内容不存储在结构体实例本身中,而是存储在分配的内存块中,结构体实例中的array成员存储了数组的首地址。在第三个例子中,数组的每一行都被单独分配了内存,结构体实例中的array成员存储了指向这些行的指针数组的首地址。

使用哪种方法取决于你的需求,每种方法都有其优点和缺点。

英文:

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.

<hr>

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 &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &quot;dumper.h&quot;

struct args {
  int number;
  int array[];
};

int main( int argc, char **argv )
{
  if ( argc &lt; 3 )
  {
    fprintf( stderr, &quot;USAGE: %s rows columns\n&quot;, 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&#39;m allocating
   * enough space for r instances of c-element arrays.
   */
  struct args *a = malloc( sizeof *a + sizeof (int [c]) * r );

  if ( a )
  {
    a-&gt;number = r;

    /**
     * a-&gt;array is a 1D array - however, we can map a 
     * pointer to a c-element array onto a-&gt;array,
     * allowing us to use regular 2D array subscripting
     * to access array elements.  
     */
    int (*map)[c] = (int (*)[c]) a-&gt;array;

    for ( size_t i = 0; i &lt; r; i++ )
      for ( size_t j = 0; j &lt; c; j++ )
        map[i][j] = c * i + j; // rather than a-&gt;array[c * i + j]

    /**
     * Dumper is a utility I wrote to display the addresses
     * and contents of multiple objects.
     */
    char *names[] = { &quot;a&quot;, &quot;a-&gt;array&quot;, &quot;map&quot; };
    void *addrs[] = { a, a-&gt;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( &quot;a-&gt;number = %d\n&quot;, a-&gt;number );
    for ( size_t i = 0; i &lt; r; i++ )
      for ( size_t j = 0; j &lt; c; j++ )
        printf( &quot;a-&gt;array[%zu][%zu] = %d\n&quot;, 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-&gt;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-&gt;number = 2
a-&gt;array[0][0] = 0
a-&gt;array[0][1] = 1
a-&gt;array[0][2] = 2
a-&gt;array[1][0] = 3
a-&gt;array[1][1] = 4
a-&gt;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-&gt;array[c * i + j] = ...;

I just think it's easier and more clear to use 2D subscripting.

<hr>

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  -------&gt; |   | 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 &lt; r; i++ )
      for ( size_t j = 0; j &lt; 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 &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &quot;dumper.h&quot;

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

int main( int argc, char **argv )
{
  if ( argc &lt; 3 )
  {
    fprintf( stderr, &quot;USAGE: %s rows columns\n&quot;, 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 &lt; r; i++ )
      for ( size_t j = 0; j &lt; c; j++ )
        map[i][j] = c * i + j;

    char *names[] = { &quot;a&quot;, &quot;a.array&quot;, &quot;map&quot; };
    void *addrs[] = { &amp;a, a.array, map };
    size_t sizes[]  = { sizeof a, sizeof *map * r, sizeof *map * r };

    dumper( names, addrs, sizes, 3, stdout );

    printf( &quot;a.number = %d\n&quot;, a.number );
    for ( size_t i = 0; i &lt; r; i++ )
      for ( size_t j = 0; j &lt; c; j++ )
        printf( &quot;a.array[%zu][%zu] = %d\n&quot;, 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.

<hr>

If you don't need the array to be contiguous you can allocate the rows individually, such as:

+---+
|   | number
+---+                +---+                  +---+    
|   | array  ------&gt; |   | array[0] ------&gt; |   | array[0][0]
+---+                +---+                  +---+
                     |   | array[1] ----+   |   | array[0][1]
                     +---+              |   +---+
                      ...               |    ...
                                        |   
                                        |   +---+
                                        +-&gt; |   | 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 &lt; r; i++ )
{
  a.array[i] = malloc( sizeof *a.array[i] * c );
}

for ( size_t i = 0; i &lt; r; i++ )
  for ( size_t j = 0; j &lt; 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 &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &quot;dumper.h&quot;

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

int main( int argc, char **argv )
{
  if ( argc &lt; 3 )
  {
    fprintf( stderr, &quot;USAGE: %s rows columns\n&quot;, 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 &lt; r; i++ )
      a.array[i] = malloc( sizeof *a.array[i] * c );

    for ( size_t i = 0; i &lt; r; i++ )
      for ( size_t j = 0; j &lt; c; j++ )
        a.array[i][j] = c * i + j;

    char *names[] = { &quot;a&quot;, &quot;a.array&quot;, &quot;a.array[0]&quot;, &quot;a.array[1]&quot; };
    void *addrs[] = { &amp;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( &quot;a.number = %d\n&quot;, a.number );
    for ( size_t i = 0; i &lt; r; i++ )
      for ( size_t j = 0; j &lt; c; j++ )
        printf( &quot;a.array[%zu][%zu] = %d\n&quot;, i, j, a.array[i][j] );

    for ( size_t i = 0; i &lt; 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.

<hr>

Which method you use depends on your needs. All have their strengths and weaknesses.

答案5

得分: 0

以下是您要翻译的内容:

如果您的编译器支持可变长度数组(VLA),您可以尝试以下选项:

#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);
}

在这里,您的数组分配在堆栈上(因此请注意其大小),并在程序退出时自动释放。因此,您无需管理堆。

缺点是您必须手动计算从给定列和行中的项的线性索引。在C中,不可能声明不完整的二维数组,以便可以使用*[i][j]*表示法引用其元素。

英文:

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

#include &lt;stdio.h&gt;

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

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

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

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

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

  dumpStruct(&amp;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.

huangapple
  • 本文由 发表于 2023年5月10日 20:34:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76218493.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定