将灵活数组成员分配为结构

时间:2014-10-04 20:34:00

标签: c arrays pointers struct flexible-array-member

我更了解指针和东西,但我不知道我在这里做错了什么。 如果我有     char *(* data)[] 那只会被解释为“一个指向char指针数组的指针”,对吧? 然后我有一个像这样的结构,typedef将成为myStruct,可能是多余的,但这不是重点:

    typedef struct myStruct myStruct;
    struct myStruct{
      int size;
      char *name;
      myStruct *(*array)[];
}

浏览过网站上的类似帖子后,我得到了类似的内容:

    //let's say allocating 5 spaces for this case
    myStruct *a = malloc(sizeof(myStruct)+ sizeof(struct myStruct *)*5);

我确信我分配结构的数字是数组的大小。 我不能完全理解这一点,如果它是一个结构,它是如何工作的? 这里的计划是拥有这个结构,它包含一个包含5个myStruct的数组。我还必须单独分配它们吗?像这样?

    a->array[0] = malloc( .... ) 

我试过,它一直给我一个错误,说明无效使用带有未指定边界的数组。 我做错了什么或如何解决这个问题? 谢谢

2 个答案:

答案 0 :(得分:1)

myStruct *(*array)[];不是灵活的数组成员,因为它不是数组类型。它是一个指针,恰好指向一个不完整的数组类型。

灵活数组成员的一般模式是:

struct myStruct {
    int size;
    char *name;
    Type array[];
};

在您的情况下,Type将由typedef MyStruct * Type;定义。我假设你想要一个包含5个指针的数组。 (这与结构中myStruct *array[];的效果相同)。

(如果你确实希望你的结构包含一个指针,指向一个包含5个元素的数组;那么灵活的数组成员就不是正确的技术了。)

你的malloc对我刚才给出的这个定义是正确的。它分配了一个连续的内存块,您可以使用array,就好像它实际上是一个包含5个对象的数组一样,除非您无法对其进行sizeof查找大小。

答案 1 :(得分:1)

从你的评论中,听起来你想要一个指向结构指针数组的指针,而不是指向结构数组的指针,因为“指向char *数组的指针”也有两个间接层。

区别在于:

  1. 指向结构的指针: enter image description here

  2. 指向结构数组的指针: enter image description here

  3. 指向结构指针数组的指针: enter image description here

  4. 假设你想要#3,你可以这样做(在“传统的”C中):

    typedef struct myStruct myStruct;
    
    struct myStruct
    {
        int        size;
        char      *name;
        myStruct **array;
    };
    
    myStruct *allocate_node(char *name, int size)
    {
        myStruct *p_node;
        if (size < 0)
            size = 0;
        p_node = calloc(1, sizeof(myStruct));
        p_node->name = name;
        p_node->size = size;
        p_node->array = calloc(1, size * sizeof(myStruct *));
    
        return p_node;
    }
    
    void expand_node_child_array(myStruct *p_node, int size_to_add)
    {
        if (size_to_add < 1 || p_node == NULL)
            return;
        if (p_node->array == NULL)
        {
            p_node->size = size_to_add;
            p_node->array = calloc(1, size_to_add * sizeof(myStruct *));
        }
        else
        {
            p_node->array = realloc(p_node->array, (p_node->size + size_to_add) * sizeof(myStruct *));
            memset(p_node->array + p_node->size * sizeof(myStruct *), 0, size_to_add * sizeof(myStruct *));
            p_node->size += size_to_add;
        }
    }
    
    myStruct *get_child_node(myStruct *p_node, int index)
    {
        if (index < 0 || index >= p_node->size)
            return 0;
        return p_node->array[index];
    }
    
    int set_child_node(myStruct *p_node, int index, myStruct *p_child)
    {
        if (index < 0 || index >= p_node->size)
            return FALSE;
        p_node->array[index] = p_child;
        return TRUE;
    }
    
    void free_node(myStruct **pp_node)
    {
        // Free p_node and the array but DO NOT free the children
        if (pp_node == NULL || *pp_node == NULL)
            return;
        if ((*pp_node)->array != NULL)
            free((*pp_node)->array);
        free((*pp_node));
        *pp_node = NULL;
    }
    
    void free_node_and_children(myStruct **pp_node)
    {
        int iChild;
    
        if (pp_node == NULL || *pp_node == NULL)
            return;
        for (iChild = 0; iChild < (*pp_node)->size; iChild++)
        {
            myStruct *p_child = get_child_node((*pp_node), iChild);
            if (p_child != NULL)
                free_node_and_children(&p_child);
            set_child_node((*pp_node), iChild, NULL);
        }
        free_node(pp_node);
    }
    

    <强>更新

    根据C99标准语法,灵活数组是一个可变长度数组,它出现在结构的尾部,其实际长度在运行时设置。它在内存中看起来像这样: enter image description here

    假设您的编译器支持这种语法(并非所有语法都支持),您可以这样声明:

    struct myStruct
    {
        Type  array_of_type[]; /* AT THE END OF THE STRUCT ONLY */
    };
    

    “myStruct”的代码变为:

    typedef struct myStruct myStruct;
    
    struct myStruct
    {
        int        size;
        char      *name;
        myStruct  *array[];
    };
    
    myStruct *allocate_node(char *name, int size)
    {
        myStruct *p_node;
        if (size < 0)
            size = 0;
        p_node = calloc(1, sizeof(myStruct) + size * sizeof(myStruct *));
        p_node->name = name;
        p_node->size = size;
    
        return p_node;
    }
    
    myStruct *get_child_node(myStruct *p_node, int index)
    {
        if (index < 0 || index >= p_node->size)
            return NULL;
        return p_node->array[index];
    }
    
    int set_child_node(myStruct *p_node, int index, myStruct *p_child)
    {
        if (index < 0 || index >= p_node->size)
            return FALSE;
        p_node->array[index] = p_child;
        return TRUE;
    }
    
    void free_node(myStruct **pp_node)
    {
        if (pp_node == NULL || *pp_node == NULL)
            return;
        free((*pp_node));
        *pp_node = NULL;
    }
    
    void free_node_and_children(myStruct **pp_node)
    {
        int iChild;
    
        if (pp_node == NULL || *pp_node == NULL)
            return;
        for (iChild = 0; iChild < (*pp_node)->size; iChild++)
        {
            myStruct *p_child = get_child_node((*pp_node), iChild);
            if (p_child != NULL)
                free_node_and_children(&p_child);
            set_child_node((*pp_node), iChild, NULL);
        }
        free_node(pp_node);
    }
    

    如果编译器没有,请参阅here了解一些变通方法。

    使用灵活的数组,扩展数组需要重新分配节点本身并修复对它的所有引用,这是“指针数组指针”设计中不需要的。

    您正在使用的语法:

        myStruct  *(*array)[];
    

    应该是read as“指向未知大小的结构指针的数组的指针”,而不是

        myStruct  **array;
    

    是“指向结构的指针的指针”,或(例如):

        myStruct *(*array)[4];
    

    是“指向长度为4的数组的指针。

    您的语法实际上会生成内存映射#3,但访问单个元素有点尴尬,因为您必须显式获取指向“未知大小数组”的第0个元素的指针,即(*p_node->array) 。因此#3中的函数修改如下:

    void expand_node_child_array(myStruct *p_node, int size_to_add)
    {
        if (size_to_add < 1 || p_node == NULL)
            return;
        if (p_node->array == NULL)
        {
            p_node->size = size_to_add;
            p_node->array = calloc(1, size_to_add * sizeof(myStruct *));
        }
        else
        {
            p_node->array = realloc(p_node->array, (p_node->size + size_to_add) * sizeof(myStruct *));
            memset((*p_node->array) + p_node->size * sizeof(myStruct *), 0, size_to_add * sizeof(myStruct *));
            p_node->size += size_to_add;
        }
    }
    
    myStruct *get_child_node(myStruct *p_node, int index)
    {
        if (index < 0 || index >= p_node->size)
            return NULL;
        return (*p_node->array)[index];
    }
    
    int set_child_node(myStruct *p_node, int index, myStruct *p_child)
    {
        if (index < 0 || index >= p_node->size)
            return FALSE;
        (*p_node->array)[index] = p_child;
        return TRUE;
    }
    

    最后,两种架构的测试代码:

    void dump_nodes_recursive(myStruct *p_node, int level)
    {
        if (p_node == NULL)
        {
            printf("%*s", 4*level, " ");
            printf("NULL\n");
        }
        else
        {
            int iChild;
            printf("%*s", 4*level, " ");
            printf("Node: Name=\"%s\", array size=%d\n", p_node->name, p_node->size);
            for (iChild = 0; iChild < p_node->size; iChild++)
            {
                myStruct *p_child = get_child_node(p_node, iChild);
                printf("%*s", 4*level, " ");
                printf("Child [%d]:\n", iChild);
                dump_nodes_recursive(p_child, level+1);
            }
        }
    }
    
    void dump_nodes(myStruct *p_node)
    {
        dump_nodes_recursive(p_node, 0);
    }
    
    void test_my_struct()
    {
        myStruct *p_top = allocate_node("top", 4);
        myStruct *p_child0 = allocate_node("child0", 1);
        myStruct *p_child1 = allocate_node("child1", 5);
        myStruct *p_child2 = allocate_node("child2", 0);
        myStruct *p_child3 = allocate_node("child3", 0);
        myStruct *p_child00 = allocate_node("child00", 0);
    
        set_child_node(p_top, 0, p_child0);
        set_child_node(p_top, 1, p_child1);
        set_child_node(p_top, 2, p_child2);
    
        set_child_node(p_top, 3, p_child3);
    
        set_child_node(p_child0, 0, p_child00);
    
        dump_nodes(p_top);
    
        free_node_and_children(&p_top);
    }