Arduino - Функция -> возвращает Cstr вместо строки

У меня есть следующая функция для моего Arduino:

String readLine() {
  String received = "";
  char ch;
  while (myFile.available()) {
    ch = myFile.read();
    if (ch == '\n' or ch == '\r') {
      if (ch == '\r')
      {
        //get rid of the \n
        myFile.read();
      }
      return String(received);
    } else {
      received += ch;
    }
  }
  myFile.close();
  hasFile = false;
  return received;
}

Эта функция используется для возврата строки из текстового файла на SD-карте. Поскольку мне действительно нужно кое-что сделать с возвращаемой строкой, я хотел бы использовать ее как cstr вместо строки для повышения производительности. Но я не могу преобразовать эту функцию в функцию, которая возвращает Cstr. Я попробовал это, но это не работает:

char* readLine() {
  char* received = "";
  char ch;
  while (myFile.available()) {
    ch = myFile.read();
    if (ch == '\n' or ch == '\r') {
      if (ch == '\r')
      {
        //get rid of the \n
        myFile.read();
      }
      return received;
    } else {
      received += ch;
    }
  }
  return received;
}

В настоящее время я конвертирую его после того, как функция вернет его, с помощью c_str(). Это работает, но мне это не нравится, и я хотел бы вернуть Cstr в первую очередь.

Как это можно сделать?

Спасибо!

, 👍1


2 ответа


Лучший ответ:

2

Вы не можете работать со строками c, как со строками. Вам необходимо использовать стандартные функции c-строки (такие как sprintf(), strcpy() и братья и сестры) или напрямую обращаться к массиву символов.

Это приводит к следующим проблемам с вашим кодом:

  • Смотрите эту строку:

      char* received = "";
    

    Здесь вы создаете указатель на постоянную пустую строку. Но это НЕ создаст буфер, в который вы можете поместить данные. Если вы хотите определить буфер для размещения данных, вам необходимо определить массив:

      char received[30] = "";
    

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

      int position = 0;
      ...
      received[position] = ch;
      position++;
    

    Затем, когда вы дойдете до конца буфера или получите конец строки, вы завершите данные в буфере нулевым символом:

      if (ch == '\n' || ch == '\r' || position == 29) {
          if (ch == '\r')
          {
              //get rid of the \n
              myFile.read();
          }
          received[position] = '\0';
    
  • Теперь у вас есть вторая проблема: при определении переменной внутри функции она станет недействительной при выходе из функции. Вы пытаетесь вернуть указатель на символ, который будет указывать на недопустимую ячейку памяти. Таким образом, вы не можете просто вернуть массив, созданный внутри функции (вне динамического выделения). Здесь обычно внешний буфер предоставляется функции ссылкой в качестве указателя. Затем функция будет записывать данные в этот буфер, и ей не нужно будет его возвращать.

      //определение массива вне функции, например, в глобальной области
      #define BUFFER_SIZE  30
      char received[BUFFER_SIZE] = "";
      ...
      void readLine(char *buffer, int size) {
          ...
          // Здесь вы можете использовать буфер в качестве простого массива символов
          buffer[position] = ch;
      }
    

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

,

1

Одним из способов является передача внешнего буфера в readLine() для заполнения:

size_t readLine(char * buff, size_t maxChars) {
  size_t pos = 0; // текущая позиция в буфере
  while (file.myFileAvalable()) {
    buff[pos] = file.read();
    if (buff[pos] == '\n' || buff[pos] == '\r') {
      buff[pos] = '\0';   // завершающая строка
      break;              // завершить цикл
    }
    if (++pos == maxChars-1) { // не забывайте о нулевом терминальном символе
      buff[pos] = '\0';
      break;              // завершить цикл
    }
  }
  return pos; // возвращает количество прочитанных символов (0 ничего не было прочитано)
}

/// ...

char ssid[25] = {0}; // создайте буфер для хранения не более 24 символов + \0
char pass[25] = {0}; // он также заполнит буферы нулями

readLine(ssid, sizeof(ssid)-1);  // было бы лучше проверить, сколько символов было прочитано
readLine(pass, sizeof(pass)-1);  // то же самое здесь

WiFi.begin(ssid, pass);
/// ... Но это выглядит намного хуже, чем строка, однако это гораздо эффективнее, так как использование += для строк довольно недружелюбно 

Большое "НЕТ-НЕТ" делает что-то вроде:

char* readLine() {
  char buff[50] = {0};
  // ... fill ...
  return buff;           // НЕТ!!!! буфер не существует после возврата - у вас болтается указательdangling pointer
}
// ...
char * ssid = readLine(); // после этого болтается указатель...
char * pass = readLine(); // теперь местоположение ssid было наверняка перезаписано

Еще одно НЕТ-НЕТ:

char* readLine() {
  static char buff[50] = {0}; // один буфер для всех вызовов, он не будет уничтожен после завершения... но
  // ... заполнять...
  return buff;           // хорошо, буфер существует после возврата,...
}
// ...
char * ssid = readLine(); // 
char * pass = readLine(); // но сюрприз сюрприз, ssid и точки передачи в один и тот же буфер и, следовательно, одни и те же данные...
,