Лучшая практика — объявлять «статичный» текст и экономить память
Я работаю над проектом, в котором мне нужно создать JSON для отправки состояния объекта. Я создал код для достижения этого, и все работает отлично, но я использовал это для объявления полей JSON:
static const String JSON_FIELD_A;
затем в файле .cpp
const String MyClass::JSON_FIELD_A = "field_a";
Таким образом, я могу использовать это в нескольких точках внутри моего класса (например, для создания и анализа JSON) без необходимости каждый раз писать имя поля, что может привести к несоответствию после рефакторинга.
Я в любом случае не уверен в этом способе... есть ли более эффективный способ добиться этого? Использование #define может быть решением?
@Noisemaker, 👍2
Обсуждение2 ответа
const String
не делает ничего константным. Вы создаете объект, который нельзя переназначить, но все методы работают. Потому что он обрабатывает символы внутри, а объявление const не изменяет его. Вы можете удалить содержимое, изменить содержимое, добавить содержимое объекта String.
Вы должны использовать const char*
или const char[]
. Это c-строка, и компилятор проверяет, обрабатывается ли она как константа, и выдает как минимум предупреждение, если нет.
Для экономии оперативной памяти const char[]
следует объявить как PROGMEM. Во время выполнения эта строка не копируется в оперативную память. Вы можете использовать ее с функциями с постфиксом _P или временно скопировать ее в оперативную память.
Пример из моего проекта:
глобальный массив символов с PROGMEM
const char data[] PROGMEM = "{\"suspension_time_seconds\":10800}";
использовать в функции
client.print(F("Content-Length: "));
client.println(strlen_P(data));
char buff[64];
strcpy_P(buff, data);
client.print(buff);
Вам не нужно копировать строку PROGMEM в ОЗУ только для того, чтобы "напечатать" ее. Просто приведите ее к const __FlashStringHelper *
: client.print((const __FlashStringHelper *)data);
, @Majenko
Я знаю. Неправильный пример с печатью. Это должна быть какая-то функция, не имеющая варианта для progmem., @Juraj
«Традиционный» способ — использовать PROGMEM
и строки const char *
.
Я не использую "традиционный" способ. Вместо этого я использую некоторые "вспомогательные" классы и макросы, которые предоставляет WString.h.
Однако это означает, что из-за ограничений C++ вам придется создавать строковые константы в два этапа.
Сначала нужно определить переменные:
const __FlashStringHelper *stringOne;
const __FlashStringHelper *stringTwo;
Затем вам нужно присвоить фактические строки этим переменным внутри функции. Это может быть в setup()
, но я предпочитаю определять функцию initializeStrings()
, которую вызывает setup()
:
void initializeStrings() {
stringOne = F("This is the first string")
stringTwo = F("This is the second string");
}
void setup() {
initializeStrings();
}
Затем вы можете передать эти объекты (они не являются объектами на самом деле, это просто магическое приведение типов, выполненное компилятором) в качестве параметров другим функциям, используя const __FlashStringHelper *
в качестве типа. Их также можно передать напрямую любой из функций .print()
и .println()
, предоставляемых классом Print
(который в конечном итоге наследуют все объекты Stream
).
Если вы хотите использовать стандартные функции библиотеки строк C _P
для работы со строками (например, для объединения их с другими строками), вы можете просто привести их к PGM_P
:
char temp[50];
strcpy_P(temp, (PGM_P)stringOne);
strcat_P(temp, (PGM_P)stringTwo);
Другой альтернативой всему этому является использование макросов прекомпилятора.
#define STRING_ONE F("This is the first string");
#define STRING_TWO F("This is the second string");
Опять же, вы можете привести их к PGM_P
, чтобы использовать варианты _P
стандартных функций библиотеки строк C, и передать их напрямую в функции print
, поскольку они заменяются дословно как F("Это первая строка")
, а F()
выполняет повторное приведение к const __FlashStringHelper *
.
это все `const char*, и мы должны отслеживать, какой из них находится во флэш-памяти, а какой в ОЗУ, и использовать правильные функции. __FlashStringHelper — это трюк для сопоставления вариантов флэш-памяти. но этот трюк не распознается функциями _P. поэтому нам нужно привести его обратно к const char*. но я сомневаюсь, что новичок сможет это понять, @Juraj
@Juraj Ах... сложности архитектуры Гарварда. Это такая радость. Одна из многих причин, по которым я никогда не использую чипы AVR :), @Majenko
- Выделение строковой памяти Arduino
- Каковы традиционные способы оптимизации использования программной памяти?
- Конкатенации строк и символов
- Оптимизированный генератор случайных буквенно-цифровых строк
- Оптимизация скорости с использованием const, static, constexpr и т. д. в функции
- Предотвращает ли toCharArray создание строки в куче?
- Включает ли скомпилированный бинарный файл скетча неиспользуемые функции из библиотеки?
- Можно ли хранить данные в программном пространстве вместо динамической памяти?
Это выглядит разумно., @esoterik
Вам следует обратить внимание на макрос
F()
и класс-заглушку__FlashStringHelper
. А также на варианты_P
стандартных функций библиотеки строк., @Majenko