Ошибка выполнения при использовании массива структур неизвестного размера в качестве переменной внутри класса — какая-то проблема переполнения?
У меня есть класс, определенный ниже. По сути, это простой кольцевой буфер с некоторыми методами для получения полезной статистики из данных, хранящихся в буфере.
enum BalanceMsgType{
BAL_MSG_NOT = 0,
BAL_MSG_READING = 1,
BAL_MSG_SPECIAL = 2,
BAL_MSG_ERROR = 3
};
typedef struct MeasuredMass{
float mg;
unsigned long time;
bool steady;
BalanceMsgType type;
};
class RingBuffer{
private:
unsigned int _size = 0;
unsigned int _pos = 0;
MeasuredMass *_array;
void LinearRegression(){
//... опущено для простоты
}
public:
float lin_reg_m = 0;
float lin_reg_c = 0;
float lin_reg_r = 0;
float est_current_mg = 0;
//методы
RingBuffer(unsigned int size){
_size = size; //забыл вставить эту строку ->div0-> >1 час отладки :(
_array = new MeasuredMass[_size];
for (int i = 0; i < _size; i++){
_array[i].mg = 0.0;
}
}
void Add(MeasuredMass in_element){
_array[_pos] = in_element;
_pos = (_pos + 1)%_size;
LinearRegression();
est_current_mg = lin_reg_m*(in_element.time+BALANCE_DELAY) + lin_reg_c;
}
void ReadInOrder(MeasuredMass *pdata){ //Вызов с
//my_buffer.ReadInOrder(данные)
for(int i = 0; i < _size; i++){
pdata[i]=_array[(_pos+i+1)%_size];
}
}
float Sum(){
//...
}
float Mean(){
//...
}
float StdDev(){
//...
}
bool SteadyEnough(float max_sd){
//...
}
};
Он отлично компилируется и прекрасно работает в тестовых программах. Однако, если я включаю его в более крупные программы (что-то вроде progmem 117404 из 122880 байт, global vars 13604 из 32768), я получаю предупреждение «Плата на COM8 недоступна» при использовании Arduino Serial Monitor после загрузки в мой Fubarino Mini. (Даже без фактического взаимодействия с этими программами, кроме создания экземпляра класса.) Недавний опыт подсказывает мне, что это какой-то сбой из-за ошибки времени выполнения.
Повозившись с кодом, я обнаружил, что проблема не возникает в версии библиотеки, которая содержит массив float вместо моего MeasuredMass
. То, что моя структура MeasuredMass
, очевидно, намного больше float, наводит меня на мысль, что это связано с поведением указателя/массива, приводящим к переполнению.
Мне также удалось показать, что если я изменю класс таким образом, что он будет использовать массив фиксированной длины, а не указатель на него, проблема исчезнет.
т.е. закрытый член RingBuffer MeasuredMass *_array;
становится MeasuredMass _array[25];
и
RingBuffer(unsigned int size){
_size = size; //забыли вставить эту строку -> div0 -> >1hr bugtracking
_array = new MeasuredMass[_size];
for (int i = 0; i < _size; i++){
_array[i].mg = 0.0;
}
}
становится
RingBuffer(){
for (int i = 0; i < _size; i++){
_array[i].mg = 0.0;
}
}
Я бы предпочел сохранить возможность установки размера кольцевого буфера во время выполнения (в моем случае он устанавливается на основе количества секунд запрошенных данных), так что может ли кто-нибудь помочь мне выяснить, как мне этого добиться, не сталкиваясь с проблемами переполнения/памяти, которые у меня, похоже, есть?
Спасибо
@JRVeale, 👍1
1 ответ
Лучший ответ:
Проблема, скорее всего, здесь:
_array = new MeasuredMass[_size];
Вы выделяете _size * sizeof(MeasuredMass)
байт в куче. Если _size
слишком большой, объем выделенной памяти становится слишком большим, и ее не удается выделить. Вам следует проверить, является ли _array
NULL
или нет после new
. Если он NULL
, то выделение не удалось.
Проблема в том, что выделение кучи ограничено 2 КБ. Это не зависит от фактического объема памяти в чипе. Это ограничение накладывается компилятором (на самом деле библиотеками, поставляемыми вместе с компилятором) и устанавливается значением в скрипте компоновщика. Я много лет агитировал за то, чтобы это было удалено из библиотек, но это всегда оставалось без внимания. Настолько, что я предоставляю «исправленную» версию компилятора в UECIDE, которая снимает это жесткое ограничение.
Лучшее, что вы можете сделать, это не использовать динамическое выделение. Вместо этого используйте статически созданный массив, который вы затем передаете в конструктор .
MeasuredMass massBuffer[25];
RingBuffer massRing(massBuffer, 25);
и:
RingBuffer(MeasuredMass *buf, unsigned int size){
_size = size; //забыл вставить эту строку ->div0-> >1 час отладки :(
_array = buf;
for (int i = 0; i < _size; i++){
_array[i].mg = 0.0;
}
}
- Условное присвоение массива
- Глобальному массиву не присваивается то место которое он занимал бы в памяти
- Получить доступ к EEPROM ATtiny с помощью кода Arduino?
- Выделение строковой памяти Arduino
- Почему считается плохой практикой использовать ключевое слово "new" в Arduino?
- Как очистить кучу памяти в esp32
- Есть ли способ подключить оперативную память компьютера к Arduino?
- Работает с gcc, но не с Arduino. ошибка: taking address of temporary array