Структура, которая содержит массив массивов символов
Я пытаюсь определить экземпляр структуры, и у меня возникли особые проблемы с установкой этой переменной. Это массив массивов символов.
Вот моя структура в моем заголовочном файле...
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 ответа
Один из вариантов будет:
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
Проблема в том, что 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;
};
То, что вы пытаетесь сделать, это назначить строковый литерал 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
вместо.
Здесь есть разные проблемы.
Сначала ваш последний член неполного типа, потому что это массив необъявленного измерения. Это не разрешено в 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