Sind Zeichenfolgenliterale konstant?

Lesezeit: 8 Minuten

Benutzer-Avatar
Junge

Sowohl GCC als auch Clang beschweren sich nicht, wenn ich a ein String-Literal zuweise char*auch wenn viele umständliche Optionen verwendet werden (-Wall -W -pedantic -std=c99):

char *foo = "bar";

während sie sich (natürlich) beschweren, wenn ich einen beauftrage const char* zu einem char*.

Bedeutet dies, dass Zeichenfolgenliterale als solche betrachtet werden char* Typ? Sollten sie nicht sein const char*? Es ist kein definiertes Verhalten, wenn sie modifiziert werden!

Und (eine unkorrelierte Frage) was ist mit Befehlszeilenparametern (dh: argv): wird es als ein Array von String-Literalen betrachtet?

  • Wenn Sie möchten, dass es eine Warnung ausgibt, verwenden Sie -Wwrite-strings. Das Äquivalent wird in g++ gesetzt

    – moinudin

    20. Dezember 2010 um 19:38 Uhr

Sie sind vom Typ char[N] wo N ist die Anzahl der Zeichen einschließlich des Abschlusszeichens \0. Also ja, Sie können sie zuweisen char*aber Sie können immer noch nicht darauf schreiben (der Effekt ist undefiniert).

Wrt argv: Es zeigt auf ein Array von Zeigern auf Strings. Diese Zeichenfolgen sind explizit änderbar. Sie können sie ändern und sie müssen den zuletzt gespeicherten Wert halten.

  • +1 – Beachten Sie, dass dies ihr Typ ist, aber Sie müssen sie so behandeln, als ob sie es wären const (oder die undefinierte Verhaltens-/Zugriffsverletzungsfee wird Ihnen einen Besuch abstatten)

    – Billy ONeal

    20. Dezember 2010 um 19:34 Uhr

  • @Billy: siehst du das nicht irgendwie inkonsequent? Andernfalls müsste das OP die Frage überhaupt nicht stellen.

    – Vlad

    20. Dezember 2010 um 19:36 Uhr


  • @Vlad: Ich habe die Standards nicht geschrieben: P

    – Billy ONeal

    20. Dezember 2010 um 19:42 Uhr


  • @Vlad das C ++ – Komitee hat das Zeichenfolgenliteral bereits verboten char* Wandlung. Es wird unpassend, so etwas in C++0x zu tun.

    – Johannes Schaub – litb

    20. Dezember 2010 um 19:47 Uhr

  • C hatte const seit C89. Es wurde nicht zu C99 hinzugefügt.

    – Johannes Schaub – litb

    20. Dezember 2010 um 20:13 Uhr

Benutzer-Avatar
Shafik Yaghmur

Der Vollständigkeit halber die C99-Standardentwurf(C89 und C11 haben einen ähnlichen Wortlaut) im Abschnitt 6.4.5 Zeichenfolgenliterale Absatz 5 sagt:

[…]ein Byte oder Code von Wert Null wird angehängt zu jeder Multibyte-Zeichenfolge, die sich aus einem oder mehreren String-Literalen ergibt. Die Multibyte-Zeichenfolge wird dann verwendet, um an zu initialisieren Array mit statischer Speicherdauer und -länge, die gerade ausreicht, um die Sequenz aufzunehmen. Für Zeichenfolgenliterale haben die Array-Elemente Typ Zeichenund werden mit den einzelnen Bytes der Multibyte-Zeichenfolge initialisiert;[…]

Das sagt also a String-Literal hat eine statische Speicherdauer (dauert die Lebensdauer des Programms) und sein Typ ist char[](nicht char *) und seine Länge ist die Größe des Zeichenfolgenliterals mit einer angehängten Null. *Absatz 6` sagt:

Wenn das Programm versucht, ein solches Array zu ändern, ist das Verhalten undefiniert.

Versuchen Sie also, a zu ändern String-Literal ist undefiniertes Verhalten ungeachtet der Tatsache, dass sie es nicht sind const.

In Gedenken an argv im Abschnitt 5.1.2.2.1 Programmstart Absatz 2 sagt:

Wenn sie deklariert werden, müssen die Parameter der Hauptfunktion den folgenden Einschränkungen genügen:

[…]

– Die Parameter argc und argv und die Zeichenfolgen, auf die das argv-Array zeigt, sollen durch das Programm änderbar sein und ihre zuletzt gespeicherten Werte zwischen Programmstart und Programmende beibehalten.

So argv wird nicht als Array von Zeichenfolgenliteralen betrachtet und es ist in Ordnung, den Inhalt von zu ändern argv.

Benutzer-Avatar
Narr

Verwenden -Wwrite-strings Option erhalten Sie:

warning: initialization discards qualifiers from pointer target type

Unabhängig von dieser Option fügt GCC Literale in den Nur-Lese-Speicherabschnitt ein, sofern nicht anders durch Verwendung angegeben -fwritable-strings (diese Option wurde jedoch aus neueren GCC-Versionen entfernt).

Befehlszeilenparameter sind nicht konstant, sie leben normalerweise auf dem Stack.

Benutzer-Avatar
Aaron McDaid

(Tut mir leid, ich habe gerade erst bemerkt, dass diese Frage markiert ist als cnicht c++. Vielleicht ist meine Antwort für diese Frage doch nicht so relevant!)

Zeichenfolgenliterale sind nicht ganz const oder not-constgibt es eine spezielle seltsame Regel für Literale.

(Zusammenfassung: Literale können durch Verweis auf Array als übernommen werden foo( const char (&)[N]) und kann nicht als nicht konstantes Array genommen werden. Sie vorziehen zu verfallen const char *. Bisher sieht es so aus, als wären sie es const. Aber Es gibt eine spezielle Legacy-Regel, die es Literalen erlaubt, zu zerfallen char *. Siehe Experimente unten.)

(Nach Experimenten, die auf clang3.3 mit durchgeführt wurden -std=gnu++0x. Vielleicht ist dies ein C++ 11-Problem? Oder spezifisch für Klirren? Auf jeden Fall passiert etwas Seltsames.)

Auf den ersten Blick scheinen Literale zu sein const:

void foo( const char  * ) { std::cout << "const char *" << std::endl; }
void foo(       char  * ) { std::cout << "      char *" << std::endl; }

int main() {
        const char arr_cc[3] = "hi";
        char arr_c[3] = "hi";

        foo(arr_cc); // const char *
        foo(arr_c);  //       char *
        foo("hi");   // const char *
}

Die beiden Arrays verhalten sich wie erwartet und zeigen dies foo kann uns sagen, ob der Zeiger ist const oder nicht. Dann "hi" wählt die aus const Version von foo. Damit scheint es also erledigt zu sein: Literale sind const … nicht wahr?

Aberwenn Sie entfernen void foo( const char * ) dann wird es seltsam. Zuerst der Anruf bei foo(arr_c) schlägt mit einem Fehler zur Kompilierzeit fehl. Das wird erwartet. Aber der wörtliche Aufruf (foo("hi")) funktioniert über den nicht konstanten Aufruf.

Literale sind also “konstanter” als arr_c (denn sie verfallen lieber zum const char *nicht wie arr_c. Aber Literale sind “weniger konstant” als arr_cc weil sie bereit sind zu verfallen char * wenn benötigt.

(Clang gibt eine Warnung aus, wenn es auf abklingt char *).

Aber was ist mit dem Verfall? Vermeiden wir es der Einfachheit halber.

Nehmen wir stattdessen die Arrays per Referenz in foo. Dies gibt uns “intuitivere” Ergebnisse:

void foo( const char  (&)[3] ) { std::cout << "const char (&)[3]" << std::endl; }
void foo(       char  (&)[3] ) { std::cout << "      char (&)[3]" << std::endl; }

Wie zuvor sind das Literal und das const-Array (arr_cc) verwenden die konstante Version und die nicht konstante Version wird von verwendet arr_c. Und wenn wir löschen foo( const char (&)[3] )dann bekommen wir bei beiden Fehler foo(arr_cc); und foo("hi");. Kurz gesagt, wenn wir den Zeigerverfall vermeiden und stattdessen die Referenz auf das Array verwenden, verhalten sich Literale so, als ob sie es wären const.

Vorlagen?

Bei Vorlagen wird das System davon ableiten const char * Anstatt von char * und du “steckst” damit fest.

template<typename T>
void bar(T *t) { // will deduce   const char   when a literal is supplied
    foo
}

Also im Grunde verhält sich ein Literal wie const zu jeder Zeit, außer in dem besonderen Fall, in dem Sie a direkt initialisieren char * mit wörtlich.

Die Antwort von Johannes ist nach Art und Inhalt richtig. Aber darüber hinaus, ja, es ist ein undefiniertes Verhalten, den Inhalt eines String-Literals zu ändern.

Zu deiner Frage bzgl argv:

Die Parameter argc und argv und die Zeichenfolgen, auf die das argv-Array zeigt, sollen durch das Programm modifizierbar sein und ihre zuletzt gespeicherten Werte zwischen dem Programmstart und der Programmbeendigung behalten.

Sowohl in C89 als auch in C99 sind Zeichenfolgenliterale vom Typ char * (aus historischen Gründen, wie ich es verstehe). Sie haben Recht, dass der Versuch, einen zu ändern, zu einem undefinierten Verhalten führt. GCC hat ein spezielles Warnflag, -Wwrite-Strings (was nicht Teil von -Wall), die Sie warnen, wenn Sie dies versuchen.

Wie für argvwerden die Argumente in den Adressraum Ihres Programms kopiert und können sicher in Ihrem geändert werden main() Funktion.

BEARBEITEN: Hoppla, hatte -Wno-write-strings versehentlich kopiert. Aktualisiert mit der korrekten (positiven) Form des Warnflags.

Benutzer-Avatar
R.. GitHub HÖREN SIE AUF, ICE ZU HELFEN

Zeichenfolgenliterale haben formell Typ char [] aber semantisch Typ const char []. Die Puristen hassen es, aber das ist im Allgemeinen nützlich und harmlos, außer dass es viele Neulinge zu SO mit “WHY IS MY PROGRAM CRASHING?!?!” Fragen.

  • Macht mich neugierig, haben Sie ein Beispiel dafür, dass dies nützlich ist? Ich meine, jenseits dieser Abwärtskompatibilitätsidee, die meiner Meinung nach immer weniger relevant ist?

    – Jens Gustedt

    20. Dezember 2010 um 22:42 Uhr

  • Manchmal möchte man ein char * anstelle einer const char * zum Iterieren über eine Zeichenfolge, insbesondere wenn Sie einen Zeiger darauf an eine Funktion wie übergeben strtol. Es ist weniger eine Frage der Abwärtskompatibilität als vielmehr eine Frage der const manchmal ein Schmerz. Natürlich muss ich mich in solchen Fällen meistens auch mit nicht wörtlichen Zeichenfolgen befassen und die einfach wegwerfen const

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    21. Dezember 2010 um 2:52 Uhr

  • @R .. Ich sehe das Problem beim Iterieren über a nicht const char * oder sogar weitergeben strtol(). Es ist kein const char const * schließlich.

    – Sterblicher

    14. September 2017 um 14:03 Uhr

  • @morty: strtol nimmt char **nicht const char **für seine endptr Streit. Ebenso für verwandte Funktionen. Wenn Sie eine haben const char *pdu kannst nicht bestehen &p zu strtol; Stattdessen müssen Sie den Wert nach a kopieren char *tmp (mit einem Gipsverband, um die zu entfernen const vom Pointed-to-Typ), pass &tmpund kopieren Sie dann das Ergebnis zurück.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    14. September 2017 um 18:31 Uhr


  • Beachten Sie insbesondere, dass Sie kann nicht passieren (char **)&p hier; dies führt zu undefiniertem Verhalten (Aliasing-Verletzung).

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    14. September 2017 um 18:34 Uhr

1368520cookie-checkSind Zeichenfolgenliterale konstant?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy