Что происходит с точки зрения памяти, когда я вызываю функцию из другой функции?
Я проверяю, где используется память в приложении, которое работает на Arduino. К моему удивлению, есть несколько мест, где простым вызовом функции потребляется от 100 до 200 байт. Пример:
void Dispatcher::processCommand() {
...
displayMemory();
this->process(this->instruction);
}
void Dispatcher::process(const Instruction &instruction) {
displayMemory();
if (instruction.length > 0) {
...
return;
}
ErrorResponse::componentNotFound(instruction).output(this->serial);
TerminationResponse(instruction.commandId, 0xFF).output(this->serial);
}
Первый вызов DisplayMemory()
сообщит, что осталось 351 байт. Второй сообщит, что осталось всего 159 байт (то есть на 192 байта меньше).
Если я изменю тело функции процесса
, разница между двумя отчетами о памяти изменится. Например, если я удалю две последние строки, разница уменьшится со 192 байт до 12 байт.
Я не понимаю, почему это происходит. Я думал, что между моментом, когда я готов вызвать функцию, и моментом, когда функция запускается, память должна использоваться исключительно дополнительным элементом в стеке вызовов (может быть, вышеупомянутыми 12 байтами?) и копированием параметров, передаваемых по значению (здесь нет). Почему это не так? Что на самом деле происходит под капотом?
2 ответа
Лучший ответ:
Ключ к разгадке кроется в этих последних двух строках:
ErrorResponse::componentNotFound(instruction).output(this->serial);
TerminationResponse(instruction.commandId, 0xFF).output(this->serial);
Давайте разорвем их на части и посмотрим, что они на самом деле делают.
ErrorResponse::componentNotFound(instruction).output(this->serial);
Вызовите статическую функцию componentNotFound
с помощью вашей инструкции
, затем возьмите возвращаемый объект и вызовите для него метод output()
, передав this->serial
в качестве параметра.
Подсказка там заключается в том, чтобы взять возвращаемый объект.
componentNotFound()
возвращает объект. Этот объект должен где-то храниться. Это "где-то" находится в стеке. А пространство стека выделяется во время преамбулы функции. Это фрагмент кода, который выполняется в начале функции, выделяющей стек, помещающей регистры в стек и т.д.
То же самое происходит и во второй строке:
TerminationResponse(instruction.commandId, 0xFF).output(this->serial);
Создайте новый объект TerminationResponse и вызовите метод
вывода
.
Все это попадает в стек.
Это не вызов функции, которая использует память, это то, что делает функция, которая использует память.
Пространство памяти данных функции резервируется в стеке при вызове функции и возвращается при возврате функции. Если эта function1 вызывает другую function2, то место function1 остается в стеке, и больше места зарезервировано для function2 на время его выполнения. Когда функция 2 возвращается к функции 1, память функции 2 освобождается, и, наконец, когда функция 1 возвращается, ее память освобождается.
Теперь немного подробнее:
Когда вы вызываете функцию function1, аргументы, которые вы ей передаете, помещаются в стек. Когда управление фактически переходит к function1, адрес инструкции, следующий за вызовом, куда function1 в конечном итоге должен вернуться, помещается в стек. Если функции 1 требуется временное пространство данных для данных (автоматические переменные), место для них помещается в стек. (То же самое для функции 2, если она вызывается из функции 1; и так далее.)
Когда каждая из этих функций возвращается, их автоматическое пространство данных (только пространство, а не данные!) и адрес возврата удаляются из стека; вызывающий удаляет пространство для переданных им аргументов, и выполнение возобновляется сразу после вызова.
Разница в использовании, которую вы видите в зависимости от наличия или отсутствия последних двух строк Dispatcher::process() , заключается в памяти (аргументы + адрес возврата + автоматические данные), используемой этими двумя функциями.
- Получить доступ к EEPROM ATtiny с помощью кода Arduino?
- Выделение строковой памяти Arduino
- Как очистить кучу памяти в esp32
- Есть ли способ подключить оперативную память компьютера к Arduino?
- Последовательная печать из флэш-памяти (F() macro, PROGMEM, sprintf_P, SPTR)
- Альтернатива SoftwareSerial с низким объемом памяти?
- Как функция/метод может определить, является ли передаваемый массив const PROGMEM (flash) или нет (RAM)?
- Считывание байтов из массива PROGMEM