Wie werden C-Strings im Speicher zugewiesen?

Lesezeit: 6 Minuten

Angenommen, ich habe eine einfache Funktion, die einen C-String auf diese Weise zurückgibt:

const char * getString()
{
  const char * ptr = "blah blah";
  return ptr; 
}

und ich rufe getString() von main() auf diese Weise auf:

  const char * s = getString();

1) Laut gdb die Variable ptr wird auf dem Stack gespeichert, aber die Zeichenfolge, auf die ptr zeigt ist nicht:

(gdb) p &ptr
$1 = (const char **) 0x7fffffffe688

(gdb) p ptr
$2 = 0x4009fc "blah blah"

Bedeutet dies, dass “blah blah” keine lokale Variable in getString() ist?

Ich denke, wenn es eine lokale Variable wäre, könnte ich sie nicht an meine main () -Funktion übergeben … Aber wenn nicht, wo wird sie gespeichert? Auf dem Haufen? Ist das eine “Art” dynamische Speicherzuweisung, die vom Betriebssystem jedes Mal implementiert wird, wenn es auf eine Zeichenfolge trifft, oder was?

2) Wenn ich ein Array anstelle eines Zeigers verwende, so:

const char *getString2()
{
  const char a[] = "blah blah blah";
  return a;
}

Der Compiler warnt mich, dass:

warning: address of local variable ‘a’ returned

(und natürlich wird das Programm kompiliert, aber es funktioniert nicht).

Eigentlich, wenn ich gdb frage, bekomme ich

(gdb) p &a
$2 = (const char (*)[15]) 0x7fffffffe690

Aber das dachte ich const char * ptr und konstantes Zeichen a[] waren im Grunde dasselbe. Sieht so aus, als wären sie es nicht.

Liege ich falsch? Was genau ist der Unterschied zwischen den beiden Versionen?

Vielen Dank!

  • stackoverflow.com/questions/3862842/…

    – Matte

    22. Dezember 2012 um 16:08 Uhr

  • Gut formulierte Frage, auch wenn es sich um eine Wiederholung handelt.

    – Mats Petersson

    22. Dezember 2012 um 16:11 Uhr

Wenn du schreibst

const char *ptr = "blah blah";

dann passiert folgendes: der Compiler generiert einen konstanten String (vom Typ char []) mit Inhalt "blah blah" und speichert es irgendwo im Datensegment der ausführbaren Datei (es hat im Grunde eine ähnliche Speicherdauer wie die von Variablen, die mit der deklariert wurden static Stichwort).

Dann die Adresse dieser Zeichenfolge, die lautet gültig während der gesamten Laufzeit des Programms, gespeichert ist in der ptr Zeiger, der dann zurückgegeben wird. Alles ist gut.

Bedeutet dies das "blah blah" ist keine lokale Variable in getString()?

Lassen Sie mich mit einem gebrochenen englischen Satz antworten: ja, das ist es nicht.

Wenn Sie jedoch ein Array deklarieren, wie in

const char a[] = "blah blah";

dann generiert der Compiler keinen statischen String. (Tatsächlich ist dies ein etwas spezieller Fall beim Initialisieren von Strings.) Es generiert dann Code, der ein ausreichend großes Stack-Speicherstück für die zuweist a Reihe (es ist kein Zeiger!) und füllt es mit den Bytes des Strings. Hier a ist eigentlich eine lokale Variable und die Rückgabe seiner Adresse führt zu undefiniertem Verhalten.

So…

Aber das dachte ich const char *ptr und const char a[] waren im Grunde dasselbe.

Nein, überhaupt nicht, weil Arrays sind keine Zeiger.

  • Zeichenfolgenliterale haben einen Typ char []Sie sind nicht const-qualifiziert, obwohl es schön wäre.

    – effe

    22. Dezember 2012 um 16:16 Uhr

  • @effeffe Du bist die zweite Person, die das sagt. Es muss wahr sein. (Was für eine hirnlose Widersprüchlichkeit!)

    Benutzer529758

    22. Dezember 2012 um 16:20 Uhr

  • Es ist wahr. Es ist ein Überbleibsel der frühen Tage, als es keine gab const. Vor der Einführung von constdie einzigen Zeichenfolgenliterale vom Typ könnte haben war char[N] (für angemessen N). Eine spätere Änderung wäre wahrscheinlich als zu bahnbrechende Änderung angesehen worden.

    – Daniel Fischer

    22. Dezember 2012 um 16:24 Uhr

  • Bah, was für ein dummer Tippfehler, danke für die Korrektur @DanielFischer. (Ja, ich verstehe die Argumentation, aber wir hatten zwei große Überarbeitungen des Standards seit K&r, C89 und C99, ich verstehe nicht, warum diese Änderung nicht vorgenommen werden konnte.)

    Benutzer529758

    22. Dezember 2012 um 16:25 Uhr


  • Zitieren der C99-Begründung: “String-Literale haben jedoch nicht den Typ Array von const char, um die Probleme der Zeigertypprüfung zu vermeiden, insbesondere bei Bibliotheksfunktionen, da die Zuweisung eines Zeigers auf const char zu einem einfachen Zeiger auf char nicht zulässig ist.”

    – effe

    22. Dezember 2012 um 16:26 Uhr


Benutzeravatar von md5
md5

Ich denke, wenn es eine lokale Variable wäre, könnte ich sie nicht an meine main () -Funktion übergeben … Aber wenn nicht, wo wird sie gespeichert?

Zeichenfolgenliterale werden normalerweise in einem schreibgeschützten Datenabschnitt gespeichert (.rodata). C-Standard sagt nur, dass sie eine statische Speicherdauer haben. Daher können Sie einen Zeiger auf ein solches Literal zurückgeben, dies ist jedoch bei Arrays nicht der Fall.

Im folgenden Beispiel zeigt das Objekt von p1 hat eine statische Speicherdauer, während das Array p2 hat eine automatische Speicherdauer.

char *f(void)
{
    const char *p1 = "hello, world";
    char p2[] = "hello, world";

    return p1; /* allowed */
    return p2, /* forbidden */
}

  • Danke auch für die Details 😉

    – Lauriden

    22. Dezember 2012 um 16:30 Uhr

  • Beide p1 und p2 hat eine automatische Speicherdauer, während das Objekt durch zeigt p1 hat eine statische Speicherdauer. (und vielleicht sollte die Funktion nicht sein void :P)

    – effe

    22. Dezember 2012 um 16:30 Uhr


Sie haben recht damit, dass sie nicht dasselbe sind. Zeichen a[] ist ein Array, das auf dem Stack gebildet und dann mit “blah..” gefüllt wird – innerhalb der Funktion haben Sie im Wesentlichen `const char a[15]; strcpy(a, “bla bla bla”);”

The const char *ptr = "blah blah blah"; Andererseits ist es einfach ein Zeiger (der Zeiger selbst befindet sich auf dem Stapel), und der Zeiger zeigt auf die Zeichenfolge “blah blah blah”, die woanders gespeichert ist [in “read only data” most likely].

Sie werden einen großen Unterschied feststellen, wenn Sie versuchen, etwas zu ändern, z.
a[2] = 'e'; vs ptr[2] = 'e'; – Der erste wird erfolgreich sein, weil Sie einen Stapelwert ändern, während der zweite (wahrscheinlich) fehlschlägt, weil Sie einen schreibgeschützten Speicher ändern, der natürlich nicht funktionieren sollte.

Benutzeravatar von Grijesh Chauhan
Grijesh Chauhan

In Ihrer Funktion ist der Umfang von a[] array ist innerhalb der Funktion getString2(). es ist lokale Array-Variable.

const char *getString2()
{
  const char a[] = "blah blah blah";
  return a;
}  

Im obigen Fall Zeichenfolge "blah blah blah" Kopien Faust hinein a[] und Sie versuchen, dieses Array mit zurückzugeben return a Anweisung, aber keine konstante Zeichenfolge.

Wo wie im ersten Code getString() : ptr = "blah blah"; ptr zeigt auf Speicher mit globalem Geltungsbereich.

const char * getString()
{
  const char * ptr = "blah blah";
  return ptr; 
}

In diesem Fall geben Sie die Adresse des konstanten Strings zurück "blah blah" das ist legal zu tun.

Also eigentlich sein Scope-Problem.

es hilfreich zu lernen Speicherlayout von C-Programmen und Variabler Umfang in C.

Sie sind nicht gleich.

Der erste ist ein Zeiger auf ein Zeichenfolgenliteral. Der Zeiger selbst befindet sich im automatischen Speicher. Die Zeichenfolge befindet sich im statischen Nur-Lese-Speicher. Es ist unveränderlich.

Der zweite ist ein automatischer (Stapel) char array (und diese Rückgabe ist, wie die Warnung sagt, nicht zulässig).

1432450cookie-checkWie werden C-Strings im Speicher zugewiesen?

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

Privacy policy