Структура с массивом структур неизвестного размера - PullRequest
11 голосов
/ 01 сентября 2011

Я пытался обернуться вокруг этого весь день ...

По сути, у меня есть структура с именем State, у которой есть имя, и еще одна, называемая StateMachine, с добавлением имени, массива состояний и общего числа состояний:

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

typedef struct State {
  const char * name;

} State;

typedef struct StateMachine {
  const char * name;

  int total_states;
  State ** states;

} StateMachine;

StateMachine * create_state_machine(const char* name) {
  StateMachine * temp;

  temp = malloc(sizeof(struct StateMachine));

  if (temp == NULL) {
    exit(127);
  }

  temp->name = name;
  temp->total_states = 0;

  temp->states = malloc(sizeof(struct State));
  return temp;
}

void destroy_state_machine(StateMachine* state_machine) {
  free(state_machine);
}

State * add_state(StateMachine* state_machine, const char* name) {
  State * temp;

  temp = malloc(sizeof(struct State));

  if (temp == NULL) {
    exit(127);
  }

  temp->name = name;

  state_machine->states[state_machine->total_states]= temp;
  state_machine->total_states++;

  return temp;
}

int main(int argc, char **argv) {

  StateMachine * state_machine;

  State * init;
  State * foo;
  State * bar;

  state_machine = create_state_machine("My State Machine");

  init = add_state(state_machine, "Init");
  foo  = add_state(state_machine, "Foo");
  bar  = add_state(state_machine, "Bar");

  int i = 0;

  for(i; i< state_machine->total_states; i++) {
    printf("--> [%d] state: %s\n", i, state_machine->states[i]->name);
  }

}

По какой-то причине (читай ниже C-фу / годы ruby ​​/ python / php) я не могу выразить тот факт, что states является массивом состояний. Вышеприведенный код печатает:

--> [0] state: ~
--> [1] state: Foo
--> [2] state: Bar

Что случилось с первым добавленным состоянием?

Если я добавляю массив состояний в первое добавленное состояние (например, state_machine = malloc (sizeof (temp)), то я получаю первое значение, но не второе.

Любые советы?

Это вопрос С. Я использую gcc 4.2.1 для компиляции образца.

Ответы [ 5 ]

7 голосов
/ 01 сентября 2011

Похоже, вы не выделяете место для своих состояний в машине после первой.

StateMachine * create_state_machine(const char* name) {
  StateMachine * temp;

  temp = malloc(sizeof(struct StateMachine));

  if (temp == NULL) {
    exit(127);
  }

  temp->name = name;
  temp->total_states = 0;

  temp->states = malloc(sizeof(struct State)); // This bit here only allocates space for 1.
  return temp;
}

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

Между прочим, init, foo и bar никогда не используются.

Редактировать: То, что я предлагаю, выглядит так:

#define MAX_STATES 128 // Pick something sensible.
typedef struct StateMachine {
  const char * name;
  int total_states;
  State *states[MAX_STATES];
} StateMachine;
5 голосов
/ 01 сентября 2011

Похоже, вы хотите иметь переменное число состояний в каждом автомате состояний, но вы неправильно распределяете память.В create_state_machine эта строка:

temp->states = malloc(sizeof(struct State));

Распределяет один объект State, а не массив указателей (как вы его используете).

Существует два способаВы могли бы изменить это.

  1. Объявите states как State states[<some-fixed-size>];, но тогда вы не сможете иметь больше фиксированного числа состояний.
  2. Добавьте еще один элемент, чтобы указать, сколько памяти было выделено для states, так что вы можете отслеживать это, а также сколько используется (для этого используется total_states).

В последнем случае это будет выглядеть примерно так:

#include <stdlib.h>
#include <string.h>

typedef struct 
{
    const char *name;
} State;

typedef struct 
{
    const char *name;
    int total_states;
    int states_capacity;
    State *states;
} StateMachine;

StateMachine *create_state_machine(const char *name)
{
    StateMachine *temp = malloc(sizeof(StateMachine));
    memset(temp, 0, sizeof(*temp));

    temp->name = name;
    temp->states_capacity = 10;
    temp->states = malloc(sizeof(State) * temp->states_capacity);

    return temp;
}

State *add_state(StateMachine *machine, const char *name)
{
    if (machine->total_states == machine->states_capacity)
    {
        // could grow in any fashion.  here i double the size, could leave
        // half the memory wasted though.
        machine->states_capacity *= 2;

        machine->states = realloc(
            machine->states, 
            sizeof(State) * machine->states_capacity);
    }

    State *state = (machine->states + machine->total_states);
    state->name = name;

    machine->total_states++;

    return state;
}
3 голосов
/ 01 сентября 2011

Внутри вашей функции add_state:

temp = malloc(sizeof(struct StateMachine)); 

должно быть

temp = malloc(sizeof(struct State));

Однако, даже если это изменилось, я все равно получаю правильный вывод:

--> [0] state: Init
--> [1] state: Foo
--> [2] state: Bar

Возможно, в вашем коде нет ничего плохого.Я использую gcc версии 4.4.3

1 голос
/ 01 сентября 2011

Вы делаете концептуальную ошибку:

State ** states;

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

state_machine->states[state_machine->total_states]= temp;

, вы делаете что-то неправильно, если total_states больше нуля, потому что вы указываете на сегменты памяти, которые не были выделены (мне интересно, почему вы не получаете SEGFAULT),Чтобы хранить динамическое число State таким образом, вам нужен связанный список или вызывать realloc для каждого добавляемого состояния (но это не очень хорошая идея).Память, которую вы выделяете различными вызовами malloc, не является непрерывной.

1 голос
/ 01 сентября 2011
State ** states;

создаст массив массивов состояний.

Я честно не прочитал все решение (нужно запустить), но вы упомянули, что хотите получить массив состояний - возможно, вы захотите сделать:

State* states

или

State states[size];

вместо этого? Просто пища для размышлений. Скорее всего, это была не ваша проблема, поскольку я ее не полностью прочитал

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