char * переменная - правильно обрабатывать, чтобы избежать переполнения

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");

, 👍0


1 ответ


Лучший ответ:

3

При использовании указателей 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