Jak pozbyć się przestarzałej konwersji ostrzeżeń ze stałej ciągu na ostrzeżenia „char *” w GCC?


409

Pracuję więc nad bardzo dużą bazą kodu, a ostatnio zaktualizowałem do gcc 4.3, która teraz wyzwala to ostrzeżenie:

ostrzeżenie: przestarzała konwersja stałej ciągu na „char *”

Oczywiście poprawnym sposobem jest znalezienie każdej takiej deklaracji

char *s = "constant string";

lub wywołanie funkcji, takie jak:

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

i uczyń je const charwskaźnikami. Oznaczałoby to jednak dotknięcie minimum 564 plików, co nie jest zadaniem, które chcę wykonać w tym momencie. Problem w tej chwili polega na tym, że biegam -werror, więc potrzebuję sposobu, aby stłumić te ostrzeżenia. Jak mogę to zrobić?


Kiedy przychodzi zmierzyć się z zastąpieniem linii 554, sed jest dobrym przyjacielem. Najpierw jednak wykonaj kopię zapasową.
Matt

2
Patrzyłem na dyskusje o tym, jak ukryć komunikaty o błędach i jakie powinny być poprawne zamienniki. Nie mam na ten temat żadnych opinii. Myślę jednak, że Matt jest na dobrej drodze. Zdefiniuj, co chcesz zastąpić czym. Potrzebujesz tylko odpowiedniego wyrażenia regularnego. Wprowadź zmiany w kopii. Użyj „diff”, aby porównać je z oryginałem. Wprowadzanie zmian za pomocą sed jest szybkie, łatwe i bezpłatne, a diff jest również szybkie, łatwe i bezpłatne. Wypróbuj i sprawdź, ile zmian musisz sprawdzić. Publikuj to, co chcesz zastąpić, i pozwól użytkownikom sugerować zamiany wyrażeń regularnych.
Thomas Hedden

Cała dyskusja nie wyjaśnia, dlaczego jest to problem, który w ogóle wymaga naprawy zgodnie z ostrzeżeniem gcc. Powodem jest odpowiedź Davida Schwartza stackoverflow.com/questions/56522654/… .
andig

Odpowiedzi:


227

Wierzę, że przejście -Wno-write-stringsdo gcc zniesie to ostrzeżenie.


6
Czy można to wyłączyć dla poszczególnych plików za pomocą pragm.
Priyank Bolia

18
@PriyankBolia bdonlan skomentował odpowiedź Roba Walkera, z której może korzystać #pragma GCC diagnostic ignored "-Wwrite-strings".
MasterMastic,

9
Z wyjątkiem sytuacji, gdy kontrolujesz interfejs API, w którym to przypadku odpowiedź @ Johna poniżej, dotycząca zmiany podpisu na akceptację const char *, jest bardziej poprawna.
jcwenger

215
TO JEST HORRIBLY BAD PRAKTYKA, i przykro mi, że dostała tyle głosów. Ostrzeżeń nie ma, więc je zignoruj. Ostrzeżenia mówią „koleś, robisz coś, co może być nie tak, bądź ostrożny” i powinieneś je tłumić tylko wtedy, gdy chcesz odpowiedzieć „zamknij się, wiem co robię”, co jest najprawdopodobniej nie w przypadku programistów dla niemowląt.
The Quantum Physicist,

9
Zgadzam się, nie powinieneś pozbywać się ostrzeżenia i zamiast tego skorzystać z rozwiązania dostarczonego przez Johna. Szkoda, że ​​to zaakceptowana odpowiedź!
Jérôme

563

Wszelkie funkcje, do których przekazujesz literały łańcuchowe, "I am a string literal"powinny być używane char const *jako typ zamiast char*.

Jeśli chcesz coś naprawić, napraw to dobrze.

Wyjaśnienie:

Nie można używać literałów łańcuchowych do inicjowania łańcuchów, które zostaną zmodyfikowane, ponieważ są one typu const char*. Casting dala constness aby później je modyfikować jest niezdefiniowane zachowanie , więc trzeba skopiować const char*ciągi charprzez charsię dynamicznie alokowanych char*ciągów w celu ich modyfikacji.

Przykład:

#include <iostream>

void print(char* ch);

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

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

25
Chociaż jest to prawda, nie zawsze masz kontrolę nad interfejsami API innych firm, które mogą nie używać poprawnie char */ const char *, więc w takim przypadku zwykle przesyłam.
ideasman42

15
@ppumkin Niestety, wiele standardowych funkcji łańcuchowych w bibliotece C przyjmuje argumenty, char*nawet jak w przypadku ciągów, które nie zostaną zmodyfikowane. Jeśli weźmiesz parametr jako char const*i przekażesz go do standardowej funkcji, to char*trafisz w to. Jeśli funkcja biblioteki nie będzie manipulować ciągiem, możesz odrzucić const.
John

To, że nie zawsze jest to możliwe, nie oznacza, że ​​nie jest to preferowana opcja w wielu przypadkach, gdy to ostrzeżenie pojawia się we wspólnym kodzie produkcyjnym.
LovesTha

1
Teraz w pełni rozumiem rozwiązanie i funkcjonalność literałów łańcuchowych. Ale może inni tego nie robią, więc „podtrzymuję” potrzebę wyjaśnienia
NicoBerrogorry

1
Nie rozumiem, jak zastosować twoje rozwiązanie :(
desmond13

69

Miałem podobny problem, rozwiązałem go w następujący sposób:

#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);
}

Czy to odpowiedni sposób na rozwiązanie tego? Nie mam dostępu do foodostosowania go do akceptacji const char*, chociaż byłoby to lepsze rozwiązanie (ponieważ foosię nie zmienia m).


8
@elcuco, co byś zaproponował? Nie mogłem edytować foo i próbowałem znaleźć rozwiązanie, które nie wymagałoby zniesienia ostrzeżenia. W moim przypadku ten ostatni był raczej kwestią ćwiczeń, ale dla oryginalnego plakatu wydawało się to ważne. O ile wiem, moja odpowiedź jest jedyną, która rozwiązałaby jednocześnie zarówno moje, jak i PO warunki, więc mogłaby być dla kogoś wartościową odpowiedzią. Jeśli uważasz, że moje rozwiązanie nie jest wystarczająco dobre, czy możesz podać alternatywę? (Nie obejmuje to edytowania foo ani ignorowania ostrzeżenia.)
BlackShift,

jeśli założymy, że foo jest poprawnie zakodowany (co niestety nie wydaje się mieć miejsca w przypadku kodu „Josh Matthews”), jest to najlepsze rozwiązanie. dzieje się tak dlatego, że jeśli funkcja musi faktycznie zmienić ciąg „msg” przekazując go, ciąg stały złamałby kod, prawda? ale i tak nie wydaje się to odpowiadać na pytanie, ponieważ błędy są już w starym kodzie, a nie w nowym, więc i tak musiałby zmienić stary kod.
João Portela

Takie podejście też wybrałem. A jeśli ktoś szuka to dla przypadków char **w PyArg_ParseTupleAndKeywordsI zrobić coś takiego:static char kw[][16] = {"mode", "name", "ip", "port"}; static char * kwlist[] = {kw[0], kw[1], kw[2], kw[3], NULL};
dashesy

@elcuco: Nie jestem pewien, jak działają tablice statyczne C ++. Czy to naprawdę skopiuje jakieś dane, a nie tylko wskaźnik?
Alexander Malakhov

Chociaż w niektórych przypadkach takie podejście może mieć sens, stosowanie go na ślepo, IMO może wyrządzić więcej szkody niż pożytku. Ślepe stosowanie tego może łatwo prowadzić do zwisających wskazówek. Spełnia również kod niepotrzebnymi kopiami ciągów.
płukanie


30

Jeśli jest to aktywna baza kodu, nadal możesz chcieć zaktualizować bazę kodu. Oczywiście ręczne wykonanie zmian nie jest możliwe, ale uważam, że ten problem można rozwiązać raz na zawsze za pomocą jednego sedpolecenia. Jednak tego nie próbowałem, więc weź to z odrobiną soli.

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

Może to nie znaleźć wszystkich miejsc (nawet nie biorąc pod uwagę wywołań funkcji), ale złagodziłoby problem i umożliwiło ręczne wykonanie kilku pozostałych zmian.


7
która rozwiązuje tylko ostrzeżenia deklaracyjne i nie wywołuje funkcji +1 dla sed fu: p
João Portela

25

Nie mogę użyć przełącznika kompilatora. Więc zmieniłem to:

char *setf = tigetstr("setf");

do tego:

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

1
+1 - nie możesz zmienić wartości aplikacji, tylko wartość. okazało się to rozwiązać prawdziwy problem. inne po prostu omijają niektóre problemy z kompilatorem.
elcuco

1
Naprawdę denerwujące jest to, że tigetstr () powinien być prototypowany za pomocą (const char *), a nie (char *)
vy32

2
Gdy to robię, pojawia się komunikat „Warning: cast from type„ const char * ”na type„ char * ”casting constness away. Musiałem użyć const_cast, aby pozbyć się wszystkich ostrzeżeń: const_cast <char *> ("setf")
CrouZ

2
Myślę, że const cast jest pierwszym akceptowalnym rozwiązaniem na tej stronie (oprócz zmiany API).
pierwszy

25

Oto, jak to zrobić w pliku, abyś nie musiał modyfikować pliku Makefile.

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

Możesz później ...

#pragma GCC diagnostic pop

25

Zastąpić

char *str = "hello";

z

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

lub jeśli wywołujesz funkcję:

foo("hello");

zamień to na

foo((char*) "hello");

15

Zamiast:

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

To działa:

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

Jest to właściwy sposób, aby to zrobić, ponieważ nie powinieneś przekazywać (stałego) łańcucha do funkcji, która i tak oczekuje ciągłego łańcucha!
jfla,

15

W C ++ użyj const_castponiższych instrukcji

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

7

Test stringjest const string. Możesz więc rozwiązać w ten sposób:

char str[] = "Test string";

lub:

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

4

Dlaczego nie użyć po prostu rzutowania?

(char*) "test"

2

Wykonuj rzutowanie od stałego łańcucha do wskaźnika char, tj

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

1

W C ++ zamień:

char *str = "hello";

z:

std::string str ("hello");

A jeśli chcesz to porównać:

str.compare("HALLO");

1

Nie rozumiem, jak zastosować twoje rozwiązanie :( - kalmanIsAGameChanger

Pracując z Arduino Sketch, miałem funkcję powodującą moje ostrzeżenia.

Oryginalna funkcja: char StrContains (char * str, char * sfind)

Aby zatrzymać ostrzeżenia, dodałem const przed char * str i char * sfind.

Zmodyfikowano: char StrContains (const char * str, const char * sfind).

Wszystkie ostrzeżenia zniknęły.


Oto poprawna odpowiedź, zgodnie z ostrzeżeniem: „ostrzeżenie: przestarzała konwersja stałej ciągowej na„ char * ””.
Norbert Boros,

0

zobacz tę sytuację:

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
};

obserwuj pole nazwy, w gcc kompiluje się bez ostrzeżenia, ale w g ++ zrobi to, nie wiem dlaczego.


gcc sugeruje traktowanie pliku jako pliku źródłowego w języku C, g ++ traktuje go jako plik źródłowy w języku c ++, chyba że zastąpienie przez -x ?? opcja. Tak więc inny język, c i c ++ ma subtelne różnice dotyczące tego, co powinno być ostrzeżeniem.
zhaorufei

0

Można również utworzyć zapisywalny ciąg ze stałej ciągu, wywołując strdup().

Na przykład ten kod generuje ostrzeżenie:

putenv("DEBUG=1");

Jednak następujący kod nie robi tego (tworzy kopię ciągu na stercie przed przekazaniem go do putenv):

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

W tym przypadku (i być może w większości innych) wyłączenie ostrzeżenia jest złym pomysłem - istnieje z jakiegoś powodu. Inna alternatywa (domyślnie domyślna możliwość zapisywania wszystkich ciągów) jest potencjalnie nieefektywna.

Posłuchaj, co mówi ci kompilator!


6
A także przecieka pamięć przydzieloną dla tego zapisywalnego ciągu.
RBerteig

1
Tak, robi to - to celowo. Nie ma problemu z jednorazowym (np. Inicjalizacyjnym) kodem, jak wyżej. Możesz też samodzielnie zarządzać pamięcią i zwolnić ją, gdy skończysz.
BillAtHRST

1
Szczególny przypadek putenv()jest napięty - nie jest to dobry wybór przykładu (przynajmniej nie bez większej ilości dyskusji na temat tego, co putenv()robi, niż w tej odpowiedzi). To cała osobna dyskusja. (Zauważ, że specyfikacja POSIX dla zachowania putenv()jest problematyczna, w oparciu o starsze implementacje sprzed zdefiniowania POSIX.) IIRC, wystąpił błąd w najnowszej (tej tysiącleciu) wersji biblioteki GNU C, która była związana ze putenv()zmianą zachowania, i powrót do przeszłości.)
Jonathan Leffler

0

po prostu użyj opcji -w dla g ++

przykład:

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

Pamiętaj, że nie pozwala to uniknąć wycofania, a raczej zapobiega wyświetlaniu ostrzeżenia na terminalu.

Teraz, jeśli naprawdę chcesz uniknąć utraty wartości, użyj słowa kluczowego const w następujący sposób:

const char* s="constant string";  

0

Dlaczego nie skorzystasz z -Wno-deprecatedopcji, aby zignorować przestarzałe komunikaty ostrzegawcze?


0

Problem polega na tym, że korzystam z opcji -Werror

To twój prawdziwy problem, IMO. Możesz wypróbować kilka zautomatyzowanych sposobów przejścia z (char *) na (const char *), ale nałożyłbym na nie pieniądze, nie tylko działając. Będziesz musiał zaangażować człowieka do przynajmniej części pracy. Krótko mówiąc, po prostu zignoruj ​​to ostrzeżenie (ale IMO zostaw je włączone, bo nigdy nie zostanie naprawione) i po prostu usuń -Werror.


9
Powodem, dla którego ludzie używają -Werror jest tak, że ostrzeżenia nie dostać stałe. W przeciwnym razie nigdy nie zostaną naprawione.
Zan Lynx

2
Ludzie używają opcji -Werror, ponieważ pracowali tylko nad projektami zabawek lub są masochistyczni. Brak kompilacji kodu z powodu aktualizacji GCC to prawdziwy problem, gdy masz ponad 100 000 LOC. Dito ktoś dodaje do kompilacji śmieci takie jak „-Wno-write-strings”, aby pozbyć się irytujących ostrzeżeń (jak sugeruje najwyżej oceniany komentarz w tym poście).
James Antill

2
w tym temacie jest wyraźna różnica zdań, na przykład programmer.97things.oreilly.com/wiki/index.php/…
João Portela

3
@James: Robisz interesujący punkt, ale musi być lepszy sposób. Nie ma sensu natychmiast naprawiać ostrzeżeń - jak rozpoznać, kiedy nowy kod wywołał nowe ostrzeżenie, gdy nie usunąłeś wszystkich starych ostrzeżeń? Z mojego doświadczenia wynika, że ​​ludzie ignorują ostrzeżenia, których nie powinni ignorować.
nobar

2
@James: nasz projekt zabawek to 1,5 + M LOC (wielojęzyczny). Jak powiedział nobar: -Werror unika ignorowania ostrzeżeń, które nie powinny być i tak, za każdym razem, gdy pojawia się nowa wersja kompilatora, musimy ponownie sprawdzić wszystko. -Wno-write-strings jest używane tylko podczas używania Boost dla owijków Pythona w pliku po pliku, ponieważ nie zamierzamy przepisywać Boosta (a obecnie, 2017, wolimy nie używać Boost, ale C ++ 11 / cython). Każde zignorowane ostrzeżenie należy następnie okresowo sprawdzać za pomocą kontroli jakości, aby sprawdzić, czy można go uniknąć za pomocą kodu, czy też nie jest to jeszcze możliwe.
msn

0

Dziękuję wszystkim za pomoc. Wybór z tu i tam pochodzi z tego rozwiązania. To się kompiluje. Nie przetestowałem jeszcze kodu. Może jutro...

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 }

Wiem, że w tablicy timeServer jest tylko 1 element. Ale może być więcej. Reszta została na razie skomentowana, aby zaoszczędzić pamięć.


-1
PyTypeObject PyDict_Type=
{ ...

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

obserwuj pole nazwy, w gcc kompiluje się bez ostrzeżenia, ale w g ++ zrobi to, nie wiem dlaczego.

w gcc (Compiling C) , -Wno-write-string jest domyślnie aktywne.

w g++ (Compiling C++) -Wwrite-string jest domyślnie aktywne

Dlatego zachowuje się inaczej. Dla nas używanie makr Boost_pythongeneruje takie ostrzeżenia. Więc używamy -Wno-write-stringsprzy kompilacji C ++, ponieważ zawsze używamy-Werror


-1

Deklaracja ciągu jako constrozwiąże problem:

char const*s = "constant string";
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.