【问题标题】:flexible length struct array inside another struct using C使用 C 的另一个结构内的灵活长度结构数组
【发布时间】:2010-03-12 02:30:21
【问题描述】:

您好,我正在尝试使用 C 来实现一个简单的结构:
2个盒子,每个盒子包含不同数量的粒子;在 main() 中传递粒子的确切数量。

我写了以下代码:

typedef struct Particle{
    float x;
    float y;
    float vx;
    float vy;
}Particle;

typedef struct Box{
    Particle p[];
}Box;

void make_box(Box *box, int number_of_particles);

int main(){
    Box b1, b2;
    make_box(&b1, 5);  //create a box containing 5 particles
    make_box(&b2, 10); //create a box containing 10 particles
}

我尝试使用以下代码实现 make_box

void make_box(struct Box *box, int no_of_particles){
    Particle po[no_of_particles];
    po[0].x = 1;
    po[1].x = 2;
    //so on and so forth...
    box->p = po;
}

它总是给我“无效使用灵活数组成员”。如果有人能对此有所了解,将不胜感激。

【问题讨论】:

  • 如果我没记错的话,C 不允许你使用变量来表示数组的大小。
  • C99 可以。它被称为可变长度数组,或VLA。 C99 还通过引入“灵活数组成员”来祝福“struct hack”。

标签: c arrays struct


【解决方案1】:
void make_box(struct Box *box, int no_of_particles){
    Particle po[no_of_particles];
    //...
    box->p = po;
}

po是一个局部变量,它的存储空间是自动在栈上分配的;返回它的地址是个坏主意。您应该改为从堆中分配内存,并记住在使用完这些框后释放该内存:

#include <stdlib.h>
#include <stdio.h>

typedef struct Particle_ {
    float x;
    float y;
    float vx;
    float vy;
} Particle;

typedef struct Box_ {
    Particle *p;
} Box;

void make_box(Box *box, int no_of_particles);

void make_box(Box *box, int no_of_particles){
    Particle *po = (Particle *) malloc ( no_of_particles*sizeof(Particle) );
    po[0].x = 1;
    po[1].y = 2;
    //so on and so forth...
    box->p = po;
}

void destroy_box(Box *box){
    free(box->p);
}


int main(){
    Box b1, b2;
    make_box(&b1, 5);  //create a box containing 5 particles
    make_box(&b2, 10); //create a box containing 10 particles

    // do the job...
    printf("box b1, point 0, x: %5.2f\n", b1.p[0].x);
    printf("box b2, point 1, y: %5.2f\n", b2.p[1].y);

    destroy_box(&b1);
    destroy_box(&b2);

    return 0;
}

【讨论】:

  • 谢谢,我一直是 Java 人,对 C 语言很陌生。内存分配的想法让我发疯。我可以知道在 main() 中如何访问粒子数组的每个元素吗?比如 printf("%f", b1.p[1].x)
  • @Federico,你可能想试试用于 c 或 c++ 的 Boehem GC hpl.hp.com/personal/Hans_Boehm/gc
【解决方案2】:

您不能分配给Particle p[],只能使用Particle* p

当然,您需要在堆上分配粒子数组,而不是在堆栈上!否则一离开函数make_box就会被销毁。

void make_box(struct Box *box, int no_of_particles){
    Particle* po = (Particle*)malloc(sizeof(Particle)*no_of_particles);
    for (int i = 0; i < no_of_particles; i++)
        po[0].x = i;
    //so on and so forth...
    box->p = po;
}

struct Box
{
    Particle* p;
};

当你不再需要它时,别忘了释放内存。

【讨论】:

    【解决方案3】:

    您需要动态分配struct Box

    struct Box *box1 = malloc(sizeof *box1 +
                              sizeof (Particle[number_of_particles]));
    
    for (size_t i=0; i < number_of_particles; ++i)
        box1->p[i] = po[i];
    /* or memcpy(box1->p, po, sizeof po); */
    

    但是,如果您正在执行上述操作,您不妨声明struct Box 以在其中包含一个指针。如果您希望struct 中的所有数据都是连续的,则灵活的数组成员“技巧”很有用。如果您在struct 中有一个Point * 指针并动态分配它,则struct 的内存布局和点将不连续。

    您收到错误的原因是您无法在 C 中分配给数组,而 box-&gt;p 是一个数组(来自 n1124 6.7.2.1p16):

    但是,当.(或-&gt;)运算符的左操作数是(指向)具有灵活数组成员的结构并且右操作数命名该成员时,它的行为就好像该成员是替换为不会使结构大于被访问对象的最长数组(具有相同元素类型);数组的偏移量应保持灵活数组成员的偏移量,即使这与替换数组的偏移量不同。如果这个数组没有元素,它的行为就好像它只有一个元素,但如果尝试访问该元素或生成一个越过它的指针,则行为是不确定的。

    除了灵活数组成员之外,您还需要在 struct 中至少有一个其他成员。

    【讨论】:

      【解决方案4】:

      如果你有一个带有灵活数组成员的结构,那么你不能直接创建该类型的对象,因为编译器不知道你想要它多长时间。这就是您尝试对这一行执行的操作:

      Box b1, b2;
      

      这样的structs 总是需要动态分配,这意味着你只需要声明指向它们的指针。因此,您可以将该声明更改为:

      Box *b1, *b2;
      

      ...并更改您的 make_box() 函数以返回这样的指针:

      Box *make_box(int no_of_particles)
      {
          Box *box = malloc(sizeof *box + no_of_particles * sizeof box->p[0]);
      
          if (box)
          {
              box->p[0].x = 1;
              box->p[1].x = 2;
              //so on and so forth...
          }
      
          return box;
      }
      
      void destroy_box(Box *box){
          free(box);
      }
      
      int main()
      {
          Box *b1 = make_box(5);  //create a box containing 5 particles
          Box *b2 = make_box(10); //create a box containing 10 particles
      
          // Test that b1 and b2 are not NULL, then do the job...
      
          destroy_box(b1);
          destroy_box(b2);
      
          return 0;
      }
      

      PS: 您还需要向 Box 结构中添加至少一个其他成员(no_of_particles 似乎是一个不错的选择),因为灵活的数组成员不能是结构的唯一成员。

      【讨论】:

      • 您最后的观察是一个很好的观点,但是您的 make_box() 的第一条指令对我来说没有任何意义。更好: Box box = malloc(sizeof Box); if(box) { box->nparticles = no_of_particles; box->p = malloc(no_of_particlessizeof(Particle)); if(box->p) { etc... 然后 void destroy_box(Box *box){ if(box){ if(box->p) free(box->p);免费(框);}}
      • Federico:我正在使用的方法(以及 OP 询问的方法)使用 灵活的数组成员,它允许在单个分配中分配整个事物.注意struct Box 中成员p 的类型是不完整的数组类型,而不是指针。关于此的 GCC 文档在这里:delorie.com/gnu/docs/gcc/gcc_42.html,尽管请注意它标准 C.
      • 哇!对不起我的蹩脚评论,它有效。我的问题实际上是在 box 本身的声明中对“*box”和“box->p”的引用。就此而言,即使是像“int a = sizeof(a);”这样的声明本来是非常可疑的(但现在我看到它编译正确并符合预期)。好吧,显然我对 C 的了解比我想象的还要生疏和陈旧。 +1
      【解决方案5】:

      在 Box 结构中,为什么不使用指向 Particle 的指针来创建动态数组?

      【讨论】:

        【解决方案6】:

        如果您要拥有一个“灵活数组”,它必须是结构中的最后一个元素。这仅仅是因为编译器无法预测下一个元素的偏移量。此外,在分配结构时,您需要为数组元素分配额外的内存。

        历史记录: 我过去经常看到的一件事是这样的做法......

        struct a {
          int x, y, z;
          char name[1];
        };
        
        struct a *  ptr;
        
        ptr = (struct a*) malloc (sizeof (a) + EXTRA SPACE FOR name);
        
        or
        
        ptr = (struct a*) buffer;   /* <buffer> would have been a passed character array */
        

        以上内容将允许访问超出 [name] 的范围。完全合法且有效的 C,但存在潜在危险,因为您将故意超出 [name] 最初声明的范围。

        在外面小心点。

        【讨论】:

        • 它是有效的 C 吗? char a[1] 可以转换为 char *a,但它是不同的类型。如果 struct *foo 包含字段 char a[1],标准是否会禁止编译器将 foo-&gt;a[i] 优化为 foo-&gt;a[0],因为数组的最高合法下标是声明大小的 lesser和分配的空间。如果foo-&gt;a[i] 会导致i 的任何非零值出现未定义的行为,这表明编译器有权假设i 为零(否则,代码将被允许做任何它想做的事情,包括表现得好像 i 为零)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-08-13
        • 1970-01-01
        • 2011-07-25
        • 2011-03-04
        • 1970-01-01
        • 1970-01-01
        • 2013-06-23
        相关资源
        最近更新 更多