С++ для микроконтроллеров - управление памятью

Как известно, в языке Си для динамического выделения и освобождения памяти на куче используются функции malloc() и free(). В С++ для этих целей предусмотрены операторы new и delete. Когда оператор new не может удовлетворить запрос на выделение памяти, он возбуждает исключение std::bad_alloc, сигнализируя тем самым об ошибке. В C++ для микроконтроллеров механизм обработки исключений обычно отключён для экономии ресурсов, однако согласно спецификации С++, прежде чем возбудить исключение std::bad_alloc, оператор new вызывает специальную функцию-обработчик. Эту функцию можно переопределять std::set_new_handler, что позволяет хоть как-то контролировать / фиксировать ошибки выделения памяти на куче в процессе выполнения программы. Но это только верхушка айсберга, а ниже описаны более эффективные и надёжные приёмы. Чтобы было нескучно, в качестве тестового материала взята игра «Змейка».

MSP430.js | исходники

screenshot

  • операторы new и delete в С++ можно перегружать, а в контексте микроконтроллеров может оказаться весьма полезным вообще сломать компиляцию при попытке динамического выделения памяти на куче no-new.h. Для того, чтобы проверка происходила во всех исходных файлах проекта автоматически в gcc подобных компиляторах достаточно просто указать в командной строке опцию -include no-new.h, в случае со «Змейкой» используется IDE Code Composer Studio, там есть настройка --preinclude. Что касается наследия Си — функций динамического выделения памяти malloc, calloc, realloc — то красиво отцепить их увы не получится. У gcc компиляторов есть волшебный флаг компоновщика -Wl,--wrap=malloc, который даёт возможность ставить обёртки на функции. Как это сделать на примере покажу чуть позже

  • «Змейка» кушает и растёт, т.е. представляет из себя динамический массив. Для STL контейнеров в С++ можно задавать альтернативные аллокаторы памяти. В большинстве случаев разработчик знает, сколько элементов контейнера может потребоваться для его алгоритма и тогда хорошей идеей выглядит аллокатор, использующий стек вместо кучи. Для древнего C++98 найти рабочий аллокатор затруднительно, поэтому для игры «Змейка» был взят этот контейнер, также использующий стек вместо кучи

  • существует особая форма оператора new, называемая Placement new. Данный оператор не выделяет память, а получает своим аргументом адрес на уже выделенную каким-либо образом память. Например память под объект «Змейка» выделяется в стеке на этапе компиляции, а размещение (инициализация) объекта в ней путём вызова конструктора происходит в процессе выполнения программы, таким образом можно пересоздавать «Змейку» при перезапуске игры

Под капотом «Змейки» QP/C++ и соответствущая диаграмма состояний UML:

screenshot

На картинке иерархический конечный автомат Play где отсчитываются временные интервалы для движения «Змейки» и обрабатывается нажатие от кнопок. Если метод eat_or_move() вернёт false переход в состояние GameOver где отображается количество набранных очков. При нажатии любой кнопки снова Play.

«Змейка» представляет из себя динамический массив сегментов (квадратиков), для её передвижения в заданном направлении достаточно добавить новый сегмент головы в один конец массива и удалить сегмент хвоста — последний элемент с другого конца. На экране аналогично достаточно очистить хвост и дорисовать голову. При поедании пищи добавляется сегмент головы, а сегмент хвоста не удаляется.

Далее паттерн «Наблюдатель».

links

social