reserve()
String.reserve() заранее выделяет буфер нужного размера, чтобы при последующей конкатенации строка не перераспределяла память и не фрагментировала кучу.
Полная статья
Описание
Функция String.reserve() заранее выделяет внутренний буфер заданного размера для объекта типа String. Это позволяет избежать многократного перераспределения памяти, которое происходит каждый раз, когда строка растёт — например, при вызовах concat(), += или append(). На платах с ограниченной оперативной памятью, таких как Arduino Uno, частые перераспределения постепенно фрагментируют кучу и могут привести к нестабильной работе программы или зависаниям.
Если вы знаете максимальную длину строки заранее — вызовите reserve() один раз при инициализации, и дальнейшие операции над строкой не будут запрашивать новые блоки памяти до тех пор, пока строка не превысит зарезервированный размер.
Синтаксис
myString.reserve(size)
Параметры
myString: переменная типаString, над которой выполняется операция.size: количество байтов, которые нужно зарезервировать. Учтите, что строка в Arduino хранит символы плюс завершающий нулевой байт, поэтому для строки из 10 символов нужно резервировать не менее 11 байт. Допустимый тип данных:unsigned int.
Возвращает
1 — если память успешно выделена, 0 — если выделить память не удалось (например, из-за её нехватки).
Возвращаемое значение стоит проверять в критичных скетчах: если reserve() вернула false, дальнейшие операции конкатенации могут давать непредсказуемые результаты или вовсе не работать.
Пример кода
String myString;
void setup() {
// инициализация serial, ожидание открытия порта:
Serial.begin(9600);
while (!Serial) {
; // ожидание подключения serial-порта. Необходимо для native USB
}
myString.reserve(26);
myString = "i=";
myString += "1234";
myString += ", is that ok?";
// вывод строки:
Serial.println(myString);
}
void loop() {
// больше ничего делать не нужно
}
Где применяется
- Сборка длинных строк по частям — например, когда вы формируете HTTP-запрос или JSON-пакет, добавляя поля в цикле.
- Работа с Serial-буфером — если вы читаете данные по символу и накапливаете их в
String, заранее зарезервированный буфер снизит нагрузку на кучу. - Динамическое формирование команд — когда строка собирается из нескольких переменных и её максимальная длина известна на этапе написания кода.
- Долгоработающие скетчи — в проектах, где плата работает без перезагрузки часами или сутками, фрагментация памяти особенно опасна, и
reserve()помогает её контролировать.
Практические замечания
- Вызывайте
reserve()как можно раньше — в идеале вsetup(), до первой конкатенации. - Если строка уже содержит данные,
reserve()сохранит их содержимое и просто расширит буфер. - Зарезервированная память не освобождается автоматически при укорачивании строки. Чтобы вернуть память, нужно уничтожить объект
Stringили присвоить ему пустую строку и вызватьreserve(0). - Не резервируйте с запасом «на всякий случай» слишком много: на Uno доступно всего 2 КБ ОЗУ, и каждый лишний байт уменьшает место для стека и других переменных.
- Если
reserve()возвращаетfalse, проверьте, сколько памяти занято другими объектами. ФункцияfreeMemory()из библиотекиMemoryFreeпоможет диагностировать нехватку ОЗУ.