Как вернуть string из функции c
Перейти к содержимому

Как вернуть string из функции c

  • автор:

Как вернуть строку из функции

чтобы иметь возможность возвращать из функции указатель на какие-то строки их надо создавать динамически:

char* str = new char[10]; 
char* str = (char*)malloc(10 * sizeof(char)); 
char* str = (char*)calloc(10, sizeof(char)); 

Ну можно еще создать переменную со статическим классом хранения. – @eanmos

char *foo()

И ещё буфер можно передать как параметр для заполнения. – @Stanislav Volodarskiy

char *foo(char* str)

Как вернуть массив строк C++?

Учи матчасть: массив из функции вернуть нельзя. Можно вернуть указатель на массив или структуру содержащую массив.

string arr[0] = func(exp);

Слева в этом выражении стоит массив из нуля элементов. Справа — указатель на string.
Что ты хотел этим выражением сказать?

Ответ написан более трёх лет назад
Комментировать
Нравится Комментировать
Программист на «си с крестами» и не только

ВНИМАНИЕ: У вас есть пара тонких мест.
РАЗ. не забудьте, что строковые литералы в Си — это нуль-терминированные строки, и «\0» по факту будет пустой строкой. Строку из одного нулевого символа можно загнать в string — например, конструктором string(begin, end) или s += ‘\0’, но не конструктором string(const char*).
ДВА. Этот static будет инициализирован при первом вызове. Второй вызов вернёт тот же массив.
ТРИ. Строчку лучше передавать как string *func(const string& s) <>

К делу. Массив — это довольно сложный объект, и возникает вопрос: кто этим массивом будет владеть после того, как он покинет пределы функции?
1. Владеет система времени выполнения. Это отлично ответил Роман, дам только ключевую строчку.
string* arr = func(exp);
НЕЛЬЗЯ ИСПОЛЬЗОВАТЬ: если массив используется в чьих-то «конских» конструкторах-деструкторах статических объектов (из-за отсутствия модулей Си++ не позволяет установить на уровне языка порядок создания/уничтожения статических объектов).

2. Владеет какой-то объект.

string *func(string s)

НЕЛЬЗЯ ИСПОЛЬЗОВАТЬ: ну, это уж разбирайтесь сами с этим объектом, сколько он будет жить и насколько долго он будет держать массив. В данном случае каждый новый вызов уничтожает старый массив. (Код корявый, потому что последний массив не уничтожается.)

3. Владеет тот, кто вызывает. Лучше для таких целей использовать какой-нибудь std::vector.

vector func(string s) < vectorr; r.reserve(3); r.push_back("qwe"); r.push_back(s); r.push_back(std::string()); return r; >

click fraud protection

Способ идентификации последовательности строк как члена класса указан в определении C++. Класс String содержит атрибуты в виде потока битов с возможностью обработки однобайтовых символов. В C++ мы можем получить строку, но нам все равно следует проверить, как эта строка будет сохранена и передана. Поскольку C++ возвращает элементы в куче, которая имеет ограниченный объем пространства, предоставление огромных компонентов вызовет проблемы с переполнением стека, что может привести к ошибкам и бредам в системе безопасности.

Если мы можем вернуть объект std:: string из стандартной библиотеки шаблонов, мы можем предоставить постоянный указатель на строку. Убедитесь, что строка сохранена в статической памяти. В этой статье описаны различные подходы к возврату строки из функции C++.

Используйте технику std:: string function()

Возврат по значению является предпочтительным методом при извлечении строковых данных из функций. Возвращать относительно большие строки по данным эффективно благодаря конструктору перемещения в классе std:: string. Было сказано, что элемент переместил семантическое содержимое, если он содержит конструктор перемещения. Семантика перемещения предполагает, что данные не дублируются в новую позицию, когда функция возвращается, что приводит к эффективному времени завершения функции.

#включать
#включать
#включать
с использованием стандарт :: cout ; с использованием стандарт :: конец ;
с использованием стандарт :: нить ; с использованием стандарт :: обратный ;
строка RevString ( нить & с ) <
оборот струны ( с. rначать ( ) , с. разрывать ( ) ) ;
возврат оборот ;
>
инт главный ( ) <
строка ул = «Я люблю играть в бадминтон» ;

При запуске программы мы должны подключить три заголовочных файла. для функций ввода и вывода. задает набор функций, предназначенных для использования в группах элементов. Любая последовательность элементов, которая может быть извлечена с помощью итераторов или ссылок, считается диапазоном. как следует из их названия, используются для работы с набором чисел. Мы вызываем стандартную функцию «cout» для получения вывода, стандартную «endl», которая показывает, что программа переходит со следующей строки, стандартная «строка», которая содержит функциональные возможности строки, и стандартная «обратная», которая используется для получения строки в обратный порядок.

Теперь вызывается функция «RevString()». Здесь мы передаем определенную строку в качестве параметра этой функции. Мы применяем функции rbegin() и rend(). rbegin() — встроенная функция C++, предоставляющая инвертированный итератор, ссылающийся на последний компонент списка. rend() — это встроенная функция C++, используемая для возврата инвертированного итератора, который ведет к точке перед началом списка. Мы вводим оператор «return rev», чтобы получить обратную строку.

Мы применяем «endl», который показывает, что курсор перемещается на следующую строку кода. Чтобы напечатать указанную строку в обратном порядке, мы использовали «RevString». Эта функция содержит введенную строку в качестве аргумента. В конце концов, «EXIT_SUCCESS» применяется для завершения программы.

Используйте технику std:: string &function()

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

#включать
#включать
#включать

с использованием стандарт :: cout ; с использованием стандарт :: конец ;
с использованием стандарт :: нить ; с использованием стандарт :: обратный ;

инт главный ( ) <
строка ул = «Информационные технологии» ;

Первым делом интегрируем три библиотеки >, и для конкретных функций. Мы используем стандартную функцию «cout» для получения вывода, стандартную «endl», чтобы указать, что программа продолжает работу. следующая строка, стандартная «строка», чтобы сохранить функции строки, и стандартная «реверс», чтобы получить строку в обратном порядке. порядок. Теперь используется указатель строки «RevStr()». Указанная строка предоставляется в качестве параметра этому методу. Мы вызываем функции begin() и end().

Мы используем оператор «return s», чтобы получить обратную строку. Теперь будет вызвана функция main(). Здесь декларируется логика программы. Мы объявляем строку «Информационные технологии». Эта строка сохраняется в переменной «str». Оператор «cout» будет использоваться для получения печати строки. Мы также используем «endl», что означает, что курсор переместится на новую строку кода. «RevString» использовался для отображения требуемой строки в обратном порядке.

Указанная строка передается в качестве параметра этому методу. Наконец, программа завершилась командой «EXIT SUCCESS».

Используйте метод массива символов

Точно так же мы могли бы получить строку из функции, используя массив символов. Класс string использует постоянный массив для хранения символов. Вызывая встроенный метод, мы можем получить ссылку на первый символ этого массива.

#включать
#включать
с использованием пространство имен стандарт ;

строка str1 = «я люблю играть в бадминтон» ;

строка str2 = «информационные технологии» ;

стр1. копировать ( ч, 13 , 0 ) ;

стр1. менять ( стр2 ) ;

Здесь мы представляем заголовочные файлы и для использования строкового класса. Наряду с этим мы использовали стандартное пространство имен. Мы используем функцию main() и начинаем писать код в теле этой функции. Мы инициализируем две строки. Первая строка хранится в переменной «str1», а вторая строка хранится в переменной «str2». Теперь массив символов объявлен.

Мы указываем размер массива символов. Вызывается функция copy(). Подстрока в целевом массиве символов, указанном в параметрах, копируется этим методом. Три аргумента — это целевой массив символов, длина для дублирования и начальная точка в строке для инициирования дублирования. Мы хотим отобразить этот массив символов, используя оператор cout.

Обе строки были показаны перед заменой с помощью оператора «cout». Мы применяем функцию swap(), которая меняет одно строковое содержимое на другое. После замены мы снова вводим «cout», чтобы получить замененные строки. Примените «возврат 0», чтобы завершить код.

Вывод

Класс string в стандартной библиотеке C++ включает все перечисленные выше методы. В этой статье мы рассмотрели множество методологий возврата строки из функции на C++. Для описания методологий использовались различные экземпляры, включая метод std:: string &function() и метод std:: string function().

Arduino.ru

Dinosaur аватар

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

const char ml_1[] PROGMEM = «January»; const char ml_2[] PROGMEM = «February»; const char ml_3[] PROGMEM = «March»; const char ml_4[] PROGMEM = «April»; const char ml_5[] PROGMEM = «May»; const char ml_6[] PROGMEM = «June»; const char ml_7[] PROGMEM = «July»; const char ml_8[] PROGMEM = «August»; const char ml_9[] PROGMEM = «September»; const char ml_10[] PROGMEM = «October»; const char ml_11[] PROGMEM = «November»; const char ml_12[] PROGMEM = «December»; const char* const months_names[] PROGMEM = < ml_1, ml_2, ml_3, ml_4, ml_5, ml_6, ml_7, ml_8, ml_9, ml_10, ml_11, ml_12 >; void setup() < Serial.begin(115200); >void loop() < for (uint8_t i = 0; i < 12; i++) < String arrayBuf = tprint(i); Serial.print(F("i - ")); Serial.println(arrayBuf); delay(1000); >> String tprint(uint8_t val)

Прошу опытных товарищей подсказать, как правильно реализовать подобную конструкцию.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 08:36

Dinosaur аватар

Зарегистрирован: 01.01.2018

Возвращаемый функцией текст будет использоваться для формирования строки, выводимой на дисплей.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 10:28

andriano аватар

Зарегистрирован: 20.06.2015

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 11:33

Dinosaur аватар

Зарегистрирован: 01.01.2018
andriano пишет:
Не прошло и недели: http://arduino.ru/forum/obshchii/rezultat-funktsii-stroka

Спасибо, читал, но за всеми dtostrf и strcat мысль как то затерялась, уровень у вопрошающего гораздо выше моего, хотя я тоже с ссылками и указателями разобраться не могу (вроде и понимаю что указатель нужен для доступа к переменной, но не могу понять «а зачем?», если к переменной доступ итак есть. какая выгода работать с ней через указатель, а не напрямую?).

Можете тезисно объяснить как это правильно должно быть реализовано? Сейчас на мой взгляд происходит следующее (заранее извиняюсь если не понимаю элементарных вещей — учусь, но не все дается с первого раза):

1. Внутри функции создается массив arrayBuf фиксированного размера и в него копируется содержимое соответствующего массива из Progmem.

2. Затем содержимое массива arrayBuf конвертируется в строку и возвращается.

Что меня смущает — массив arrayBuf объявлен внутри функции, поэтому после return arrayBuf перестает существовать. Поэтому указатель на него вернуть в функцию (в том виде что написано у меня) невозможно. А возврат сроки допустим (массив arrayBuf перестанет существовать уже после того, как вызывающий функцию получит строку через return), но насколько я понимаю, такой способ не оптимален в плане использования ресурсов мк.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 11:45
Зарегистрирован: 19.10.2016

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

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 12:02

Dinosaur аватар

Зарегистрирован: 01.01.2018
sadman41 пишет:

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

Позвольте «поесть» Ваш мозг еще немного — сразу выплевывать это на дисплей пока не вариант, а вот про «использовать внешний (по отношению к функции) буфер (массив)» можно подробнее? Вы имеете ввиду глобальную переменную (в чем тогда профит, ведь она будет занимать память постоянно, плюс непонятно как ее объявить без указания размера) или что то иное? Еще вопросик — я копирую в arrayBuf из progmem значение и затем его отдаю вызывающему (то есть получается куча операций с довольно приличным объемом данных). но вероятно есть способ (и возможно это правильнее) передавать вызывающему ссылку на массив progmem (не то чтобы я спрашиваю разрешение сделать это, просто рассуждаю вслух. )? Или это так не работает?

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 12:35
Зарегистрирован: 19.10.2016

Внешняя — это не обязательно глобальная.

В функции showData() создали буфер на 40 символов, передали его в функцию formData, там накопировали чего хотели в буфер. Потом напечатали и завершили функцию showData(). Буфер автоматически прибился, память вернулась в систему.

А если и глобальная — что вы ее как Гитлера боитесь? Я, к примеру, выделил глобальный буфер на 200 и юзаю его для входящих данных, вывода на дисплей и формирования пакета, который в сеть выплевываю. Главное — не портить в нем данные, пока они не использованы.

И зачем «без объявления размера“? LCD внезапно, в 00:00 начнет принимать строки в 800кб и отображать их на всю ширину?

Перестаньте придумывать функции «на всякий случай, неизвестную длину». Пишите под конкретную задачу. С практикой уже сами начнёте «на автомате» делать универсальные.

Указатель на массив PROGMEM ничем не хуже другого указателя. Если принимающая функция понимает, что это именно PROGMEM и для доступа нужен pgm_read() то работать это будет так же стабильно.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 15:09

Dinosaur аватар

Зарегистрирован: 01.01.2018

Кстати, я погорячился — вариант со String тоже не сильно работоспособен оказался в реальной задаче, видимо arrayBuf уничтожается при выходе из функции, и то что оно работало в тестовом файле только благодаря тому, что ничто другое не затирало освободившуюся память 🙁 На данный момент тестирую такой вариант:

const char ml_1[] PROGMEM = «January»; const char ml_2[] PROGMEM = «February»; const char ml_3[] PROGMEM = «March»; const char ml_4[] PROGMEM = «April»; const char ml_5[] PROGMEM = «May»; const char ml_6[] PROGMEM = «June»; const char ml_7[] PROGMEM = «July»; const char ml_8[] PROGMEM = «August»; const char ml_9[] PROGMEM = «September»; const char ml_10[] PROGMEM = «October»; const char ml_11[] PROGMEM = «November»; const char ml_12[] PROGMEM = «December»; const char ms_1[] PROGMEM = «JAN»; const char ms_2[] PROGMEM = «FEB»; const char ms_3[] PROGMEM = «MAR»; const char ms_4[] PROGMEM = «APR»; const char ms_5[] PROGMEM = «MAY»; const char ms_6[] PROGMEM = «JUN»; const char ms_7[] PROGMEM = «JUL»; const char ms_8[] PROGMEM = «AUG»; const char ms_9[] PROGMEM = «SEP»; const char ms_10[] PROGMEM = «OCT»; const char ms_11[] PROGMEM = «NOV»; const char ms_12[] PROGMEM = «DEC»; const char* const months_names[] PROGMEM = < ml_1, ml_2, ml_3, ml_4, ml_5, ml_6, ml_7, ml_8, ml_9, ml_10, ml_11, ml_12, ms_1, ms_2, ms_3, ms_4, ms_5, ms_6, ms_7, ms_8, ms_9, ms_10, ms_11, ms_12 >; char* arrayBuf; void setup() < Serial.begin(115200); >void loop() < for (uint8_t i = 0; i < 24; i++) < Serial.print(i); Serial.print(F(" - ")); Serial.println(monthToString(i)); delay(500); >> char* monthToString(uint8_t val)

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 15:15

Dinosaur аватар

Зарегистрирован: 01.01.2018
sadman41 пишет:

Внешняя — это не обязательно глобальная. В функции showData() создали буфер на 40 символов, передали его в функцию formData, там накопировали чего хотели в буфер. Потом напечатали и завершили функцию showData(). Буфер автоматически прибился, память вернулась в систему.

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

sadman41 пишет:
А если и глобальная — что вы ее как Гитлера боитесь?

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

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 15:22
Зарегистрирован: 03.07.2016
sadman41 пишет:

Указатель на массив PROGMEM ничем не хуже другого указателя. Если принимающая функция понимает, что это именно PROGMEM и для доступа нужен pgm_read() то работать это будет так же стабильно.

Dinosaur или возвращайте String.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 15:35

Dinosaur аватар

Зарегистрирован: 01.01.2018
qwone пишет:

или возвращайте String.

Можете скетч в первом примере глянуть где у меня ошибка? В том виде в каком он представлен — все работает, но стоит вынести его в реальный проект — лезет разный мусор на экран 🙁

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 15:43
Зарегистрирован: 23.11.2016

Прочитай как работают строки в C. И прочитай как конструируется String в ардуино.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 16:03
Зарегистрирован: 03.07.2016

String(const __FlashStringHelper *str); // конструктор для flash-строк
  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 16:06

Dinosaur аватар

Зарегистрирован: 01.01.2018
rkit пишет:

Прочитай как работают строки в C. И прочитай как конструируется String в ардуино.

Ткните пожалуйста носом в нужный учебник/статью (желательно уровня для новичков).

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 16:54

andriano аватар

Зарегистрирован: 20.06.2015
Dinosaur пишет:

. но не могу понять «а зачем?», если к переменной доступ итак есть. какая выгода работать с ней через указатель, а не напрямую?).

Так через указатель — это и есть напрямую.

Передать параметр в функцию можно двумя способами: по значению и по адресу. Если передается по значению, то в памяти создается копия переменной и она передается в функцию. Соответственно, если функция меняет переданный параметр, это изменение происходит только в копии, а оригинал остается неизменным. Если же передается по адресу, то функция знает, где лежит оригинал переменной и может его изменять. Т.е. работать с ним напрямую.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 16:56
Зарегистрирован: 19.10.2016

void showDate() char buff[40];
.
formatDate(i, buff, sizeof(buf));
.
lcd.print(buff);
>

Вот и все, без стрингов и глобалов.

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

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 19:07

Dinosaur аватар

Зарегистрирован: 01.01.2018
sadman41 пишет:

void showDate() < char buff[40]; . formatDate(i, buff, sizeof(buf)); . lcd.print(buff); >Вот и все, без стрингов и глобалов. Между прочим — я не советовал глобальный буфер для обмена использовать. Это, как раз, порочная практика. Я его использую для подготовки данных непосредственно перед выводом.

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

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 17:43

Dinosaur аватар

Зарегистрирован: 01.01.2018
andriano пишет:

Передать параметр в функцию можно двумя способами: по значению и по адресу. Если передается по значению, то в памяти создается копия переменной и она передается в функцию. Соответственно, если функция меняет переданный параметр, это изменение происходит только в копии, а оригинал остается неизменным. Если же передается по адресу, то функция знает, где лежит оригинал переменной и может его изменять. Т.е. работать с ним напрямую.

То есть внутри функции Func1 объявляем переменную byte per (она занимает 1 байт памяти), из этой функции вызываем Func2 (void) и передаем указатель на per в качестве аргумента, Func2 по полученному указателю может читать и изменять per (несмотря на то что per объявлена не глобально, а внутри Func1), при этом в памяти per занимает все тот же один байт (поскольку копия в Func2 не создавалась), по завершении Func2 возвращаемся в Func1 где нам доступна измененная per. По выходу из Func1, переменная per удаляется из памяти. Я верно понял?

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 18:33
Зарегистрирован: 06.08.2015
Dinosaur пишет:

Можете скетч в первом примере глянуть где у меня ошибка? В том виде в каком он представлен — все работает, но стоит вынести его в реальный проект — лезет разный мусор на экран 🙁

вы сами ответили на этот вопрос, (см ниже цитату). Только вот утверждение жирным шрифтом, имхо, не правильное. Видимо массив перестает существовать до его использования. Квалификатор static массива arrayBuf решит эту проблему, т.к. массив уничтожаться не будет при выходе из функции, но, разумеется, таким примером со String лучше не пользоваться. как нужно сделать уже объяснили.

Dinosaur пишет:

Что меня смущает — массив arrayBuf объявлен внутри функции, поэтому после return arrayBuf перестает существовать. Поэтому указатель на него вернуть в функцию (в том виде что написано у меня) невозможно. А возврат сроки допустим (массив arrayBuf перестанет существовать уже после того, как вызывающий функцию получит строку через return)

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 19:06

Dinosaur аватар

Зарегистрирован: 01.01.2018

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

const char m_s_1[] PROGMEM = «JAN»; const char m_s_2[] PROGMEM = «FEB»; const char m_s_3[] PROGMEM = «MAR»; const char m_s_4[] PROGMEM = «APR»; const char m_s_5[] PROGMEM = «MAY»; const char m_s_6[] PROGMEM = «JUN»; const char m_s_7[] PROGMEM = «JUL»; const char m_s_8[] PROGMEM = «AUG»; const char m_s_9[] PROGMEM = «SEP»; const char m_s_10[] PROGMEM = «OCT»; const char m_s_11[] PROGMEM = «NOV»; const char m_s_12[] PROGMEM = «DEC»; const char* const month_names[] PROGMEM = ; struct time < uint8_t seconds: 6; uint8_t minutes: 6; uint8_t hours: 5; uint8_t dow: 3; uint8_t date: 5; uint8_t month: 4; uint16_t year: 12; >; time newTime; void setup() < newTime.year = 2000; Serial.begin(115200); >void loop() < uint32_t actualTime = millis(); static uint32_t lastChangeTime; if (actualTime - lastChangeTime >500) < static uint32_t num; String printstring = (String)num + F(" passed"); Serial.println(printstring); num++; lastChangeTime = actualTime; changeTime(); printTime(); >> void changeTime() < newTime.seconds++; if (newTime.seconds >59) < newTime.seconds = 1; >newTime.minutes++; if (newTime.minutes > 59) < newTime.minutes = 1; >newTime.hours++; if (newTime.hours > 23) < newTime.hours = 1; >newTime.dow++; if (newTime.dow > 6) < newTime.dow = 0; >newTime.date++; if (newTime.date > 31) < newTime.date = 1; >newTime.month++; if (newTime.month > 12) < newTime.month = 1; >newTime.year++; if (newTime.year > 2100) < newTime.year = 20100; >> void printTime() < String displayString = ((String)F("Time:") + (String)newTime.hours + F(":") + (String)newTime.minutes + F(":") + (String)newTime.seconds); Serial.println(displayString); displayString = ((String)F("Date:") + (String)newTime.date + F(" ") + getMonthShort() + F(" ") + (String)newTime.year + F(" ") + (String)newTime.dow); Serial.println(displayString); Serial.println("---"); >String getMonthShort() < uint8_t idx = (getMonth() - 1); char arrayBuf[strlen_P(pgm_read_ptr_near(month_names + idx))]; // длина массива рассчитывается правильно strcpy_P(arrayBuf, pgm_read_ptr_near(month_names + idx)); Serial.print("arrayBuf - "); Serial.println(arrayBuf); // и вот тут даже печатается правильно //return (String)(idx); // и в таком варианте не виснет и не перезагружается return (String)(arrayBuf); // но стоит вернуть arrayBuf, как все виснет/перегружается >uint8_t getMonth()

То есть ошибка возникает в функции getMonthShort при попытке вернуть arrayBuf (видимо я как то не так это делаю). Усиленно пытаюсь понять что все написали, но не могли бы в этом примере ткнуть носом — где я неправ. Подозреваю что возвращаю я все же не строку, а что то совсем не то.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 19:19
Зарегистрирован: 19.10.2016

И чему равна размерность arrayBuf ?

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 19:22

Dinosaur аватар

Зарегистрирован: 01.01.2018
sadman41 пишет:
И чему равна размерность arrayBuf ?

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 19:28
Зарегистрирован: 19.10.2016

А скоко нужно выделять памяти под строку в Си? Подсказываю: иначе они называются ASCIIZ строки и все строковые функции в Си работают корректно только с ASCIIZ строками.

PS. String — не строка, а класс в Wiring

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 19:52
Зарегистрирован: 06.08.2015
Пусть фунция возвращает не String а сразу указатель на bufarray. Нах этот String не пойму.

char * stringMonth() < static char bufArray[20]; // тут заполняем bufArray return bufArray; >
  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 19:53

Dinosaur аватар

Зарегистрирован: 01.01.2018

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 20:00

Dinosaur аватар

Зарегистрирован: 01.01.2018
MaksVV пишет:
Пусть фунция возвращает не String а сразу указатель на bufarray. Нах этот String не пойму.

А зачем постоянно висящий в памяти bufArray? На данный момент я решил задачу через malloc (хотя по факту память занята до следующего вызова функции), но хочется все же разобраться с передачей/возвратом char/string.

char* monthToString(uint8_t val)

О, и вопрос к sadman41 — тут тоже размер буфера 3, но все работает.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 20:05

Dinosaur аватар

Зарегистрирован: 01.01.2018
sadman41 пишет:

А скоко нужно выделять памяти под строку в Си? Подсказываю: иначе они называются ASCIIZ строки и все строковые функции в Си работают корректно только с ASCIIZ строками. PS. String — не строка, а класс в Wiring

Fuck, при размере буфера +1 заработало (в первом приближении). Завтра буду еще проверять. Но почему при использовании malloc и размере буфера 3 все работает?

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 20:20
Зарегистрирован: 19.10.2016

Случайное совпадение: память ещё не была ранее засрана и в позиции +1 находился 0x00, он же \0, он же Z-терминатор строки.

Немного иное распределение памяти при компиляции, работа чуть подольше — в позиции +1 может оказаться что угодно и функция, которая ищет его для определения конца строки, будет искать по всей памяти, до второго пришествия.

С маллоками я бы не советовал вам пока связываться. До тех пор, пока не поймёте как распределяется память под переменную и как работают функции с памятью.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 20:22
Зарегистрирован: 23.11.2016
MaksVV пишет:
Пусть фунция возвращает не String а сразу указатель на bufarray. Нах этот String не пойму.

char * stringMonth() < static char bufArray[20]; // тут заполняем bufArray return bufArray; >

А если 10 раз функцию вызвать — где-то в памяти будет висеть 10 вариантов bufArray?

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 20:25
Зарегистрирован: 19.10.2016

Будет висеть один экземпляр на 20 байт.
Но если такая функция, например, попадет под template — это будет гульба на все деньги.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 20:33
Зарегистрирован: 23.11.2016

Гульба будет, когда незнающий программист попробует вызвать функцию 10 раз, возвращая результаты в разные переменные, и вместо общепринятого поведения функции получит 10 копий одного и того же. Конечно это не очень сильно относится к самописному коду, но писать лучше по правилам. А по правилам либо передают буфер как аргумент, либо возвращают String.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 20:38
Зарегистрирован: 19.10.2016

Так 10 вариантов или 10 копий одного и того же (последнего) варианта?

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 21:11
Зарегистрирован: 03.07.2016
sadman41 пишет:
Так 10 вариантов или 10 копий одного и того же (последнего) варианта?

Возьмите и проверьте. Но скорее всего одна копия.Код во флеш, статические данные в ОЗУ. А где реально все лежит определит компилятор. То есть во время компиляции оперативка уйдет на это безобразие.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 21:37
Зарегистрирован: 06.08.2015

Да я не на статик акцентировал внимание, а на то что ТС каждый байт пытается сэкономить, а сам String использует направо и налево. Ну пусть глобальный буфер сделает или локальный из вызывающей функции, Sadman ведь уже советовал. В этом случае можно даж указатель не возвращать на буфер. А просто фунция void обновляет буфер и все.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 21:44
Зарегистрирован: 03.07.2016

MaksVV,ТС даже String не может пользоваться. Хочет экономить память, то пусть вернет String правой ссылкой.Распечатает и освободит память из кучи с помощью скобки.> Вот только что бы это понять надо получить базовые знания. А если их нет, то объянять долго и бесполезно.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 22:18
Зарегистрирован: 06.08.2015
sadman41 пишет:
Но если такая функция, например, попадет под template — это будет гульба на все деньги.

Объясни , если не трудно, что это означает

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 22:08
Зарегистрирован: 06.08.2015
rkit пишет:

Гульба будет, когда незнающий программист попробует вызвать функцию 10 раз, возвращая результаты в разные переменные, и вместо общепринятого поведения функции получит 10 копий одного и того же. Конечно это не очень сильно относится к самописному коду, но писать лучше по правилам. А по правилам либо передают буфер как аргумент, либо возвращают String.

я вообще не понял к чему это. У нас результат указатель на массив. Вызвали эту функцию, получили указатель, в данном случае распечатали массив по указателю на лсд. Каждый вызов сразу используем, какие еще копии. Или я чего то не понимаю. Но то что лучше так не делать это я уже понял, а вот почему, еще не очень

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 22:19
Зарегистрирован: 03.07.2016

Ну дам решение.И что это спасет ТС. Скорее всего нет

const char ml_1[] PROGMEM = "January"; const char ml_2[] PROGMEM = "February"; const char ml_3[] PROGMEM = "March"; const char ml_4[] PROGMEM = "April"; const char ml_5[] PROGMEM = "May"; const char ml_6[] PROGMEM = "June"; const char ml_7[] PROGMEM = "July"; const char ml_8[] PROGMEM = "August"; const char ml_9[] PROGMEM = "September"; const char ml_10[] PROGMEM = "October"; const char ml_11[] PROGMEM = "November"; const char ml_12[] PROGMEM = "December"; const char* const months_names[] PROGMEM = < ml_1, ml_2, ml_3, ml_4, ml_5, ml_6, ml_7, ml_8, ml_9, ml_10, ml_11, ml_12 >; void setup() < Serial.begin(115200); >void loop() < for (uint8_t i = 0; i < 12; i++) < Serial.print(F("i - ")); Serial.println( String((const __FlashStringHelper *)pgm_read_word(months_names + i)) ); delay(1000); >>
  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 22:53
Зарегистрирован: 19.10.2016
MaksVV пишет:

Объясни , если не трудно, что это означает

Без примера будет сложновато, а примеры с мобилы не понатыкиваешь.

Есть такая рутина — EEPROM.put(). Принимает на вход переменные стандартных типов и структуры нестандартные. Внутри себя делает цикл от 0 до sizeof() параметра и побайтово сторит. Но как она управляется с любым типом — ведь универсального нет? А все просто — нет функции EEPROM.put(float) изначально. Есть шаблон, из которого компилятор сгенерирует функцию для float, если таковой вызов будет замечен в исходнике. И для int создаст. И для myStruct. Каждому типу — отдельная функция. Типы byte array[5] и byte array[6] тоже разные, к примеру.

В итоге имеем штук 20 практически одинаковых функций. И тут мы решили, что нам срочно нужен static char[20] внутри . Ну, как буфер. Суем объявление в темплейт, компилятор размножает функции и хренак — мы одним движением минусанули 400байт, которые не освободить, ни реюзнуть не можем — область видимости, однако, мешает.

Набили, короче, багажник воздушными шарами и ездим.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 22:35
Зарегистрирован: 19.10.2016
MaksVV пишет:

Но то что лучше так не делать это я уже понял, а вот почему, еще не очень

Решил ты напечатать три месяца рождения — свой, жены и мамы. Но не просто, а весело — за один прием. Типа «Декапрелеюнь». Сделал три запроса в функцию, каждый результат в свою переменную char * поместил. Думаешь, что в первой будет «Декабрь», во второй «Апрель», в третьей «Июнь»?

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 22:46
Зарегистрирован: 06.08.2015

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

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 22:48
Зарегистрирован: 06.08.2015

Нет конечно. Цель была при каждом вызове сразу печатать. Типа так

sadman41 пишет:

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

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 22:49
Зарегистрирован: 06.08.2015

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 22:50
Зарегистрирован: 19.10.2016
MaksVV пишет:
Нет конечно. Цель была при каждом вызове сразу печатать

Ну так это ты будешь делать, если помнишь, что результат работы функции нужно юзать сразу. А через год просто закопипастишь все в другой скетч, строк этак на 20 000 и будешь ловить веселые глюки, пытаясь понять почему в трёх переменных одно и то же.
Поэтому от этого лучше отвыкать не привыкнув.

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 22:53
Зарегистрирован: 06.08.2015

  • Войдите на сайт для отправки комментариев

Сб, 07/11/2020 — 22:57
Зарегистрирован: 03.07.2016
MaksVV пишет:

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

Ну здесь надо понять что Ложки нет. https://www.youtube.com/watch?v=tHQr6qLJeWE

По факту язык Си/Си+ так же далек от программиста, как и от камня. Просто это удобный инструмент для описания сложных механизмов не менее сложными программами.

  • Войдите на сайт для отправки комментариев

Вс, 08/11/2020 — 09:56

Dinosaur аватар

Зарегистрирован: 01.01.2018
sadman41 пишет:

Случайное совпадение: память ещё не была ранее засрана и в позиции +1 находился 0x00, он же \0, он же Z-терминатор строки. Немного иное распределение памяти при компиляции, работа чуть подольше — в позиции +1 может оказаться что угодно и функция, которая ищет его для определения конца строки, будет искать по всей памяти, до второго пришествия.

Возможно не все так однозначно: сидел мучал сегодня скетч, обратил внимание что если размер буфера задаю равным количеству символов, но строку 80 скетча (из поста 19) излагаю в следующей редакции:

displayString = ((String)F("Date:") + newTime.date + F(" ") + getMonthShort() + F(" ") + newTime.year + F(" ") + newTime.dow);

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

sadman41 пишет:

С маллоками я бы не советовал вам пока связываться. До тех пор, пока не поймёте как распределяется память под переменную и как работают функции с памятью.

Ну это я сгоряча сделал, метод тыка в поисках рабочего решения 🙂

  • Войдите на сайт для отправки комментариев

Вс, 08/11/2020 — 10:10

Dinosaur аватар

Зарегистрирован: 01.01.2018
MaksVV пишет:

Да я не на статик акцентировал внимание, а на то что ТС каждый байт пытается сэкономить, а сам String использует направо и налево. Ну пусть глобальный буфер сделает или локальный из вызывающей функции, Sadman ведь уже советовал. В этом случае можно даж указатель не возвращать на буфер. А просто фунция void обновляет буфер и все.

ЦЕЛЬ экономии каждого байта не стоит, но стараюсь (по возможности, и в меру своих знаний) к памяти относиться бережно (а то и блинком можно всю память занять при желании). String использую потому что:

а) мне это более менее знакомо

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

displayString = ((String)F("Date:") + newTime.date + F(" ") + getMonthShort() + F(" ") + newTime.year + F(" ") + newTime.dow); Serial.println(displayString);

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

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

  • Войдите на сайт для отправки комментариев

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *