Условное присвоение массива
У меня есть очень длинные массивы глобальных переменных, заполненные данными.
Есть единственная функция, которая обрабатывает данные. Функция каждый раз работает только с одним массивом. Значение этого массива изменяется на каждой итерации и получает значение из массивов глобальных переменных.
Поскольку я объявляю множество массивов глобальных переменных, чтобы передать их функции array_to_be_processed_by_the, память заполняется очень быстро.
Как лучше всего условно присвоить значения массиву?
Например, в программе может быть счетчик, и если счетчик представляет собой определенное значение, массив будет инициализирован определенными данными. Конечно, мне также пришлось бы вызывать функцию, и я должен был бы отслеживать счетчик, увеличивать его и обнулять в начале цикла, чтобы начать с самого начала.
При таком подходе всегда будет инициализирована только одна переменная.
PSEUDOCODE:
int looper = 0;
void loop()
{
switch(looper)
{
case 0:
my_array[] = {DATA HERE};
exec_func(myarray);
looper++;
continue;
case 1:
......
case 'last_case':
...
looper = 0;
break;
}
}
Однако я не уверен, что этот подход правильный. Особенно для микроконтроллера.
Одна очевидная проблема заключается в том, что значения массива не обязательно имеют одинаковый размер.
Следовательно, базовый массив должен уничтожаться в каждом case
?
Если это хороший подход, следует ли использовать new/delete
на Arduino?
Каков наилучший подход к условной инициализации массива, чтобы не выходить из памяти
@user1584421, 👍1
Обсуждение1 ответ
Лучший ответ:
Давайте рассмотрим это:
case 0:
my_array[] = {DATA HERE};
exec_func(myarray);
Это не совсем допустимый C++, но давайте представим, что вы правильно понимаете синтаксис
чтобы заставить его работать. Проблема в том, что это не поможет вам сэкономить
Память. Как вы думаете, где компилятор будет хранить {DATA HERE}
?
В памяти, верно. Он будет храниться как анонимный массив. Затем
инициализация my_array
создаст вторую копию данных в
памяти, на этот раз в именованном массиве.
Вы можете избежать этой второй копии, назвав свои массивы констант и передача правильного в вашу функцию:
// В глобальном масштабе:
const int my_array_0[] = {...};
const int my_array_1[] = {...};
// Внутри переключателя/кейса:
case 0:
exec_func(my_array_0);
Вы даже можете полностью избежать switch
/case
, используя массив
указатели, но это не имеет отношения к вашей текущей проблеме.
Ваша идея инициализировать только тот массив, который вам действительно нужен, может сработать, если вам удается сделать это процедурно, т.е. реализуя какой-то рецепт с инструкции, а не копирование набора констант:
case 0: {
int my_array[array_size];
for (int i = 0; i < array_size; i++) {
my_array[i] = some_expression_to_compute_this_array_item;
}
exec_func(myarray);
}
Однако это не всегда возможно.
Если вам действительно нужно хранить константы в программе, и вы используя Arduino на базе AVR (например, Uno, Mega, Micro...), вы можете экономить оперативную память, сохраняя массивы констант во флеш-памяти, и копируя только тот, который вам нужен для оперативной памяти:
// В глобальном масштабе:
const int my_array_0[] PROGMEM = {...};
const int array_size_0 = sizeof my_array_0 / sizeof my_array_0[0];
const int my_array_1[] PROGMEM = {...};
const int array_size_1 = sizeof my_array_1 / sizeof my_array_1[0];
// Внутри переключателя/кейса:
case 0: {
int my_array[array_size_0]; // массив в оперативной памяти
memcpy_P(my_array, my_array_0, sizeof my_array);
exec_func(my_array);
}
case 1: {
int my_array[array_size_1]; // массив в оперативной памяти
memcpy_P(my_array, my_array_1, sizeof my_array);
exec_func(my_array);
}
Проверьте документацию по PROGMEM и memcpy_P()
для
подробности.
Если вы пойдете по этому пути, вы можете рассмотреть возможность изменения функции
exec_func()
, так что он ожидает, что его параметр будет указателем на flash
вместо указателя на оперативную память. Тогда вы полностью избежите копирования в
ОЗУ.
Правка: расширение идеи передачи указателя во flash.
Компилятор C++ на самом деле не понимает разницы между указателем на ОЗУ и указатель на флешку. Если вы хотите передать указатель flash на функции, вы должны написать функцию таким образом, чтобы она ожидала указатель на вспышку. Например, эта функция выводит содержимое массив на основе флэш-памяти:
// Аргумент должен быть указателем на flash.
void exec_func(const int *data)
{
for (int i = 0; i < array_size; i++) {
int number = pgm_read_word(&data[i]);
Serial.println(number);
}
}
Обратите внимание, что к массиву нет прямого доступа (оценка data[i]
давать мусор). Вместо этого адрес нужного элемента (&data[i]
,
адрес флэш-памяти) передается макросу pgm_read_word()
, который использует
встроенная сборка для получения соответствующих данных из флэш-памяти.
Теперь вы можете вызвать эту функцию, передав ей адрес PROGMEM
.
массив, как в exec_func(my_array_0);
.
Просто для полноты картины я покажу вам, как использовать массив указателей.
чтобы избежать конструкции switch
/case
:
const int my_array_0[] PROGMEM = {...};
const int my_array_1[] PROGMEM = {...};
...
const int *arrays[] = {my_array_0, my_array_1, ...};
int looper = 0;
void loop()
{
exec_func(arrays[looper]);
if (++looper == number_of_arrays) looper = 0;
}
Обратите внимание, что здесь массивы
— это массив в оперативной памяти. Вот почему вы можете получить доступ
элементы непосредственно как массивы[looper]
. Эти элементы, однако,
указатели на массивы на основе флэш-памяти. Если у вас так много массивов, что массивы
становится слишком большим, вы можете подумать о том, чтобы поместить его также во флэш-память.
Большое спасибо за этот подробный ответ! Проблема в том, что это не поможет вам сэкономить память. Как вы думаете, где компилятор будет хранить {ДАННЫЕ ЗДЕСЬ}?
Моя логика, стоящая за этой мыслью, заключалась в том, что я видел гораздо большие кодовые базы в arduino. Я думал, что часть CODE отличается от назначения переменной. Я читаю о PROGMEM прямо сейчас. Еще один вопрос. Если я передам указатель на место флэш-памяти, то мне вообще не нужен my_array[]
? Я просто даю указатель на my_array_0[]
(например)?, @user1584421
@ user1584421 Это зависит от того, хотите ли вы изменить данные. exec_func()
можно написать для чтения данных из флэш-памяти, но вы не можете изменить его там. Если вам нужно преобразовать необработанные данные массива, вам все равно нужно скопировать их в ОЗУ, как это сделал Эдгар в своем последнем фрагменте кода., @chrisl
@chrisl Нет, массивы будут константами. Так что мне даже не нужен my_array[]
. Просто указатели на константные массивы во флэш-памяти. Спасибо за совет!, @user1584421
РЖУ НЕ МОГУ! Вы только что отредактировали его в то же время, когда мне удалось заставить его работать! Я прочитаю его и отмечу его как принятый позже! Большое спасибо за ваш ответ!, @user1584421
Я пробовал указатель на массив мигать раньше, но это не сработало. Конечно, может я что-то не так сделал. Однако мне удалось заставить его работать с фрагментом кода, который вы предоставили непосредственно перед редактированием. С дополнительной работой по выводу количества элементов в массиве во флэш-памяти. int ArrayElements = sizeof(my_array_0) / sizeof(int);
И затем я бы создал пустой массив, как в вашем примере, передав элементы массива в качестве его размера. Да, возможно, это не так быстро и эффективно с точки зрения памяти, но это три строки кода. И только один массив в памяти. Я могу уйти с этим., @user1584421
Я столкнулся с другой проблемой. Хотя я могу назначить массив из «PROGMEM» и передать его функции, программа должна будет перейти от «case 0» к другим случаям. Таким образом, базовый массив необходимо будет повторно инициализировать. Учитывая, что я не использовал new
, могу ли я как-то переназначить массив? Массивы в PROGMEM
имеют разный размер., @user1584421
@user1584421: user1584421: я изменил ответ, чтобы справиться с массивами разных размеров. Обратите внимание, что теперь есть несколько переменных с общим именем my_array
, но они являются _разными_ переменными, каждая из которых локальна для блока {}
в switch
/case
., @Edgar Bonet
- 1 глобальная переменная, потребляющая 19% памяти
- Глобальному массиву не присваивается то место которое он занимал бы в памяти
- Получить доступ к EEPROM ATtiny с помощью кода Arduino?
- Выделение строковой памяти Arduino
- Некоторые переменные не сохраняют свои значения при выходе из цикла while?
- Как очистить кучу памяти в esp32
- Есть ли способ подключить оперативную память компьютера к Arduino?
- Последовательная печать из флэш-памяти (F() macro, PROGMEM, sprintf_P, SPTR)
Являются ли ваши базовые массивы постоянными? Если да, вы можете поместить их во флэш-память/программную память вместо оперативной памяти. Насколько я понял, вы все равно скопируете данные в дополнительный архив, чтобы работать с данными там., @chrisl
В примере псевдокода
my_array[]
, определенный в кейсе 1, будет автоматически уничтожен в конце кейса 1. Между прочим, если вы определяете переменные в кейсе, блок case должен быть заключен в квадратные скобки { }, иначе вы получите предупреждения компилятора. ., @6v6gtЧтобы ответить на ваш вопрос, нам нужно знать, как вы определяете
exec_func(myarray)
? Каков тип данныхmy_array[]
? Принимает ли функцияmy_array[]
или указатель на массив? Будет лиmy_array[]
использоваться только вcase 0
или где-то еще? Тем временем я бы посоветовал вам прочитать [Learn C++] (https://www.learncpp.com/cpp-tutorial/arrays-part-i/). Кстати,new/delete
используется для динамического выделения памяти с помощью команды, такой какmalloc()
, она не применима к созданию статического массива., @hcheungНе могли бы вы [отредактировать] свой вопрос и предоставить минимальный, воспроизводимый и полный пример, пожалуйста? Он должен показывать ваш вариант использования, даже если он не оптимизирован так, как вам нравится., @the busybee
@chrisl Да, они постоянны. Я изучаю PROGMEM прямо сейчас! Спасибо за совет!, @user1584421
@hcheung Мои массивы имеют тип
int
со многими значениями. Функции получают указатели напрямую, а не указатель на них. На самом деле это одно и то же, поскольку имя массива является указателем на первый элемент. В моей теоретической реализации (она предназначалась для решения проблемы полной памяти) она использовалась бы только в ситуацииcase 0
., @user1584421@thebusybee В этом нет ничего особенного. Просто длинные массивы int с большим количеством данных. Затем они присваиваются рабочему массиву, который передается функции для обработки. Добавьте много длинных глобальных переменных, и вам быстро не хватит памяти. Ничего особенного. Если вы хотите, я все еще могу предоставить код, если вы заинтересованы., @user1584421
Конечно. Например, нам нужно знать, содержат ли эти массивы постоянные значения. Или изменяет ли вызываемая функция элементы массивов. И так далее. Убедитесь, что любой важный аспект освещен для хорошего ответа. Только хорошие вопросы получают хорошие ответы, это принцип [GIGO](https://en.wikipedia.org/wiki/Garbage_in,_garbage_out)., @the busybee