Структура, которая содержит массив массивов символов - PullRequest
0 голосов
/ 16 января 2019

Я пытаюсь определить экземпляр структуры, и у меня возникают особые проблемы с настройкой этой переменной. Это массив массивов символов.

Вот моя структура в моем заголовочном файле ...

struct widget_t {
    char *name;
    uint8_t numberOfNicknames;
    char *nicknames[];
};

И вот я пытаюсь настроить экземпляр структуры widget_t ...

widget_t SomeWidget;

void setUpFunction () {
    SomeWidget.name = (char *)"Lawn Mower";
    SomeWidget.numberOfNicknames = 2;
    SomeWidget.nicknames = {
        "Choppie McGrasschopper",
        "Really Noisy"
    };
}

Итак, ошибка возникает, когда я пытаюсь ввести псевдонимы в SomeWidget.nicknames. Я не уверен, что мне нужно сделать что-то напуганное, как я делаю, когда name является указателем ...?

Хитрость в том, что число nicknames является переменным. Таким образом, каждый экземпляр захочет установить разное их количество.

Ответы [ 4 ]

0 голосов
/ 16 января 2019

Здесь разные проблемы.

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

Далее вы пытаетесь присвоить массиву. Ты не можешь. Массивы не являются объектами первого класса в C ++ (и в C). Вы можете инициализировать массив целиком, но можете назначить его только элементу массива.

И, наконец, вы присваиваете литеральную строку C для char *. Это плохо, потому что стандарт объявляет, что литеральные строки const, поэтому использование более позднего указателя для изменения символа будет неопределенным поведением. Это будет работать, если вы этого не сделаете, но указатели должны быть по крайней мере объявлены как const.

Вот как вы могли бы использовать все это:

widget_t* setUpFunction () {
    // allocates a widget_t with 2 slots in nicknames
    widget_t *someWidget = (widget_t *) malloc(sizeof(widget_t) + 2 * sizeof(char *));
    someWidget.name = (char *)"Lawn Mower";   // VERY DANGEROUS: pointer should be const
    someWidget.numberOfNicknames = 2;
    someWidget.nicknames[0] = (char *) "Choppie McGrasschopper"; // SAME DANGER
    someWidget.nicknames[1] = (char *) "Really Noisy"            // Still same danger
    return widget_t;
}

Но все это скорее C-ish и его следует избегать в C ++. Кроме того, он все еще требует выделенной памяти, что может не соответствовать тому, что вы хотите для Arduino

0 голосов
/ 16 января 2019

Проблема в том, что c ++ не поддерживает переменные массивы. Вместо этого вам придется динамически выделять память, используя new или в вашем случае new [] .

Сначала вам нужно изменить тип данных на char**, почти равный предыдущему. Затем вы можете выделить столько строк, сколько хотите nicknames = new char*[number_of_nicknames].

Важно, что с помощью этого метода вам придется вручную добавлять свои псевдонимы следующим образом: delete[] nicknames;. Лучший способ сделать это - использовать RAII (удалите свои псевдонимы в деконструкторе)

Если у вас есть динамические строки, вы должны использовать следующую структуру

struct widget_t {
    // optional constructor to allocate nicknames

    ~widget_t()
    {
        for (int i = 0; i < numberOfNicknames; ++i)
        {
            char* nickname = nicknames[i];

            if (nickname)
                delete[] nickname;
        }

        delete[] nicknames;
    }

    char *name;
    uint8_t numberOfNicknames;
    char **nicknames = NULL;
};

и с постоянной строкой следующий

struct widget_t {
    // optional constructor to allocate nicknames
    // allocate nicknames like
    // -> nicknames = new const char*[numberOfNicknames];

    ~widget_t()
    {
         if (nicknames) delete[] nicknames;
    }

    char *name;
    uint8_t numberOfNicknames;
    const char **nicknames = NULL;
};
0 голосов
/ 16 января 2019

Один из вариантов будет:

struct widget_t {
    char const *name;
    uint8_t numberOfNicknames;
    char const * const *nicknames;
};

static char const *mower_nicknames[] = { "Choppie", "Bob" };
widget_t SomeWidget = { "Lawn Mower", 2, mower_nicknames };

static char const *bus_nicknames[] = { "Wheels", "Go", "Round" };
widget_t OtherWidget = { "Bus", 3, bus_nicknames };

// no setup function needed
0 голосов
/ 16 января 2019

Что вы пытаетесь сделать, это назначить строковый литерал указателю char * - это плохо (подробнее см. Как избавиться от deprecated conversion from string constant to ‘char*’ предупреждений в GCC? ). Вот возможный подход:

#define MAX_NAME_LEN 128
#define MAX_NICK_NAMES 10

struct widget_t {
    char name[MAX_NAME_LEN];
    uint8_t numberOfNicknames;
    char nicknames[MAX_NICK_NAMES][MAX_NAME_LEN];
};

widget_t SomeWidget;

void setUpFunction () {
    strcpy(SomeWidget.name, "Lawn Mower");
    SomeWidget.numberOfNicknames = 2;
    strcpy(SomeWidget.nicknames[0], "Choppie McGrasschopper");
    strcpy(SomeWidget.nicknames[1], "Really Noisy");
}

В любом случае, поскольку вы пометите свой вопрос с помощью C++, я бы посоветовал вам использовать std::string и std::vector вместо этого.

...