char * переменная - правильно обрабатывать, чтобы избежать переполнения
Я пытаюсь понять, можно ли обновить переменную char *
в коде (поскольку она хранит только указатель) или может произойти переполнение.
Например:
char *A="/file.txt";
символ *B;
void print_filename(char *filename="/defname.txt"){
Serial.println(filename);
Это правильный способ использования переменной char*
, поэтому в будущем я присваиваю новые значения:
A="/anyOtherfile.txt";
B="Это_не_пусто";
print_filename("/logfile.txt");
@Guy . D, 👍0
1 ответ
Лучший ответ:
При использовании указателей char
, как вы сделали в своем вопросе, переполнение не может произойти.
Вы используете несколько различных строковых литералов (строки, заключенные в двойные кавычки). Они автоматически помещаются в оперативную память компилятором, но не предназначены для записи. Когда вы это сделаете
char *A="/file.txt";
компилятор поместит литерал в оперативную память (не в стек, а в секцию только для чтения, даже если литерал используется внутри функции (спасибо Эдгару Бонету за комментарий)) и затем установит указатель A
на адрес этого литерала. Запись по этому адресу не определена в стандарте C++ и не должна выполняться.
Когда вы сделаете что-то подобное
A = "some other literal";
тогда вы не меняете литерал. Вместо этого вы просто меняете указатель на другой литерал, помещенный в ОЗУ. Таким образом, переполнение не может произойти, поскольку компилятор просто берет все строковые литералы, помещает их в ОЗУ (неизменяемо) и обрабатывает только их адреса памяти. Таким образом, в первую очередь нет доступного для записи буфера.
С
void print_filename(char *filename="/defname.txt"){
Serial.println(filename);
вы устанавливаете значение по умолчанию для указателя filename
на адрес этого конкретного литерала. Установка параметра на адрес другого литерала или существующего массива char
просто изменит указатель. Если вы в другом указателе снова установите filename
на этот точный литерал, литерал все еще находится в ОЗУ, и компилятор снова будет использовать его адрес (поэтому многократное использование одного и того же литерала не приведет к увеличению использования памяти) .
Если вы в какой-то момент захотите записать данные в этот указатель, вам сначала нужно установить его в массив char
, либо адрес статического массива, либо создать новый динамически выделяемый массив. с ключевым словом new
:
char my_static_array[10];
A = my_static_array;
// теперь вы можете писать в массив my_static_array, используя A
A = new char[15];
// теперь вы можете писать во вновь созданный массив, используя A
Хотя при динамическом распределении необходимо помнить, что Arduino на основе AVR (например, Uno/Nano/Mini) не имеют большого объема памяти, и часто динамическое объявление и удаление памяти может привести к фрагментации памяти (по сути, операции удаления освобождают память, но эти дыры со свободной памятью часто слишком малы для следующих выделений, поэтому они не используются, и ваша память становится как швейцарский сыр). Это также является причиной того, что неправильное использование класса Arduino String
действительно плохо.
Таким образом, передача другого строкового литерала не вызовет переполнения, поскольку вы передаете только параметр, если я правильно понимаю?, @Guy . D
Это не может привести к переполнению, потому что вы передаете **указатель** только на уже существующую строку в памяти. Он не копируется в другое место, @chrisl
+1, хороший ответ! Но обратите внимание, что строковые литералы не хранятся в стеке: они статически размещаются в разделе .data программы (или .rodata для «данных только для чтения», если они доступны на платформе). Это так, даже если в исходном коде они появляются в теле функции., @Edgar Bonet
@EdgarBonet А, спасибо. Думал будет в стеке, теперь узнал обратное. Я отредактирую свой ответ, чтобы отразить это, @chrisl