Динамически Распределение Массива Указателей Структуры - PullRequest
0 голосов
/ 01 февраля 2020

Я работаю над API для создания структур меню для моего проекта stm32 (stm32f0). Я создал структуру, которая представляет элемент в меню, и навигация вверх и вниз по уровням отслеживается путем установления связи указателей с родительскими и дочерними структурами в меню. Существует 2 различных типа, папки и цели в зависимости от того, является ли пункт меню настройкой / параметром (целью) или просто папкой.

Вот моя функция для создания нового элемента папки. ParentItem передается функции, которая неотъемлемо создает ссылку для нового элемента меню, однако, поскольку папка (parent) может содержать несколько дочерних элементов, мне нужно динамически выделить память для хранения указателя на новый дочерний элемент в родительском элементе. Мне трудно понять правильный синтаксис указателя, чтобы добиться этого как. Я получаю сообщение об ошибке "несовместимые типы при назначении типу" struct MenuItems "из типа" MenuItem * {aka struct MenuItems *} '"

Вот мой файл .h с typedefs:

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

Заранее спасибо!

Ответы [ 2 ]

1 голос
/ 01 февраля 2020

, поскольку папка (родитель) может содержать несколько дочерних элементов, мне нужно динамически выделить память для хранения указателя на нового дочернего элемента в родительском элементе.

Ну, нет, вы для этого не обязательно нужно Dynami c. Вместо этого вы можете выбрать фиксированную верхнюю границу для количества дочерних элементов в каждой папке и поместить массив этого измерения в struct MenuItems. Поскольку вы говорите о хранении только указателей для детей, это выглядело бы примерно так:

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

Но динамический подход к распределению c дает вам большую гибкость и возможно также более эффективное использование памяти за счет сложности кода. Большинство людей взяли бы этот обмен. Но теперь сравните вышесказанное с соответствующей частью вашего фактического объявления:

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. Они никоим образом не взаимозаменяемы.

Я получаю сообщение об ошибке "несовместимые типы при назначении типа" struct MenuItems "из типа" MenuItem * {aka 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, потому что, опять же, вы говорите, что хотите хранить указатели там, не сами дети.

1 голос
/ 01 февраля 2020

Ошибка компиляции в этой строке кода:

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, тогда вы могли бы просто назначить вновь выделенный элемент .

...