在C中初始化可变大小数组

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

Initializing variable size arrays in C

问题

我尝试定义一个带有可变大小数组成员的结构体类型,如下所示:

typedef struct {
  const int M;
  const int N;
  int       x[];    // 应该是 1-D M 个元素
  int       y[][];  // 2-D M*N 个元素
} xy_t;

可变大小数组的原因是我有一个应该适用于可变维度的函数。
然而,这会导致错误,所以我改写成了:

typedef struct {
  const int M;
  const int N;
  int       *x;    // 1-D M 个元素
  int       **y;   // 2-D M*N 个元素
} xy_t;

这样编译正常。但问题是如何初始化它?

static xy_t xy = {.M = 3, .N = 2, .x = ???, .y = ???};

.x = (int[3]){0} 似乎可行,但我还没有找到一种分配 y 的方法。

我尝试过 .y = (int[3][2]){{0,0}, {0,0}, {0,0}} 和几种类似的变种,但都没有成功。


<details>
<summary>英文:</summary>

I try to define a struct type with array members of variable size like this:

typedef struct {
const int M;
const int N;
int x[]; // Should be 1-D M elements
int y[][]; // 2-D M*N elements
} xy_t;

The reason for variable sized arrays is that I have a function that should work on variable dimensions.  
However that gives an error, so I rewrote to:

typedef struct {
const int M;
const int N;
int *x; // 1-D M elements
int *y; // 2-D M elements
} xy_t;

which compile fine. However, the problem is how to I initialize this?

static xy_t xy = {.M = 3, .N = 2, .x = ???, .y = ???};


`.x = (int[3]){0}` seems to work, but I haven&#39;t found a way to assign y.

I tried `.y = (int[3][2]){{0,0}, {0,0}, {0,0}}` and several similar variant without success.

</details>


# 答案1
**得分**: 2

你可以将成员`y`定义为不完整数组类型的指针。

```c
typedef struct {
  ...
  int (*y)[]; // 指向长度未指定的数组的指针
} xy_t;

这允许你使用复合文字来初始化y

xy_t xy;
xy.y = (int[3][2]){{0,0}, {0,0}, {0,0}};

然而,无法对这个二维数组进行解引用,因为xy.y的类型是不完整的。可以通过将这个指针分配给具有完整类型的VLA指针来解决这个问题。

int (*arr)[xy.N] = xy.y;

arr[i][j] = 42;
英文:

You can make member y a pointer to incomplete array type.

typedef struct {
  ...
  int       (*y)[]; // a pointer an array of unspecified length
} xy_t;

This would let initialize y with a compound literal.

xy_t xy;
xy.y = (int[3][2]){{0,0}, {0,0}, {0,0}};

However it will not be possible to dereference this 2D array because the type of xy.y is incomplete. This can be solved by assigning this pointer to a pointer to VLA with completed type.

int (*arr)[xy.N] = xy.y;

arr[i][j] = 42;

答案2

得分: 1

int** 不再是一个二维数组;它是一个指向指针的指针,更具体地说,在我们的情况下,它是一个指向指针数组的指针。

这就是为什么你在尝试将指针分配给一个二维数组((int[3][2]){{0,0}, {0,0}, {0,0}})时出现问题的原因。

要初始化 int**,你需要像下面这样做:

xy_t xy = {
   .M = 3,
   .N = 2,
   .x = (int[3]){ 0,0,0 },
   .y = (int*[3]){
      (int[2]){ 0,0 },
      (int[2]){ 0,0 },
      (int[2]){ 0,0 },
   },
};

你也可以让编译器来计算:

xy_t xy = {
   .M = 3,
   .N = 2,
   .x = (int[]){ 0,0,0 },
   .y = (int*[]){
      (int[]){ 0,0 },
      (int[]){ 0,0 },
      (int[]){ 0,0 },
   },
};

尽管不是一个二维数组,你仍然可以通过 xy.y[ i ][ j ] 来访问 y[i][j]。


上面的结构需要 M+2 个指针和 M+3 个内存块。如果你想使结构更小,你可以使用以下方式,它只使用了 2 个指针和 3 个内存块:

typedef struct {
  const int M;
  const int N;
  int       *x;   // 应该是 1-D M 元素
  int       *y;   // 2-D M*N 元素
} xy_t;

xy_t xy = {
   .M = 3,
   .N = 2,
   .x = (int[]){ 0,0,0 },
   .y = (int[]){ 0,0, 0,0, 0,0 },
};

你可以通过 xy.y[ i * xy.N + j ] 来访问 y[i][j]。

你还可以将它强制转换为指向二维数组的指针,以便使用 y[i][j]。这样做如下:

int (*y)[ xy.M ][ xy.N ] = (int(*)[ xy.M ][ xy.N ])xy.y;

你甚至可以使它更小!以下方式不使用任何指针,只使用一个内存块:

typedef struct {
  const int M;
  const int N;
  int xy[];
} xy_t;

xy_t xy = {
   .M = 3,
   .N = 2,
   .xy = {
       0,0,0,
       0,0, 0,0, 0,0
   },
};

你可以通过 xy.xy[ i ] 来访问 x[i]。
你可以通过 xy.xy[ xy.M + i * xy.N + j ] 来访问 y[i][j]。

或者:

int (*x)[ xy.M ]         = (int(*)[ xy.M ])xy.xy;
int (*y)[ xy.M ][ xy.N ] = (int(*)[ xy.M ][ xy.N ])( xy.xy + xy.M );

x[i]
y[i][j]

不过,GCC 会发出警告:

警告:对可变大小数组成员的初始化 [-Wpedantic]

因此,最后一种解决方案可能不是标准的。

英文:

int** is no longer a 2d array; it's a pointer to a pointer, or more specifically a pointer to an array of pointers in our case.

That's why you are having problems assigning a pointer to a 2d array ((int[3][2]){{0,0}, {0,0}, {0,0}}) to it.

To initialize int**, you would need something like

xy_t xy = {
   .M = 3,
   .N = 2,
   .x = (int[3]){ 0,0,0 },
   .y = (int*[3]){
      (int[2]){ 0,0 },
      (int[2]){ 0,0 },
      (int[2]){ 0,0 },
   },
};

You can also let the compiler do the counting:

xy_t xy = {
   .M = 3,
   .N = 2,
   .x = (int[]){ 0,0,0 },
   .y = (int*[]){
      (int[]){ 0,0 },
      (int[]){ 0,0 },
      (int[]){ 0,0 },
   },
};

Despite not being a 2d array, you can still access y[i][j] using
xy.y[ i ][ j ].


The above structure needs M+2 pointers and M+3 memory blocks. If you wanted to make the structure smaller, you could use the following which only uses 2 pointers and 3 memory blocks:

typedef struct {
  const int M;
  const int N;
  int       *x;   // Should be 1-D M elements
  int       *y;   // 2-D M*N elements
} xy_t;

xy_t xy = {
   .M = 3,
   .N = 2,
   .x = (int[]){ 0,0,0 },
   .y = (int[]){ 0,0, 0,0, 0,0 },
};

You would access y[i][j] using xy.y[ i * xy.N + j ].

You could also cast it to a pointer to a 2d array in order to use y[i][j]. This is done as follows:

int (*y)[ xy.M ][ xy.N ] = (int(*)[ xy.M ][ xy.N ])xy.y;

You can make it even smaller! The following uses zero pointers and just one memory block.

typedef struct {
  const int M;
  const int N;
  int xy[];
} xy_t;

xy_t xy = {
   .M = 3,
   .N = 2,
   .xy = {
       0,0,0,
       0,0, 0,0, 0,0
   },
};

You would access x[i] using xy.xy[ i ].<br>
You would access y[i][j] using xy.xy[ xy.M + i * xy.N + j ].

Or:

int (*x)[ xy.M ]         = (int(*)[ xy.M ])xy.xy;
int (*y)[ xy.M ][ xy.N ] = (int(*)[ xy.M ][ xy.N ])( xy.xy + xy.M );

x[i]
y[i][j]

That said, gcc warns

> warning: initialization of a flexible array member [-Wpedantic]

So this last solution is probably non-standard.

答案3

得分: 0

如果您想要用零初始化xy,您可以创建一个使用malloc初始化它们的函数。

typedef struct {
  const int M;
  const int N;
  int      *x;  // 1-D M elements
  int      *y;  // 2-D M*N elements
} xy_t;

void init_arrays(xy_t *xy){
    int *new_x = malloc(sizeof(int)*(xy->M));
    for (int i = 0; i < xy->M; i++) {
        new_x[i] = 0;
    }
    xy->x = new_x;

    int *new_y = malloc(sizeof(int)*(xy->M*xy->N));
    for (int i = 0; i < xy->M; i++) {
        for (int j = 0; j < xy->N; j++) {
            new_y[i*xy->N + j] = 0;
        }
    }
    xy->y = new_y;
}

这只是一个示例,您也可以选择创建一个类似这样的构造函数:xy_t *new_xy_t(int M, int N),两者都可以工作。

注意:

  • y 已经变成了1-D,2-D是通过i*xy->M + j(如klutt提到的)人工创建的,这样更安全。您可以创建getter和setter来更轻松地访问数字。
  • 由于我们使用了动态分配,这必须伴随着一个释放函数(如不再提到的sigsev)。
英文:

If you want to initialize x and y with zeros, you can create a function that initializes them using malloc.

typedef struct {
  const int M;
  const int N;
  int      *x;  // 1-D M elements
  int      *y;  // 2-D M*N elements
} xy_t;

void init_arrays(xy_t *xy){
    int *new_x = malloc(sizeof(int)*(xy-&gt;M));
    for (int i = 0; i &lt; xy-&gt;M; i++) {
        new_x[i] = 0;
    }
    xy-&gt;x = new_x;

    int *new_y = malloc(sizeof(int)*(xy-&gt;M*xy-&gt;N));
    for (int i = 0; i &lt; xy-&gt;M; i++) {
        for (int j = 0; j &lt; xy-&gt;N; j++) {
            new_y[i*xy-&gt;N + j] = 0;
        }
    }
    xy-&gt;y = new_y;
}

This is an example, you may prefer to create a constructor like that : xy_t *new_xy_t(int M, int N), both can work.

NB:

  • y has become 1-D, the 2-D is artificially created by i*xy-&gt;M + j (as klutt mentioned), it makes it safer to manipulate. You can create a getter and a setter to access numbers more easily.
  • Since we used dynamic allocation, this has to come with a free function (as no more sigsev mentioned).

答案4

得分: 0

没有简单的方法在C中。即使在C++中也没有简单的方法 在C中初始化可变大小数组
你将不得不做以下之一:

  1. 在某处声明完全初始化的二维数组,然后将其名称用作.y的初始化程序。
  2. 几乎相同,但使用主维度(指针数组)的malloc,然后使用其他malloc(或多个)来为次维度分配内存(指针数组中的每个指针)。这就是 Tristan Riehs 在上面提出的方法。

然而,如果你愿意放弃两个维度的可调整大小性,你可以使用宏来模拟C++的模板,以便你的结构的每个具体实例都具有固定的第二维。

英文:

There is no easy way in C. There is no easy way even in C++ 在C中初始化可变大小数组
You will have to either:

  1. Declare somewhere fully initialized two-dimensional array and then use its name as the initalizer for your .y.
  2. Almost same, but with malloc of primary dimension (array of pointers) and another malloc (or multiple) for secondary dimension (each pointer in the array of pointers). This is what Tristan Riehs proposed above.

However if you are ready to give up on resizibility of both dimensions, you can use macro to simulate templates of C++, so that each concrete instance of your struct will be with fixed 2nd dimension.

huangapple
  • 本文由 发表于 2023年2月23日 21:56:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/75545764.html
匿名

发表评论

匿名网友

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

确定