我如何将两个灵活的数组放在一个结构中?

How can i put two flexible array in a single struct?

我是编程和学习结构的新手,当我试图将两个灵活的数组放在一个结构中时,他们给我一个错误,为什么我不能将两个数组放在一个结构中?我创建了一个 pokemon 示例来测试结构中的数组,但只有 *pokemon_name[] 有效,为什么?

#include <stdio.h>

void main()
{
  struct pokemon
  {
    int first_generation;
    char *pokemon_type[];
    char *pokemon_name[];
  } number_pokedex[438];

  number_pokedex[23].pokemon_name[5] = "Arbok";
  number_pokedex[23].pokemon_type[6] = "Poison";
  number_pokedex[23].first_generation = 1;

  printf("Name of the pokemon: %s\n", number_pokedex[23].pokemon_name[5]);
  printf("Type of the pokemon: %s\n", number_pokedex[23].pokemon_type[6]);
  printf("From first generation?: %d\n", number_pokedex[23].first_generation);
}

为什么会出现错误信息?

C 标准在第 6.7.2.1 / 18 节中说:

As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply.

在你的例子中,你有这样一个 flexible array ,它不是结构的最后一个元素,因此是错误的。

为什么 C 不允许超过一个灵活的数组成员?

具有灵活数组的结构的行为被定义为使其工作就像灵活数组在结构代码之后 开始一样。

 +--------------------------------+----+----+----+----+
 | struct without flexible array  |f[0]|f[1]|f[2]|... |
 +--------------------------------+----+----+----+----+

所以你可以这样写代码:

  struct test
  {
    int a;
    int flex[];
  } *pt, array[10];

  static struct test t={ 1, {2,3,4}};
  printf ("%x %x %x\n", &t, &t.flex[0], &t.flex[3]);

  pt = malloc(sizeof(struct test)+20*sizeof(int)); 
  printf ("%x %x\n", pt, &pt->flex[0]);

问题是你必须知道为灵活数组保留了多少元素(静态保留或动态分配)。如果 C 允许多个灵活数组,这种行为将不再可能,因为编译器不知道第二个灵活数组从哪里开始。

另类

现在您可以通过使用更健壮的固定大小数组或通过指向指针的指针使用动态数组来很好地重写您的代码。

struct pokemon
{
  int first_generation;
  char **pokemon_type;
  char **pokemon_name;
} number_pokedex[438];

在这种情况下,您必须通过分配足够大小的数组来初始化 char** 指针:

// assuming that no more than 10 elements in item 23.  
number_pokedex[23].pokemon_name = calloc (10, sizeof(char*));
number_pokedex[23].pokemon_type = calloc (10, sizeof(char*));

您还需要在不再需要时释放数组。最后,在复制结构元素时必须格外小心,因为您会克隆指针。

虽然 给出了一个完美的传统答案,但您可能想了解一个替代方案。如果您愿意限制名称的长度,则可以使用数组数组代替指针数组。

typedef char name_type[20];

struct pokemon
{
  int first_generation;
  name_type *pokemon_type, *pokemon_name;
} number_pokedex[438];

void b() {
  number_pokedex[23].pokemon_name = calloc (10, sizeof(name_type));
  number_pokedex[23].pokemon_type = calloc (10, sizeof(name_type));
}

这是说你的名字是 20 个字节,你的数组是 200 个字节:10 个元素,每个元素 20 个字节。与指针数组技术不同,此处 calloc 分配的不是指针,而是数组,因此您只有一次分配和一次释放。

IMO 数组的数组更容易使用:当需要填充名称时,存储已经分配,​​当需要释放数组时,您不必为指针追逐每个元素。

一个普遍的反对意见是,此技术需要在编译时确定名称大小,使其小于 "flexible"。不过,这并没有看起来那么严重,因为无论名称出现在何处,都有一个隐含的限制,无论它是 GUI 中的字段还是数据库中的列,还是终端的宽度或 [=22 的大小=] 在信封上。还不如决定你的名字有多大,然后继续下去。