Структура c массивом структур неизвестного размера

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

в основном, у меня есть структура, называемая 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: %sn", i, state_machine->states[i]->name);
  }

}

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

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

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

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

какие советы?

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

5 ответов


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

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

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

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

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

#define MAX_STATES 128 // Pick something sensible.
typedef struct StateMachine {
  const char * name;
  int total_states;
  State *states[MAX_STATES];
} StateMachine;

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

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

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

есть два способа изменить это.

  1. объявить states as 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;
}

внутри вашей функции 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


State ** states;

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

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

State* states

или

State states[size];
? Просто пища для размышлений, скорее всего, это не ваша проблема, так как я не полностью ее прочитал: p

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

State ** states;

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

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

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