Как избавиться от `устаревшего преобразования из строковой константы в '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.