Понимание глобальных переменных на Arduino

Я переписываю набор функций манипулирования светодиодами, чтобы они были более объектно-ориентированными, и обнаружил, что каким-то загадочным образом пространство моей памяти для глобальных переменных было перегружено, несмотря на сокращение использования глобальных переменных.

Я поигрался с множеством вещей и нашел кое-что любопытное.

Вот фрагмент функции, которая не находится в объекте и вызывается напрямую из loop():

Animation *getNewAnim(int index)
{
    switch(index)
    {
        case 0:
            return new RedClouds();
        case 1:
            return new TwoLayerClouds();
        case 2:
            return new LavaLamp1();
        case 3:
            return new Bubbles1();

        ...  (20 or so more cases)
     }

Это функция, которая позволяет мне переключаться между режимами отображения на основе значения index. В данный момент времени создается только один из этих объектов; Я удаляю старый перед вызовом этого, поэтому одновременно не будет использоваться несколько экземпляров.

Любопытно то, что если я удалю несколько из этих операторов возврата, блок свернётся примерно так:

Animation *getNewAnim(int index)
{
    switch(index)
    {
        case 0:
        case 1:
        case 2:
        case 3:
            return new Bubbles1();
}

Объем используемой глобальной памяти значительно снижается.

Моя гипотеза заключается в том, что среда сборки Arduino предварительно выделяет место для каждого из этих объектов в глобальной памяти еще до запуска программы, а не делает это динамически по мере необходимости.

Правильна ли моя гипотеза? И если да, то есть ли способ сказать среде сборки не делать этого, поскольку я предпочитаю использовать для моего нового объекта ту же память, которая использовалась старым.

Для пояснения: при сборке я вижу такой результат:

Sketch uses 6,550 bytes (21.3%) of program storage space. Maximum is 30,720 bytes.
Global variables use 1,167 bytes (57.0%) of dynamic memory, leaving 881 bytes for local variables. Maximum is 2,048 bytes.

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

НОВАЯ ГИПОТЕЗА

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

, 👍6

Обсуждение

Вы имеете в виду PROGMEM или RAM? Пожалуйста, опубликуйте [Минимальный, полный и проверяемый пример](http://stackoverflow.com/help/mcve). Я не понимаю, как «среда сборки» (в основном g++) может предварительно выделить пространство для объектов, которые в любом случае будут распределяться динамически. Как вы измеряете «глобальное пространство памяти» и что именно вы под этим подразумеваете?, @Nick Gammon

return new Bubbles1(); - зачем скобки? Почему бы и нет: вернуть новые пузырьки1;?, @Nick Gammon

@Ник: Я добавил немного внизу своего вопроса, который, надеюсь, прояснит ситуацию. Что касается ()... хороший вопрос. Я посмотрю на это., @watusimoto

Не видя остальную часть вашего кода, а только [фрагмент](http://snippets-r-us.com/), трудно сказать. Возможно, другие классы имеют статические переменные. Если компилятор никогда не видит их создания, он может пропустить статические переменные., @Nick Gammon

Сокращение памяти программы имеет смысл; если экземпляр класса никогда не создается, компоновщик, вероятно, не будет включать код. Однако я попробовал добавить больше статики как в класс, так и в некоторые его методы, и это вообще не повлияло на глобальное пространство памяти (чего я и ожидал). Если мне удастся решить несвязанную проблему и убедиться, что с лицензией все в порядке, я, вероятно, смогу загрузить все это на github., @watusimoto

Люди часто спрашивают: «Почему программа становится намного больше, когда я **вызываю** функцию?». С их точки зрения, они добавили всего одну строку кода. С точки зрения компилятора, вещи, которые были вызваны, теперь необходимы, тогда как раньше они не были нужны., @Nick Gammon

Я должен отметить, что это немного выдуманная проблема. Вы спрашиваете: «Если я опускаю какой-то код (чего на практике я не могу), почему исполняемый файл становится меньше и использует меньше оперативной памяти?». Это гипотетическая проблема. Вы *не* пропустите код. И в компиляторе нет переключателя типа «не тратить память». Это уже высокооптимизирующий компилятор. Он склонен делать вещи, которые выглядят странно, если вы этого не осознаете., @Nick Gammon

В каком-то смысле это теоретически, но если я смогу понять, почему происходит то, что я вижу, я смогу спроектировать эту проблему. На самом деле я просто догадываюсь о том, что происходит в компиляторе, и, как вы говорите, он не всегда ведет себя так, как ожидалось. Что мне нужно, так это инструмент, который сообщал бы мне, что компилятор помещает в пространство глобальных переменных., @watusimoto

Класс Animation определяет ряд виртуальных функций-членов. Компоновщику сложно удалить виртуальные функции-члены, которые не используются/вызываются, поскольку в виртуальной таблице есть ссылка. Если функции используют строковые литералы, все они размещаются в «глобальных переменных». Вот почему важно использовать макрос F()., @Mikael Patel

http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_flashstrings, @Mikael Patel

см. также http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_varinit., @Mikael Patel

Попробуйте запустить avr-nm -Crtd --size-sort для вашего файла elf. Ищите символы типа «D» (инициализированные данные), «B» (BSS) и «V» (vtable) в верхнем или нижнем регистре. Полученный список отсортирован по размеру. Литеральные строки могут отсутствовать в этом списке, но вы увидите их с помощью avr-objdump -s -j .data., @Edgar Bonet


1 ответ


1

Я предлагаю вам взглянуть на https://stackoverflow.com/questions/16274451/operator- new-for-arduino оператор new взят не из библиотек C++, он взят из библиотек Arduino. Это оболочка для malloc. Использование malloc() и free() - плохая идея для Arduino?

,