Ich habe einen Zeiger ptr
an eine Funktion übergeben, deren Prototyp sie als const
nimmt.
Was meiner Meinung nach bedeutet, dass es den Inhalt von ptr
nicht ändern kann. Wie im Falle von foo( const int i )
. Wenn foo()
versucht, den Wert von i
zu ermitteln, gibt der Compiler einen Fehler aus.
Aber hier sehe ich, dass es den Inhalt von ptr
leicht ändern kann.
Bitte sehen Sie sich den folgenden Code an
Beim Kompilieren bekomme ich nur eine Warnung, keinen Fehler:
warning: passing argument 1 of ‘strcpy’ discards qualifiers from pointer target type
und wenn ich es ausführe, bekomme ich eine Ausgabe statt Segmentation Fault
main (): Es ist nur um den Raum zu füllen foo (): ABC
main (): ABC
Nun, meine Fragen sind: 1 - Was bedeutet const char *str
im Prototyp eigentlich?
Bedeutet das, dass die Funktion den Inhalt von str
nicht ändern kann? Wenn das so ist, wie kommt dann das obige Programm ändert den Wert?
2 - Wie kann ich sicherstellen, dass der Inhalt des Zeigers, den ich übergeben habe, nicht geändert werden?
Von "Inhalt des Zeigers" in der oben genannten Frage meine ich "Inhalte des Speichers, auf den der Zeiger zeigt", nicht "Adresse, die im Zeiger enthalten ist".
Die meisten Antworten sagen, dass dies wegen strcpy
und C impliziter Typ-Konvertierung ist. Aber jetzt habe ich das versucht
Diesmal ist die Ausgabe ohne Warnung vom Compiler
main (): Es ist nur um den Raum zu füllen foo (): Tim
main (): Es ist nur um den Raum zu füllen
Offensichtlich wird der Speicher, auf den str
zeigt, auf den Speicherort geändert, der "Tim"
enthält, während er in foo()
ist. Obwohl ich dieses Mal strcpy()
nicht benutzt habe.
Soll nicht const
das stoppen? oder mein Verständnis ist falsch?
Mir scheint, dass ich auch mit const
die Speicherreferenz und den Inhalt der Speicherreferenz ändern kann. Was nützt es dann?
Können Sie mir ein Beispiel geben, in dem Compiler mir einen Fehler geben wird, dass ich versuche, einen const Zeiger zu ändern?
Vielen Dank an alle für Ihre Zeit und Mühe.
Ihr Verständnis ist richtig, const char*
ist ein Vertrag, der bedeutet, dass Sie den Speicher nicht durch diesen bestimmten Zeiger ändern können.
Das Problem ist, dass C sehr lax mit Typkonvertierungen ist. strcpy
nimmt einen Zeiger auf nicht-const Zeichen, und es wird implizit konvertiert von const char*
in char*
(wie Ihnen der Compiler hilfreich sagt). Sie könnten genauso einfach eine Ganzzahl anstelle eines Zeigers übergeben. Daher kann Ihre Funktion den Inhalt von ptr
nicht ändern, aber strcpy
kann, weil sie einen nicht konstanten Zeiger sieht. Sie erhalten keinen Absturz, weil der Zeiger in Ihrem Fall auf einen tatsächlichen Puffer mit ausreichender Größe und nicht auf ein schreibgeschütztes Zeichenfolgenliteral zeigt.
Um dies zu vermeiden, suchen Sie nach Compilerwarnungen oder kompilieren Sie zum Beispiel mit -Wall -Werror
(wenn Sie gcc verwenden).
Dieses Verhalten ist spezifisch für C. C ++ zum Beispiel erlaubt das nicht und erfordert eine explizite Umwandlung (C-Style Cast oder const_cast
), um const
Qualifikationsmerkmal zu entfernen, wie Sie es erwarten würden.
Sie ordnen ein Zeichenfolgenliteral einem nichtkonstanten Zeichen zu, das leider in C und sogar C ++ zulässig ist! Es wird implizit in char*
konvertiert, obwohl das Schreiben über diesen Zeiger jetzt zu einem nicht definierten Verhalten führt. Es ist ein veraltetes Feature und nur C ++ 0x erlaubt dies bisher nicht.
Mit dem gesagt, Um den Zeiger selbst zu ändern, müssen Sie es als const Zeiger auf char ( char *const
) zu deklarieren. Oder, wenn Sie es so machen wollen, dass sowohl der Inhalt, auf den es zeigt, als auch der Zeiger selbst sich nicht ändern, verwenden Sie einen const Zeiger auf const char ( const char * const
).
Beispiele:
%Vor%Für alle Fehlerzeilen gibt GCC mir "Fehler: Zuweisung der schreibgeschützten Stelle".
"const" ist wirklich eine Kompilierungszeit, also erwarten Sie keinen segfault, wenn der Zeiger nicht auf einen ungültigen Speicher zeigt. Wenn const-Zeiger in einer Weise verwendet werden, die möglicherweise alles ändern kann, auf das sie zeigen (in diesem Fall an strcpy übergeben, das nicht-const akzeptiert), wird eine Warnung generiert.
1- Was bedeutet const char * str im Prototyp eigentlich? Bedeutet dies, dass die Funktion den Inhalt von str nicht ändern kann?
Ja! Das bedeutet, dass wir den Inhalt von etwas (entweder char
oder array of chars
), auf das str verweisen kann, nicht ändern können.
Wenn das so ist, wie kommt es dann, dass das obige Programm den Wert ändert?
Das liegt daran, dass der Prototyp von strcpy()
char * strcpy ( char * destination, const char * source );
In Ihrem Code gibt es eine implizite Konvertierung von const char*
nach char*
type, weil strcpy()
verlangt, dass das erste Argument vom Typ char*
ist.
Technisch gesehen ist Ihr Code inkorrekt, eher gefährlich. Wenn Sie den gleichen Code in C ++ versuchen, erhalten Sie einen Fehler. C ++ erlaubt solche impliziten Konvertierungen nicht, aber C tut es.
IIRC const
bedeutet, dass der Wert des Parameters nicht geändert werden darf. In Ihrem Fall ist der Wert die Adresse, auf die der Zeiger zeigt. strcpy
ändert nicht die Adresse, auf die der Zeiger zeigt, sondern den Speicher, auf den der Zeiger zeigt.
Die Verwendung von const
stellt sicher, dass die Speicherreferenz (Adresse) nicht geändert wird. Der referenzierte Speicher kann jedoch geändert werden. Das ist was hier passiert. Das Zuweisen einer neuen Adresse zu dem Zeiger würde zu einem Fehler führen.
SEITENHINWEIS
Ich würde Ihre foo
-Methode als unsicher ansehen. Sie sollten besser auch die maximale Länge von str
übergeben und eine Längenüberprüfung durchführen, sonst sind Sie für Pufferüberläufe offen.
BEARBEITEN
Ich habe Folgendes von dieser Website genommen:
Das
const char *Str
sagt dem Compiler dass die DATA der Zeiger auch zeigt ist const. Das heißt,Str
kann sein innerhalb von Func geändert, aber*Str
kann nicht. Als Kopie wird der Zeiger übergeben Func, alle Änderungen anStr
sind nicht möglich gesehen von main ....
const char*
ist dasselbe wie char const*
und nicht char* const
. Das bedeutet also einen Zeiger auf etwas, das Sie nicht ändern können.
Ausarbeitung nach der Bearbeitung Das erste Formular verhindert, dass die Daten geändert werden (außer Sie implizit oder explizit umwandeln), das zweite verhindert, dass der Zeiger selbst geändert wird.
Was Sie in Ihrer bearbeiteten Version tun, ist den Zeiger zu ändern. Das ist hier legal. Wenn Sie beides verhindern würden, müssten Sie char const* const
schreiben.