【问题标题】:Struct with array of structs of unknown size具有未知大小的结构数组的结构
【发布时间】:2011-11-07 23:43:15
【问题描述】:

我整天都在努力解决这个问题......

基本上,我有一个名为 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-fu/ruby/python/php 年),我无法表达状态是状态数组的事实。上面的代码打印:

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

添加的第一个状态发生了什么?

如果我在添加的第一个状态上 malloc 状态数组(例如 state_machine = malloc(sizeof(temp)); 那么我得到第一个值而不是第二个值。

有什么建议吗?

这是一道 C 题。我正在使用 gcc 4.2.1 编译示例。

【问题讨论】:

    标签: c arrays struct


    【解决方案1】:

    看起来您没有为机器中的状态分配空间,超过第一个状态。

    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;
    

    【讨论】:

    • 如果我将它设置为 NULL(因为在创建 StateMachine 时我不知道状态的大小)我将如何添加状态?
    • @apann:您不需要知道状态的大小,只需要知道指向状态的指针的大小。在StateMachine 中粘贴State * 的数组应该可以工作,只要您确保不要超出它。
    • 谢谢,我会对此投赞成票,但我没有足够的业力:)
    • @apann:保持你的问题质量,你不会有代表问题。这是我见过的最好的第一个问题。
    【解决方案2】:

    您似乎希望在每个状态机中拥有可变数量的状态,但您分配的内存不正确。在create_state_machine,这一行:

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

    分配单个 State 对象,而不是指针数组(这是您使用它的方式)。

    有两种方法可以改变这一点。

    1. states 声明为State states[&lt;some-fixed-size&gt;];,但您不能拥有超过固定数量的状态。
    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;
    }
    

    【讨论】:

    • 谢谢!我看到这个工作,但我不能选择超过 1 个答案:)
    【解决方案3】:

    在你的 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

    【讨论】:

    • 感谢您发现这一点,我将编辑我的帖子。但是,它并没有解决问题(我得到了相同的结果)
    • 我想知道是否有编译器标志或其他东西可以使它在 4.2.1 下工作(Snow Leopard 下的默认编译器)。
    【解决方案4】:
    State ** states;
    

    将创建一个状态数组。

    我还没有如实阅读整个解决方案(必须运行),但你提到想要一组状态 - 你可能想做吗:

    State* states
    

    State states[size];
    

    相反?只是深思熟虑,很可能不是你的问题,因为我没有完全阅读它:p

    【讨论】:

    • 我也试过了,但我得到了错误:执行 state_machine->states[state_machine->total_states]= temp; 时分配的类型不兼容;
    【解决方案5】:

    你犯了一个概念性错误:

    State ** states;
    

    确实,您可以将状态视为指向 State 对象的指针数组,但您只为一个状态分配空间。 当你这样做时:

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

    如果 total_states 大于零,你就做错了,因为你指向的是未分配的内存段(我想知道为什么你没有得到 SEGFAULT)。要以这种方式存储动态数量的状态,您需要一个链表,或者调用 realloc 您添加的每个状态(但这不是一个好主意)。您使用不同的 malloc 调用分配的内存不是连续的。

    【讨论】:

    • 段错误可能是因为状态非常小(每个状态只有 1 个单词)。如果 C 是一种更安全的语言,它会检查这类事情。
    • 感谢您提供详细信息,现在我明白我做错了什么。
    猜你喜欢
    • 2011-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-26
    相关资源
    最近更新 更多