Warum ist die übergabe eines string-literal in einem char* - argument nur manchmal zu einem compiler-Fehler?
Arbeite ich in einer C-und C++ - Programm. Wir kompilieren ohne make-Saiten-beschreibbar option. Aber das war immer eine ganze Reihe von Warnungen, so dass ich es ausgeschaltet habe.
Dann bekam ich eine ganze Reihe von Fehlern in der form "kann Nicht konvertieren, const char* zu char* argmuent 3 der Funktion foo". So, ich ging durch und hat eine ganze Menge änderungen festzusetzen.
Heute jedoch, das Programm STÜRZTE ab, weil das literal "" war immer die einer Funktion übergeben werden, die erwartet einen char*, und die Einstellung war, das 0. Zeichen auf 0. Es war nicht alles schlecht, nur versucht zu Bearbeiten, eine Konstante ist, und abstürzt.
Meine Frage ist, warum war nicht so, dass ein compiler-Fehler?
Falls es eine Rolle spielt, wurde diese auf einem mac kompiliert mit gcc-4.0.
EDIT: code Hinzugefügt:
char * host = FindArgDefault("EMailLinkHost", "");
stripCRLF(linkHost, '\n');
wo:
char *FindArgDefault(char *argName, char *defVal)
{//simplified
char * val = defVal;
return(val);
}
und
void stripCRLF(char *str, char delim)
{
char *p, *q;
for (p = q = str; *p; ++p) {
if (*p == 0xd || *p == 0xa) {
if (p[1] == (*p ^ 7)) ++p;
if (delim == -1) *p = delim;
}
*q++ = *p;
}
*q = 0; //DIES HERE
}
Diese kompilierte und lief, bis er versuchte, zu *q 0...
EDIT 2:
Meisten Leute sind fehlt der Punkt meiner Frage. Ich weiß, warum char foo[] = "bar" arbeiten. Ich weiß, warum char * foo = "bar"; funktioniert nicht.
Meine Frage ist meist mit Bezug auf die übergabe von Parametern. Eine Sache, die existiert für mich "Ist es möglich, dass dies einem C vs C++ Problem?", weil ich einige .in c-Dateien und einige .cpp-Dateien, und es ist durchaus möglich, dass C es zulässt, aber C++ nicht... oder Umgekehrt...
Die Faustregel ist, dass, sollten Sie die Behandlung von string-Literale immer als const char *. Behandeln Sie als char * stellt Sie für diese Art von Problemen.
natürlich. Meine Frage ist, warum der compiler fangen EINIGE von Ihnen, aber nicht alle...
Das Verhalten von dem code, den Sie Hinzugefügt haben, ist vollkommen logisch. Ja, dies sollte kompilieren und ausführen, bis Sie versuchen, ändern Sie etwas. Allerdings, so wie ich es verstanden habe, Ihr Hauptproblem war, dass der compiler entdeckt einige Fehler und konnte nicht erkennen, einige andere Fehler. Das in einer der "gescheiterten". Jetzt zeigen Sie uns ein successfuly "erkannt".
Sie haben drei Funktionen. Man nennt eine Funktion zurück, was sich auf die ein Zeiger auf ein string-literal und ordnet, dass eine
char *
, und ruft dann eine Funktion zu ändern. Sie nicht machen es einfach für den compiler, um herauszufinden, was Los ist. Die tödliche Umwandlung ist mit dem Aufruf FindArgDefault()
, und alles andere ist richtig. Es gibt viel alten code, der nicht const-korrekt, und es ist zulässig, nach der Norm zu übergeben wird ein string-Literals zu einer char *
(obwohl nicht definiertes Verhalten, um zu versuchen, es zu ändern). Ich denke, Sie erwarten zu viel von gcc.
InformationsquelleAutor Brian Postow | 2010-05-03
Du musst angemeldet sein, um einen Kommentar abzugeben.
Standard gibt eine spezielle Regel, die den literal-zu-
char*
Bekehrung, die leise Tropfenconst
Qualifikation. (4.2/2):Den C++0x-standard nimmt, dass die Missbilligung weiter... diese Regel ist Unsinn komplett entfernt aus dem kommenden standard.
Den
const char*
zuchar*
Fehler muss ein Ergebnis der Umwandlung von einer wörtlichen zu einerconst char*
ersten.foo = "bar"; erstellt einen Zeiger auf non-const-char, und es auf const char.
char foo[] = "bar";
schafft eine vier-Zeichen-array von char initialisiert, die mit 'b', 'a', 'r', '\0'. Die Aussagen völlig verschiedene Dinge.Für mich, mit
g++ -V 4.0.1 -Wall -ansi
auf OS X 10.6.3,char *foo = "bar";
kompiliert im namespace oder Funktion Bereich ohne Warnhinweise. Mit dem Standard-4.2.1 erzeugt eine Warnung.ja genau. @Potatoswatter, ist es möglich, dass dies ein differencebetween gcc und g++? (also zwischen C und C++), die tatsächlich erklären könnte evrything...
Wenn Sie versuchen zum ausführen von C++ - code durch den GCC anstatt G++, sollten Sie link-Fehler. Wenn Sie mit XCode, ich glaube nicht, dass es erlauben würde, Sie zu machen, dass Fehler, noch irgendwelche anderen anständigen IDE.
InformationsquelleAutor Potatoswatter
Mit einem string-literal initialisieren einer
char *
Zeiger in C++ ist ein veraltetes feature, aber trotzdem ist es legal. Es ist nicht ein Fehler. Es ist Ihre Verantwortung, um sicherzustellen, dass keine änderung versucht wird, durch einen solchen Zeiger.In anderen Worten, Sie müssen Missverständnisse etwas über die Zusammenstellung von Fehlern, die Sie bekam früher. Ich glaube nicht, dass Sie jemals irgendwelche Fehler für eine solche Initialisierung/Zuordnung. "Kann Nicht konvertieren, const char* to char*" Fehler, die Sie erwähnen in Ihrer Frage produziert worden sein durch etwas anderes.
Beachten Sie, dass die Tatsache, dass Sie initialisiert werden können
char *
Zeiger mit einem string-literal bedeutet nicht, dass Sie können jede beliebigeconst char *
Wert zu initialisierenchar *
Zeiger. Dieser codewird ein Fehler erzeugt, während dies
nicht. Die oben genannten deprecated feature gilt nur für string-Literale nur, nicht für alle
const char *
Zeiger.Siehe den Nachtrag zu meiner Antwort für dieses Problem.
Der Grund dafür, dass
char foo[] = "bar"
nicht geben Ihnen ein Fehler, weil der compiler behandelt den string als ein array-Initialisierer. Stattdessen deklarieren einen Zeiger auf eine Konstante Zeichenkette (kopieren der Adresse), es deklariert ein array und initialisiert es mit dem Wert "bar" (kopieren der Daten). Die Daten infoo
werden sollte, veränderlich in diesem Fall. Dies ist eine subtile nuance, die an das array/pointer Dualität.InformationsquelleAutor AnT
Ich Stimme völlig mit den anderen Antworten, ich möchte nur hinzufügen, dass g++ (ab version 4.4) und eigentlich fängt diese veralteten Umsetzungen sowie Warnungen bei jedem warning level (wenn Vorherige Versionen tun dies nicht standardmäßig, wahrscheinlich haben Sie zu heben Sie die Warnstufe):
Darüber hinaus gibt es einige interessante, was Los ist unter der Haube: wenn ich es kompilieren, ohne Optimierung, es "macht einfach, was der code sagt", so stürzt es ab, da es versucht zum schreiben in ein nur-lese-Speicher Ort:
aber, wenn Sie schalten Sie den Optimierer, es gibt keine crash:
Ich nehme an, dass dies aufgrund einiger magic-Optimierung-trick, aber ich verstehe nicht, warum es angewendet wird; irgendeine Idee?
Nachtrag
Hey, vorsichtig sein, nicht zu verwechseln Sie die beiden Dinge: mit
erklären Sie einen Zeiger auf char, und weisen Sie ihm die Adresse von dem literal "bar", die eigentlich gespeichert ist, in einigen nur-lese-Speicher (in der Regel ist es ein Teil der ausführbaren Datei zugeordnet ist, im Speicher).
Stattdessen mit
erklären Sie und die Zuweisung von RW Arbeitsspeicher (auf dem stack oder irgendwo anders, je nach Kontext) für ein array von chars, die initialisiert wird, mit der "bar" - Wert, aber es ist nicht im Zusammenhang mit der string-Tabelle an alle, und es ist völlig legitim, das zu ändern string.
InformationsquelleAutor Matteo Italia
Es hängt wirklich, wie Sie "ging durch und hat eine ganze Menge verpasst zu beheben."
Wenn Sie nur niedergeschlagen die string-Literals zu einer
char*
dann sagen Sie dem compiler nicht zu fangen, dass Fehler. Sie müssen eine Kopie zu machen, wenn Sie gehen, um es zu ändern. Sonst deklarieren Sie Ihre Funktion-Schnittstellen nehmen eineconst
so dass der compiler kann prüfen diese für Sie.InformationsquelleAutor J. Cordasco
Antwort auf die Frage, warum dieser Umbau legal ist (obwohl veraltet). Nun, es war eine Zeit, als es noch kein const-Schlüsselwort in der Sprache C und die Menschen es geschafft, zu produzieren, ein Stück code, der während dieser Zeit. Die Designer der C++ muss herausgefunden haben, dass es war eine gute Idee nicht zu stören so viele Menschen, durch zu brechen, Ihren code.
-Wwrite-strings
option aber nicht einschalten standardmäßig oder durch-Wall
(es würde zu viele Warnungen, die auf älteren oder nicht-const-korrekter code).InformationsquelleAutor Maciej Hehl
Da die
stripCRLF
- Funktion ändert eine Zeichenfolge im Ort, aber nicht alles tun, oder mit return einen beliebigen Wert, und übergeben Sie einen string-literal zu, das es im wesentlichen eine no-op und sollte als ein bug. Sie können entweder lösen dies, indem er die Funktion ändern, und geben Sie einen kopieren von der Schnur, oder durch die Festlegung strenger Warnsignale zu erkennen, wenn dies geschieht.Wenn Sie möchten, gcc, Sie zu warnen, Dinge wie diese, schalten Sie den
-Wwrite-strings
compiler-option. Dies zwingt den compiler, um Sie zu warnen, wenn eine string-Konstante umgewandelt, um eine nicht-Konstantechar*
. Eventuell auch nützlich ist die-Wcast-qual
option; dies sollte eine Warnung ausgeben, wenn ein Zeiger ist, werfen Sie in einer Weise, die entfernt ein Typ-Qualifizierer an (in deinem Fallconst
wurden entfernt). Wenn Sie möchten, dass diese Meldungen vorgenommen werden stärker, verwenden Sie-Werror
schalten Sie alle Warnungen in Fehler.Der andere Streitpunkt ist die
FindArgDefault
Funktion. Wie, die Funktion Unterschrift sollte genauer verwendenconst char*
stattchar*
für die Rückgabe-und Parametertypen. Dies sollte dazu führen, dass der compiler sich beschweren, wenn der return-Wert einechar*
(wenn die-Wcast-qual
- option verwendet wird). Da Sie nicht posten die komplette Funktion, könnte dies nicht eine gültige änderung zu machen. Wenn entweder der string geändert wird, innerhalb der Funktion wird dann der entsprechende parameter bleiben musschar*
, aber in diesem Fall übergeben Sie einen string-literal als argument zu generieren sollte eine compiler-Warnung (verwenden Sie-Wwrite-strings
).Durch die Art und Weise, Ihre
stripCRLF
Funktion ist anfällig für Probleme, wenn ein NULL-Zeiger übergeben wird. Auch, meinst du zu sagenif (delim == -1)
oder soll das so sein!=
?Edit:, Nachdem er mehr Informationen über die Fehlermeldungen, die OP war immer, ich entfernte Teile des ursprünglichen Beitrags waren off-topic und fügte einige zusätzliche Kommentare.
Edit2: getestet habe ich die folgende vereinfachte version des Programms:
Wenn ich kompiliert habe, mit
gcc -Wall test.c -o test.o
ich habe null compiler Warnungen oder Fehler.Wenn ich kompiliert habe, mit
gcc -Wwrite-strings -Wall test.c -o test.o
bekam ichIch denke auf jeden Fall, dass die
-Wwrite-strings
compiler-option, die Sie aktivieren möchten, um Sie zu warnen, über diese Art von problem.Sind Sie richtig. Das ist das Letzte mal, dass ich versuche zu Lesen ein Komplexes problem und nach einer Lösung, während auf einer Telefonkonferenz, das verspreche ich. Ich habe bearbeitet Sie meine Antwort genauer Adresse das problem.
Ich bin notexactly wissen was delim hat. Es ist ursprünglich nicht mein code... und den fehlenden code in findargdefault ruft eine andere Funktion (in einer if-Verzweigung wird nicht genommen, in dem Fall, wo es abstürzt) und die Rückkehr ist nicht recessarily wieder ein const, so ändern char* consts ist keine gute Idee...
InformationsquelleAutor bta