Was bedeutet dieser Fehler? Ich kann es in keiner Weise lösen.
Warnung: Veraltete Konvertierung von String-Konstante zu 'char *' [-Wwrite-strings]
programming
arduino-ide
c
Federico Corazza
quelle
quelle
Antworten:
Wie es meine Gewohnheit ist, werde ich ein paar technische Hintergrundinformationen zum Warum und Woher dieses Fehlers geben.
Ich werde vier verschiedene Arten der Initialisierung von C-Strings untersuchen und die Unterschiede zwischen ihnen herausfinden. Dies sind die vier in Frage kommenden Möglichkeiten:
Aus diesem Grund möchte ich den dritten Buchstaben "i" in "o" ändern, um daraus "Thos is some text" zu machen. Dies könnte in allen Fällen (Sie würden denken) erreicht werden durch:
Schauen wir uns nun an, was die einzelnen Deklarationsarten der Zeichenfolge bewirken und wie sich diese
text[2] = 'o';
Anweisung auf die Dinge auswirken würde.Zunächst wird die am häufigsten gesehen Art und Weise:
char *text = "This is some text";
. Was bedeutet das wörtlich? Nun, in C bedeutet es wörtlich "Erzeuge eine Variable, die aufgerufentext
wird und ein Lese- / Schreibzeiger auf dieses Zeichenkettenliteral ist, das im Nur-Lese- (Code-) Raum enthalten ist". Wenn Sie die Option-Wwrite-strings
aktiviert haben , wird eine Warnung angezeigt (siehe Frage oben).Grundsätzlich bedeutet dies "Warnung: Sie haben versucht, eine Variable mit Lese- / Schreibzugriff auf einen Bereich zu setzen, in den Sie nicht schreiben können". Wenn Sie versuchen, das dritte Zeichen auf "o" zu setzen, versuchen Sie tatsächlich, in einen schreibgeschützten Bereich zu schreiben, und die Dinge werden nicht schön. Auf einem herkömmlichen PC mit Linux führt dies zu:
Nun ist die zweite:
char text[] = "This is some text";
. In C bedeutet dies wörtlich: "Erstellen Sie ein Array vom Typ" char "und initialisieren Sie es mit den Daten" Dies ist etwas Text \ 0 ". Die Größe des Arrays ist groß genug, um die Daten zu speichern." Das reserviert also tatsächlich RAM und kopiert zur Laufzeit den Wert "Dies ist etwas Text \ 0" hinein. Keine Warnungen, keine Fehler, vollkommen gültig. Und die richtige Vorgehensweise, wenn Sie die Daten bearbeiten möchten . Versuchen wir den Befehl auszuführentext[2] = 'o'
:Es hat perfekt funktioniert. Gut.
Nun ist die dritte Möglichkeit:
const char *text = "This is some text";
. Wieder die wörtliche Bedeutung: "Erstellen Sie eine Variable mit dem Namen" text ", die ein schreibgeschützter Zeiger auf diese Daten im Nur-Lese-Speicher ist." Beachten Sie, dass sowohl der Zeiger als auch die Daten jetzt schreibgeschützt sind. Keine Fehler, keine Warnungen. Was passiert, wenn wir versuchen, unseren Testbefehl auszuführen? Das können wir nicht. Der Compiler ist jetzt intelligent und weiß, dass wir versuchen, etwas Schlechtes zu tun:Es wird nicht einmal kompiliert. Der Versuch, in den Nur-Lese-Speicher zu schreiben, ist jetzt geschützt, da wir dem Compiler mitgeteilt haben, dass unser Zeiger auf den Nur-Lese-Speicher gerichtet ist. Natürlich ist es nicht hat Speicher zu schreibgeschützt zu zeigen, aber wenn Sie darauf zeigen Speicher - Lese-Schreib (RAM) , dass der Speicher noch nicht geschrieben , um durch den Compiler geschützt werden.
Schließlich die letzte Form:
const char text[] = "This is some text";
. Auch hier[]
ordnet es wie zuvor ein Array im RAM zu und kopiert die Daten hinein. Dies ist jedoch jetzt ein schreibgeschütztes Array. Sie können nicht darauf schreiben, da der Zeiger darauf als markiert istconst
. Der Versuch, darauf zu schreiben, führt zu:Also, eine kurze Zusammenfassung, wo wir sind:
Dieses Formular ist vollständig ungültig und sollte unter allen Umständen vermieden werden. Es öffnet die Tür für alle Arten von schlechten Dingen:
Dieses Formular ist das richtige, wenn Sie die Daten bearbeitbar machen möchten:
Dieses Formular ist das richtige, wenn Sie Zeichenfolgen möchten, die nicht bearbeitet werden können:
Diese Form scheint RAM verschwenderisch zu sein, hat aber ihre Verwendung. Am besten vorerst vergessen.
quelle
PROGMEM
,PSTR()
oderF()
. Soconst char text[]
verwendet nicht mehr RAM alsconst char *text
.(const char *)(...)
Casting zu definieren . Kein wirklicher Effekt, wenn das Board es nicht benötigt, aber eine große Ersparnis, wenn Sie dann Ihren Code auf ein Board portieren, das dies tut.Um die ausgezeichnete Antwort von Makenko zu erläutern, gibt es einen guten Grund, warum der Compiler Sie davor warnt. Machen wir eine Testskizze:
Wir haben hier zwei Variablen, foo und bar. Ich ändere eine davon in setup (), sehe aber, was das Ergebnis ist:
Sie haben sich beide umgezogen!
In der Tat, wenn wir uns die Warnungen ansehen, sehen wir:
Der Compiler weiß, dass dies zweifelhaft ist, und es ist richtig! Der Grund dafür ist, dass der Compiler (vernünftigerweise) erwartet, dass sich die String-Konstanten nicht ändern (da es sich um Konstanten handelt). Wenn Sie also
"This is some text"
in Ihrem Code mehrmals auf die Zeichenfolgenkonstante verweisen, ist es zulässig , allen denselben Speicher zuzuweisen . Wenn Sie jetzt eine ändern, ändern Sie alle!quelle
*foo
und*bar
Verwendung unterschiedlicher String- "Konstanten" dies verhindern? Inwiefern unterscheidet sich dies auch davon, überhaupt keine Zeichenfolgen zu setzen, wie zum Beispielchar *foo;
:?new
,strcpy
unddelete
).Beenden Sie entweder den Versuch, eine Zeichenfolgenkonstante an die Stelle zu übergeben, an der eine Funktion a annimmt
char*
, oder ändern Sie die Funktion so, dass sie a annimmtconst char*
.Zeichenfolgen wie "Zufallszeichenfolgen" sind Konstanten.
quelle
Beispiel:
Warnung:
Die Funktion
foo
erwartet ein Zeichen * (das sie daher ändern kann), aber Sie übergeben ein Zeichenfolgenliteral, das nicht geändert werden sollte.Der Compiler warnt Sie davor. Wenn es veraltet ist, wird es möglicherweise in einer zukünftigen Compilerversion zu einem Fehler.
Lösung: Lassen Sie foo einen const char * nehmen:
In älteren Versionen von C (und C ++) können Sie Code wie in meinem obigen Beispiel schreiben. Sie könnten eine Funktion (wie
foo
) erstellen, die etwas druckt, das Sie übergeben haben, und dann eine wörtliche Zeichenfolge (z. B.foo ("Hi there!");
) übergeben.Eine Funktion, die
char *
als Argument nimmt, darf jedoch ihr Argument ändern (dhHi there!
in diesem Fall ändern ).Sie könnten zum Beispiel geschrieben haben:
Durch die Übergabe eines Literal haben Sie dieses Literal jetzt möglicherweise so geändert, dass "Hi there!" ist jetzt "Auf Wiedersehen", was nicht gut ist. Wenn Sie eine längere Zeichenfolge kopieren, werden möglicherweise andere Variablen überschrieben. Oder bei einigen Implementierungen wird eine Zugriffsverletzung angezeigt, weil "Hi there!" Möglicherweise wurde es in den schreibgeschützten Arbeitsspeicher (RAM) gestellt.
Daher wird diese Verwendung von den Compilern nach und nach abgelehnt , sodass Funktionen, an die Sie ein Literal übergeben, dieses Argument als deklarieren müssen
const
.quelle
can not
modifiziert werden?Ich habe diesen Kompilierungsfehler:
Bitte ersetzen Sie diese Zeile:
#define TIME_HEADER "T" // Header tag for serial time sync message
mit dieser Zeile:
#define TIME_HEADER 'T' // Header tag for serial time sync message
und die zusammenstellung geht gut.
quelle