2010-03-12 2 views
4

Привет я пытаюсь использовать C для реализации простой структуры:
2 коробки, каждая содержит различное количество частиц; точное число частиц передается в main().гибкая длина struct array внутри другой структуры с использованием C

Я написал следующий код:

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

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

+0

Если я правильно помню, C не позволяет использовать переменную для размера массива. –

+1

C99 делает. Он называется * массивами переменной длины * или VLA. C99 также благословил «struct hack», представив «гибкий элемент массива». –

ответ

2
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; 
} 
+0

Спасибо, я всегда парень Java и новый для C. Идея выделения памяти сводит меня с ума. Могу ли я узнать в main(), как я могу получить доступ к каждому элементу массива частиц? например printf ("% f", b1.p [1] .x) – renz

+0

Точно (см. обновление). –

+0

@ Federico, вам может понравиться попробовать Boehem GC для c или C++ http://www.hpl.hp.com/personal/Hans_Boehm/gc/ – philcolbourn

0

Вы не можете назначить 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

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

1

Вы должны динамически выделять 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 были смежными. Если у вас есть указатель Point * в struct и назначил его динамически, макет памяти для struct и точек не будет смежным.

Причина вы получаете сообщение об ошибке, что вы не можете присвоить массив в C, и box->p массив (от n1124 6.7.2.1p16):

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

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

1

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

Box b1, b2; 

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

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

+0

Последнее замечание - хороший момент, но первая инструкция вашего make_box() не имеет для меня никакого смысла. Лучше: Box * box = malloc (sizeof Box); if (box) {box-> nparticles = no_of_particles; box-> p = malloc (no_of_particles * sizeof (Particle)); if (box-> p) {etc ... Затем void destroy_box (Box * box) {if (box) {if (box-> p) free (box-> p); free (box);}} –

+0

Federico: метод, который я использую (и который ОП задает) использует * элемент гибкого массива *, который позволяет выделить всю вещь в одном распределении. Обратите внимание, что тип '' '' '' '' '' '' '' 'является неполным типом массива, а не указателем. Документация GCC по этому вопросу находится здесь: http://www.delorie.com/gnu/docs/gcc/gcc_42.html, хотя обратите внимание, что он * является * стандартным C. – caf

+0

Wow! Извините за мой хромой комментарий, он работает. Моя проблема была на самом деле со ссылкой на «* box» и «box-> p» в самом объявлении самого окна. В этом отношении даже объявление типа «int a = sizeof (a);» было бы очень подозрительным (но теперь я вижу, что он правильно компилируется и делает то, что ожидается).Ну, очевидно, мои знания о C более ржавые и старые, чем я думал. +1 –

0

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

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

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 */ 

выше позволило бы один для доступа за пределами [название]. Совершенно законный и действительный C, но потенциально опасный, поскольку вы намеренно выходите за пределы первоначально объявленных границ [name].

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

+0

Действительно ли C? Символ 'char a [1]' преобразуется в 'char * a', но это другой тип. Если struct '* foo' содержит поле' char a [1] ', будет ли стандарт запрещать компилятору оптимизировать' foo-> a [i] 'to' foo-> a [0] ', поскольку самый высокий юридический индекс для массива будет * меньше * объявленного размера и выделенного пространства. Если 'foo-> a [i]' приведет к неопределенному поведению для любого ненулевого значения 'i', это предполагает, что компилятор будет в пределах своих прав предполагать, что' i' равно нулю (в противном случае код будет разрешено делать все, что захочет, в том числе вести себя так, как если бы 'i' было нулевым). – supercat

Смежные вопросы