Як позбутися від "застарілої конверсії з постійних рядків у попередження" char * "в GCC?


409

Тож я працюю над надзвичайно великою базою кодів і нещодавно оновленою до gcc 4.3, що тепер викликає це попередження:

попередження: застаріле перетворення з константного рядка в "char *"

Очевидно, що правильний спосіб це виправити - це знайти кожну декларацію як

char *s = "constant string";

або функціонувати як:

void foo(char *s);
foo("constant string");

і зробити їх const charпокажчиками. Однак це означатиме торкання 564 файлів мінімум, що не є завданням, яке я хочу виконати в цей час. Проблема зараз полягає в тому, що я працюю -werror, тому мені потрібен певний спосіб заглушити ці попередження. Як я можу це зробити?


Коли ви вирішите замінити 554 рядки, sed є хорошим другом. Не забудьте створити резервну копію спочатку.
Метт

2
Я розглянув дискусії про те, як придушити повідомлення про помилки та якими мають бути правильні заміни. Я не маю жодної думки з цього приводу. Однак я думаю, що Метт на правильному шляху. Визначте, що ви хочете замінити чим. Вам просто потрібні правильні регулярні вирази. Внесіть зміни в копію. Використовуйте "diff", щоб порівняти їх з оригіналом. Внесення змін за допомогою sed - це швидко, легко і безкоштовно, а також різко - швидко, легко і безкоштовно. Спробуйте і подивіться, скільки змін вам доведеться переглянути. Опублікуйте те, що ви хочете замінити на що, і дозвольте користувачам запропонувати заміни регулярного виразу.
Томас Гедден

У всій дискусії відсутня точка, чому це проблема, яка взагалі потребує виправлення відповідно до попередження gcc. Причина в відповідь Девіда Шварца stackoverflow.com/questions/56522654 / ... .
andig

Відповіді:


227

Я вірю, що перехід -Wno-write-stringsна gcc придушить це попередження.


6
Чи можна його відключити на базі кожного файлу за допомогою прагм.
Priyank Bolia

18
@PriyankBolia bdonlan прокоментував відповідь Роб Уокера, який він може використовувати #pragma GCC diagnostic ignored "-Wwrite-strings".
MasterMastic

9
За винятком випадків, коли ви керуєте API, в цьому випадку правильнішою є відповідь @ Джона нижче про зміну підпису на прийняття const char *.
jcwenger

215
ЦЕ ПРАВИЛЬНО БАГА ПРАКТИКА, і мені сумно, що вона отримала всі ці голоси. Попереджень немає, щоб ви їх ігнорували. Попередження там говорять вам "чувак, ти робиш щось, що могло бути неправильним, будь обережним", і ти повинен придушувати їх лише тоді, коли хочеш відповісти на кшталт "заткнись, я знаю, що я роблю", що, швидше за все, це не так з дитячими програмістами.
Квантовий фізик

9
Я погоджуюся, вам не слід позбавлятися попередження, а натомість використовувати рішення, яке надає Джон. Шкода, що ця відповідь прийнята!
Jérôme

563

Будь-які функції, до яких ви передаєте літеральні рядки, "I am a string literal"повинні використовуватись char const *як тип замість char*.

Якщо ви збираєтеся щось виправити, виправте це правильно.

Пояснення:

Не можна використовувати літеральні рядки для ініціалізації рядків, які будуть модифіковані, оскільки вони мають тип const char*. Відкидаючи константность , щоб пізніше змінити їх в невизначене поведінку , так що ви повинні скопіювати const char*рядки charшляхом charв динамічно виділених char*рядків, щоб змінити їх.

Приклад:

#include <iostream>

void print(char* ch);

void print(const char* ch) {
    std::cout<<ch;
}

int main() {
    print("Hello");
    return 0;
}

25
Хоча це правда, ви не завжди маєте контроль над сторонніми API, які можуть неправильно використовувати char */ const char *, тому в такому випадку я зазвичай передаваю це.
ideaman42

15
@ppumkin На жаль, багато стандартних функцій бібліотеки C використовують аргументи char*навіть для рядків, які не будуть змінені. Якщо ви приймаєте параметр як a char const*і передаєте його стандартній функції, приймаючи його, char*ви натиснете це. Якщо функція бібліотеки не буде маніпулювати рядком, ви можете відкинути це const.
Джон

Тільки тому, що це не завжди можливо, не означає, що він не є кращим варіантом у багатьох випадках, коли це попередження з’являється у загальному виробничому коді.
Любить,

1
Зараз я повністю розумію рішення та функціональність рядкових літералів. Але, можливо, інші цього не роблять, тому я "тримаю" потребу в поясненні
NicoBerrogorry

1
Я не розумію, як застосувати ваше рішення :(
desmond13

69

У мене була подібна проблема, я вирішив її так:

#include <string.h>

extern void foo(char* m);

int main() {
    // warning: deprecated conversion from string constant to ‘char*’
    //foo("Hello");

    // no more warning
    char msg[] = "Hello";
    foo(msg);
}

Це відповідний спосіб вирішення цього питання? У мене немає доступу до того, fooщоб адаптувати його до прийняття const char*, хоча це було б кращим рішенням (тому fooщо не змінюється m).


8
@elcuco, що б ти запропонував? Я не зміг редагувати foo і намагався знайти рішення, яке не потребувало придушення попередження. У моєму випадку останнє було більшою мірою вправою, але для оригінального плаката це здавалося важливим. Наскільки я можу сказати, моя відповідь є єдиною, яка вирішила б одночасно і мої умови, і ОП, щоб вона могла бути корисною відповіддю для когось. Якщо ви вважаєте, що моє рішення недостатньо добре, ви можете запропонувати альтернативу? (Це не включає редагування foo або ігнорування попередження.)
BlackShift

якщо припустити, що foo зашифровано правильно (що, на жаль, не стосується коду "Джош Меттьюз"), це найкраще рішення. це тому, що якщо функції потрібно фактично змінити рядок 'msg', передаючи її, постійний рядок порушить код, правда? але все одно це, здається, не відповідає на питання, оскільки помилки вже є в старому коді, а не в новому, тому йому потрібно було б змінити старий код все одно.
Жоао Портела

Такий підхід я і застосував. І якщо хтось шукає це для справ char **у, PyArg_ParseTupleAndKeywordsя роблю щось подібне:static char kw[][16] = {"mode", "name", "ip", "port"}; static char * kwlist[] = {kw[0], kw[1], kw[2], kw[3], NULL};
тире

@elcuco: Я не впевнений, як працюють статичні масиви C ++. Чи справді це буде копіювати будь-які дані, а не лише покажчик?
Олександр Малахов

Хоча цей підхід може мати користь у деяких випадках, застосовуючи його наосліп, IMO швидше принесе більше шкоди, ніж користі. Застосування цього сліпо може легко призвести до звисання покажчиків. Він також роздує код безглуздими рядковими копіями.
підключення

69

Ознайомтеся з підтримкою діагностичної прагми gcc і списком опцій попередження -W (змінено: нове посилання на параметри попередження ).

Для gcc ви можете використовувати #pragma warningдирективи, як пояснено тут .


54
Це насправді: #pragma GCC діагностики проігноровано "-Wwrite-string"
bdonlan

30

Якщо це активна база коду, ви, можливо, захочете оновити базу коду. Звичайно, виконувати зміни вручну не представляється можливим, але я вважаю, що цю проблему можна раз і назавжди вирішити однією sedкомандою. Я ще не пробував цього, тому візьміть наступне із зерном солі.

find . -exec sed -E -i .backup -n \
    -e 's/char\s*\*\s*(\w+)\s*= "/char const* \1 = "/g' {} \;

Це може виявити не всі місця (навіть не враховуючи виклики функцій), але це полегшить проблему і дасть можливість виконати кілька змін, що залишилися вручну.


7
це вирішує лише попередження декларацій, а не функціонує дзвінків +1 для sed fu: p
João Portela

25

Я не можу використовувати перемикач компілятора. Тому я перетворив це:

char *setf = tigetstr("setf");

до цього:

char *setf = tigetstr((char *)"setf");

1
+1 - ви не можете змінювати цінність додатків, лише rvalue. це виявило виправити реальну проблему. інші просто вирішують деякі проблеми з компілятором.
elcuco

1
Те, що насправді дратує, це те, що tigetstr () має бути прототипом (const char *), а не (char *)
vy32

2
Коли я це роблю, я отримую "попередження: замість типу" const char * "до типу" char * "відкидає незмінність". Мені довелося скористатися const_cast, щоб позбутися всіх попереджень: const_cast <char *> ("setf")
CrouZ

2
Я думаю, що проект const - це перше прийнятне рішення на цій сторінці (крім зміни API).
rwst

25

Ось як це зробити вбудований у файл, так що вам не доведеться змінювати Makefile.

// gets rid of annoying "deprecated conversion from string constant blah blah" warning
#pragma GCC diagnostic ignored "-Wwrite-strings"

Потім можна пізніше ...

#pragma GCC diagnostic pop

25

Замініть

char *str = "hello";

з

char *str = (char*)"hello";

або якщо ви телефонуєте у функції:

foo("hello");

замініть це на

foo((char*) "hello");

15

Замість:

void foo(char *s);
foo("constant string");

Це працює:

void foo(const char s[]);
foo("constant string");

Це правильний спосіб зробити це, оскільки ви не повинні передавати (постійну) рядок функції, яка очікує непостійну рядок у будь-якому випадку!
jfla


7

Test stringє рядком const. Тож ви можете вирішити так:

char str[] = "Test string";

або:

const char* str = "Test string";
printf(str);

4

Чому б не просто використовувати кастинг типів?

(char*) "test"

2

Робіть набір тексту від постійної рядки до покажчика char, тобто

char *s = (char *) "constant string";

1

В C ++ замініть:

char *str = "hello";

з:

std::string str ("hello");

І якщо ви хочете порівняти це:

str.compare("HALLO");

1

Я не розумію, як застосувати ваше рішення :( - kalmanIsAGameChanger

Працюючи з ескізом Arduino, у мене була функція, яка викликала мої застереження.

Оригінальна функція: char StrContains (char * str, char * sfind)

Щоб зупинити попередження, я додав const перед char * str та char * sfind.

Змінено: char StrContains (const char * str, const char * sfind).

Усі попередження пропали.


Це правильна відповідь згідно з попередженням: "попередження: застаріла конверсія з постійної константи в" char * "".
Норберт Борос

0

дивіться цю ситуацію:

typedef struct tagPyTypeObject
{
    PyObject_HEAD;
    char *name;
    PrintFun print;
    AddFun add;
    HashFun hash;
} PyTypeObject;

PyTypeObject PyDict_Type=
{
    PyObject_HEAD_INIT(&PyType_Type),
    "dict",
    dict_print,
    0,
    0
};

дивіться поле імен, в gcc воно збирається без попередження, але в g ++ воно буде, я не знаю чому.


gcc має на увазі трактувати файл як вихідний файл C, g ++ трактувати його як вихідний файл c ++, якщо тільки не замінити на -x ?? варіант. Отже, різні мови, c і c ++ мають тонкі відмінності щодо того, що має бути попередженням.
zhaorufei

0

Ви також можете створити рядок, що можна записати, з константи рядка, зателефонувавши strdup() .

Наприклад, цей код генерує попередження:

putenv("DEBUG=1");

Однак наступного коду немає (він робить копію рядка на купі перед тим, як передавати його putenv):

putenv(strdup("DEBUG=1"));

У цьому випадку (і, можливо, у більшості інших) вимкнення попередження є поганою ідеєю - це є чомусь причиною. Інша альтернатива (зробити всі рядки для запису за замовчуванням) потенційно неефективна.

Послухайте, що вам каже компілятор!


6
І також просочується пам'ять, виділена для цього записуваного рядка.
RBerteig

1
Так, це так - це спеціально. Не проблема з одноразовим (наприклад, ініціалізацією) кодом, як зазначено вище. Або ви можете самостійно керувати пам'яттю та звільняти її, коли закінчите з нею.
BillAtHRST

1
Конкретний випадок putenv()загрожує - це не вдалий вибір прикладу (принаймні, не без набагато більшого обговорення того, що putenv()означає, ніж є у цій відповіді). Це зовсім окрема дискусія. (Зауважте, специфікація POSIX для поведінки putenv()є проблематичною, базуючись на застарілих реалізаціях, перш ніж визначено POSIX.) IIRC, в нещодавньому (цього тисячоліття) випуску Бібліотеки GNU C виникла помилка, пов’язана зі putenv()зміною поведінки, і змінюється назад.)
Джонатан Леффлер

0

просто використовуйте -w варіант для g ++

приклад:

g ++ -w -o simple.o simple.cpp -lpthread

Пам'ятайте, що це не дозволяє уникнути застарівання, а запобігає появі попереджувального повідомлення на терміналі.

Тепер, якщо ви дійсно хочете уникнути застарілого використання, використовуйте таке ключове слово const:

const char* s="constant string";  

0

Чому ви не використовуєте -Wno-deprecatedопцію ігнорування застарілих попереджувальних повідомлень?


0

Проблема зараз полягає в тому, що я працюю з -Werror

Це ваша реальна проблема, ІМО. Ви можете спробувати кілька автоматизованих способів переходу від (char *) до (const char *), але я б поклав гроші на них не просто працюючи. Вам доведеться брати участь хоча б в якійсь роботі. На короткий термін просто ігноруйте попередження (але IMO залиште його, інакше воно ніколи не виправиться) та просто видаліть -Werror.


9
Причина , по якій люди використовують -Werror так , що попередження дійсно виправлені. Інакше вони ніколи не виправляються.
Зан Лінкс

2
Причина, яку люди використовують -Werror, полягає в тому, що вони працювали лише над іграшковими проектами, або вони мазохістські. Неможливо створити ваш код через оновлення GCC - справжня проблема, коли у вас є 100k + LOC. Дито. хтось додає сміття на зразок "-Wno-write-string" в збірку, щоб позбутися від дратівливих попереджень (як це підказує найвищий рейтинг у цій публікації).
Джеймс Антілл

2
в цій темі є чітка незгода, наприклад програміст.97things.oreilly.com
wiki/index.php/…

3
@James: Ви зауважуєте цікавий момент, але має бути кращий спосіб. Немає сенсу негайно виправляти попередження - як розпізнати, коли новий код викликав нове попередження, коли ви не видалили всі старі попередження? На мій досвід, це призводить до того, що люди ігнорують попередження про те, що вони не повинні ігнорувати.
nobar

2
@James: наш іграшковий проект - 1,5 + M LOC (багатомовний). Як сказав nobar, -Werror уникає ігнорувати попередження, яких не повинно бути, і так, щоразу, коли нова версія компілятора піднімається, ми повинні повторно перевірити всі. -Wno-write-рядки просто використовуються при використанні Boost для обгортки python у файлі файловим способом, тому що ми не збираємося переписувати Boost (і зараз, 2017, ми вважаємо за краще більше не використовувати Boost, а C ++ 11 / цитон). Кожне попередження, яке ігнорується, необхідно періодично переглядати за допомогою перевірки якості, щоб побачити, чи їх тепер можна уникнути за допомогою коду чи це ще неможливо.
msn

0

Спасибі всім за допомогу. Вибираючи звідси-сюди, приходить таке рішення. Це збирає чисто. Ще не перевірили код. Завтра ... можливо ...

const char * timeServer[] = { "pool.ntp.org" }; // 0 - Worldwide 
#define WHICH_NTP            0 // Which NTP server name to use.
...
sendNTPpacket(const_cast<char*>(timeServer[WHICH_NTP])); // send an NTP packet to a server
...
void sendNTPpacket(char* address) { code }

Я знаю, у масиві timeServer є лише 1 елемент. Але може бути і більше. Решта зараз прокоментували, щоб зберегти пам’ять.


-1
PyTypeObject PyDict_Type=
{ ...

PyTypeObject PyDict_Type=
{
  PyObject_HEAD_INIT(&PyType_Type),
                     "dict",
                     dict_print,
                     0,
                     0
}; 

дивіться поле імен, в gcc воно збирається без попередження, але в g ++ воно буде, я не знаю чому.

в gcc (Compiling C) , -Wno-write-рядки активовано за замовчуванням.

в g++ (Compiling C++) -Wwrite-string є активним за замовчуванням

Ось чому є інша поведінка. Для нас, що використовують макроси Boost_pythonгенерує такі попередження. Таким чином, ми використовуємо -Wno-write-stringsпри компілюванні C ++, оскільки завжди використовуємо-Werror


-1

Оголосити рядок як засіб constвирішить проблему:

char const*s = "constant string";
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.