Лучшая практика — объявлять «статичный» текст и экономить память

Я работаю над проектом, в котором мне нужно создать JSON для отправки состояния объекта. Я создал код для достижения этого, и все работает отлично, но я использовал это для объявления полей JSON:

static const String JSON_FIELD_A;

затем в файле .cpp

const String MyClass::JSON_FIELD_A = "field_a";

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

Я в любом случае не уверен в этом способе... есть ли более эффективный способ добиться этого? Использование #define может быть решением?

, 👍2

Обсуждение

Это выглядит разумно., @esoterik

Вам следует обратить внимание на макрос F() и класс-заглушку __FlashStringHelper. А также на варианты _P стандартных функций библиотеки строк., @Majenko


2 ответа


1

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


3

«Традиционный» способ — использовать 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