Использование указателей, ссылок и векторов

Slava68
Offline
Зарегистрирован: 14.07.2014

#include <StandardCplusplus.h>
#include <vector>
using namespace std;


void setup() {
  Serial.begin(9600);
}
void Funk_1(int a, vector<int> b) {             // надо принять минимум два разных типа данных
  // - вектор неизвестной длины
  // - массив 8 байт.
  for (unsigned long int x = 0; x < b.size(); x++) {
    Serial.print(a);
    Serial.print(" + ");
    Serial.print(b[x]);                         // обработать полученные данные одинаково.
    Serial.print(" = ");
    Serial.println(a + b[x]);
  }
}

void Funk_2(int c, int d) {

  int Y[8] = {8, 7, 6, 0, 4, 3, 2, 1};        // массивы будут неизвестной длины.
  Y[3] = c + d;                               // какое-то действие с массивом.
  vector<int> v_data(Y, Y + 8);               // а теперь надо отправить данные в функцию Funk_1
  Funk_1 (v_data.size(), v_data);
  v_data.clear();
}

void loop() {
  vector<int> v_addr;
  int A[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  for (byte x = 0; x < 10; x++) v_addr.push_back(A[x]);

  for (unsigned long int x = 0; x < v_addr.size(); x++) {
    Serial.print(v_addr[x]);
  }
  Serial.println("");

  Funk_1(1, v_addr);
  Funk_2(2, 3);

  
}

Сочиняю программку, которая будет иметь неопределённое количество входных данных, надо их обработать и записать на SD карту. Для манипуляций с данными использую vektor<byte>.

Вот, специально, чтобы не морочить голову товарищам, написал специальный код для примера, несколько дней изучаю великий интернет и ничего подходящего не нашёл :-( , теперь прошу помощи здесь.

1.) Т.к. массив (вектор) большой, то в определённый момент просто не хватает памяти для создания его копии при передаче в функцию.  Как можно сделать это с помощью указателя или ссылки на вектор ?

2.) Можно ли как-то принимать в функцию Funk_1  и массив(вектор) и просто массив не преобразовывая его в вектор ?

 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

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

void foo( int a, const vector<int> &b )

Slava68
Offline
Зарегистрирован: 14.07.2014
#include <StandardCplusplus.h>
#include <vector>
#include <OneWire.h>
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "RTClib.h"
#include "TM1638.h"

kisoft пишет:
Можно запомнить одну простую вещь, передавая объект в качестве параметра, передавайте его как константную ссылку, тогда и копирования не будет. void foo( int a, const vector<int> &b )

Спасибо за совет, я уже вторую неделю изучаю С++ и третий день ищу решение похожей проблемы в интернете.

Я попробовал, если ранее приведённый пример "сбрасывался" на 32-33-й итерации, то с вашим советом стал "сбрасываться" на 33-й.  Ясно, что заканчивается память в Атмеге328, но я предполагал, что влияние факта копирования массива (появление второго такого же) было значительнее.

Раньше влезало 330 значений int, теперь влезает 340 значений int.

Значений типа byte влезло 670 , что, впрочем логично :-))), но это в пустом тестовом примере, а в рабочей программе , после подключения библиотек (вверху) памяти остаётся совсем чуть-чуть :-(

Вопрос: Есть ли какой-то оператор(функция) позволяющий применительно к Ардуино , следить за оставшейся памятью и останавливать подключение новых устройств (в моём случае) для предотвращения зависания программы ?

Это точно, что теперь мы передаём в функцию "ссылку на массив", а не что-то другое ?

З.Ы. для того, что бы пример "вывалился" от переполнения памяти, надо убрать из 31-й строки объявление вектора и поместить его в начало программы, т.е. объявить глобально.

З.Ы.Ы.  Это только у меня не работает Copy, Paste на этом форуме и код вставляется всегда сверху ?

Slava68
Offline
Зарегистрирован: 14.07.2014
#include <StandardCplusplus.h>
#include <vector>
using namespace std;

int s=0;                                 // Счетчик итераций
vector<byte> v_addr;                     // Объявляем глобально, чтобы измерить "вместимость" памяти.

void setup() {
  Serial.begin(9600);
}
void Funk_1(int a, const vector<byte> &b) {             // надо принять минимум два разных типа данных
                                                        // - вектор неизвестной длины
                                                        // - массив 8 байт.
  for (unsigned long int x = 0; x < b.size(); x++) {
    Serial.print(a);
    Serial.print(" + ");
    Serial.print(b[x]);                         // обработать полученные данные одинаково.
    Serial.print(" = ");
    Serial.println(a + b[x]);
  }
  return;
}

void Funk_2(int c, int d) {

  int Y[8] = {8, 7, 6, 0, 4, 3, 2, 1};        // массивы будут неизвестной длины.
  Y[3] = c + d;                               // какое-то действие с массивом.
  vector<byte> v_data(Y, Y + 8);              // а теперь надо отправить данные в функцию Funk_1
  Serial.print("Funk_2 v_data.size() = ");
  Serial.println(v_data.size());
  for (int k = 0; k < v_data.size(); k++) Serial.print(v_data[k]);  // Проверяем то, что будем передавать.
  Serial.println("");
  Funk_1 (v_data.size(), v_data);
  v_data.clear();
  return;
}

void loop() {
  
  int A[10] = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
  for (byte x = 0; x < 10; x++) v_addr.push_back(A[x]);

  for (unsigned long int x = 0; x < v_addr.size(); x++) {
    Serial.print(v_addr[x]);
  }
  Serial.println("");
  Serial.print("Shag = ");
  Serial.println(s++);
  Serial.print("Size = ");
  Serial.println(v_addr.size());

  Funk_1(v_addr.size(), v_addr);
  Funk_2(2, 3);

  //v_addr.clear();
}

kisoft пишет:
Можно запомнить одну простую вещь, передавая объект в качестве параметра, передавайте его как константную ссылку, тогда и копирования не будет. void foo( int a, const vector<int> &b )

Правильно ли я потом использую полученный вектор b ( ссылку на вектор)

Писать дальше &b в части обработки не позволяет компилятор.

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

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

extern unsigned int __bss_end;
extern unsigned int __heap_start;
extern void *__brkval;

unsigned int availableMemory()
{
  unsigned int l_free_memory;

  if ((int) __brkval == 0)
  {
    l_free_memory = ((int) &l_free_memory) - ((int) &__bss_end);
  }
  else
  {
    l_free_memory = ((int) &l_free_memory) - ((int) __brkval);
  }
  return l_free_memory;
}

void setup()
{
  Serial.begin( 57600 );
}

void loop()
{
  Serial.print( "Free memory: " );
  Serial.println( availableMemory() );
  delay( 1000 );
}

Я этот код использовал, но давно, брал откуда то.

Вот еще есть ссылка на официалов http://playground.arduino.cc/code/AvailableMemory там почти тоже самое.

Передача параметра в виде const Object &p_obj говорит о том, что внутри функции объект не будет изменяться и он передается по ссылке (т.е. не копия, а просто ссылка). Аналогичный вариант можно написать так: const Object *pp_obj - это будет передача по указателю и смысл будет практически такой же. Обычно удобней передавать в виде константной ссылки, проще использовать (писать точку вместо стрелки).

Еще, в Вашем примере где ни попадя используются векторы, а памяти то кот наплакал, потому одно из двух, либо забыть про STL, либо использовать STL очень аккуратно, отдавая себе отчет, либо перейти на ARM с линуксом и не париться этими проблемами. Да, еще, скорей всего это некорректная реализация и векторы Вам совсем не нужны.

PS Использование STL в Ардуино - это почти нонсенс (IMHO), я вчера копилировал векторы на Tiva C (Cortex-M4F), простой пример еле влез (после танцев с бубном) в 32КБ (Flash), при этом память (SRAM) я не смотрел, конечно.

PPS Прежде чем юзать STL в Ардуино, я бы провел исследование, сколько Flash и памяти жрет пустой вектор, а также те или иные действия (копирование, инициализация). На основании исследования я бы сделал вывод нужно это или нет.

PPPS По поводу "бесконечного" массива, можно использовать, например, двойную буфферизацию с фиксированным набором и размером буферов. Т.е. в один буфер пишем программой, другой сбрасываем на SD.

 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

В 41 строке Вы заполняете вектор, но нигде его не очищаете? Самое правильное, очищает его ДО исопльзования, так меньше вероятности забыть по ошибке его очистить.

Если это намерения такие, заполнить вектор до отказа, тогда другое дело.

Кстати, кроме двойного буфера, возможно проще использовать кольцевой буфер. Условие задачи не дописано, как часто, сколько по объему и т.п. и что делать, если переполнилась SD.

 

Slava68
Offline
Зарегистрирован: 14.07.2014

kisoft пишет:

В 41 строке Вы заполняете вектор, но нигде его не очищаете? Самое правильное, очищает его ДО исопльзования, так меньше вероятности забыть по ошибке его очистить.

Если это намерения такие, заполнить вектор до отказа, тогда другое дело.

Кстати, кроме двойного буфера, возможно проще использовать кольцевой буфер. Условие задачи не дописано, как часто, сколько по объему и т.п. и что делать, если переполнилась SD.

 

в этом примере специально переполняется память. Я этими измерениями и расчётами разных вариантов уже три листа исписал :-) Мне "вектор" нравится, он требует не больше ресурсов, чем другие, пока известные мне способы.

Использование вектора выбрал по следующим причинам:

1. Типа, стандартный пример с датчиком температуры DS18s20,  а теперь представим, что датчиков на шине 1-Wire примерно от 100 до 200. Я сканирую шину и записываю в массив всё, что там нашёл. Сохраняю время и данные на SD карты.

2.  Опрашиваю все датчики, которые были найдены, "одновременно", как фотоснимок. Записываю имена датчиков и их показания на SD карту.

3. После этого могу очистить вектор (так и делаю) и начать сканирование заново.

4. Вот для передачи в функцию записи на SD и пытаюсь оптимизировать данные. Быстрее всего (за один раз) пишется String , но вот как её получить не скопировав и так много данных ? С перегрузкой получается сделать обычный массив в String, а вектор приходится писать на SD карту побайтно из цикла.

Задача, получить что-то вот такое, это у меня 19 датчиков работают: http://slava68.lj.ru/1538275.html

Вот такую схемку нарисовал: http://b6.icdn.ru/s/slava_68/0/38067800CjS.jpg

И вот так это пока в макете :-) http://b6.icdn.ru/s/slava_68/7/38006747tWP.jpg  http://b6.icdn.ru/s/slava_68/6/37982486Lrs.jpg

Огромное спасибо за подсказку с памятью ! Скорее всего, сделаю "страницы" в зависимости от свободной памяти и буду подтягивать их с SD карты.