массив массивов гибкой длины внутри другой структуры с использованием C - PullRequest
4 голосов
/ 12 марта 2010

Привет, я пытаюсь использовать 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;
}

Это всегда дает мне «недопустимое использование члена гибкого массива». Буду очень признателен, если кто-нибудь сможет пролить свет на это.

Ответы [ 6 ]

2 голосов
/ 12 марта 2010
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;
}
1 голос
/ 12 марта 2010

Если у вас есть структура с гибким членом массива, вы не можете напрямую создавать объекты этого типа, потому что компилятор не знает, как долго вы хотите, чтобы это было. Вот что вы пытаетесь сделать с этой строкой:

Box b1, b2;

Такие struct всегда должны выделяться динамически, что означает, что вы когда-либо объявляете только указатели на них. Таким образом, вы изменили бы эту декларацию на:

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 может показаться хорошим выбором), поскольку член гибкого массива не может быть единственным членом структуры.

1 голос
/ 12 марта 2010

Вам нужно динамически выделить 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->p - это массив (из n1124 6.7.2.1p16):

Однако, когда у оператора . (или ->) есть левый операнд, который является (указатель) на структуру с элементом гибкого массива и правый операнд называет этот член, он ведет себя так, как если бы этот член был заменен самым длинным массивом (с тем же типом элемента), который не сделает структуру больше, чем объект, к которому осуществляется доступ; смещение массива должно оставаться смещением элемента гибкого массива, даже если оно будет отличаться от смещения массива замены. Если в этом массиве нет элементов, он ведет себя так, как если бы он имел один элемент, но поведение не определено, если предпринята какая-либо попытка получить доступ к этому элементу или сгенерировать указатель один за ним.

Кроме того, в вашем struct должен быть хотя бы один член, кроме члена гибкого массива.

1 голос
/ 12 марта 2010

Вы не можете присвоить 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;
};

И не забудьте освободить память, когда она вам больше не нужна.

0 голосов
/ 12 марта 2010

ЕСЛИ у вас будет «гибкий массив», он ДОЛЖЕН быть последним элементом в структуре. Это просто потому, что компилятор не может предсказать смещение следующего элемента. Кроме того, когда структура распределяется, вам необходимо выделить дополнительную память для элементов массива.

Историческая справка: Одна вещь, которую я видел больше в прошлом, была такая практика ...

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].

Будь осторожен там.

0 голосов
/ 12 марта 2010

в структуре Box, почему бы вам не использовать указатель на частицу, чтобы создать динамический массив?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...