【问题标题】:Dynamically Allocating Array of Struct Pointers动态分配结构指针数组
【发布时间】:2020-05-17 15:27:34
【问题描述】:

我正在开发一个 api 来为我的 stm32 项目 (stm32f0) 创建菜单结构。我创建了一个结构来表示菜单中的一个项目,并通过建立指向菜单中父结构和子结构的指针关系来跟踪上下导航。根据菜单项是设置/参数(目标)还是只是文件夹,有 2 种不同的类型、文件夹和目标。

这是我创建新文件夹项的功能。 parentItem 被传递给为新菜单项创建链接的函数,但是由于文件夹(父项)可能包含多个子项,我需要动态分配内存以在父项中保存新子项的指针。我很难理解正确的指针语法来实现这一点。 我收到错误“从类型'MenuItem * {aka struct MenuItems *}'分配给类型'struct MenuItems'时类型不兼容”

这是我的带有 typedefs 的 .h 文件:

typedef enum
{
    Folder,
    Target
} ItemType;

typedef struct MenuItems
{
    struct MenuItems *parent;   // pointer to parent level item (-1 level)
    struct MenuItems *child;    // pointer to child level items (+1 level) (FOLDER ONLY)
    uint8_t numChildren;        // number of child items held by this item (FOLDER ONLY)
    ItemType type;              // whether item is a folder or target
    uint8_t index;              // assigned index of this item
    uint8_t visible;            // flag to set whether the item is visible to the user
    void (*func)();             // pointer to the function when target is chosen
    char name[ITEM_NAME_LENGTH];// displayed string for the menu item

} MenuItem;

这是我在 .c 中创建新文件夹项的函数:

MenuItem *menu_addFolder(MenuItem *parentItem, char name[])
{
    // newIndex is used to assign the index to the new item
    // Due to 0 indexing, this is used verbatim
    // e.g. if numChildren = 1, the index assigned will be 1 as there are now 2 items
    // By assigning the index before incrementing newChildren, 0 indexing is handled
    uint8_t newIndex = parentItem->numChildren;
    parentItem->numChildren++;
    // create item and allocate memory
    MenuItem *newItem = malloc(sizeof(MenuItem));
    newItem->child = malloc(sizeof(MenuItem));
    newItem->parent = malloc(sizeof(MenuItem));
    // check for memory allocation errors
    if(newItem == NULL || newItem->child == NULL || newItem->parent == NULL)
    {
        return NULL;
    }

    newItem->numChildren = 0;
    newItem->type = Folder;
    newItem->index = newIndex;
    newItem->func = NULL;
    strncpy(newItem->name, name, ITEM_NAME_LENGTH);
    // establish the child-parent links
    newItem->parent = parentItem;

    // When a new folder is created, memory for a single child is allocated
    // So for the first child assignment per parent, no new memory allocation is required
    // However, for consecutive child assignments, new memory allocation is required
    // If this is the first child assigned to the parent item
    if(parentItem->numChildren == 1)
    {    
        parentItem->child = newItem;
    }

    // If there are existing items assigned to the parent item
    else if(parentItem->numChildren > 1)
    {
    parentItem->child = realloc(parentItem->child, sizeof(MenuItem) * parentItem->numChildren);
        if(parentItem->child == NULL)
        {
            return NULL;
        }
        parentItem->child[newIndex] = newItem;
    }

    return newItem;
    }

提前致谢!

【问题讨论】:

  • 您应该始终指出哪一行代码会导致编译器错误。
  • 代码执行newItem->parent = malloc(sizeof(MenuItem));,然后在几行之后:newItem->parent = parentItem;,丢弃之前的分配。当然不需要第一次分配。
  • 虽然可以指出函数中的明显问题,但如果没有A Minimal, Complete, and Verifiable Example (MCVE),就无法判断这是否是您所看到问题的根源。使用 MCVE 可以编译和验证代码的工作原理。
  • 请注意,void (*func)(); 没有指定函数所需的参数列表——参数列表可以是任何固定的参数列表(不是像 printf() 这样的变量参数;这需要一个适当的原型省略号 , ... 在签名的末尾。最好避免使用此类指向函数的非原型指针,尽管有时这样做可能会令人担忧。(这与您当前面临的问题完全相干;它可能变得相关不过稍后。)
  • @samspencer 在 uC 设计中使用 malloc 是一个坏习惯,应该避免。许多嵌入式编码标准明确禁止动态分配。 Yuor 代码表明您刚刚开始使用 uC,并且已经习惯了使用几乎无限资源的大型机器。

标签: c arrays pointers struct dynamic


【解决方案1】:

编译错误似乎在这行代码上:

parentItem->child[newIndex] = newItem;

您使用child 作为指向可扩展菜单项数组的指针(注意:items 不是指向项的指针),通过调用realloc() 对其进行扩展。问题是,虽然您在 parentItem->child 的末尾为新项目创建了新空间,但您尝试将 newItem 指针分配给子数组,而您需要 memcpy() 项目将其自身放入您创建的新空间,然后释放最近分配的newItem。例如:

memcpy(&parentItem->child[newIndex], newItem, sizeof(*newItem));
free(newItem);

更好的选择是将 child 从 struct MenuItems 的数组更改为有效地成为指向 struct MenuItems 的指针数组,然后您可以简单地分配新分配的项目。

【讨论】:

    【解决方案2】:

    因为一个文件夹(父级)可能包含多个子项,我需要动态分配内存来保存父级中新子项的指针。

    嗯,不,您不一定需要为此进行动态分配。您可以改为选择每个文件夹可能具有的子项数量的固定上限,并将该维度的数组放入struct MenuItems。由于您说的是只存储 pointers 给孩子,这看起来像这样:

    typedef struct MenuItems
    {
        struct MenuItems *parent;
        struct MenuItems *child[MAX_CHILDREN];
        uint8_t numChildren;
        // ...
    

    但动态分配方法确实为您提供了更大的灵活性,并且可能还会更有效地使用内存,但代价是代码复杂性。大多数人会采取这种权衡。但现在将上述内容与您实际声明的相应部分进行比较:

    typedef struct MenuItems
    {
        struct MenuItems *parent;   // pointer to parent level item (-1 level)
        struct MenuItems *child;    // pointer to child level items (+1 level) (FOLDER ONLY)
        uint8_t numChildren;        // number of child items held by this item (FOLDER ONLY)
    

    仔细比较MenuItem.child 的两个不同声明的类型。它们绝对不能互换。

    我收到错误“从类型'MenuItem * {aka struct MenuItems *}'分配给类型'struct MenuItems'时类型不兼容”

    我确定你是。这将从以下声明中得出:

            parentItem->child[newIndex] = newItem;
    

    ,假设newItem 的类型是struct MenuItems *。

    该语句对于我提出的数组替代方案是正确的,因为parentItem->child[newIndex] 的类型也是struct MenuItems *。但这就是我让你比较的原因。在 您的 情况下,parentItem->child[newIndex] 的类型是 struct MenuItems(即,不是指针)。这适用于存储子菜单项本身,而不是指向它们的指针。这也是一种可行的方法。

    然而,假设您想坚持您声明的策略,并让每个文件夹项存储 指针 指向其子项,您需要适当地修改 struct MenuItems:

    typedef struct MenuItems
    {
        struct MenuItems *parent;
        struct MenuItems **child;
        uint8_t numChildren;
        // ...
    

    然后,您还希望减少为 child 成员分配的大小,因为您再次说要在其中存储 指针,而不是子节点本身。

    【讨论】:

    • 嗨约翰,这是一个非常有用的答案,感谢您抽出宝贵的时间。因此,如果我理解正确,我的主要问题是使用“struct MenuItems child}”而不是“struct MenuItems **child”。所以要正确分配只需要存储指针的内存,我的 malloc/realloc 大小应该简单地是“sizeof(struct MenuItems)”,对吗?
    • @SamSpencer,阅读我认为您通过 SO 解释的格式实际键入的内容,是的,这是正确的。
    • 哦,是的,对不起。我的意思是说 sizeof(struct MenuItems*)。再次感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-18
    • 2022-01-14
    • 2020-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多