Как избавиться от `устаревшего преобразования из строковой константы в '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 быстро, легко и бесплатно, а diff также быстро, легко и бесплатно. Попробуйте и посмотрите, сколько изменений вам нужно пересмотреть. Опубликуйте то, что вы хотите заменить чем-либо, и пусть пользователи предложат регулярные замены.
— Томас Хедден

Во всем обсуждении отсутствует точка зрения, почему эта проблема должна быть решена вообще согласно предупреждению gcc. Причина в ответе Дэвида Шварца stackoverflow.com/questions/56522654/… .
— andig

Ответы:


227

Я верю, что переход -Wno-write-stringsк gcc подавит это предупреждение.


6
Это может быть отключено на каждый файл с помощью прагм.
— Приянк Болия

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

9
За исключением случаев, когда вы управляете API, в этом случае ответ @ John ниже об изменении подписи для принятия const char * является более правильным.
— jcwenger

215
Это ужасно плохая практика, и мне грустно, что она получила все эти голоса. Предупреждений нет, чтобы вы их игнорировали. Предупреждения говорят вам: «Чувак, ты делаешь что-то, что может быть не так, будь осторожен», и ты должен подавлять их только тогда, когда хочешь ответить, как «заткнись, я знаю, что я делаю», что наиболее вероятно не в случае с программистами младенцев.
— Квантовый физик

9
Я согласен, вы не должны избавляться от предупреждения и вместо этого использовать решение, предоставленное Джоном. Жаль, что это принятый ответ!
— Жером

564

Любые функции, в которые вы передаете строковые литералы, "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 *, поэтому в этом случае я обычно использую.
— ideasman42

15
@ppumkin К сожалению, многие строковые функции стандартной библиотеки C принимают аргументы char*даже для строк, которые не будут изменены. Если вы возьмете параметр как a char const*и передадите его в стандартную функцию, то char*вы попадете в него. Если библиотечная функция не будет манипулировать строкой, вы можете отказаться от const.
— Джон

То, что это не всегда возможно, не означает, что во многих случаях это предупреждение не является предпочтительным вариантом в обычном рабочем коде.
— LovesTha

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_ParseTupleAndKeywordsI сделать что - то вроде этого:static char kw[][16] = {"mode", "name", "ip", "port"}; static char * kwlist[] = {kw[0], kw[1], kw[2], kw[3], NULL};
— dashesy

@elcuco: Я не уверен, как работают статические массивы C ++. Будет ли это действительно копировать какие-либо данные, а не только указатель?
— Александр Малахов

Хотя этот подход может иметь смысл в некоторых случаях, слепое применение его, скорее всего, принесет больше вреда, чем пользы. Применение этого вслепую может легко привести к висящим указателям. Это также раздувает код бессмысленными копиями строк.
— штепсельная розетка

69

Проверьте поддержку gcc Diagnostic Pragma и список параметров предупреждения -W (изменено: новая ссылка на параметры предупреждений ).

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


54
На самом деле это так: #pragma Диагностика GCC игнорируется "-Write-strings"
— 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 - вы не можете изменить lvalue приложений, только rvalue. это оказалось, чтобы решить реальную проблему. другие просто обходят некоторые проблемы с компилятором.
— Elcuco

1
Что действительно раздражает, так это то, что tigetstr () должен быть прототипирован с (const char *), а не с (char *)
— vy32

2
Когда я делаю это, я получаю «предупреждение: приведение типа« const char * »к типу« char * »отбрасывает constness». Мне пришлось использовать const_cast, чтобы избавиться от всех предупреждений: const_cast <char *> ("setf")
— CrouZ

2
Я думаю, что const cast является первым приемлемым решением на этой странице (кроме изменения API).
— первое

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

15

В C ++ используйте const_castкак показано ниже

char* str = const_cast<char*>("Test string");

7

Test stringконстантная строка Таким образом, вы можете решить так:

char str[] = "Test string";

или:

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

4

Почему бы просто не использовать приведение типов?

(char*) "test"

2

Делать типизацию от константной строки до указателя на символ

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

1

В C ++ замените:

char *str = "hello";

с:

std::string str ("hello");

И если вы хотите сравнить это:

str.compare("HALLO");

1

Я не понимаю, как применить ваше решение :( - kalmanIsAGameChanger

Работая с Arduino Sketch, у меня была функция, вызывающая мои предупреждения.

Оригинальная функция: 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 ++ имеют тонкие различия в том, что должно быть предупреждение.
— Жаоруфеи

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-strings", чтобы избавиться от надоедливых предупреждений (как предполагает самый высокий комментарий в этом посте).
— Джеймс Антилл

2
в этой теме есть явное несогласие, например программист.97things.oreilly.com
— wiki/index.php/…

3
@James: Вы делаете интересное замечание, но должен быть лучший способ. Кажется бессмысленным не исправлять предупреждения немедленно - как вы узнаете, когда новый код вызвал новое предупреждение, если вы не удалили все старые предупреждения? По моему опыту, это просто приводит к тому, что люди игнорируют предупреждения, которые они не должны игнорировать.
— Нобар

2
@James: наш игрушечный проект 1.5 + M LOC (многоязычный). Как сказал nobar, -Werror избегает игнорирования предупреждений, которых не должно быть, и да, каждый раз, когда появляется новая версия компилятора, мы должны перепроверить все. -Wno-write-strings просто используется при использовании Boost для оболочек Python в виде файла за файлом, потому что мы не собираемся переписывать Boost (и сейчас, 2017, мы предпочитаем больше не использовать Boost, а C ++ 11 / Cython). Каждое игнорируемое предупреждение должно затем периодически проверяться проверкой качества, чтобы увидеть, можно ли их теперь избежать с помощью кода или это пока невозможно.
— 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 }

Я знаю, есть только 1 элемент в массиве timeServer. Но могло быть и больше. Остальные были закомментированы, чтобы сохранить память.


-1
PyTypeObject PyDict_Type=
{ ...

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

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

в gcc (Compiling C) -Wno-write-strings активен по умолчанию.

в g++ (Compiling C++) -Wwrite-strings активен по умолчанию

Вот почему существует другое поведение. Для нас использование макросов 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.