Динамический массив из String

bigory
Offline
Зарегистрирован: 17.10.2018

Доброго времени суток и плодотворгого творчества!

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

Значит с помощью malloc я пытаюсь создать массив sname из n элементов, потом получая значения в переменной name_buf записать в созданный массив sname. После всего этого получить результат на мониторе. Укажите мне на ошибки и как это исправить

...
String *sname;
  byte n;
  array_name_key(sname,n);
...

void array_name_key(String *sname, byte n)
{
  // получаю заранее неизвестное количество элементов массива n
  n=(какое-то тут количество);

  sname = (String*) malloc(n * sizeof(String[28]));
  String name_buf; // Для составления строки
  byte size_s; // Для длины каждой строки
  size_s=(длина от 2 до 28);
  
  for(byte z=0;z<n;z++) 
  {
    for(byte i=0;i<28;i++) 
    {
        if(size_s>i)name_buf+=char((символ)[i]);
    }
    sname[z]=name_buf;
  }
 
  //Проверка работы
  for(int i=0; i<n; i++)
  {
    Serial.print(sname[i]);
    Serial.println(" ");
  }
}

Если требуется все же углубиться в нормальный код, то он ниже

void setup()
{
...
  String *sname;
  byte *key[8];
  byte n;
  byte *addr;
  array_name_key(sname,key,n,addr);
...
}

void array_name_key(String *sname, byte **key, byte n, byte *addr)
{
  n=0;
  byte *k;
  bool a=true;
  for(int m=0;m<511;)
  {
    a=EEPROM.read(m);
    if(a==true){m=m+38;}
    else
    { 
      n++;
      m=m+38;
    }
  }
  //Выше отработалось нормально

/*  for(byte x = 0; x < 14; x++)
    sname[x] = (String*) malloc(n * sizeof(String));*/
  sname = (String*) malloc(n * sizeof(String[28]));
  String name_buf;
  for(byte x = 0; x < 8; x++)
    key[x] = (byte*) malloc(n * sizeof(byte));
  addr = (byte*) malloc(n * sizeof(byte));
  byte z=0;
  byte size_s=0;
  
  for(int m=0;m<511;)
  {
    name_buf="";
    a=EEPROM.read(m);
    if(a==true){m=m+38;}
    else
    {
      m++;
      size_s=EEPROM.read(m);
      m=m+2;
      addr[z]=m;
      for(byte i=0;i<28;i++) 
      {
        if(size_s>i)name_buf+=char(EEPROM.read(i+m));
      }
      sname[z]=name_buf;
      Serial.println(sname[z]);
      m=m+27;
      m++;
      for (byte x = 0; x < 8; x++) 
      {
        EEPROM.get(m,key[x][z]);
        m=m+1;
      }
      z++;
    }
  }
//Serial.println(n);  
  //Проверка работы
  for(int i=0; i<n; i++)
  {
    Serial.print(sname[i]);
    Serial.println(" ");
    for(int j=0; j<8; j++){Serial.print(key[j][i], HEX);Serial.print(":");}
    Serial.println("");
  }
}

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Вы бы еще русскими словами написали - просто тренируетесь или какую-то полезную задачу пытаетесь решить? В этих ваших a, m, x, z сам черт ногу сломит. Зачем вот вам String, если фактически делаете char[29]?

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015
String* s = new String();

*s = F("bla bla bla");

delete s;

 

bigory
Offline
Зарегистрирован: 17.10.2018

sadman41 пишет:
Вы бы еще русскими словами написали - просто тренируетесь или какую-то полезную задачу пытаетесь решить?

Я тренируюсь на полезной задаче.

sadman41 пишет:
В этих ваших a, m, x, z сам черт ногу сломит.

К данными переменными у меня нет вопросов и проблема не в них. 

sadman41 пишет:
Зачем вот вам String, если фактически делаете char[29]?

Со String(ом) просто удобнее, но cделать я патаюсь String[i] (размер каждого стринга по 28), либо если char, то char[28][i] 

bigory
Offline
Зарегистрирован: 17.10.2018

DIYMan пишет:
String* s = new String();

*s = F("bla bla bla");
delete s;

Не понимаю как это можно применить в данной задаче... Понятно что String* s = new String(); создает элемент, но я не понимаю как тогда к нему обращаться, если мне нужно будет более чем 1 элемент. Если есть пример какой-нибудь, хотя бы из любой задачи, скиньте кусок кода, плиз

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

bigory пишет:

DIYMan пишет:
String* s = new String();

*s = F("bla bla bla");
delete s;

Не понимаю как это можно применить в данной задаче... Понятно что String* s = new String(); создает элемент, но я не понимаю как тогда к нему обращаться, если мне нужно будет более чем 1 элемент. Если есть пример какой-нибудь, хотя бы из любой задачи, скиньте кусок кода, плиз

String* array[20];

for(int i=0;i<20;i++)
array[i] = new String();

В примере выше - массив из 20 элементов, созданных динамически. Если надо динамически менять размерность массива, то:

String** array = new String*[100];
for(....) array[i] = new String();

 Ну и не забывать чистить всё правильно.

 

sadman41
Offline
Зарегистрирован: 19.10.2016

bigory пишет:

sadman41 пишет:
В этих ваших a, m, x, z сам черт ногу сломит.

К данными переменными у меня нет вопросов и проблема не в них. 

Так код вы нам предлагаете читать. Прочитать, разобраться в вашей идее, потом еще показать, где вы допустили ошибку. Не пытались никогда с закрытыми глазами незнакомый фотоаппарат разбирать и собирать? Ощущения примерно такие же - хер его знает, должен этот винтик тут стоять или его надо с соседним поменять.

bigory
Offline
Зарегистрирован: 17.10.2018

a - булевый тип. При чении из памяти EEPROM, если получаю true, то блок памяти занят, false блок памяти свободно. Это условные обозначения, чтобы считать тот блок помяти, который заполнен полезными данными.

m -  адрес ячейки памяти EEPROM

x - переменная для цикла чтобы перебрать массив

z - счетчик, показывающий сколько раз мы прошли цикл

Из всех переменных что спрашивались, важен только счетчик z.

Код что я выложил 2й, это просто более полный, а сама проблема отображена в 1м коде

 

bigory
Offline
Зарегистрирован: 17.10.2018
...
String *sname[28];
  byte n;
  array_name_key(sname,n);
...

void array_name_key(String *sname, byte n)
{
  // получаю заранее неизвестное количество элементов массива n
  n=(какое-то тут количество);

  String name_buf; // Для составления строки
  byte size_s; // Для длины каждой строки
  size_s=(длина от 2 до 28);
  
  for(byte z=0;z<n;z++) 
  {
    for(byte i=0;i<28;i++) 
    {
        if(size_s>i)name_buf+=char((символ)[i]);
        else name_buf+='\0';

    }
    for (byte x = 0; x < 28; x++) 
    {
        sname[x] = new String();
        sname[x][z]=name_buf[x];
    }
    //Проверка работы
    for (byte x = 0; x < 28; x++) 
    {
       Serial.print(sname[x][z]);
    }
  Serial.println("");
  }
 }

Что я не так сделал?

Как применить delete тоже не понимаю

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

bigory пишет:

Что я не так сделал?

Как применить delete тоже не понимаю

1. Всё не так сделали;

2. Без обид, но - почитайте основы С++ - там как раз написано и про new/delete. Пересказ учебника - такое себе занятие.

Вам что надо? Прочитать из EEPROM сохранённые там строки в массив строк? Это делается сильно проще, чем у вас написано. Однако - всегда есть нюансы, например - может быть заранее неизвестно кол-во сохранённых строк. В общем случае это делается алгоритмически примерно так:

String** array = NULL;
int arraySize = 0;

int eepromAddress = ...;

while(true)
{
	byte b = EEPROM.read(eepromAddress++);
	if(b == 1) //запись существует
	{
		String** newArray = new String*[++arraySize];
		for(int i=0;i<arraySize-1;i++)
		{
			newArray[i] = array[i];
		}

		newArray[arraySize-1] = new String();

		delete[] array;
		array = newArray;
		
		while(true)
		{
			char ch = (char) EEPROM.read(eepromAddress++);

			if(!ch) // хранящаяся строка заканчивается нулевым символом
				break;

			*(newArray[arraySize-1]) += ch;
		}
	}
	else break;
}

// чистим за собой
for(int i=0;i<arraySize;i++)
	delete array[i];

delete[] array;

 Код - навскидку писан, без претензий.

bigory
Offline
Зарегистрирован: 17.10.2018

Сделал по той схеме что скинули, но что-то не понятно. Мне нужно вытащить за приделы функции полученный массив со всеми элементами. Может опять что не так?

void array_name_key()
{
	String** array=NULL;
	int arraySize=28; //Длина строки 28 символов
	byte sizeStr=0;
	n=0; //Количество полученных записей
	
	//Пробегаемся по всей памяти, за исключением ех участков, которые
	//не умещают в себе 38 байт
	for(int eepromAddress=0;eepromAddress+38<EEPROM.length();)
	{
		//Считываем первый байт блока данных из памятиб
		//в котором хранится метка о существовании записи
		//(true-свободно/false-занято). Вывести надо занятые области
		bool b=EEPROM.read(eepromAddress);
		if(b==false)
		{
			//Второй байт блока данных, это размер считываемой сроки
			eepromAddress++;
			//Длина строки
			sizeStr=EEPROM.read(eepromAddress);
			eepromAddress += 2;
			//С 4 байта блока данных начинается строка,
			//которую необходимо извлечь
			String** newArray=new String*[++arraySize];
			for(int i=0; i<arraySize-1;i++)
				newArray[i]=array[i];
			newArray[arraySize-1]=new String();
			delete[] array;
			array=newArray;
			
			//Максимальный объем строки из русских букв - 28 байт
			int addr_sizeStr=eepromAddress+28;
			while(eepromAddress<addr_sizeStr)
			{
				char ch=(char)EEPROM.read(eepromAddress++);
				if(!ch) //Если строка заканчивается нулем
				{
					//В блоке данных у нас строка фиксированного размера, т.е. 28 байт,
					//поэтому сразу переходим в конец адреса строки
					eepromAddress=addr_sizeStr;
					break;
				}
				*(newArray[arraySize-1]) += ch;
			}
			//Последние данные блока, это ключи
			//Код записи ключа в массив я пока отпущу
			eepromAddress+=8;
		}
		else eepromAddress+=38;
	}
	//Чистим память
	for(int i=0;i<arraySize;i++)
		delete array[i];
	delete array;
}

Я пока удалил входные параметры функции для тього, чтобы разобраться в логике и в работе того алгоритмя, что скинули выше.

Не понятно почему в переменной array NULL+ch? NULL+ch != ch, уже проверял. Как вообще работать с delete  мне понятно, мне не понятно как потом после удаления работать с полученными значениями. Мне та надо в функции создать массив и заполнить этот массив значениями, а потом за пределами функции с ними работать.

Пока что должным образом у меня не работает....

b707
Offline
Зарегистрирован: 26.05.2017

bigory пишет:

Мне та надо в функции создать массив и заполнить этот массив значениями, а потом за пределами функции с ними работать.

Почитайте, что такое "область видимости" в языке Си. Любая переменная или структура, например массив, созданная внутри функции - только внутри этой функции и видна. Более того, если переменная не обьявлена static. то при каждом выходе из функции она уничтожается, а при следующем входе в функцию создается вновь.

Чтобы работать с массивом снаружи - его нужно и создавать "снаружи", например обьявить глобальным.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

bigory пишет:

Как вообще работать с delete  мне понятно

Нет, непонятно. У вас утечка памяти в строке 55. Читайте основы языка, как минимум. Иначе это - разговор немого с глухим.

bigory
Offline
Зарегистрирован: 17.10.2018

b707 пишет:

Чтобы работать с массивом снаружи - его нужно и создавать "снаружи", например обьявить глобальным.

С областью видимости все понятно, но вот как быть тогда с динамическим массивом?

sadman41
Offline
Зарегистрирован: 19.10.2016

Извращаться надо. Ардуина - это вам не Big PC,  тут подобные вещи только через .... аккуратное программирование. Будете со своим динамическим массивом в push/pop играть - всю память мигом растеряете. Сборщика памяти тут нет, устранять дырки в куче никто не станет.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

bigory пишет:

С областью видимости все понятно, но вот как быть тогда с динамическим массивом?

Правильно создавать/освобождать. Примеры - вам не помогут: вы начинаете бездумно оттуда копировать, в надежде, что оно само по себе заработает. С++ - это даже не пистолет, это - берданка, и выстрелить из такой берданки себе в ногу - как два пальца. Чем вы сейчас героически и занимаетесь вместо того, чтобы взять учебник по С++ - и почитать про динамическое выделение и освобождение памяти.

Сколько бы вам примеров ни давали - без понимания основ всё сведётся к тупой копипасте. С заведомо известным плачевным результатом на выходе.

Чечако
Offline
Зарегистрирован: 15.06.2018

sadman41 пишет:

Извращаться надо. Ардуина - это вам не Big PC,  тут подобные вещи только через .... аккуратное программирование. Будете со своим динамическим массивом в push/pop играть - всю память мигом растеряете. Сборщика памяти тут нет, устранять дырки в куче никто не станет.

Я так понимаю, что полноценно динамических массивов тут быть вообще не может. Бездумных, в смысле. Потому как некое максимальное значение мы должны заранее предусмотреть. Ну, по крайней мере, я это так для себя понял, когда разбирался. Ибо тоже избалован Big PC. :(

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Чечако пишет:

Я так понимаю, что полноценно динамических массивов тут быть вообще не может. Бездумных, в смысле. Потому как некое максимальное значение мы должны заранее предусмотреть. Ну, по крайней мере, я это так для себя понял, когда разбирался. Ибо тоже избалован Big PC. :(

Чой-то? Всё есть в соответствии со стандартом С++. Можно выделять память, сколько заблагорассудится, пока менеджер памяти - может выделить запрошенный кусок. Когда не сможет - вернёт NULL, эту ситуацию, ессно, надо отрабатывать в программе.

Конечно, оперативки не вагон, но жить можно. Постоянно юзаю динамические массивы (да тот же String - внутри держит динамический массив) - и не вижу в этом никаких недостатков. Кроме, конечно, ограниченного размера оперативки.

Чечако
Offline
Зарегистрирован: 15.06.2018

DIYMan пишет:

Чой-то? Всё есть в соответствии со стандартом С++. Можно выделять память, сколько заблагорассудится, пока менеджер памяти - может выделить запрошенный кусок. Когда не сможет - вернёт NULL, эту ситуацию, ессно, надо отрабатывать в программе.

Я о том и говорю, что бездумно не получится. Надо все контролировать. Некоторые вещи, на которые закрывают глаза большие машины и мощные компиляторы, тут неприемлимы. Как в старые, добрые времена. :)

sadman41
Offline
Зарегистрирован: 19.10.2016

А что там будет, если типа такого устраивать:

s1 = malloc(..);
s2 = malloc(..);
s3 = malloc(..);
delete s2;
s4 = malloc(..);
delete s1;
s2 = malloc(..);

Дырки же в куче понаделаются? 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

sadman41 пишет:

А что там будет, если типа такого устраивать:

s1 = malloc(..);
s2 = malloc(..);
s3 = malloc(..);
delete s2;
s4 = malloc(..);
delete s1;
s2 = malloc(..);

Дырки же в куче понаделаются? 

Могут и наделаться, конечно - всё зависит "от". Бейсбольной битой если вломить - сломается вообще всё :)

bigory
Offline
Зарегистрирован: 17.10.2018

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

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

struct Memory
{
  ...
};
Memory *arr;
int n_arr=0

void setup()
{
  ...
  read_upd_struct(arr,n_arr);
}

void read_upd_struct(Memory *&arr, int &n_arr)
{
  n_arr=0;
  ...
  free(arr);
  arr=(Memory*)malloc(n_arr*sizeof(Memory));
  ...
};

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

bigory пишет:

Мда... жаль толкогого ответа, кроме критики, так и не получил... 

Если вы считаете совет "читать учебник" критикой - тогда вообще никаких ответов не будет.