Результат функции строка.

AsNik
Offline
Зарегистрирован: 24.10.2020

Здравствуйте. Мне нужна функция, результат которой пойдет в другую, а точнее в метод

lcd.print(myFunc());

Хотелось бы максимально быструю и не затратную. Сам я сделал так:

void setup() {
  Serial.begin(9600);
  while (!Serial);
  float t = 49.37; //-99,99..99,99; 
  Serial.print(">>>Result: >"); Serial.print(myStr(&t)); Serial.println("<");
}

String myStr(float* x) {
  char str[6] = {0, 0, 0, 0, 0, 0};
  dtostrf(*x, 1, 1, str);

  for (byte n = 0; n < 6; n++) {
    if (str[n] == 0) {
      if (n == 5) {
        str[n] = 0x43;
      }
      else {
        str[n] = 0x20;
        str[n + 1] = 0x43;
        break;
      }
    }
  }
  return str;
}

void loop() {
}

Тут не на дисплей, а в сериал идет отправка но это не суть пока.

Она работает, но так как я в си не очень силен, хотелось бы услышать как можно ее(myStr) улучшить.

Или может вообще другой вариант предложите?

Что нужно. В функцию передается float параметр (я, если я правильно сделал, передаю параметр по ссылке) В функции просто этот параметр конвертируем в строку, после точки оставляем 1 знак и потом если есть место(результат шесть символов) то добавить проел и символ "С" или без пробела (например "99,9 С" или "-99.9С")

Я пошел на одно "преступление" в функции dtostrf вторым параметром нужно по идее передавать '6', а я передаю '1'. Если передавать 6 - то строка выравнивается по правой стороне. И что бы вставить "С" получится не так просто. 

Я попробовал с sprintf, но что-то у меня с ним не получилось

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

А зачем все так сложно?

AsNik
Offline
Зарегистрирован: 24.10.2020

BOOM пишет:

А зачем все так сложно?

А как проще сделать?

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

Подозреваю, что терминальный ноль затираю когда мудрю с str

PS Так и есть исправил так:

...
  char str[7] = {0, 0, 0, 0, 0, 0, 0};
....

т.е. просто седьмым символом добавил 0

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AsNik пишет:

Я пошел на одно "преступление" в функции dtostrf вторым параметром нужно по идее передавать '6', а я передаю '1'. Если передавать 6 - то строка выравнивается по правой стороне.

Если нужно выравнивание влево, передавайте -6 и не надо никаких преступлений :-)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AsNik пишет:
float параметр (я, если я правильно сделал, передаю параметр по ссылке)
Вы не по ссылке передаёте, а по указателю.

Ну, цикл в строке 12, конечно, не нужен. Там все через strend делается (или через strlen). Попробуйте, если не получится, я после хоккея посмотрю.

AsNik
Offline
Зарегистрирован: 24.10.2020

С выравниванием, да получилось, но strlen вернет размер массива т.е. в моем случае 7. Но мне это не нужно я и без strlen'а знал длину массива :)

А по strend вообще ничего не нашел :( Если я правильно понял, то она должна была вернуть индекс в массиве char с последним значащим символом... Получается цикл все равно нужен(

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

AsNik пишет:

 хотелось бы услышать как можно ее(myStr) улучшить.

для начала стоило бы исправить несоответвие -  функция описано с возвращаемым типом String. а на самом деле отдаете char* - непорядок

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AsNik пишет:

strlen вернет размер массива т.е. в моем случае 7. Но мне это не нужно я и без strlen'а знал длину массива :)

Да, неужели? Это Вам кто сказал? Вас обманули. Почитайте её описание и попробуйте сделать.

Ваши двенадцать строк (с 12-ой по 23-ю) заменяются на две строки, если делать правильно и на одну, если хочется выпендриться и уложиться в одну.

Попробуйте, а если не получится, я покажу.

AsNik
Offline
Зарегистрирован: 24.10.2020

ЕвгенийП пишет:

AsNik пишет:

strlen вернет размер массива т.е. в моем случае 7. Но мне это не нужно я и без strlen'а знал длину массива :)

Да, неужели? Это Вам кто сказал? Вас обманули. Почитайте её описание и попробуйте сделать.

Хм... Странно, в описании действительно должна вернуть длину строки, но у меня код :

void setup() {
  Serial.begin(9600);
  while (!Serial);
  float t = 9.9;
  Serial.print(">>>Result: >"); Serial.print(myStr(&t)); Serial.println("<");
}

String myStr(float* x) {
  char str[7]; // = {0, 0, 0, 0, 0, 0};
  dtostrf(*x, -7, 1, str);
Serial.println(strlen(str));
//  for (byte n = 0; n < 6; n++) {
//    if (str[n] == 0) {
//      if (n == 5) {
//        str[n] = 0x43;
//      }
//      else {
//        str[n] = 0x20;
//        str[n + 1] = 0x43;
//        break;
//      }
//    }
//  }
  return str;
}

void loop() {
}

Возвращает:

20:47:43.661 -> >>>Result: >7
20:47:43.661 -> 9.9    <
 
AsNik
Offline
Зарегистрирован: 24.10.2020

b707 пишет:

для начала стоило бы исправить несоответвие -  функция описано с возвращаемым типом String. а на самом деле отдаете char* - непорядок

Да я думал об этом, но пока моих знаний не хватает избавится от String...

а результат функции массив:

char myStr[7] (float* x) с ходу не получается. Я подозреваю, что нужно свой тип создавать и результат функции уже его...

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

AsNik пишет:

Возвращает:

20:47:43.661 -> >>>Result: >7
20:47:43.661 -> 9.9    <
 

А кто будет за вас присваивать начальное значение массиву str[7] ?

AsNik
Offline
Зарегистрирован: 24.10.2020

Ну даже если расскоментировать char str[7]; // = {0, 0, 0, 0, 0, 0};

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

char str[7] = {0x20, 0x20, 0x20, 0x20, 0x20, 0x20};

все равно 7 показывает

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AsNik,

Вы, похоже, не знаете что такое длина строки. Она же (строка) у Вас пробелами заполнена (а во втором случае, так одними пробелами - char str[7] = {0x20, 0x20, 0x20, 0x20, 0x20, 0x20};). А пробел - это такой же символ, как и все остальные - ничем не хуже других, также считается, как и любая буква. Длина строки считается до символа '\0' - именно он является признаком конца строки. А пробелы - это обычные символы.

Так что Вам нужно либо искать пробел, либо делать так, чтоб dtostrf не расширяла строку пробелами. Последнее Вы знаете как делать.

AsNik
Offline
Зарегистрирован: 24.10.2020

ЕвгенийП пишет:

Попробуйте, а если не получится, я покажу.

Так то думаю должно что-то типа такого получится:

str[strlen(str)]=0x20; str[strlen(str)+1]=0x43;

И все равно нужно проверить нужен пробел или нет. Так что одной-двумя строками не получится...

AsNik
Offline
Зарегистрирован: 24.10.2020

ЕвгенийП пишет:

Последнее Вы знаете как делать.

В dtostrf указать длину 1?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AsNik пишет:
Так что одной-двумя строками не получится...
Вы тут второй день ... в общем, если я говорю, что получится, то обычно это означает, что у меня уже есть готовое решение. Привыкайте.

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

AsNik
Offline
Зарегистрирован: 24.10.2020

ЕвгенийП пишет:

AsNik,

Вы, похоже, не знаете что такое длина строки. Она же (строка) у Вас пробелами заполнена (а во втором случае, так одними пробелами - char str[7] = {0x20, 0x20, 0x20, 0x20, 0x20, 0x20};). А пробел - это такой же символ, как и все остальные - ничем не хуже других, также считается, как и любая буква. Длина строки считается до символа '\0' - именно он является признаком конца строки. А пробелы - это обычные символы.

Так что Вам нужно либо искать пробел, либо делать так, чтоб dtostrf не расширяла строку пробелами. Последнее Вы знаете как делать.

Да, не я в курсе про символы и терминальный ноль.

'\0'  - а это разве не два символа?. Но и такая инициализация char str[7] = {'\0', '\0', '\0', '\0', '\0', '\0'};

Возвращает 7 :(

В общем я немного запутался... :( Буду думать почему после dtostrf в строке нет терминального ноля

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AsNik пишет:

В dtostrf указать длину 1?

А Вы описание dtostrf читали? Судя по всему, нет. Что вообще означает второй параметр?

Я Вам говорю, разбирайтесь и пробуйте. Выкладывайте свой код и результат. Посмотрим.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AsNik пишет:

'\0'  - а это разве не два символа?

Вы не поверите, но даже '\377' - это ОДИН символ :-)

AsNik пишет:

Но и такая инициализация char str[7] = {'\0', '\0', '\0', '\0', '\0', '\0'};

Возвращает 7 :(

А вот это уже, как говаривал Бегемот, - "типичный случай так-называемого вранья" :(

Запускаем и смотрим

void setup(void) {
	Serial.begin(115200);
	char str[7] = {'\0', '\0', '\0', '\0', '\0', '\0'};
	Serial.println(strlen(str));
}

void loop(void){}

У меня печатается ноль. У Вас тоже :-)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AsNik пишет:
Буду думать почему после dtostrf в строке нет терминального ноля
Почему нет? Есть. Я же Вам сказал прочитать её описание. Она делает буфер указанной длины, а за ним ставит ноль, разумеется.

AsNik
Offline
Зарегистрирован: 24.10.2020

ЕвгенийП пишет:

AsNik пишет:

В dtostrf указать длину 1?

А Вы описание dtostrf читали? 

Ну так-то читал... У меня с англ проблема. Пользуюсь переводчиками...

Но я читал в разных местах и русские варианты. И вот в некоторых местах пишут, что второй параметр это кол-во символов до точки, а в других общая длина строки.

Вот по Вашей ссылке - это минимальная длина строки.

AsNik
Offline
Зарегистрирован: 24.10.2020

ЕвгенийП пишет:

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

т.е. он с моим объявлением str[7] и вторым параметром dtostrf  -7 делает его за пределами моей переменной?

AsNik
Offline
Зарегистрирован: 24.10.2020

В общем я теперь не пойму для чего второй параметр dtostrf

void setup() {
  Serial.begin(9600);
  while (!Serial);
  float t = 9.9;
  Serial.print(">>>Result: >"); Serial.print(myStr(&t)); Serial.println("<");
}

String myStr (float* x) {
  char str[7]; // = {'\0', '\0', '\0', '\0', '\0', '\0'};
  dtostrf(*x, 1, 1, str);
  Serial.println(strlen(str));
  return str;
}

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

ЗЫ Если я правильно понял, то второй параметр dtostrf нужен для выравнивания строки и не более. В моем случае можно смело указывать 1. Или я вообще не понял :(

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AsNik пишет:

т.е. он с моим объявлением str[7] и вторым параметром dtostrf  -7 делает его за пределами моей переменной?

Таки да. И испортит соседнюю переменную, если она там есть.

Вот, запустите этот пример и полюбуйтесь, что происходит с переменной duck

int duck;;
char s[8];

void setup() {
	Serial.begin(115200);
	Serial.println("Go on!");

	duck = 123;
	Serial.println(duck);	// duck - 123
	dtostrf(3.14,8,1,s);		// 8 - слишком много
	Serial.println(duck);	// duck испорчена
}

void loop(void){}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AsNik пишет:

В общем я теперь не пойму для чего второй параметр dtostrf

Он задаёт МИНИМАЛЬНУЮ длину строки.

Вот Вы печатает отчет. Числа должны быть друг под другом. А как сделать друг под другом 3 и 100500? Очень просто, сказать что минимальная длина 7 и она Вам выдаст "      3" и " 100500" - при печати они будут друг под другом по правому краю

AsNik
Offline
Зарегистрирован: 24.10.2020

ЕвгенийП пишет:

минимальная длина 7 и она Вам выдаст "      3" и " 100500" - при печати они будут друг под другом по правому краю

Это я уже понял.  при -7 она забьет пробелами до 7 справа т.е. строка получится "3      "

Но что значит:

ЕвгенийП пишет:

Он задаёт МИНИМАЛЬНУЮ длину строки.

[/quote]

Вот тут я туплю :( Если я указываю в моем случае 6 (это максимальная длинна моей строки) то strlen всегда вернет 6. Если укажу 1, то strlen возвращает как положено, но после манипуляций с добавлением пробела и "С" после С может еще какой-то мусор появится.... В общем сижу, разбираюсь.... Боюсь как бы моя ардуина не задымила раньше меня)

ЗЫЖ Вот например:

22:33:59.224 -> 3
22:33:59.224 -> 0:57-9
22:33:59.224 -> 1:46-.
22:33:59.224 -> 2:48-0
22:33:59.224 -> 3:32- 
22:33:59.272 -> 4:67-C
22:33:59.272 -> 5:32- 
22:33:59.272 -> 6:73-I
22:33:59.272 -> >>>Result: >9.0 C I<
 
Вот откуда эта I появилась? И это вроде как связано с этим вторым параметром :( Может я со своей str  как то неправильно обращаюсь, но:
void setup() {
  Serial.begin(9600);
  while (!Serial);
  float t = 9;
  String s = myStr(&t);
  Serial.print(">>>Result: >"); Serial.print(s); Serial.println("<");
}

String myStr (float* x) {
  char str[7]; // = {'\0', '\0', '\0', '\0', '\0', '\0'};
  dtostrf(*x, 1, 1, str);
  Serial.println(strlen(str));
  byte n = strlen(str);
  if (n == 5) str[n] = 0x43; else {
    str[n] = 0x20;
    str[n+1] = 0x43;
  }
  for (byte k = 0; k<7; k++) {
    Serial.print(k); Serial.print(":");
    Serial.print((int)str[k]); Serial.print("-"); Serial.println(str[k]);
  }
  return str;
}

Вроде за рамки не выхожу..

AsNik
Offline
Зарегистрирован: 24.10.2020

Вот этот вариант правильно все делает, но:

void setup() {
  Serial.begin(9600);
  while (!Serial);
  float t = 99.1; //-99.5
  String s = myStr(&t);
  Serial.print(">>>Result: >"); Serial.print(s); Serial.println("<");
}

String myStr (float* x) {
  char str[7]; // = {'\0', '\0', '\0', '\0', '\0', '\0'};
  dtostrf(*x, 1, 1, str);
  Serial.println(strlen(str));
  byte n = strlen(str);
  if (n == 5) {
    str[n] = 0x43;
    str[n + 1] = 0;
  } else {
    str[n] = 0x20;
    str[n + 1] = 0x43;
    str[n + 2] = 0;
  }
  for (byte k = 0; k < 7; k++) {
    Serial.print(k); Serial.print(":");
    Serial.print((int)str[k]); Serial.print("-"); Serial.println(str[k]);
  }
  return str;
}

void loop() {
}

Н-да. Вот так вот я избавился от цикла, но строк кода меньше не стало(

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AsNik пишет:

Вот тут я туплю :( 

Бывает.

AsNik пишет:

Вот откуда эта I появилась? 

Вы затёрли ноль, который там был, а новый кто за Вас прописывать будет?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AsNik пишет:
Но подскажите, что нужно что бы от стринга избавится
Сначала победите так и убедитесь, что всё понимаете. Если задачи не решать, а бросать, так и не разобравшись, то Вам не поможет ни стринг, ни его отсутствие.

AsNik
Offline
Зарегистрирован: 24.10.2020

Блин, движок форума тут ужасный... Сейчас набирал текст, и что-то случилось и текст потерялся :(

Но вот еще раз, хоть уже и ответили, но я сам до этого додумался.

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

AsNik
Offline
Зарегистрирован: 24.10.2020

ЕвгенийП пишет:

кто за Вас прописывать будет?

в #26 сделал)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, а где вариант в две строки после dtostrf? Зачем Вы там циклов развели?

После вызова dtistrf всё остальное делается в одну - две строки. Всё, что свыше - неоправданное усложнение и вынос самому себе мозга.

AsNik
Offline
Зарегистрирован: 24.10.2020

Циклов там на самом деле нет, касаемо самой функции.

Циклы там для отладки в сериал...

Ну так-то там в один оператор if после dtostrf ну если не считать временную переменную n.

Согласен, строк получилось 6 :( Но я со своими знаниями лучше не соображу :(

ЗЫЖ Мне еще важен конечный результат. Так как на lcd уже места отведены. Поэтому и пробел убираю, когда "-99.9С" в других случаях добавляю "9.9 С"

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Да там соображать-то нечего, держите и разбирайтесь! Запустите, проверьте, кстати.

String myStr (float x) {
  char str[12];
  dtostrf(x, 1, 1, str);
  return strcat(str, strlen(str) <= 5 ? " C" : "C");
}

void setup(void) {
	Serial.begin(115200);
	Serial.print(">>>Result: >"); Serial.print(myStr(9.9)); Serial.println("<");
	Serial.print(">>>Result: >"); Serial.print(myStr(99.9)); Serial.println("<");
	Serial.print(">>>Result: >"); Serial.print(myStr(999.9)); Serial.println("<");
	Serial.print(">>>Result: >"); Serial.print(myStr(9999.9)); Serial.println("<");
	Serial.print(">>>Result: >"); Serial.print(myStr(99999.9)); Serial.println("<");
}

void loop(void) {}
AsNik
Offline
Зарегистрирован: 24.10.2020

Ё, моё.... Спасибо.

Реально все просто, но я бы долго до этого доходил... :(

Я эту конструкцию ? : знаю, но почему то ей не пользуюсь. Но тут даже не в ней дело, а в объединении строк (strcat)

Я даже и не думал об этом. Ох... стар я наверное для программирования)

AsNik
Offline
Зарегистрирован: 24.10.2020

ЕвгенийП пишет:

Запустите, проверьте, кстати.

Немного подправил:

   void setup() {
  Serial.begin(9600);
  while (!Serial);
  float t = 9; //-99.5
  String s = myStr(&t);
  Serial.print(">>>Result: >"); Serial.print(s); Serial.println("<");
}

String myStr (float* x) {
  char str[7];
  dtostrf(*x, 1, 1, str);
  return strcat(str, strlen(str) <= 5 ? " C" : "C");
}

Работает четко. ПодскАжите как от String'а избавится или это лучше не сделает?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

1. Нафига вернули указатель? Вы ничего этим не выиграли, а время выполнения увеличилось. Зачем?

2. Я уже писал выше. что в одну строку можно, но это выпендрёж. Лучше добавить ещё одну строку, но работать будет вдвое быстрее. Вот так.

String myStr (float x) {
  char str[12];
  const size_t len = strlen(dtostrf(x, 1, 1, str));
  strcpy(str + len, len <= 5 ? " C" : "C");
  return str;
}

void setup(void) {
	Serial.begin(115200);
	Serial.print(">>>Result: >"); Serial.print(myStr(9.9)); Serial.println("<");
	Serial.print(">>>Result: >"); Serial.print(myStr(99.9)); Serial.println("<");
	Serial.print(">>>Result: >"); Serial.print(myStr(999.9)); Serial.println("<");
	Serial.print(">>>Result: >"); Serial.print(myStr(9999.9)); Serial.println("<");
	Serial.print(">>>Result: >"); Serial.print(myStr(99999.9)); Serial.println("<");
}

void loop(void) {}

Дело в том, что и strlen, и strcat требуют прохода вдоль всей строки. А в таком варианте проход всего один - в strlen.

(хотя, если уж выпендриваться, то можно и такой вариант в одну строку упихать, примерно так)

String myStr (float x) {
  char str[12];
  const size_t len = strlen(dtostrf(x, 1, 1, str));
  return strcpy(str + len, len <= 5 ? " C" : "C"), str;
}

void setup(void) {
	Serial.begin(115200);
	Serial.print(">>>Result: >"); Serial.print(myStr(9.9)); Serial.println("<");
	Serial.print(">>>Result: >"); Serial.print(myStr(99.9)); Serial.println("<");
	Serial.print(">>>Result: >"); Serial.print(myStr(999.9)); Serial.println("<");
	Serial.print(">>>Result: >"); Serial.print(myStr(9999.9)); Serial.println("<");
	Serial.print(">>>Result: >"); Serial.print(myStr(99999.9)); Serial.println("<");
}

void loop(void) {}

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

AsNik
Offline
Зарегистрирован: 24.10.2020

ЕвгенийП пишет:

1. Нафига вернули указатель? 

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

Ну я очень давно писал на делфях. И там были переменные var и const ну и просто типа так:

function Fu (const a: byte; var b: integer; c: float): PChar;

begin

end;

так вот там было так, что переменные a и b не копировались а передавались по ссылке. Отличие a - можно изменить в функции и она изменится там где вызывали эту функцию а b тоже самое, только меня ее нельзя, а вот с: float - будет передана копия этой переменной в функцию.

Я думал и тут так же(

ЕвгенийП пишет:

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

Не, мне так не пойдет. Я расчитывал на результат функции. Т.е. ее передаю в lcd.print(MyFunc());

Нашел такой пример:

char * MyFunc() {

char ch[6]="hello";

return ch; }

Но в моем случае не смог ее прикрутить (

А если честно, меня пугают эти * &. Я наверное никогда не запомню для чего они.

Они то спереди ставятся, то сзади, то посередине, то к переменной, то к типу.... мой мозг взрывается когда я с ними что-то делаю. И если Вы говорите убрать эту * и будет даже быстрее, то для меня это даже камень с плеч.

AsNik
Offline
Зарегистрирован: 24.10.2020

ЕвгенийП пишет:

(хотя, если уж выпендриваться, то можно и такой вариант в одну строку упихать, примерно так)

У меня не та цель, что бы было как можно меньше строк. А чтоб код был оптимальным, быстрым и в тоже время понятным.

А вообще я восхищаюсь, когда вижу такие вещи, кода я делаю 1000 строк, оптимизировав вроде до нехочу , а кто-то берет и эту тыщу впихивает в десяток строк. Круто.

AsNik
Offline
Зарегистрирован: 24.10.2020

Почитал я про передачу параметров по указателю и по ссылке

Т.е. как у меня - это по указателю. По ссылке будет так:

String myStr(float &x) {

dtostrf(x, 1, 1, str);

}

Ну и вызов уже не lcd.print(myStr(&gg)); а просто как обычно lcd.print(myStr(gg));

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AsNik пишет:

Я думал что наоборот так быстрее будет. Т.е. не будет копироваться переменная в функцию

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

AsNik пишет:

там были переменные var и const

Кстати, const и у нас не помешает. Лучше написать заголовок функции как "String myStr (const float x) {"

AsNik пишет:

ЕвгенийП пишет:

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

Не, мне так не пойдет. Я расчитывал на результат функции. Т.е. ее передаю в lcd.print(MyFunc());

Кто ей мешает получать адрес буфера параметром и этот же адрес возвращать? Тогда всё будет как надо.

AsNik пишет:

Нашел такой пример:

char * MyFunc() {
   char ch[6]="hello";
   return ch;
}

Запомните, где взяли этот пример (автора) и впредь ни строчки оттуда не берите. Уровень безграмотности автора - примерно первая неделя изучения языка, не более. Здесь ошибка за которую бьют по рукам студентов - первокурсников (и школьников там, где преподают программирование). Переменная ch существует только внутри функции. После выхода её уже нет и занимаемая ею память свободна. Если до того, как Вы используете результат никто ничего туда не пропишет, то может показаться, что всё работает. Но потом, любое изменения в программе, кто-то загадит эту память и всё - приплыли.

AsNik пишет:
И в чем тогда разница по указателю или по ссылке? Если и там и там передается адрес а не значение....

В том, что можно с этим параметром делать. Указатель - честная переменная, его можно менять и он начнёт указывать на что-то другое. Ссылка намертво привязана и с ней ничего делать нельзя.

AsNik
Offline
Зарегистрирован: 24.10.2020

ЕвгенийП пишет:

Длина переменой типа float в данной платформе - 2 байта, длина указателя - тоже два байта. 

Четыре вроде:

void setup() {
  float f;
  Serial.begin (9600); while (!Serial);
  Serial.print("Size var: "); Serial.println(sizeof(f));
  Serial.print("Size pointer: "); Serial.println(sizeof(&f));
}

void loop() {}

Результат:

Size var: 4
Size pointer: 2
 
или я мимо?

ЕвгенийП пишет:

Кстати, const и у нас не помешает. Лучше написать заголовок функции как "String myStr (const float x) {"

а это не одно и тоже с: String myStr(float &x) { - получается?
 

ЕвгенийП пишет:

В том, что можно с этим параметром делать. Указатель - честная переменная, его можно менять и он начнёт указывать на что-то другое. Ссылка намертво привязана и с ней ничего делать нельзя.

Ну, да я почитал про это, немного даже вроде разобрался с положениями * и &, хотя все равно если честно для меня это темный лес.
Еще ресурс где я это читал - русскоязычный, но вроде грамотно написано.
 

ЕвгенийП пишет:

Запомните, где взяли этот пример (автора) и впредь ни строчки оттуда не берите. .... Переменная ch существует только внутри функции.

Так то, да. Про жизнь переменных я в курсе. Они живы только в том месте, где они описаны. Я еще удивился, что в результате

char * MyFunc() {

   char ch[6]="hello";

   return ch;
}

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

ЕвгенийП пишет:

Кто ей мешает получать адрес буфера параметром и этот же адрес возвращать? Тогда всё будет как надо.

т.е. примерно как в плохом коде выше char * MyFunc() { только внутри выделить память под результат.... new()? или как-то подобно... тут наверное malloc

И вопрос, если я выделю память в myStr сделав вызов lcd.print(myStr()); то кто ее освободит потом?

Если Вам не сложно, напишите. Я так-то разберусь, но потрачу уйму времени. Но обидно не это, а то что "завтра" я снова забуду это все. :(

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

AsNik пишет:

т.е. примерно как в плохом коде выше char * MyFunc() { только внутри выделить память под результат....

вроде пишете выше, что "понимаете жизнь переменных" - и снова хотите выделить память внутри?? Посмотрите, что вам пишет Евгений - создать массив снаружи и в функцию передать на него ссылку

Цитата:
И вопрос, если я выделю память в myStr сделав вызов lcd.print(myStr()); то кто ее освободит потом?

вооот! правильный вопрос. Поэтому и не нужно так делать

AsNik пишет:

Если Вам не сложно, напишите. Я так-то разберусь, но потрачу уйму времени. Но обидно не это, а то что "завтра" я снова забуду это все. :(

если кто-то другой напишет - забудете еще быстрее. Поэтому старайтесь разобраться сами.

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

AsNik
Offline
Зарегистрирован: 24.10.2020

Понятно. Спасибо. Вот читаю статью ЕвгенияП очень хорошо написано, понятно... Но у меня в голове какой-то крах становится :(

Более того уже думаю, что все то, что уже написал возможно вообще не оптимально.... жуть.

Но все таки надеюсь, что это только если работать с памятью напрямую.... И так как в основном по моему коду я с памятью напрямую не работаю, то может и не все так плохо у меня. Решил пока не нырять сильно глубоко в си. Оставлю стринг...

Статью еще читаю... по несколько раз некоторые места. Тяжко для меня это дается(

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

На самом деле я не программист по специальности. Программирование - это мое хоби в молодости. Вот решил вспомнить, потому как хобби все равно осталось, но появились МК - и это еще больше нравится, чем писать для ПК.

А вообще скажу честно, я пользуюсь такой схемой. Отлажу какой-то момент, алгоритм или принцип в каком-то проекте. И потом я просто помню, что в таком-то проекте я это делал. И если мне где это нужно потом, то я не помню как я это делал, но помню где и оттуда беру как шаблон.

Поэтому я стараюсь доводить некоторые моменты до "совершенства". Но если не получается, то я даже и не запоминаю это для следующего шаблона. И потом если нужно, то буду опять с нуля решать данный вопрос. Получится - запомню как шаблон, нет - ну сделаю как получится) Все что я делаю - это для себя. Так что криминального тут ничего нет и никого я таким образом не обижу.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

AsNik пишет:

Результат:

Size var: 4

Size pointer: 2

или я мимо?

Нет, это я мимо - забыл, что там 4. Но всё равно, это ничего не меняет, делайте параметр const float и не выпендривайтесь.

AsNik пишет:

только внутри выделить память под результат.... new()?

Память под результат нужно иметь снаружи, а не внутри. Посмотрите на dtostrf - Вы же резервируете память под результат снаружи и ей передаёте указатель. Так же и свою функцию делайте.

Писать не буду - это медвежья услуга. Вам это важно самому написать, я то умею.

AsNik
Offline
Зарегистрирован: 24.10.2020

ЕвгенийП пишет:

 делайте параметр const float и не выпендривайтесь.

Да я не выпендриваюсь, просто хочу разобраться.

ЕвгенийП пишет:

Писать не буду - это медвежья услуга. Вам это важно самому написать, я то умею.

Дико извиняюсь за свою наглость :(
 

ЕвгенийП пишет:

Память под результат нужно иметь снаружи, а не внутри. Посмотрите на dtostrf

За подсказку огромное спасибо.

Если я правильно понял, то должно быть примерно так (рядом ардуины сейчас нет, ели что за синтаксис прошу прощение):

char * myStr(const float x, char *_str) {

  const size_t len = strlen(dtostrf(x, 1, 1, _str));
  strcpy(_str + len, len <= 5 ? " C" : "C");
  return _str;

}

ну и вызов так:

void myfunc () {

char str[7];

float f = 9.99;

lcd.print(myStr(f, str));

}

Чуть позже смогу проверить на ардуине... На верном я хоть пути?

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

AsNik пишет:

Если я правильно понял, то должно быть примерно так (рядом ардуины сейчас нет, ели что за синтаксис прошу прощение):

char * myStr(const float x, char *_str) {

  const size_t len = strlen(dtostrf(x, 1, 1, _str));
  strcpy(_str + len, len <= 5 ? " C" : "C");
  return _str;

}

ну и вызов так:

void myfunc () {

char str[7];

float f = 9.99;

lcd.print(myStr(f, str));

}

Чуть позже смогу проверить на ардуине... На верном я хоть пути?

нифига не понял, зачем вызов отдельной процедурой? Откуда в этой процедуре возьмется float f ?

По-моему, вас опять что-то не туда понесло

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, примерно так.

AsNik
Offline
Зарегистрирован: 24.10.2020

Ок. Тогда как доберусь до ардуины буду рыть в эту сторону... И почитывать Ваши этюды;)

AsNik
Offline
Зарегистрирован: 24.10.2020

b707 пишет:

нифига не понял, зачем вызов отдельной процедурой? Откуда в этой процедуре возьмется float f ?

По-моему, вас опять что-то не туда понесло

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

вместо lcd.print(x, 1); lcd.print(" C"); А еще нужно так же, только без пробела перед С....

Я не думаю, что меня понесло в другую сторону. Хотя все может быть :(

AsNik
Offline
Зарегистрирован: 24.10.2020
Вот это да. В общем добрался я до своей бедной ардуины и первым делом проверил код.
Я не ожидал, что он заработает прямо так как я его набрал в #45, но он заработал)
 
Скажу честно, я перед его набором подглядел исходный код dtostrf. Ну не совсем "настоящий" а нашел на гитхабе что-то типа mydtostrf.
Ну в общем подглядел как там это все реализовано и родил myStr.
 
Меня смущали как всегда * :( Я думал, что с ними еще что-то нужно будет делать...
Ну в теле добавлять или апостроф в вызывающей... Ну не могу я понять как их правильно использовать :(
В общих чертах понимаю, а в деталях увы. Звездочка указывает на адрес, апостроф разыменовывает указатель и берет данные...
Ну не знаю... тяжко это(
 
Но тут у меня еще возник вопросик. Вот полный код моей функции:
char * myStr(const float x, bool _isT, bool _isCur, char *_str) {
  if (_isCur && f_DHTError) {
    if (_isT) return (char*)"TErr  ";
    else return (char*)"HErr  ";
  }
  if (isnan(x)) return (char*)"-nd-";
  const size_t len = strlen(dtostrf(x, 1, 1, _str));
  if (_isT) strcpy(_str + len, len <= 5 ? " C" : "C");
  else strcpy(_str + len, len <= 5 ? " %" : "%");
  return _str;
}

(Ой, сейчас в меня полетят помидоры :))

изначально было так:
  if (_isCur && f_DHTError) {
    if (_isT) return "TErr  ";
    else return "HErr  ";
  }
  if (isnan(x)) return "-nd-";

но при компиляции вылезли варнинги по возврату строковых констант. Правильно ли я их привел к PChar'у?