Почему указатель на класс занимает меньше памяти SRAM, чем «классическая» переменная

#c #pointers #optimization #memory #arduino

Вопрос:

у меня есть микро Arduino с 3-мя микродатчиками ЛИДАРА времени полета, приваренными к нему. В своем коде я создавал 3 глобальные переменные, подобные этой:

 Adafruit_VL53L0X lox0 = Adafruit_VL53L0X();
Adafruit_VL53L0X lox1 = Adafruit_VL53L0X();
Adafruit_VL53L0X lox2 = Adafruit_VL53L0X();
 

И это заняло около 80% памяти
Теперь я создаю свои объекты вот так

 Adafruit_VL53L0X *lox_array[3] = {new Adafruit_VL53L0X(), new Adafruit_VL53L0X(), new Adafruit_VL53L0X()};
 

И это занимает 30% всей моей программы

Я пытаюсь просмотреть документацию по arduino, но не нахожу ничего, что могло бы мне помочь. Я могу понять, что создание «классического» объекта может заполнить память. Но где находится зона памяти при создании указателя ?

Комментарии:

1. Как вы измеряете использование памяти программы ?

2. Не имея более глубоких знаний об Arduino, я подозреваю, что глобальные переменные занимают память, отличную от выделенной new . Однако… К Вашему сведению: Память Arduino и сокращение использования Вашей памяти

3. @TedLyngmo Это, должно быть, опечатка из-за копирования и вставки.

4. @Fareanor Да, наверное. Я изменил его, чтобы не было больно глазам.

5. @Fareanor В Arduino IDE сообщает мне следующее: «Глобальные переменные используют 810 байт (31%) динамической памяти, оставляя 1750 байт для локальных переменных. Максимум-2560 байт». когда вы проверяете код или загружаете его на карту

Ответ №1:

В любом случае вы используете одинаковый объем памяти. (на самом деле, второй способ использует немного больше, потому что указатели также необходимо хранить.)

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

Я осмелюсь сказать, что второй метод более опасен, потому что рассмотрим следующий сценарий: предположим, что ваш другой код и данные уже используют 90% памяти во время компиляции. Если вы используете свой первый метод, вы не сможете загрузить программу, потому что теперь она будет использовать что-то около 150%, так что вы уже знаете, что она не будет работать. Но если вы используете второй метод, ваша программа будет скомпилирована и загружена просто отлично, но затем произойдет сбой при попытке выделить дополнительную память во время выполнения.

(Кстати, сообщение компилятора немного неполное. Скорее следует сказать «оставляя 1750 байт для локальных переменных и динамически выделяемых объектов» или что-то в этом роде).

введите описание изображения здесь

Вы можете проверить это самостоятельно, используя эту функцию, которая позволяет оценить объем свободной памяти во время выполнения (сравнивая верхнюю часть кучи с нижней [физически, а не логически] частью стека, последнее достигается путем просмотра адреса локальной переменной, которая была бы выделена в стеке в этот момент).:

 int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) amp;v - (__brkval == 0 ? (int) amp;__heap_start : (int) __brkval);
}
 

См. также: https://playground.arduino.cc/Code/AvailableMemory/