Betrachten Sie den folgenden Code.
%Vor%Meiner Meinung nach sollte dies abcdexghij drucken. Es wird jedoch nur beendet, ohne etwas zu drucken.
%Vor%Das funktioniert jedoch gut, also habe ich das Konzept der Manipulation von C-Saiten oder so etwas falsch verstanden? Falls es wichtig ist, verwende ich Mac OS X 10.6 und es ist eine 32-Bit-Binärdatei, die ich kompiliere.
Die akzeptierte Antwort ist gut, aber nicht vollständig.
%Vor% Ein String-Literal bezieht sich auf ein anonymes Array-Objekt vom Typ char[N]
mit statischer Speicherdauer (dh es existiert für die gesamte Ausführung des Programms), wobei N
die Länge des Zeichenfolge plus eins für den Abschluss '
. Dieses Objekt ist nicht const
'char[27]
, aber jeder Versuch, es zu ändern, hat ein undefiniertes Verhalten. (Eine Implementierung kann String-Literale schreibbar machen, wenn sie dies wünscht, aber die meisten modernen Compiler tun dies nicht.)
Die obige Deklaration erstellt ein solches anonymes Objekt vom Typ test
und verwendet die Adresse des ersten Elements dieses Objekts, um test[5] = 'x'
zu initialisieren. Daher versucht eine Zuweisung wie const
das Array zu modifizieren und hat ein undefiniertes Verhalten; In der Regel wird es Ihr Programm zum Absturz bringen. (Die Initialisierung verwendet die Adresse, da das Literal ein Ausdruck des Array-Typs ist, der implizit in den meisten Kontexten in einen Zeiger auf das erste Element des Arrays konvertiert wird.)
Beachten Sie, dass in C ++ String-Literale tatsächlich test
sind und die obige Deklaration unzulässig wäre. In C oder C ++ empfiehlt es sich, char
als Zeiger auf const test
:
Der Compiler warnt Sie, wenn Sie versuchen, das Array über const
zu ändern.
(C-Stringliterale sind aus historischen Gründen nicht const
. Vor dem ANSI C-Standard von 1989 existierte das const
-Schlüsselwort nicht. Die Angabe in Deklarationen wie deins hätte für sichereren Code gesorgt, aber es hätte erfordert, dass bestehender Code geändert wird, etwas, das das ANSI-Komitee zu vermeiden versucht hat. Vorgeben , dass String-Literale -Wwrite-strings
sind, auch wenn sie es nicht sind. Wenn Sie gcc verwenden, Die Option const
bewirkt, dass der Compiler String-Literale als test
behandelt - was dazu führt, dass gcc nicht konform ist.
Wenn Sie die Zeichenfolge, auf die sich test
bezieht, ändern möchten, können Sie dies wie folgt definieren:
Der Compiler betrachtet den Initialisierer, um zu bestimmen, wie groß test
sein muss. In diesem Fall hat char[27]
den Typ test
. Das Zeichenfolgenliteral bezieht sich immer noch auf ein anonymes Arrayobjekt, das hauptsächlich schreibgeschützt ist, aber sein Wert wird in &
kopiert. (Ein String-Literal in einem Initialisierer, der zum Initialisieren eines Array-Objekts verwendet wird, ist einer der Kontexte, in denen ein Array nicht zu einem Zeiger "zerfällt"; die anderen sind, wenn es der Operand von unary sizeof
oder test
ist.) Seit Es gibt keine weiteren Verweise auf das anonyme Array, der Compiler kann es optimieren.
In diesem Fall ist '
selbst ein Array mit den von Ihnen angegebenen 26 Zeichen plus dem Terminator test
'test
. Die Lebensdauer dieses Arrays hängt davon ab, wo static
deklariert ist, was von Bedeutung sein kann oder nicht. Zum Beispiel, wenn Sie dies tun:
Der Aufrufer erhält einen Zeiger auf etwas, das nicht mehr existiert. Wenn Sie auf das Array außerhalb des Bereichs verweisen müssen, in dem malloc
definiert ist, können Sie es als free()
definieren, oder Sie können es mithilfe von strdup()
:
Damit bleibt das Array bestehen, bis Sie test
aufrufen. Die nicht standardmäßige test
-Funktion tut dies (sie wird von POSIX definiert, aber nicht von ISO C).
Beachten Sie, dass char*
entweder ein Zeiger oder ein Array sein kann, je nachdem, wie Sie es deklarieren. Übergeben Sie sizeof test
an eine Zeichenfolgenfunktion oder an eine Funktion, die ein test
übernimmt, ist das egal, aber etwas wie %code% verhält sich sehr unterschiedlich, je nachdem, ob %code% ein Zeiger oder ein Array ist .
Die comp.lang.c FAQ ist hervorragend. Abschnitt 8 behandelt Zeichen und Zeichenfolgen, und Frage 8.5 verweist auf Frage 1.32, die Ihre spezifische Frage adressiert. Abschnitt 6 behandelt die oft verwirrende Beziehung zwischen Arrays und Zeigern.
Zeichenzeiger, die mit einem Initialisierungswert definiert sind, werden in ein schreibgeschütztes Segment eingefügt. Um sie änderbar zu machen, müssen Sie sie entweder auf dem Heap (z. B. mit new / malloc) erstellen oder sie als Array definieren.
Nicht änderbar:
%Vor%Änderbar:
%Vor%Sie sollten sich angewöhnen, den Typ der Variablen dem Typ des Initialisierers anzupassen. In diesem Fall:
%Vor%Auf diese Weise erhalten Sie einen Compilerfehler statt eines Laufzeitfehlers. Wenn Sie die Warnungsstufe Ihres Compilers auf Maximum stellen, können Sie auch solche Fallstricke vermeiden. Warum dies kein Fehler in C ist, ist wahrscheinlich historisch; Frühere Compiler erlaubten es und es zu verbieten, hätte zu viel existierenden Code zerstört, wenn die Sprache standardisiert wurde. Jetzt aber Betriebssysteme erlauben es nicht, so ist es akademisch.