#arrays #c #string #struct
Вопрос:
Я разрабатываю простую , но гибкую систему меню для встроенной системы в C. Из-за ограничений платформы я хочу избежать динамического распределения и хочу, чтобы все определялось статически.
У меня есть тип меню и тип элемента меню, определенные как структуры, с меню, содержащим несколько элементов меню. Каждый пункт меню будет иметь метку, тип элемента, а затем указатель на массив опций.
Та часть, которая для меня не работает, — это создание ссылки на массив опций. Я получаю предупреждение со словами
warning: incompatible pointer types initializing 'char *' with an expression of type 'char *(*)[3]' [-Wincompatible-pointer-types]
MenuItem menu_item1 = {"Day", OPTION_LIST, 0,0,0, 4, amp;dayOptionList};
Любые предложения по правильному подходу будут высоко оценены.
#define MAX_MENU_ITEMS 16
typedef enum {OPTION_LIST, OPTION_NUMERIC, OPTION_BINARY} ItemOptionType;
typedef struct MenuItem {
char* label;
ItemOptionType optionType;
unsigned char min;
unsigned char max;
unsigned char value;
unsigned char optionCount;
char* optionList[];
} MenuItem;
typedef struct Menu {
unsigned char count;
MenuItem* items[MAX_MENU_ITEMS];
} Menu;
unsigned int MenuAddItem(Menu* menu, MenuItem* item) {
if (menu->count < MAX_MENU_ITEMS) {
menu->items[menu->count] = item;
menu->count ;
return 1;
}
return 0;
}
char* dayOptionList[] = {"Monday", "Tuesday", "Wednesday" "Thursday", "Friday", "Saturday", "Sunday"};
Menu mainMenu;
MenuItem menu_item1 = {"Day", OPTION_LIST, 0,0,0, 4, amp;dayOptionList};
MenuItem menu_item2 = {"Age", OPTION_NUMERIC, 0, 120, 25, 0, NULL};
int main(int argc, char *argv[]) {
MenuAddItem(amp;mainMenu, amp;menu_item1);
MenuAddItem(amp;mainMenu, amp;menu_item2);
while(1);
}
Комментарии:
1.
char* optionList[];
является гибким элементом массива . Вы не можете получить их без динамического распределения. То, что вы, вероятно, хотите, это что-то вродеchar **
. А затем инициализируйтеoptionList
с помощью простогоdayOptionList
(без оператора указателя toi*
).2. Кстати, полученная вами ошибка (или предупреждение) не соответствует показанному вами коду, если предположить, что вы получили его при инициализации
menu_item1
. Пожалуйста, скопируйте и вставьте полный и завершенный вывод сборки из кода, который вы на самом деле показываете.3. Ну, у вас могут быть гибкие элементы массива без выделения кучи, но тогда выделение памяти должно обрабатываться вручную путем настройки разделов памяти по фиксированным адресам и т. Д. Обычно это очень громоздкое решение, я сделал это в одном проекте, но не был доволен результатами — слишком много дополнительной сложности.
4. Я исправил образец с полным выводом компилятора. Извини за это.
Ответ №1:
typedef struct mmMenuItem {
...
char* optionList[];
} MenuItem;
В приведенном выше объявлении последняя строка является очень специальным объявлением «гибкий массив». Это позволяет иметь структуры произвольных размеров в «c». Кроме того, когда вы объявляете какой-либо массив с помощью ‘[]’, он не является реальным указателем, все его члены должны присутствовать в структуре.
В вашем случае вам просто нужен указатель на статически выделенный массив строк, который может быть выражен как
typedef struct mmMenuItem {
...
char** optionList;
} MenuItem;
Это объявление статически выделяет массив из 7 элементов. Компилятор делает это за вас:
char* dayOptionList[] = {"Monday", "Tuesday", "Wednesday" "Thursday", "Friday", "Saturday", "Sunday"};
Имена массивов в «c» также представляют их адреса. Таким образом, когда вы передаете указатель на массив в парах, вам не нужно использовать . amp;
MenuItem menu_item1 = {"Day", OPTION_LIST, 0,0,0, 4, dayOptionList};
^^^^^
Ответ №2:
Ошибка возникает при попытке назначить a char*
указателю массива, которые не являются совместимыми типами.
Прежде всего, char* dayOptionList[]
это неправильно в любой системе, так как строковые литералы доступны только для чтения. Но это особенно неправильно во встроенных системах, потому что вы хотите, чтобы эта таблица размещалась во флэш-памяти, а не в оперативной памяти. Правильный код-это:
const char* dayOptionList[] = { ...
Теперь, если у вас есть список строк, подобных этому:
const char* dayOptionList[] = {"Monday", "Tuesday", "Wednesday" "Thursday", "Friday", "Saturday", "Sunday"};
Тогда разумным типом элемента , который следует использовать в структуре, является a const char**
, чтобы указать на первый элемент в приведенном выше массиве. char* optionList[];
объявляет гибкий элемент массива, который, кратко говоря, является неправильным решением другой проблемы.
Однако нам тоже нужно как-то узнать размер массива. Это можно сделать двумя способами:
- Либо включите
NULL
значение sentinel в конце приведенного выше массива. Это оптимизация памяти по скорости, у нас не может быть тривиального прямого доступа, но мы должны перебирать контейнер по элементам. - Или включите в структуру отдельный элемент размера, что, вероятно, является наиболее разумным в большинстве случаев использования. Если мы знаем размер, мы можем получить прямой доступ к элементу в массиве, не рискуя получить доступ за его пределы.
Разное обзор кода, не связанный с вопросом:
- Вы должны продумать макет своей структуры с учетом выравнивания, чтобы в середине не было ненужных, потраченных впустую байтов заполнения. Компилятору не разрешается изменять порядок структуры, поэтому ответственность за это несет программист.
- Вам следует прекратить использовать «примитивные типы данных»
int
и т.unsigned char
Д. , Поскольку они недетерминированы и, следовательно, непереносимы. Профессиональные встраиваемые системы всегда используют систему типовstdint.h
. int main(int argc, char *argv[])
это неправильный формат для встроенных систем микроконтроллеров. Вместо этого они используют impl.definedvoid main (void)
. Если вы используете gcc, скомпилируйте,-ffreestanding
что означает цель встроенной системы.
Комментарии:
1. Спасибо за отзыв. Что касается обзора кода misc. Я разрабатываю библиотеку на настольном компьютере, прежде чем перенести ее в микроконтроллер, чтобы упростить отладку, поэтому использую int main(int argc, char *argv[]). Но есть и другие замечательные моменты. Внесу эти изменения.
2. @Dibly Вы также можете использовать
-ffreestanding
на gcc для ПК, если я правильно помню.