Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Struct with array of structs of unknown size

Tags:

arrays

c

struct

I've been trying to wrap my head around this the whole day...

Basically, I have a struct called State that has a name and another one called StateMachine with a name, an array of states and total number of states added:

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

}

For some reason (read low C-fu / years of ruby/python/php) I'm unable to express the fact that states is an Array of State(s). The above code prints:

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

What happened with the first state added?

If I malloc the states array on the first state added (e.g. state_machine = malloc(sizeof(temp)); then I get the first value but not the second.

Any advices?

This is a C question. I'm using gcc 4.2.1 to compile the sample.

like image 412
apann Avatar asked Aug 31 '11 21:08

apann


People also ask

Is it possible to have a struct with arrays?

A structure may contain elements of different data types – int, char, float, double, etc. It may also contain an array as its member. Such an array is called an array within a structure. An array within a structure is a member of the structure and can be accessed just as we access other elements of the structure.

How do you determine the size of a struct byte?

If you want to manually count it, the size of a struct is just the size of each of its data members after accounting for alignment. There's no magic overhead bytes for a struct.

Can you override a struct?

* Structs have statically-dispatched methods and properties; there's no ability to override.


2 Answers

It looks like you're not allocating space for your states in the machine past the first one.

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

You're probably better off putting an array of states of fixed size in the state machine struct. If that's not okay, you'll have to realloc and move the whole set around or allocate chunks and keep track of the current length, or make a linked list.

Incidentally, init, foo, and bar never get used.

Edit: What I'm suggesting looks like this:

#define MAX_STATES 128 // Pick something sensible.
typedef struct StateMachine {
  const char * name;
  int total_states;
  State *states[MAX_STATES];
} StateMachine;
like image 141
nmichaels Avatar answered Sep 24 '22 06:09

nmichaels


It looks like you want to have a variable number of states in each state machine, but you are allocating the memory incorrectly. In create_state_machine, this line:

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

Allocates a single State object, not an array of pointers (which is how you are using it).

There are two ways you could change this.

  1. Declare states as State states[<some-fixed-size>]; but then you cant ever have more than a fixed number of states.
  2. Add another member to indicate how much storage has been allocated for states, so you can keep track of that as well as how much is used (which is what total_states is being used for).

The later would look something like this:

#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;
}
like image 21
Node Avatar answered Sep 26 '22 06:09

Node