如何在C中创建用户定义大小的2D结构数组

46scxncf  于 2023-05-16  发布在  其他
关注(0)|答案(5)|浏览(82)

我想让用户定义的主要2D数组的大小,我目前有这个结构:

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

这很好,但是,我想删除#define SIZE命令,让SIZE成为用户定义的值,所以在main函数中,我想从键盘获取:

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

你能帮我吗?

qqrboqgw

qqrboqgw1#

您可以动态地分配数组,也可以定义一个宏或访问函数来使用二维索引访问元素。例如:

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
rxztt3cl

rxztt3cl2#

因为你需要一个运行时大小的二维数组,所以你不能使用二维数组语法,例如a[row][col]。你将不得不分配一个“平面”数组SIZE*SIZE的大小,然后使用函数调用GetAt(Row,Col)和SetAt(Row,Col,Value)并执行指针算术(简单地index = (row * SIZE) + col;),然后在平面数组中使用一维索引。
记住释放动态分配的阵列!

zphenhs4

zphenhs43#

如果你想使用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;
}
vwkv1x7d

vwkv1x7d4#

不幸的是,不能在struct定义中使用变长数组;你将不得不依赖于动态内存分配。
有几种方法可以做到这一点,但你使用哪一种取决于几件事。
数组是否需要是struct示例的一部分,与number相邻?

+---+
|   | number
+---+
|   | array[0][0]
+---+ 
|   | array[0][1]
+---+
 ...
+---+ 
|   | array[0][C-1]
+---+ 
|   | array[1][0]
+---+
|   | array[1][1]
+---+
 ...

如果您需要序列化struct示例的内容(写入二进制文件、通过网络发送等),就会出现这种情况。当然,您还需要某种方法来保存行数和列数。
AFAIK,唯一的方法是使用灵活的数组成员;这将是一个1D数组,如果你想使用2D数组下标,你可以将一个指针Map到一个c元素数组。快速和肮脏的例子:

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

下面是一个2 x 3数组的输出:

$ ./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

您可以看到数组内容与结构体示例中的number成员相邻。
如果你不想使用map指针,你可以把索引计算为

a->array[c * i + j] = ...;

我只是觉得使用2D下标更容易,更清晰。
数组是否可以存在于与struct示例不连续的内存中,但数组本身仍然需要是连续的?

+---+
|   | number
+---+                 +---+
|   | array  -------> |   | array[0][0]
+---+                 +---+
                      |   | array[0][1]
                      +---+
                       ...
                      +---+
                      |   | array[0][C-1]
                      +---+
                      |   | array[1][0]
                      +---+
                      |   | array[1][1]
                      +---+

在这种情况下,struct的定义为

struct args {
  int number;
  int *array;  // points to an array instance
};

我们仍然将array成员视为1D数组:

struct args s;
s.array = malloc( sizeof (int [c]) * r );

和上面的例子一样,我们可以创建一个指向c元素数组的指针,以允许我们使用2D数组下标:

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;

或者你也可以用

a.array[c * i + j] = c * i + j;

完整示例:

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

带输出:

$ ./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

正如你所看到的,数组的内容并不存储在struct示例本身中,相反,我们将数组的第一个元素的地址存储在array成员(0x600001d3c240)中。注意,在numberarray之间有四个字节的填充。
如果你不需要数组是连续的,你可以单独分配行,比如:

+---+
|   | number
+---+                +---+                  +---+    
|   | array  ------> |   | array[0] ------> |   | array[0][0]
+---+                +---+                  +---+
                     |   | array[1] ----+   |   | array[0][1]
                     +---+              |   +---+
                      ...               |    ...
                                        |   
                                        |   +---+
                                        +-> |   | array[1][0]
                                            +---+
                                            |   | array[1][1]
                                            +---+
                                             ...

在这种情况下,您的struct定义将是

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

你会使用两步分配方法:

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;

这种方法的优点是可以在a.array上使用常规的2D数组下标;你不需要把指向数组的指针Map到它上面。
完整示例:

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

带输出:

$ ./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

在这种情况下,a.array存储指针数组(0x600003979120)的第一个元素的地址,每个a.array[i]存储int的4元素数组的地址。
您使用哪种方法取决于您的需要。他们都有自己的长处和短处。

imzjd6km

imzjd6km5#

如果你的编译器支持可变长度数组(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] 表示法引用。

相关问题