Вопрос или проблема
Несмотря на 5 лет разработки с ESP8266 (mini D1), я все еще в замешательстве:
Версия моей IDE – 1.8.19
Мой сценарий содержит 3300 строк, но я создал тестовый случай:
Это сценарий:
// Глобальные переменные
int i=1;
void setup() {
// поместите ваш код настройки сюда, чтобы он выполнялся один раз:
Serial.begin(115200);
delay(1000);
Serial.println();
Serial.println("Небольшой тест...");
Serial.println("Случай 1");
}
void test() {
char* essai = "Hello my wolrd"; // (1) Это не переназначает essai на значение каждый раз!
// static char essai[50] = {0}; // (2) Это работает как статический, так и нет!!!!!
// char* essai = "Hello my wolrd"; // (3) Это не работает, как ожидалось
// strcpy(essai, "Hello my wolrd");// (4)
Serial.printf("essai 1='%s'\n",essai);
strcpy(essai,"Hello your wolrd"); // ваш мир
Serial.printf("essai 2='%s'\n",essai);
}
void loop() {
// поместите ваш основной код сюда, для повторного выполнения:
if (i < 4) { // то есть 3 раза
Serial.printf("Тест %d...\n",i);
test();
}
if (i == 4) {
Serial.println("Тест завершен");
}
i++;
delay(1000);
}
Я ожидал, что результаты будут напечатаны три раза с 2 разными строками, но смотрите:
результаты:
Небольшой тест...
Случай 1 активная линия: 1
Тест 1...
essai 1='Hello my wolrd'
essai 2='Hello your wolrd'
Тест 2...
essai 1='Hello your wolrd'
essai 2='d'
Тест 3...
essai 1='d'
essai 2='d'
Тест завершен
Небольшой тест...
Случай 2 активные линии: 1,4
Тест 1...
essai 1='Hello my wolrd'
essai 2='Hello your wolrd'
Тест 2...
essai 1='Hello your wolrd'
essai 2='d'
Тест 3...
essai 1='d'
essai 2='d'
Тест завершен
Небольшой тест...
Случай 3 активная линия: 2
Тест 1...
essai 1=''
essai 2='Hello your wolrd'
Тест 2...
essai 1='Hello your wolrd'
essai 2='Hello your wolrd'
Тест 3...
essai 1='Hello your wolrd'
essai 2='Hello your wolrd'
Тест завершен
Небольшой тест...
Случай 4 активные линии: 2,4 ==> результат, как ожидалось
Тест 1...
essai 1='Hello my wolrd'
essai 2='Hello your wolrd'
Тест 2...
essai 1='Hello my wolrd'
essai 2='Hello your wolrd'
Тест 3...
essai 1='Hello my wolrd'
essai 2='Hello your wolrd'
Тест завершен
Небольшой тест...
Случай 5 активная линия: 3
Тест 1...
essai 1='Hello my wolrd'
essai 2='Hello your wolrd'
Тест 2...
essai 1='Hello your wolrd'
essai 2='d'
Тест 3...
essai 1='d'
essai 2='d'
Тест завершен
Небольшой тест...
Случай 6 активные линии: 3,4
Тест 1...
essai 1='Hello my wolrd'
essai 2='Hello your wolrd'
Тест 2...
essai 1='Hello your wolrd'
essai 2='d'
Тест 3...
essai 1='d'
essai 2='d'
Тест завершен
Небольшой тест...
Случай 7 активные линии: 2 (строка 2 больше не статическая) результат, как ожидалось
Тест 1...
essai 1='Hello my wolrd'
essai 2='Hello your wolrd'
Тест 2...
essai 1='Hello my wolrd'
essai 2='Hello your wolrd'
Тест 3...
essai 1='Hello my wolrd'
essai 2='Hello your wolrd'
Тест завершен
Это нормально, что
char* essai="Hello my world";
не работает, как я ожидал?
(Извините за ошибку в написании слова World).
Ответ или решение
Вопрос о том, нужно ли объявлять переменные типа cstring
как статические (static) для работы с ESP8266, затрагивает основы работы с указателями и строками в языке C/C++. Давайте разберемся, почему ваш код ведет себя именно так.
Объяснение поведения кода
-
Строковые литералы и указатели:
Когда вы пишете:char* essai = "Hello my wolrd";
Вы при этом объявляете указатель
essai
, который указывает на строковый литерал в памяти. Строковые литералы являются неизменяемыми, и попытка изменить их приведет к неопределенному поведению. Именно поэтому в последующих вызовах, когда вы пытаетесь изменитьessai
, возникает проблема. Например,:strcpy(essai, "Hello your wolrd"); // Приведет к ошибке
-
Статические массивы:
Следующий код:static char essai[50] = {0};
создает статический массив символов, который инициализируется нулями. Вы можете использовать
strcpy
для копирования строки в этот массив, и он будет сохраняться между вызовами функцииtest()
. Статическая переменная сохраняет свое значение между вызовами функции:strcpy(essai, "Hello your wolrd");
-
Необъявленные локальные переменные:
Если вы используете локальную переменнуюchar* essai
, как в следующем:char* essai = "Hello my wolrd";
то каждый раз, когда происходит вызов функции
test
, вы заново объявляете указатель, который снова будет указывать на неизменяемый строковый литерал. Поэтому до тех пор, пока вы не используетеstrcpy
, переменная будет каждый раз указывать на один и тот же литерал.
Почему у вас возникают проблемы
- В случае
(1)
вы пытаетесь работать с неизменяемым строковым литералом, что приводит к неопределенному поведению, когда вы пытаетесь его изменить. - В случае
(2)
и(4)
, когда вы используете статический массив, все работает, так как память выделяется для вашего массива, и вы можете изменять его содержимое без последствий. - Пример
(3)
ведет себя как и в(1)
, так как строковый литерал также остается неизменным и при каждом вызове функции происходит новое объявление указателя.
Рекомендации
- Если вы хотите работать со строками, которые могут изменяться, лучше использовать массивы символов (как в случае с
static char essai[50]
) вместо указателя на строковый литерал. - Если размер вашей строки заранее известен и не изменяется, вы можете объявить его как
static
, чтобы сохранить состояние между вызовами. - Для удобства и избежания путаницы рекомендуется использовать
std::string
(если ваша среда поддерживает C++), что значительно упростит работу со строками.
Заключение
Ваша путаница связана с особенностями работы с указателями и строковыми литералами в C/C++. Использование статических массивов обеспечивает правильное функционирование вашего кода, и вам не обязательно использовать static
, если вы хотите просто сохранить значение. Однако при работе с указателями на неизменяемые строковые литералы следует обязательно избегать их изменения, чтобы избежать ошибок в программе.