“Lebensdauer” eines Zeichenfolgenliterals in C

Lesezeit: 11 Minuten

Benutzeravatar von user113454
Benutzer113454

Wäre der von der folgenden Funktion zurückgegebene Zeiger nicht unzugänglich?

char *foo(int rc)
{
    switch (rc)
    {
        case 1:

            return("one");

        case 2:

            return("two");

        default:

            return("whatever");
    }
}

Die Lebensdauer einer lokalen Variablen in C/C++ liegt also praktisch nur innerhalb der Funktion, oder? Was bedeutet, nach char* foo(int) endet, bedeutet der Zeiger, den es zurückgibt, nichts mehr, oder?

Ich bin etwas verwirrt über die Lebensdauer einer lokalen Variablen. Was ist eine gute Aufklärung?

  • Das einzige “var”, das Sie in Ihrer Funktion haben, ist der Parameter int rc. Seine Lebensdauer endet mit jedem der return-s. Die Zeiger, die Sie zurückgeben, sind auf Zeichenfolgenliterale. String-Literale haben eine statische Speicherdauer: Ihre Lebensdauer ist mindestens so lang wie die des Programms.

    – Kas

    2. April 2012 um 3:22 Uhr


  • @PedroAlves Warum nicht? Methoden erlauben Abstraktion; Was ist, wenn die Zeichenfolge in Zukunft aus einer Übersetzungsressource gelesen wird, für V1 (oder V0.5) eines Produkts jedoch keine Internationalisierungsunterstützung erforderlich ist?

    – dlev

    9. Mai 2013 um 21:02 Uhr


  • @PedroAlves “Ihr Code wird sicher funktionieren (und Sie können ihn sehen, wenn Sie versuchen zu kompilieren)” Das folgt nicht. Viele (die meisten? Eigentlich alle?) C-Compiler verbrauchen illegalen Code und geben oft Code aus, der zu funktionieren scheint. Aber probiere es aus Ein weiterer Compiler (oder sogar eine andere Version desselben Compilers) und es kann umfallen.

    – dmckee — Ex-Moderator-Kätzchen

    9. Mai 2013 um 21:51 Uhr

  • @PedroAlves, eine Funktion, die eine einzelne konstante Zeichenfolge zurückgibt, ist möglicherweise von begrenztem Nutzen, aber wie wäre es mit einer Funktion, die je nach Eingabe- oder Objektstatus eine von mehreren konstanten Zeichenfolgen zurückgibt? Ein einfaches Beispiel wäre eine Funktion zum Konvertieren einer Aufzählung in ihre Zeichenfolgendarstellung.

    – Markieren Sie Lösegeld

    9. Mai 2013 um 21:55 Uhr

  • Sie haben das noch nie gesehen strerror Funktion, offensichtlich.

    – Kas

    9. Mai 2013 um 23:30 Uhr

Benutzeravatar von Alok Save
Alok Speichern

Ja, die Lebensdauer einer lokalen Variablen liegt im Geltungsbereich ({,}), in dem es erstellt wird.

Lokale Variablen haben eine automatische oder lokale Speicherung. Automatisch weil sie automatisch zerstört werden, sobald der Bereich, in dem sie erstellt wurden, endet.

Was Sie hier jedoch haben, ist ein Zeichenfolgenliteral, das in einem implementierungsdefinierten Nur-Lese-Speicher allokiert ist. Zeichenfolgenliterale unterscheiden sich von lokalen Variablen und bleiben während der gesamten Lebensdauer des Programms aktiv. Sie haben statische Dauer [Ref 1] Lebensdauer.

Ein Wort der Vorsicht!

Beachten Sie jedoch, dass jeder Versuch, den Inhalt eines Zeichenfolgenliterals zu ändern, ein Fehler ist undefiniertes Verhalten (UB). Benutzerprogramme dürfen den Inhalt eines Zeichenfolgenliterals nicht ändern.
Daher wird immer empfohlen, a zu verwenden const beim Deklarieren eines String-Literals.

const char*p = "string"; 

Anstatt von,

char*p = "string";    

Tatsächlich ist es in C++ veraltet, ein Zeichenfolgenliteral ohne das zu deklarieren const jedoch nicht in C. Deklarieren Sie jedoch ein Zeichenfolgenliteral mit a const bietet Ihnen den Vorteil, dass Compiler Sie normalerweise warnen, falls Sie versuchen, das Zeichenfolgenliteral im zweiten Fall zu ändern.

Beispielprogramm:

#include<string.h> 
int main() 
{ 
    char *str1 = "string Literal"; 
    const char *str2 = "string Literal"; 
    char source[]="Sample string"; 
 
    strcpy(str1,source);    // No warning or error just Undefined Behavior 
    strcpy(str2,source);    // Compiler issues a warning 
 
    return 0; 
} 

Ausgabe:

cc1: Warnungen werden als Fehler behandelt
prog.c: In Funktion ‘main’:
prog.c:9: Fehler: Beim Übergeben von Argument 1 von „strcpy“ werden Qualifizierer vom Zeigerzieltyp verworfen

Beachten Sie, dass der Compiler im zweiten Fall warnt, aber nicht im ersten.


Um die Frage zu beantworten, die hier von einigen Benutzern gestellt wurde:

Was hat es mit Integralliteralen auf sich?

Mit anderen Worten, ist der folgende Code gültig?

int *foo()
{
    return &(2);
} 

Die Antwort lautet: Nein, dieser Code ist nicht gültig. Es ist falsch formatiert und gibt einen Compilerfehler aus.

Etwas wie:

prog.c:3: error: lvalue required as unary ‘&’ operand
     

String-Literale sind L-Werte, dh: Sie können die Adresse eines String-Literals nehmen, aber seinen Inhalt nicht ändern.
Alle anderen Literale (int, float, charusw.) sind r-Werte (der C-Standard verwendet den Begriff der Wert eines Ausdrucks für diese) und ihre Adresse kann überhaupt nicht genommen werden.


[Ref 1]C99-Standard 6.4.5/5 “String Literals – Semantics”:

In der Übersetzungsphase 7 wird ein Byte oder Code mit dem Wert Null an jede Mehrbyte-Zeichenfolge angehängt, die sich aus einem oder mehreren String-Literalen ergibt. Die Mehrbyte-Zeichenfolge wird dann verwendet, um ein Array von statischer Speicherdauer und -länge zu initialisieren, die gerade ausreicht, um die Folge zu enthalten. Bei Zeichenkettenliteralen sind die Array-Elemente vom Typ char und werden mit den einzelnen Bytes der Multibyte-Zeichenfolge initialisiert; für breite Zeichenfolgenliterale haben die Array-Elemente den Typ wchar_t und werden mit der Folge von breiten Zeichen initialisiert …

Es ist nicht angegeben, ob diese Arrays unterschiedlich sind, vorausgesetzt, ihre Elemente haben die entsprechenden Werte. Wenn das Programm versucht, ein solches Array zu ändern, ist das Verhalten undefiniert.

  • Was ist, wenn der Benutzer so etwas zurückgibt? char *a=&”abc”; gib a zurück; Gilt das nicht?

    – Ashwin

    2. April 2012 um 2:59 Uhr

  • @Ashwin: Der Typ des Zeichenfolgenliterals ist char (*)[4]. Dies liegt daran, Art von “ABC” ist char[4] und Zeiger auf ein Array von 4 Zeichen wird als deklariert char (*)[4]Wenn Sie es also ansprechen müssen, müssen Sie es so tun char (*a)[4] = &"abc"; und ja, es ist gültig.

    – Alok Speichern

    2. April 2012 um 3:10 Uhr


  • @Als “abc” ist char[4]. (Wegen dem '\0')

    – asaelr

    2. April 2012 um 3:13 Uhr

  • Vielleicht wäre es auch eine gute Idee, davor zu warnen char const s[] = "text"; tut nicht machen s ein Zeichenliteral, und daher s Wille am Ende des Gültigkeitsbereichs zerstört werden, sodass alle überlebenden Zeiger darauf baumeln.

    – Celtschk

    2. April 2012 um 14:34 Uhr

  • @celtschk: Würde ich gerne, aber das Q bezieht sich speziell auf Zeichenfolgenliterale. Ich würde also beim vorliegenden Thema bleiben. Für diejenigen, die daran interessiert sind, meine Antwort hier: Was ist der Unterschied zwischen Zeichen a[] = „string“ und char *p = „string“? dürfte eher hilfreich sein.

    – Alok Speichern

    2. April 2012 um 14:56 Uhr


Benutzeravatar von Daniel Fischer
Daniel Fischer

Es ist gültig. Zeichenfolgenliterale haben eine statische Speicherdauer, sodass der Zeiger nicht baumelt.

Für C, das in Abschnitt 6.4.5, Absatz 6 vorgeschrieben ist:

In der Übersetzungsphase 7 wird ein Byte oder Code mit dem Wert Null an jede Mehrbyte-Zeichenfolge angehängt, die sich aus einem oder mehreren String-Literalen ergibt. Dann wird die Multibyte-Zeichenfolge verwendet um ein Array statischer Speicherdauer zu initialisieren und eine Länge, die gerade ausreicht, um die Sequenz aufzunehmen.

Und für C++ in Abschnitt 2.14.5, Absätze 8-11:

8 Gewöhnliche String-Literale und UTF-8-String-Literale werden auch als Narrow-String-Literale bezeichnet. Ein Narrow-String-Literal hat den Typ „Array of n“. const char“, wobei n die Größe der Zeichenfolge wie unten definiert ist, und eine statische Speicherdauer (3.7) hat.

9 Ein Zeichenfolgenliteral, das mit u beginnt, z u"asdf"ist ein char16_t String-Literal. EIN char16_t String-Literal hat den Typ „Array von n const char16_t“, wobei n die Größe der Zeichenfolge ist, wie unten definiert; es hat eine statische Speicherdauer und wird mit den angegebenen Zeichen initialisiert. Ein einzelnes c-char kann mehr als eins erzeugen char16_t Zeichen in Form von Ersatzpaaren.

10 Ein Zeichenfolgenliteral, das mit U beginnt, z U"asdf"ist ein char32_t String-Literal. EIN char32_t String-Literal hat den Typ „Array von n const char32_t“, wobei n die Größe der Zeichenfolge ist, wie unten definiert; es hat eine statische Speicherdauer und wird mit den angegebenen Zeichen initialisiert.

11 Ein Zeichenfolgenliteral, das mit L beginnt, z L"asdf", ist ein Wide-String-Literal. Ein Wide-String-Literal hat den Typ „Array of n“. const wchar_t“, wobei n die Größe der Zeichenfolge ist, wie unten definiert; es hat eine statische Speicherdauer und wird mit den angegebenen Zeichen initialisiert.

  • Zu Ihrer Information: Diese Antwort wurde von stackoverflow.com/questions/16470959/… zusammengeführt.

    – Shog9

    24. Juli 2014 um 15:49 Uhr

Zeichenfolgenliterale gelten für das gesamte Programm (und werden nicht dem Stack zugewiesen), sodass sie gültig sind.

Außerdem sind Zeichenfolgenliterale schreibgeschützt, also sollten Sie (für einen guten Stil) vielleicht etwas ändern foo zu const char *foo(int)

  • Was ist, wenn der Benutzer so etwas zurückgibt? char *a=&”abc”; gib a zurück; Gilt das nicht?

    – Ashwin

    2. April 2012 um 2:59 Uhr

  • &"abc" ist nicht char*. Es ist eine Adresse eines Arrays und sein Typ ist char(*)[4]. Allerdings auch nicht return &"abc"; und char *a="abc";return a; sind gültig.

    – asaelr

    2. April 2012 um 3:03 Uhr

  • @asaelr: Eigentlich ist es mehr als nur umsonst für einen guten Stilüberprüfen Sie meine Antwort für die Details.

    – Alok Speichern

    2. April 2012 um 3:16 Uhr

  • @Als Nun, wenn er das gesamte Programm schreibt, kann er vermeiden, die Zeichenfolge zu ändern, ohne zu schreiben constund es wird völlig legal sein, aber es ist immer noch schlechter Stil.

    – asaelr

    2. April 2012 um 3:18 Uhr

  • Wenn es für das gesamte Programm gültig ist, warum müssen wir es mallocieren?

    – Tom Sawyer

    18. Juni 2020 um 19:27 Uhr

Benutzeravatar von Hyde
Hyde

Ja, es ist ein gültiger Code, siehe Fall 1 unten. Sie können C-Strings auf mindestens folgende Weise sicher von einer Funktion zurückgeben:

  • const char* zu einem String-Literal. Es kann nicht geändert werden und darf nicht vom Aufrufer freigegeben werden. Es ist wegen des unten beschriebenen Freigabeproblems selten nützlich, um einen Standardwert zurückzugeben. Es kann sinnvoll sein, wenn Sie tatsächlich irgendwo einen Funktionszeiger übergeben müssen, also eine Funktion benötigen, die einen String zurückgibt.

  • char* oder const char* in einen statischen Zeichenpuffer. Es darf nicht vom Anrufer freigegeben werden. Es kann geändert werden (entweder vom Aufrufer, wenn es nicht const ist, oder von der Funktion, die es zurückgibt), aber eine Funktion, die dies zurückgibt, kann nicht (leicht) mehrere Puffer haben, daher ist es nicht (leicht) threadsicher, und der Aufrufer muss es möglicherweise um den zurückgegebenen Wert zu kopieren, bevor die Funktion erneut aufgerufen wird.

  • char* zu einem mit zugewiesenen Puffer malloc. Es kann geändert werden, muss aber normalerweise vom Aufrufer explizit freigegeben werden und hat den Overhead für die Heap-Zuweisung. strdup ist von dieser Art.

  • const char* oder char* auf einen Puffer, der als Argument an die Funktion übergeben wurde (der zurückgegebene Zeiger muss nicht auf das erste Element des Arguments Puffer zeigen). Es überlässt die Verantwortung für die Puffer-/Speicherverwaltung dem Aufrufer. Viele Standard-String-Funktionen sind von diesem Typ.

Ein Problem ist, dass das Mischen dieser in einer Funktion kompliziert werden kann. Der Aufrufer muss wissen, wie er mit dem zurückgegebenen Zeiger umgehen soll, wie lange er gültig ist und ob der Aufrufer ihn freigeben soll, und es gibt keine (schöne) Möglichkeit, dies zur Laufzeit zu bestimmen. Sie können also beispielsweise keine Funktion haben, die manchmal einen Zeiger auf einen Heap-zugewiesenen Puffer zurückgibt, den der Aufrufer benötigt freeund manchmal ein Zeiger auf einen Standardwert aus dem Zeichenfolgenliteral, den der Aufrufer haben muss nicht free.

Benutzeravatar von thb
thb

Gute Frage. Im Allgemeinen hätten Sie Recht, aber Ihr Beispiel ist die Ausnahme. Der Compiler weist einem Zeichenfolgenliteral statisch globalen Speicher zu. Daher ist die von Ihrer Funktion zurückgegebene Adresse gültig.

Dass dem so ist, ist eine ziemlich praktische Eigenschaft von C, nicht wahr? Es ermöglicht einer Funktion, eine vorgefertigte Nachricht zurückzugeben, ohne dass sich der Programmierer Gedanken über den Speicher machen muss, in dem die Nachricht gespeichert ist.

Siehe auch die korrekte Beobachtung von @asaelr bzgl const.

  • :Was ist, wenn der Benutzer so etwas zurückgibt. char *a=&”abc”; gib a zurück; Gilt das nicht?

    – Ashwin

    2. April 2012 um 2:59 Uhr

  • Recht. Eigentlich kann man nur schreiben const char *a = "abc";unter Weglassung der &. Der Grund dafür ist, dass eine Zeichenfolge in doppelten Anführungszeichen in die Adresse ihres Anfangszeichens aufgelöst wird.

    – thb

    2. April 2012 um 3:02 Uhr


Lokale Variablen sind nur innerhalb des Bereichs gültig, in dem sie deklariert sind, Sie deklarieren jedoch keine lokalen Variablen in dieser Funktion.

Es ist absolut zulässig, einen Zeiger auf ein String-Literal von einer Funktion zurückzugeben, da ein String-Literal während der gesamten Ausführung des Programms existiert, genau wie ein static oder eine globale Variable würde.

Wenn Sie sich Sorgen machen, dass das, was Sie tun, ungültig und undefiniert sein könnte, sollten Sie Ihre Compiler-Warnungen aufdrehen, um zu sehen, ob Sie tatsächlich etwas falsch machen.

  • :Was ist, wenn der Benutzer so etwas zurückgibt. char *a=&”abc”; gib a zurück; Gilt das nicht?

    – Ashwin

    2. April 2012 um 2:59 Uhr

  • Recht. Eigentlich kann man nur schreiben const char *a = "abc";unter Weglassung der &. Der Grund dafür ist, dass eine Zeichenfolge in doppelten Anführungszeichen in die Adresse ihres Anfangszeichens aufgelöst wird.

    – thb

    2. April 2012 um 3:02 Uhr


Benutzeravatar von Peter Mortensen
Peter Mortensen

str wird niemals ein baumelnder Zeiger sein, weil es zeigt auf eine statische Adresse wo sich Zeichenfolgenliterale befinden.

Es wird meistens sein schreibgeschützt und global zum Programm, wenn es geladen wird.

Selbst wenn Sie versuchen, es zu befreien oder zu ändern, wird ein Fehler ausgelöst Segmentierungsfehler auf Plattformen mit Speicherschutz.

  • Zu Ihrer Information: Diese Antwort wurde von stackoverflow.com/questions/16470959/… zusammengeführt.

    – Shog9

    24. Juli 2014 um 15:49 Uhr

  • Wenn es nie baumeln wird, muss ich es mallocieren? Nein?

    – Tom Sawyer

    18. Juni 2020 um 19:24 Uhr

1420440cookie-check“Lebensdauer” eines Zeichenfolgenliterals in C

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

Privacy policy