Що означає ця помилка? Я не можу це вирішити жодним чином.
попередження: застаріле перетворення з постійної константи в 'char *' [-Wwrite-string]
Що означає ця помилка? Я не можу це вирішити жодним чином.
попередження: застаріле перетворення з постійної константи в 'char *' [-Wwrite-string]
Відповіді:
Як і моє бажання, я збираюся надати трохи довідкової технічної інформації в те, чому і чому ця помилка.
Я збираюся ознайомитись з чотирма різними способами ініціалізації рядків C і побачити, які відмінності між ними. Це чотири способи, про які йдеться:
char *text = "This is some text";
char text[] = "This is some text";
const char *text = "This is some text";
const char text[] = "This is some text";
Тепер для цього я хочу змінити третю букву "i" на "o", щоб це було "Thos - це якийсь текст". Це у всіх випадках (можна подумати) можна досягти:
text[2] = 'o';
Тепер давайте розглянемо, що робить кожен спосіб оголошення рядка і як це text[2] = 'o';
твердження вплине на речі.
По- перше, найбільш часто бачили шлях: char *text = "This is some text";
. Що це буквально означає? Ну, в C це буквально означає "Створити змінну, яку називають text
вказівником читання-запису на цей рядковий літерал, який зберігається у просторі для коду (код)." Якщо у вас -Wwrite-strings
включена опція, ви отримуєте попередження, як видно з питання вище.
В основному це означає "Попередження: Ви намагалися зробити змінну, яка вказує на запис читання, до області, до якої ви не можете записати". Якщо ви спробуєте, а потім встановите третій символ на "o", ви насправді намагатиметесь записати в область лише для читання, і все буде непогано. На традиційному ПК з Linux, що призводить до:
Помилка сегментації
Тепер другий один: char text[] = "This is some text";
. Буквально в C це означає "Створити масив типу" char "та ініціалізувати його з даними" Це деякий текст \ 0 ". Розмір масиву буде досить великим для зберігання даних". Таким чином, це фактично виділяє оперативну пам’ять і копіює в неї значення "Це деякий текст \ 0". Ніяких попереджень, жодних помилок, абсолютно справедливі. І правильний спосіб зробити це, якщо ви хочете мати можливість редагувати дані . Спробуємо виконати команду text[2] = 'o'
:
Тхос - це якийсь текст
Це спрацювало, ідеально. Добре.
Тепер третій шлях: const char *text = "This is some text";
. Знову буквальне значення: "Створіть змінну під назвою" текст ", яка є вказівником лише для читання на ці дані в пам'яті, що використовується лише для читання." Зауважте, що і вказівник, і дані тепер доступні лише для читання. Ні помилок, ні попереджень. Що станеться, якщо ми спробуємо виконати тестову команду? Ну, ми не можемо. Зараз компілятор розумний і знає, що ми намагаємось зробити щось погане:
помилка: присвоєння місця лише для читання "* (текст + 2у)"
Він навіть не компілюється. Намагання записати в пам'ять, лише для читання, тепер захищена, оскільки ми сказали компілятору, що наш покажчик - це пам'ять лише для читання. Звичайно, це не повинні бути вказуючи на пам'ять тільки для читання, але якщо ви вказуєте для читання-запису пам'яті (RAM) , що пам'ять по- , як і раніше буде захищений від записуються компілятором.
Нарешті остання форма: const char text[] = "This is some text";
. Знову ж таки, як і раніше, []
він виділяє масив в ОЗУ і копіює в нього дані. Однак зараз це масив лише для читання. Ви не можете записати на нього, оскільки вказівник на нього позначений як const
. Спроба написати до нього призводить до:
помилка: присвоєння місця лише для читання "* (текст + 2у)"
Отже, короткий підсумок того, де ми знаходимося:
Ця форма абсолютно недійсна і її слід уникати будь-якою ціною. Це відкриває двері для всіляких поганих речей:
char *text = "This is some text";
Ця форма є правильною формою, якщо ви хочете зробити дані редагованими:
char text[] = "This is some text";
Ця форма є правильною формою, якщо ви хочете, щоб рядки не редагувались:
const char *text = "This is some text";
Ця форма здається марною оперативною пам’яттю, але вона має своє використання. Найкраще забути це, хоча.
const char text[] = "This is some text";
PROGMEM
, PSTR()
або F()
. Таким чином, const char text[]
не використовується більше оперативної пам'яті, ніж const char *text
.
(const char *)(...)
кастинг. Немає реального ефекту, якщо плата цього не потребує, але велика економія, якщо ви потім портуєте свій код на плату.
Щоб пояснити чудову відповідь Макенко, є вагома причина, чому компілятор попереджає вас про це. Давайте зробимо тестовий ескіз:
char *foo = "This is some text";
char *bar = "This is some text";
void setup ()
{
Serial.begin (115200);
Serial.println ();
foo [2] = 'o'; // change foo only
Serial.println (foo);
Serial.println (bar);
} // end of setup
void loop ()
{
} // end of loop
У нас є дві змінні, foo та bar. Я видозмінюю одну з таких у setup (), але подивіться, що це результат:
Thos is some text
Thos is some text
Вони обоє змінилися!
Насправді, якщо ми подивимось на попередження, які ми бачимо:
sketch_jul14b.ino:1: warning: deprecated conversion from string constant to ‘char*’
sketch_jul14b.ino:2: warning: deprecated conversion from string constant to ‘char*’
Компілятор знає, що це хитро, і це правильно! Причиною цього є те, що компілятор (розумно) очікує, що константи рядків не змінюються (оскільки вони є константами). Таким чином, якщо ви посилаєтесь на постійну постійну "This is some text"
множину разів у своєму коді, дозволено виділяти однакову пам'ять всім їм. Тепер якщо ви модифікуєте один, ви модифікуєте їх усі!
*foo
та *bar
використання різних рядкових "констант" цього не статися? Крім того, чим це відрізняється від того, що взагалі не вставляти жодних рядків, наприклад char *foo;
:?
new
, strcpy
а delete
).
Або перестаньте намагатися передати рядкову константу, де функція приймає a char*
, або змініть функцію, щоб вона const char*
замість неї взяла .
Рядок типу "випадковий рядок" - константи.
Приклад:
void foo (char * s)
{
Serial.println (s);
}
void setup ()
{
Serial.begin (115200);
Serial.println ();
foo ("bar");
} // end of setup
void loop ()
{
} // end of loop
Увага:
sketch_jul14b.ino: In function ‘void setup()’:
sketch_jul14b.ino:10: warning: deprecated conversion from string constant to ‘char*’
Функція foo
очікує знаку * (який, отже, може змінювати), але ви передаєте літеральний рядок, який не слід змінювати.
Компілятор попереджає вас цього не робити. Якщо застаріло, це може перетворитися з попередження в помилку в майбутній версії компілятора.
Рішення: Зробіть foo взяти const char *:
void foo (const char * s)
{
Serial.println (s);
}
Я не розумію. Ви маєте на увазі, що неможливо змінити?
Старіші версії C (і C ++) дозволяють писати код, як мій приклад вище. Ви можете зробити функцію (на зразок foo
), яка друкує те, що ви передаєте їй, а потім передає буквальний рядок (наприклад, foo ("Hi there!");
)
Однак функції, яка береться char *
за аргумент, дозволено змінювати свій аргумент (тобто модифікувати Hi there!
в цьому випадку).
Можливо, ви написали, наприклад:
void foo (char * s)
{
Serial.println (s);
strcpy (s, "Goodbye");
}
На жаль, передаючи вниз літерал, ви потенційно змінили цей буквал так, що "Привіт там!" зараз "Прощавай", що не є добре. Насправді, якщо ви скопіювали у довший рядок, ви можете перезаписати інші змінні. Або в деяких реалізаціях ви отримаєте порушення доступу, оскільки "Привіт!" можливо, було поставлено оперативну пам'ять, доступну лише для читання.
Тож автори-компілятори поступово знецінюють це використання, так що функції, на які ви передаєте буквальний, повинні оголошувати цей аргумент якconst
.
can not
бути модифікованим?
У мене така помилка компіляції:
TimeSerial.ino:68:29: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
if(Serial.find(TIME_HEADER)) {
^
Будь ласка, замініть цей рядок:
#define TIME_HEADER "T" // Header tag for serial time sync message
за допомогою цього рядка:
#define TIME_HEADER 'T' // Header tag for serial time sync message
і складання йде добре.