Wie kann man die veraltete Konvertierung von Zeichenfolgenkonstanten in "char *" -Warnungen in GCC beseitigen?


409

Ich arbeite also an einer außerordentlich großen Codebasis und habe kürzlich ein Upgrade auf gcc 4.3 durchgeführt, das jetzt diese Warnung auslöst:

Warnung: Veraltete Konvertierung von Zeichenfolgenkonstante in 'char *'

Offensichtlich ist der richtige Weg, dies zu beheben, jede Erklärung wie zu finden

char *s = "constant string";

oder Funktionsaufruf wie:

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

und machen sie const charZeiger. Dies würde jedoch bedeuten, mindestens 564 Dateien zu berühren, was zu diesem Zeitpunkt keine Aufgabe ist, die ich ausführen möchte. Das Problem im Moment ist, dass ich mit laufe -werror, also brauche ich eine Möglichkeit, diese Warnungen zu unterdrücken. Wie kann ich das machen?


Wenn Sie versuchen, 554 Leitungen zu ersetzen, ist sed ein guter Freund. Stellen Sie jedoch sicher, dass Sie zuerst eine Sicherungskopie erstellen.
Matt

2
Ich habe mir die Diskussionen darüber angesehen, wie man die Fehlermeldungen unterdrückt und wie die richtigen Ersetzungen sein sollten. Ich habe keine Meinung dazu. Ich denke jedoch, dass Matt auf dem richtigen Weg ist. Definieren Sie, was Sie durch was ersetzen möchten. Sie brauchen nur die richtigen regulären Ausdrücke. Nehmen Sie die Änderungen in einer Kopie vor. Verwenden Sie "diff", um sie mit dem Original zu vergleichen. Das Vornehmen von Änderungen mit sed ist schnell, einfach und kostenlos, und diff ist auch schnell, einfach und kostenlos. Probieren Sie es aus und sehen Sie, wie viele Änderungen Sie überprüfen müssen. Veröffentlichen Sie, was Sie durch was ersetzen möchten, und lassen Sie Benutzer Regex-Ersetzungen vorschlagen.
Thomas Hedden

In der gesamten Diskussion fehlt der Punkt, warum dies ein Problem ist, das gemäß der gcc-Warnung überhaupt behoben werden muss. Der Grund ist in David Schwartz 'Antwort stackoverflow.com/questions/56522654/… .
andig

Antworten:


227

Ich glaube, dass die Übergabe -Wno-write-stringsan gcc diese Warnung unterdrücken wird.


6
Ist es kann pro Datei Basic mit Pragmas deaktiviert werden.
Priyank Bolia

18
@PriyankBolia bdonlan kommentierte Rob Walkers Antwort, die er verwenden kann #pragma GCC diagnostic ignored "-Wwrite-strings".
MasterMastic

9
Außer wenn Sie die API steuern. In diesem Fall ist die Antwort von @ John unten über das Ändern der Signatur zur Annahme von const char * korrekter.
JCwenger

215
Dies ist eine schrecklich schlechte Praxis, und ich bin traurig, dass sie all diese Stimmen erhalten hat. Warnungen sind nicht vorhanden, sodass Sie sie ignorieren. Es gibt Warnungen, die dir sagen "Alter, du tust etwas, das falsch sein könnte, sei vorsichtig", und du solltest sie nur unterdrücken, wenn du antworten willst wie "Halt die Klappe, ich weiß was ich tue", was höchstwahrscheinlich ist Nicht der Fall bei Programmierern.
Der Quantenphysiker

9
Ich stimme zu, Sie sollten die Warnung nicht loswerden und stattdessen die von John bereitgestellte Lösung verwenden. Schade, dass dies die akzeptierte Antwort ist!
Jérôme

563

Alle Funktionen, an die Sie Zeichenfolgenliterale übergeben, "I am a string literal"sollten char const *anstelle von verwendet werden char*.

Wenn Sie etwas reparieren wollen, reparieren Sie es richtig.

Erläuterung:

Sie können keine Zeichenfolgenliterale verwenden, um Zeichenfolgen zu initialisieren, die geändert werden, da sie vom Typ sind const char*. Wegwerfen die Konstantheit , um sie dann zu ändern ist nicht definiertes Verhalten , so dass Sie Ihre kopieren const char*Strings charvon charin dynamisch zugewiesenen char*Strings , um sie zu ändern.

Beispiel:

#include <iostream>

void print(char* ch);

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

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

25
Obwohl dies zutrifft, haben Sie nicht immer die Kontrolle über APIs von Drittanbietern, die / möglicherweise nicht richtig verwenden char *. const char *In diesem Fall habe ich normalerweise gegossen.
ideasman42

15
@ppumkin Leider verwenden viele Zeichenfolgenfunktionen der C-Standardbibliothek Argumente wie char*auch für Zeichenfolgen, die nicht geändert werden. Wenn Sie einen Parameter als nehmen char const*und ihn an eine Standardfunktion übergeben, die einen nimmt, werden char*Sie das treffen. Wenn die Bibliotheksfunktion die Zeichenfolge nicht manipuliert, können Sie die Zeichenfolge wegwerfen const.
John

Nur weil es nicht immer möglich ist, heißt das nicht, dass es für viele Fälle, in denen diese Warnung im allgemeinen Produktionscode erscheint, nicht die bevorzugte Option ist.
LovesTha

1
Ich verstehe jetzt die Lösung und die Funktionalität von String-Literalen vollständig. Aber vielleicht tun es andere nicht, also halte ich die Notwendigkeit einer Erklärung
NicoBerrogorry

1
Ich verstehe nicht, wie ich Ihre Lösung anwenden soll :(
desmond13

69

Ich hatte ein ähnliches Problem, ich habe es so gelöst:

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

Ist dies ein geeigneter Weg, um dies zu lösen? Ich habe keinen Zugriff darauf foo, um es anzupassen const char*, obwohl das eine bessere Lösung wäre (weil foosich nichts ändert m).


8
@elcuco, was würdest du vorschlagen? Ich konnte foo nicht bearbeiten und versuchte eine Lösung zu finden, bei der die Warnung nicht unterdrückt werden musste. In meinem Fall war letzteres eher eine Frage der Übung, aber für das Originalplakat schien es wichtig zu sein. Soweit ich das beurteilen kann, ist meine Antwort die einzige, die gleichzeitig meine und die Bedingungen des OP lösen würde, sodass sie für jemanden eine wertvolle Antwort sein könnte. Wenn Sie der Meinung sind, dass meine Lösung nicht gut genug ist, können Sie bitte eine Alternative anbieten? (Das beinhaltet nicht das Bearbeiten von foo oder das Ignorieren der Warnung.)
BlackShift

Wenn wir davon ausgehen, dass foo richtig codiert ist (was für den Code, über den 'Josh Matthews' spricht, leider nicht der Fall zu sein scheint), ist dies die beste Lösung. Das liegt daran, dass eine konstante Zeichenfolge den Code brechen würde, wenn die Funktion die Zeichenfolge 'msg' tatsächlich ändern muss, oder? Dies scheint jedoch die Frage nicht zu beantworten, da die Fehler bereits im alten Code und nicht im neuen Code enthalten sind, sodass er den alten Code ohnehin ändern müsste.
João Portela

Das ist der Ansatz, den ich auch gewählt habe. Und wenn jemand dies nach den Fällen von char **in sucht, PyArg_ParseTupleAndKeywordsmache ich so etwas:static char kw[][16] = {"mode", "name", "ip", "port"}; static char * kwlist[] = {kw[0], kw[1], kw[2], kw[3], NULL};
Bindestrich

@elcuco: Ich bin nicht sicher, wie statische C ++ - Arrays funktionieren. Kopiert dies wirklich Daten und nicht nur Zeiger?
Alexander Malakhov

Obwohl dieser Ansatz in einigen Fällen von Nutzen sein kann, ist es wahrscheinlich, dass IMO mehr Schaden als Nutzen anrichtet, wenn er blind angewendet wird. Eine blinde Anwendung kann leicht zu baumelnden Zeigern führen. Außerdem wird der Code mit sinnlosen Zeichenfolgenkopien aufgebläht.
Plugwash


30

Wenn es sich um eine aktive Codebasis handelt, möchten Sie die Codebasis möglicherweise trotzdem aktualisieren. Natürlich ist es nicht möglich, die Änderungen manuell durchzuführen, aber ich glaube, dass dieses Problem ein für alle Mal mit einem einzigen sedBefehl gelöst werden kann . Ich habe es jedoch nicht ausprobiert, also nimm das Folgende mit einem Körnchen Salz.

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

Dies findet möglicherweise nicht alle Stellen (auch wenn Funktionsaufrufe nicht berücksichtigt werden), lindert jedoch das Problem und ermöglicht es, die wenigen verbleibenden Änderungen manuell durchzuführen.


7
das löst nur Deklarationswarnungen und keine Funktionsaufrufe +1 für sed fu: p
João Portela

25

Ich kann den Compiler-Schalter nicht verwenden. Also habe ich das gedreht:

char *setf = tigetstr("setf");

dazu:

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

1
+1 - Sie können den Wert von Anwendungen nicht ändern, sondern nur den Wert. Dies erwies sich als das eigentliche Problem zu beheben. andere umgehen nur einige Probleme mit dem Compiler.
Elcuco

1
Was wirklich nervt, ist, dass tigetstr () mit einem (const char *) und nicht mit einem (char *)
prototypisiert werden sollte

2
Wenn ich das mache, bekomme ich stattdessen "Warnung: Umwandlung von Typ 'const char *' in Typ 'char *' wirft Konstanz weg". Ich musste einen const_cast verwenden, um alle Warnungen zu entfernen: const_cast <char *> ("setf")
CrouZ

2
Ich denke, der const cast ist die erste akzeptable Lösung auf dieser Seite (mit Ausnahme der API-Änderung).
Rwst

25

Hier erfahren Sie, wie Sie dies in einer Datei inline tun, damit Sie Ihr Makefile nicht ändern müssen.

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

Sie können dann später ...

#pragma GCC diagnostic pop

25

Ersetzen

char *str = "hello";

mit

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

oder wenn Sie die Funktion aufrufen:

foo("hello");

Ersetzen Sie dies durch

foo((char*) "hello");

15

Anstatt:

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

Das funktioniert:

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

Dies ist der richtige Weg, da Sie keine (konstante) Zeichenfolge an eine Funktion übergeben sollten, die ohnehin eine nicht konstante Zeichenfolge erwartet!
jfla

15

Verwenden Sie in C ++ const_castwie folgt

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

7

Test stringist const string. So können Sie lösen:

char str[] = "Test string";

oder:

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

4

Warum nicht einfach Typ Casting verwenden?

(char*) "test"

2

Führen Sie eine Typumwandlung von einer konstanten Zeichenfolge zu einem Zeichenzeiger durch, z

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

1

Ersetzen Sie in C ++:

char *str = "hello";

mit:

std::string str ("hello");

Und wenn Sie es vergleichen wollen:

str.compare("HALLO");

1

Ich verstehe nicht, wie Sie Ihre Lösung anwenden sollen :( - kalmanIsAGameChanger

Bei der Arbeit mit Arduino Sketch hatte ich eine Funktion, die meine Warnungen verursachte.

Ursprüngliche Funktion: char StrContains (char * str, char * sfind)

Um die Warnungen zu stoppen, habe ich die Konstante vor dem Zeichen und dem Zeichen hinzugefügt.

Geändert: char StrContains (const char * str, const char * sfind).

Alle Warnungen gingen weg.


Dies ist die richtige Antwort gemäß der Warnung: "Warnung: Veraltete Konvertierung von Zeichenfolgenkonstante in 'char *'".
Norbert Boros

0

siehe diese Situation:

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

Beobachten Sie das Namensfeld, in gcc wird es ohne Vorwarnung kompiliert, aber in g ++ wird es, ich weiß nicht warum.


gcc impliziert, dass die Datei als C-Quelldatei behandelt wird, g ++ behandelt sie als C ++ - Quelldatei, sofern sie nicht durch -x überschrieben wird. Möglichkeit. Unterschiedliche Sprachen, c und c ++, weisen also subtile Unterschiede hinsichtlich der Warnung auf.
Zhaorufei

0

Sie können auch eine beschreibbare Zeichenfolge aus einer Zeichenfolgenkonstante erstellen, indem Sie aufrufen strdup().

Dieser Code generiert beispielsweise eine Warnung:

putenv("DEBUG=1");

Der folgende Code funktioniert jedoch nicht (er erstellt eine Kopie der Zeichenfolge auf dem Heap, bevor er an übergeben wird putenv):

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

In diesem Fall (und vielleicht in den meisten anderen Fällen) ist es eine schlechte Idee, die Warnung auszuschalten - sie hat einen Grund. Die andere Alternative (standardmäßig alle Zeichenfolgen beschreibbar zu machen) ist möglicherweise ineffizient.

Hören Sie, was der Compiler Ihnen sagt!


6
Außerdem wird der für diese beschreibbare Zeichenfolge zugewiesene Speicher verloren.
RBerteig

1
Ja, das tut es - das ist absichtlich. Kein Problem mit einmaligem Code (z. B. Initialisierungscode) wie oben. Oder Sie können den Speicher selbst verwalten und freigeben, wenn Sie damit fertig sind.
BillAtHRST

1
Der besondere Fall von putenv()ist voller Probleme - es ist keine gute Wahl für ein Beispiel (zumindest nicht ohne viel mehr Diskussion darüber, was putenv()tut, als in dieser Antwort enthalten ist). Es ist eine ganz separate Diskussion. (Beachten Sie, dass die POSIX-Spezifikation für das Verhalten von putenv()problematisch ist, basierend auf den Legacy-Implementierungen von vor der Definition von POSIX.) IIRC, es gab einen Fehler in einer kürzlich (in diesem Jahrtausend) veröffentlichten Version der GNU C Library, der sich auf putenv()Verhaltensänderungen bezog. und zurück geändert werden.)
Jonathan Leffler

0

Verwenden Sie einfach die Option -w für g ++

Beispiel:

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

Denken Sie daran, dass dies nicht die Ablehnung verhindert, sondern das Anzeigen einer Warnmeldung auf dem Terminal verhindert.

Wenn Sie nun wirklich die Ablehnung vermeiden möchten, verwenden Sie das Schlüsselwort const wie folgt:

const char* s="constant string";  

0

Warum verwenden Sie die -Wno-deprecatedOption nicht, um veraltete Warnmeldungen zu ignorieren?


0

Das Problem im Moment ist, dass ich mit -Werror laufe

Das ist dein eigentliches Problem, IMO. Sie können einige automatisierte Methoden ausprobieren, um von (char *) zu (const char *) zu wechseln, aber ich würde Geld in sie stecken, nicht nur um zu funktionieren. Für mindestens einen Teil der Arbeit muss ein Mensch beteiligt sein. Ignorieren Sie kurzfristig einfach die Warnung (aber IMO lässt sie eingeschaltet, sonst wird sie nie behoben) und entfernen Sie einfach den Fehler.


9
Der Grund , warum Menschen nutzen Werror ist so , dass Warnungen tun repariert. Sonst werden sie nie repariert.
Zan Lynx

2
Der Grund, warum Leute -Werror verwenden, ist, dass sie nur an Spielzeugprojekten gearbeitet haben oder masochistisch sind. Es ist ein echtes Problem, wenn Ihr Code aufgrund eines GCC-Updates nicht erstellt werden kann, wenn Sie über 100.000 LOC verfügen. Dito. Jemand, der dem Build Junk wie "-Wno-write-strings" hinzufügt, um die nervigen Warnungen loszuwerden (wie der am höchsten bewertete Kommentar in diesem Beitrag nahe legt).
James Antill

2
Es gibt klare Meinungsverschiedenheiten in diesem Thema, zum Beispiel programmer.97things.oreilly.com/wiki/index.php/…
João Portela

3
@ James: Du machst einen interessanten Punkt, aber es muss einen besseren Weg geben. Es erscheint sinnlos, Warnungen nicht sofort zu beheben. Woran erkennen Sie, wenn neuer Code eine neue Warnung ausgelöst hat, wenn Sie nicht alle alten Warnungen entfernt haben? Nach meiner Erfahrung führt dies nur dazu, dass Leute Warnungen ignorieren, die sie nicht ignorieren sollten.
Nobar

2
@ James: Unser Spielzeugprojekt ist 1,5 + M LOC (mehrsprachig). Wie Nobar sagte, -Werror vermeidet es, Warnungen zu ignorieren, die nicht sein sollten, und ja, jedes Mal, wenn eine neue Version des Compilers erscheint, müssen wir alle erneut überprüfen. -Wno-write-Strings wird nur verwendet, wenn Boost für Python-Wrapper Datei für Datei verwendet wird, da wir Boost nicht neu schreiben werden (und ab 2017 bevorzugen wir Boost nicht mehr, sondern C ++ 11 /) Cython). Jede ignorierte Warnung muss dann regelmäßig durch Qualitätsprüfung überprüft werden, um festzustellen, ob sie jetzt durch Code vermieden werden kann oder ob dies noch nicht möglich ist.
Msn

0

Vielen Dank für die Hilfe. Von hier und da kommt diese Lösung. Dies kompiliert sauber. Habe den Code noch nicht getestet. Vielleicht morgen...

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 }

Ich weiß, es gibt nur 1 Element im timeServer-Array. Aber es könnte noch mehr geben. Der Rest wurde vorerst auskommentiert, um Speicherplatz zu sparen.


-1
PyTypeObject PyDict_Type=
{ ...

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

Beobachten Sie das Namensfeld, in gcc wird es ohne Vorwarnung kompiliert, aber in g ++ wird es, ich weiß nicht warum.

in gcc (Compiling C), -Wno-write-strings ist standardmäßig aktiv.

in g++ (Compiling C++)-Wwrite-Strings ist standardmäßig aktiv

Deshalb gibt es ein anderes Verhalten. Für uns erzeugt die Verwendung von Makros von Boost_pythonsolche Warnungen. Also verwenden wir -Wno-write-stringsbeim Kompilieren von C ++, da wir immer verwenden-Werror


-1

Deklarieren Sie einen String const, um das Problem zu lösen:

char const*s = "constant string";
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.