Нестатическая инициализация гибкого элемента массива? - PullRequest
0 голосов
/ 29 декабря 2018
#include <stdio.h>
#include <limits.h>

typedef struct item {
    int low; int high; char label[16];
} Item;

typedef struct item_coll {
    size_t length; Item *items[];
} ItemColl;

char *find_first_in_range(ItemColl *ic, int rlow, int rhigh) {
    for (size_t i = 0; i < ic->length; i++)
        if (ic->items[i]->low >= rlow && ic->items[i]->high <= rhigh)
            return &ic->items[i]->label[0];
    return NULL;
}

int main() {
    struct item fruits[] = {
        {10, 20, "Apple"},
        {12, 14, "Pear"},
        {8, 12, "Banana"},
        {2, 4, "Grape"},
        {15, 35, "Watermelon"}
    };

    struct item_coll basket = {5, fruits};

    printf("%s", find_first_in_range(&basket, 21, 22));

    return 0;
}

Это дает мне app.c:28:32: error: non-static initialization of a flexible array member struct item_coll basket = {5, fruits};

, и ошибка указывает на fruits.

Что это значит?Это выглядит хорошо для меня.

Ответы [ 2 ]

0 голосов
/ 29 декабря 2018

Если это экзамен, и вы должны использовать Flexible Array Member, то, как я указал в комментариях и как @rici объяснил в его ответе, цель FAM - предоставить заполнитель для данного typeтогда это позволяет вам выделить хранилище для самой структуры плюс хранилище для некоторого числа вашего типа FAM в одном выделении.Преимущество, которое это дает, состоит в том, что для структуры используется одиночное распределение / одиночное освобождение, а не отдельное выделение, а затем выделение для некоторого числа нужного вам типа.

(до FAM существовало так называемое struct hack , где вместо него использовался массив размером 1 для почти той же цели)

type имеет решающее значение для того, как вы справляетесь и распределяете свои FAM.В вашем случае ваш FAM равен item *items[]; (массив указателей для типа item - Item в вашем коде). Таким образом, вы выделяете для структуры, а затем X количество указателей на item.

Чтобы инициализировать каждого члена items, необходимо присвоить действительный адрес структуре типа item (или вы можете отдельно выделить, скопировать в новый блок, а затем назначить начальныйадрес для этого блока на указатель в items) В вашем случае у вас есть массив struct item, называемый fruits.Чтобы присвоить items, вы должны назначить адрес каждой структуры для каждого элемента в items (помните, что у вас есть хранилище для указателей, а не хранилище для структуры item - и вы должны обеспечить fruits остается в области действия на время вашего использования basket)

Соединяя эти кусочки, вы можете сделать что-то похожее на следующее:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

typedef struct {
    int low, high;
    char label[16];
} item;

typedef struct {
    size_t length;
    item *items[];
} item_coll;

char *find_first_in_range(item_coll *ic, int rlow, int rhigh) 
{
    for (size_t i = 0; i < ic->length; i++)
        if (ic->items[i]->low >= rlow && ic->items[i]->high <= rhigh)
            return ic->items[i]->label;
    return NULL;
}

int main() {

    item fruits[] = {
        {10, 20, "Apple"},
        {12, 14, "Pear"},
        { 8, 12, "Banana"},
        { 2,  4, "Grape"},
        {15, 35, "Watermelon"}
    };
    size_t nfruits = sizeof fruits/sizeof *fruits;  /* avoid magic-numbers */
    /* allocate storage for basket + nfruits pointers */
    item_coll *basket = malloc (sizeof *basket + 
                                nfruits * sizeof *basket->items);
    if (!basket) {  /* validate allocation succeeded */
        perror ("malloc-basket+5_item_coll");
        return 1;
    }
    basket->length = nfruits;   /* assign length */

    for (size_t i = 0; i < nfruits; i++)  /* assign addresses to structs */
        basket->items[i] = &fruits[i];

    char *label = find_first_in_range (basket, 12, 15);  /* save return */
    if (label)  /* validate not NULL before printing */
        printf ("%s\n", label);

    free (basket);  /* don't forget to free the memory you allocate */

    return 0;
}

( note Я просто использовал typedefs и сам удалил метки структуры - это ваше дело. Кроме того, вы должны проверить возвращаемое значение find_first_in_range не NULL перед печатью.)

Пример использования / вывода

Также обратите внимание, что я ограничил диапазон high/low для

$ ./bin/fam_initialization
Pear

Проверка использования памяти / ошибок

В любом написанном вами коде, который динамически распределяет память, у вас есть 2 обязанностей в отношении любого выделенного блока памяти: (1) всегда сохраняйте указатель на начальный адрес для блока памятитак, (2) он может быть освобожден , когда онбольше не требуется.

Крайне важно, чтобы вы использовали программу проверки ошибок памяти, чтобы убедиться, что вы не пытаетесь получить доступ к памяти или писать за пределами / за пределами выделенного блока, пытаться прочитать или создать условное выражениеперейти к неинициализированному значению и, наконец, подтвердить, что вы освобождаете всю выделенную память.

Для Linux valgrind - нормальный выбор.Для каждой платформы есть похожие проверки памяти.Все они просты в использовании, просто запустите вашу программу через нее.

$ valgrind ./bin/fam_initialization
==6887== Memcheck, a memory error detector
==6887== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==6887== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==6887== Command: ./bin/fam_initialization
==6887==
Pear
==6887==
==6887== HEAP SUMMARY:
==6887==     in use at exit: 0 bytes in 0 blocks
==6887==   total heap usage: 1 allocs, 1 frees, 48 bytes allocated
==6887==
==6887== All heap blocks were freed -- no leaks are possible
==6887==
==6887== For counts of detected and suppressed errors, rerun with: -v
==6887== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Всегда подтверждайте, что вы освободили всю выделенную память и нет ошибок памяти.

Посмотрите вещии дайте мне знать, если у вас есть дополнительные вопросы.

0 голосов
/ 29 декабря 2018

Я сильно подозреваю, что вы имели в виду это определение:

typedef struct item_coll {
    size_t length; Item *items;
} ItemColl;

т.е. длина и массив элементов, а не массив указателей на элемент.

Как написано, массив указателейне имеет указанной длины, что делает struct фактически неполным;компилятор не может сказать, что это так, поэтому вы никогда не сможете определить его.C11 позволяет такого рода декларации для удобства.Массив неопределенной длины - «член гибкого массива» - должен находиться в самом конце struct, а сам struct нельзя использовать внутри другого struct.

Вы можете использовать структуру с элементом гибкого массива в качестве своего рода основы для конкретного динамически размещаемого объекта, размер которого можно вычислить как sizeof (Struct) + n * sizeof (ArrayElement).Это иногда полезно в качестве оптимизации, и это законно;все, что я скажу, - это то, что его очевидные преимущества необходимо сопоставить с сложностями кода, которые он создает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...