Ошибка обработки строк и символов
У меня ошибка в буферах, обрабатывающих строки, и я не могу понять, в чем проблема. Пример ниже показывает, что происходит. Может ли кто-нибудь мне помочь?
void setup() {
char* ss1 = "";
char* ss2 = "";
String txt = "";
Serial.begin(115200);
txt = "1234560001a";
txt.toCharArray(ss1, txt.length());
Serial.printf("\n\n");
Serial.printf("ss1 = %s\n", ss1); // Печатает "1234560001" без последнего символа "a"
// Принудительно делает "ss1" неверным и равным "ss2", но при этом удаляет последний символ.
txt = "abcdef";
txt.toCharArray(ss2, txt.length());
Serial.printf("ss1 = %s\n", ss1); // Печатает "abcde" без последнего символа "f"
Serial.printf("ss2 = %s\n", ss2); // Он также печатает "abcde"
}
@wBB, 👍1
2 ответа
toCharArray
— это псевдоним для getBytes
. getBytes
документирован как «Копирует символы строки в предоставленный буфер». Вы не выделили память для скопированных символов.
правильное использование 'getBytes' и 'toCharArray'
char buffer[BUFF_SIZE];
str.getBytes(buffer, sizeof[buffer]);
примечание: функции getBytes
устанавливают конечный ноль c-строки в буфере
О проблеме с потерей последнего символа:
Как уже было сказано в комментариях, метод toCharArray()
— это всего лишь псевдоним для метода getBytes()
строковых объектов. Его реализацию можно увидеть в папке IDEs в hardware/arduino/avr/cores/arduino/WString.cpp
:
void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const
{
if (!bufsize || !buf) return;
if (index >= len) {
buf[0] = 0;
return;
}
unsigned int n = bufsize - 1;
if (n > len - index) n = len - index;
strncpy((char *)buf, buffer + index, n);
buf[n] = 0;
}
Мы задаем длину как параметр, который затем уменьшается на 1. Функция strncpy()
, которая здесь используется, скопирует только n = bufsize - 1
байт. В конце n
-й байт будет установлен в 0
как завершающий нулевой символ.
Это означает, что длина, которую мы передаем в качестве параметра, уже включает пробел для нулевого символа. Но метод String::length()
не будет включать нулевой символ в расчет длины (в большинстве случаев это нежелательно). Поэтому вы можете просто добавить 1 к длине перед передачей ее в toCharArray()
.
О проблеме перезаписи:
Как указано в ответе на этот вопрос и в нескольких других обсуждениях C/C++ (например, здесь), строковые литералы должны быть неизменяемыми. Упомянутые источники говорят, что все по-другому, если вы определяете строковую переменную как
char *str = "test";
или
char str[] = "test";
Во втором случае создается обычный изменяемый массив. В первом случае строка будет помещена в память только для чтения. Из вашей проблемы я узнал, что на Arduino она помещается в ОЗУ, поэтому технически изменяема. Но компилятор считает ее неизменяемой. Распространенная оптимизация при работе с неизменяемыми строками — сохранять каждый используемый строковый литерал с одинаковым содержимым по одному и тому же адресу памяти.
В вашем коде вы инициализируете 2 строки только с завершающим нулевым символом. Компилятор думает: «О, подождите, эти два строковых литерала неизменяемы и равны, поэтому я могу сэкономить память, сохранив их только один раз и позволив обоим указателям указывать на этот адрес памяти». При записи в строки в Arduino поведение, похоже, не определено. Поскольку Arduino не может выдать исключение, он, скорее всего, выполнит запись. Поскольку оба указателя указывают на один и тот же адрес памяти, они оба показывают одинаковое содержимое. Кроме того, при записи строки, длиннее инициализированного значения, будет записано пространство памяти за пределами исходной строки. Если там будут сохранены другие переменные, они будут перезаписаны. Вот почему вы всегда должны явно выделять память (статическую или динамическую) для строки, пока другая функция не сделает это за вас. При использовании динамического выделения памяти вы также должны учитывать фрагментацию кучи, которая может легко привести к странному поведению.
Если вы включите подробный вывод для компиляции, компилятор также предупредит вас о том, что вы пытаетесь преобразовать константный строковый литерал в неконстантный указатель на символ:
sketch_jul09a.ino:3:15: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
В заключение, вам не следует объявлять строковые переменные таким образом. Если у вас есть переменный строковый буфер с инициализированными значениями, вам следует использовать
char str[]="test";
Если вы хотите определить постоянную строку, используйте ключевое слово const
:
const char *str = "test";
Затем запись в него приведет к ошибке компиляции, так что вы почувствуете, если вы это делаете.
- Очистка строкового буфера с помощью memset после последовательного чтения
- Проблема с очисткой строки, считанной из последовательного буфера
- Очистка последовательных данных для новых входящих значений
- Как разделить входящую строку?
- Как вывести несколько переменных в строке?
- форматирование строк в Arduino для вывода
- Очень простая операция Arduino Uno Serial.readString()
- DateTime в строку