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 поможет диагностировать нехватку ОЗУ.