Was ist der Unterschied zwischen der Rückgabe eines char* und eines char[] aus einer Funktion? [duplicate]

Lesezeit: 10 Minuten

Warum gibt die erste Funktion die Zeichenfolge „Hello, World“ zurück, die zweite Funktion jedoch nichts. Ich dachte, der Rückgabewert beider Funktionen wäre undefiniert, da sie Daten zurückgeben, die außerhalb des Gültigkeitsbereichs liegen.

#include <stdio.h>
// This successfully returns "Hello, World"
char* function1()
{
    char* string = "Hello, World!";
    return string;
}
// This returns nothing
char* function2()
{
    char string[] = "Hello, World!";
    return string; 
}

int main()
{
    char* foo1 = function1();
    printf("%s\n", foo1); // Prints "Hello, World"
    printf("------------\n");
    char* foo2 = function2(); // Prints nothing
    printf("%s\n", foo2);
    return 0;
}

  • Es gibt viele kanonische Duplikate, die verwendet werden können: Was ist der Unterschied zwischen char s und char *s?, String-Literale: Wohin gehen sie?, Wie greife ich mit Zeigern von einer anderen Funktion auf eine lokale Variable zu?.

    – Ludin

    7. September 2017 um 7:59 Uhr


  • @Leushenko, denke nicht. Die Frage hier bezieht sich auf den Rückgabewert einer Funktion, nicht nur auf den Unterschied zwischen char[] und char *.

    – Maschine_1

    7. September 2017 um 8:33 Uhr

  • Ich bin es leid, dass die Administratoren hier aggressiv erklären, dass gut geschriebene Anfängerfragen Duplikate sind. Das OP wusste genug C, um die Frage auf eine Weise zu stellen, die für sie selbst hilfreich war. Das „Übersetzen“ einer Frage, die auf andere Weise gestellt wird, erfordert oft bereits die Fähigkeiten, die das OP zu entwickeln versucht.

    – Wahrhaftigkeit

    7. September 2017 um 8:37 Uhr


  • Ein Duplikat ist nicht dasselbe wie eine schlechte Frage, und im Gegensatz zu anderen engen Abstimmungen bedeutet dies nicht, dass die Frage des Benutzers nicht gültig ist. Für mich ist dieses q. ist ein Beispiel für die seltene “Upvote+Close”-Kombination: Dies ist eine großartige erste Frage, aber sie muss zu der größeren Fragenfamilie “Array-Variable vs. Zeiger” gehören, um sie vollständig zu verstehen, und die grundlegende Wahrheit hinter den Antworten ist wird gleich sein.

    – Leuschenko

    7. September 2017 um 8:45 Uhr


  • @verisimilidude – Die Idee ist, dass die Verknüpfung aller ähnlichen Fragen den Menschen helfen sollte, alle guten Antworten an einem Ort zu finden. Nicht zuletzt hilft das Google herauszufinden, welchen Beitrag sie ganz oben platzieren sollten.

    – Bo Persson

    7. September 2017 um 9:19 Uhr


Benutzeravatar von JFMR
JFMR

Die zweite Funktion gibt nichts zurück

Das string Array in der zweiten Funktion:

char string[] = "Hello, World!";

hat automatische Speicherdauer. Es existiert nicht, nachdem der Kontrollfluss von der Funktion zurückgegeben wurde.

Wohingegen string in der ersten Funktion:

char* string = "Hello, World!";

zeigt auf eine wörtliche Zeichenfolge, die hat statische Speicherdauer. Das bedeutet, dass die Zeichenfolge nach der Rückkehr von der Funktion noch vorhanden ist. Was Sie von der Funktion zurückgeben, ist ein Zeiger auf diese Literalzeichenfolge.

  • Ich denke, das könnte eine Klärung gebrauchen. In beiden Fällen die Zeichenfolge "Hello, world!"; hat eine statische Speicherdauer. Im ersten Fall erstellen wir ein automatisches Array namens string, und kopieren Sie die statische Zeichenfolge in das automatische Array. Im zweiten Fall erstellen wir einen automatischen Zeiger namens string der in den statischen Lagerbereich zeigt.

    – MM

    7. September 2017 um 11:18 Uhr


  • Exakt. Ändern Sie die Zeile in static char string[] = "Hello, world!"; und sehen was passiert. Dadurch ändert sich die Speicherdauer des Arrays von automatisch auf statisch. (Außerdem sollten Sie sich angewöhnen, Daten zu deklarieren, die Sie nicht ändern müssen const.)

    – Davislor

    7. September 2017 um 13:37 Uhr


  • Bedeutet das alles char* sind statisch? Wenn ich schrieb static char* string = "Hello, World!" würde das was ändern.

    – Tobs

    7. September 2017 um 14:01 Uhr


  • Das würde machen string ein statischer Zeiger auf ein String-Literal, das in diesem Fall nur ein paar Bytes Speicher verschwendet. Wenn Sie dieselbe Funktion erneut aufrufen, wird die static Variable wird noch existieren und gesetzt worden sein. Der Unterschied zwischen char* string und char string[] in diesem Fall zeigt Ersteres auf die String-Konstante und Letzteres erstellt eine temporäre Kopie davon in einem anderen Teil des Speichers. Normalerweise würden Sie dies tun, damit Sie die Kopie ändern können. Deklaration der Kopie static bewirkt, dass das Array nach dem Beenden der Funktion bestehen bleibt. Es ist immer noch gültig, nachdem die Funktion zurückkehrt.

    – Davislor

    7. September 2017 um 14:33 Uhr


  • Es ist der Fall, dass ein Zeichenfolgenliteral, wie z "Hello, world!", hat einen statischen Speicher. String-Literale sind aus historischen Gründen etwas seltsam. Es ist nur legal, sie in a zu lagern char* anstelle einer const char* für Abwärtskompatibilität mit zuvor geschriebenem Code const existierte. Aber das führt zu Fehlern, wenn Sie versuchen, die Zeichenfolge durch die nicht-const Zeiger.. Es wäre noch besser, es zu deklarieren const char* constda es sich um einen nie geänderten Zeiger auf einen nicht änderbaren Speicher handelt.

    – Davislor

    7. September 2017 um 14:47 Uhr


Das erste, was Sie über Strings lernen müssen, ist, dass ein String-Literal eigentlich ein Array von schreibgeschützten Zeichen mit einer Lebensdauer des vollständigen Programms ist. Das bedeutet, dass sie niemals den Geltungsbereich verlassen, sie werden immer während der Ausführung des Programms vorhanden sein.

Was ist die erste Funktion (function1) gibt einen Zeiger auf das erste Element eines solchen Arrays zurück.

Mit der zweiten Funktion (function2) Dinge sind ein bisschen anders. Hier die Variable string ist ein lokale Variable innerhalb der Funktion. Als solches wird es den Gültigkeitsbereich verlassen und nicht mehr existieren, sobald die Funktion zurückkehrt. Mit dieser Funktion geben Sie einen Zeiger auf das erste Element dieses Arrays zurück, aber dieser Zeiger wird sofort ungültig, da er auf etwas zeigt, das nicht mehr existiert. Dereferenzieren (was passiert, wenn Sie es an übergeben printf) wird dazu führen undefiniertes Verhalten.

  • Zur Erläuterung: In function2 Es gibt auch eine schreibgeschützte Zeichenfolge, die nach dem Beenden der Funktion weiterhin vorhanden ist. Aber Sie geben keinen Zeiger darauf zurück. Was passiert, ist, dass Sie die Daten aus dieser Zeichenfolge in eine lokale Variable kopieren und dann einen Zeiger auf zurückgeben das. Die Zeichenfolge existiert also immer noch irgendwo, nur nicht an der Stelle, auf die Ihr Zeiger zeigt.

    – ComicSansMS

    7. September 2017 um 10:57 Uhr

  • @ComicSansMS Möglicherweise gibt es keine schreibgeschützte Zeichenfolge. Compiler neigen dazu, solche Zuweisungen zu optimieren, indem sie wenige Zahlen verschieben, anstatt Zeichenfolgen zu kopieren.

    – bez

    7. September 2017 um 14:51 Uhr

  • @bezet Ich nehme an, die Antwort könnte genauer formuliert werden, indem die “als ob” -Terminologie verwendet wird, die die Spezifikation liebt, aber ich denke, dass wir in diesem Fall ohne sie davonkommen können. Es gibt eine solche schreibgeschützte Zeichenfolge im “Gedanken” des Compilers, wenn er Ihren Code interpretiert. Ob diese Zeichenfolge jemals wörtlich in die ausführbare Datei ausgegeben wird, muss vom Optimierer bestimmt werden.

    – Cort Ammon

    7. September 2017 um 21:01 Uhr

Eine sehr wichtige Sache, die Sie beim Codieren in C oder anderen stapelbasierten Sprachen beachten sollten, ist, dass eine Funktion (und ihr gesamter lokaler Speicher) verschwunden ist, wenn sie zurückkehrt. Das bedeutet, wenn Sie möchten, dass jemand anderes die Ergebnisse Ihrer harten Arbeit mit Methoden sehen kann, müssen Sie sie an einem Ort platzieren, der noch existiert, nachdem Ihre Methode aufgehört hat, und um dies zu tun, müssen Sie ein Verständnis dafür entwickeln wo C Sachen speichert und wie.

Sie wissen wahrscheinlich bereits, wie ein Array in C funktioniert. Es ist nur eine Speicheradresse, die um die Größe des Objekts erhöht wird, und Sie wissen wahrscheinlich auch, dass C keine Begrenzungsprüfung durchführt, wenn Sie also auf das 11. Element einer Zehn zugreifen möchten element array, niemand wird Sie daran hindern, und solange Sie nicht versuchen, etwas zu schreiben, ist nichts passiert. Was Sie vielleicht nicht wissen, ist, dass C diese Idee auf die Art und Weise ausdehnt, wie es Funktionen und Variablen verwendet. Eine Funktion ist nur ein Speicherbereich auf einem Stack, der bei Bedarf geladen wird, und der Speicher für seine Variablen sind nur Offsets von diesem Ort. Ihre Funktion hat einen Zeiger auf eine lokale Variable zurückgegeben, insbesondere die Adresse eines Speicherorts auf dem Stapel, der das „H“ von „Hello World\n\0“ enthält, aber als Sie dann eine andere Funktion (die Druckmethode) aufgerufen haben, war dieser Speicher von der Druckmethode wiederverwendet, um das zu tun, was sie benötigt. Sie können dies leicht genug sehen (TUN SIE DIES NICHT IM PRODUKTIONSCODE!!!)

char* foo2 = function2(); // Prints nothing
ch = foo2[0];  // Do not do this in live code!
printf("%s\n", foo2);  // stack used by foo2 now used by print()
printf("ch is %c\n", ch);  // will have the value 'H'!

  • Ja, und selbst wenn es irgendwie mit statischen Saiten funktioniert, ist es eine wirklich schlechte Angewohnheit (IMHO jedenfalls).

    – jamesqf

    8. September 2017 um 3:31 Uhr

Benutzeravatar von hackks
hackt

Ich dachte, der Rückgabewert beider Funktionen wäre undefiniert, da sie Daten zurückgeben, die außerhalb des Gültigkeitsbereichs liegen.

Nein. Das ist nicht der Fall.

In Funktion function1 Sie geben einen Zeiger auf ein Zeichenfolgenliteral zurück. Das Zurückgeben eines Zeigers auf ein Zeichenfolgenliteral ist in Ordnung, da Zeichenfolgenliterale dies haben statische Speicherdauer. Aber das stimmt nicht mit automatische lokale Variable.

In Funktion function2 das Array string ist eine automatische lokale Variable und die Anweisung

return string; 

gibt einen Zeiger auf eine automatische lokale Variable zurück. Sobald die Funktion zurückkehrt, wird die Variable string wird es nicht mehr geben. Das Dereferenzieren des zurückgegebenen Zeigers führt zu undefiniertem Verhalten.

Benutzeravatar von Dmitry Grigoryev
Dmitri Grigorjew

"Hello, World!" ist ein String-Literal, das eine statische Speicherdauer hat, also liegt das Problem woanders. Ihre erste Funktion gibt die zurück Wert von string, was in Ordnung ist. Die zweite Funktion gibt jedoch die zurück die Anschrift einer lokalen Variablen (string ist das gleiche wie &string[0]), was zu undefiniertem Verhalten führt. Dein zweiter printf -Anweisung könnte nichts oder “Hello, World!” oder etwas ganz anderes drucken. Auf meiner Maschine erhält das Programm nur einen Segmentierungsfehler.

Schauen Sie sich immer die Meldungen an, die Ihr Compiler ausgibt. Für dein Beispiel, gcc gibt:

file.c:12:12: warning: function returns address of local variable [-Wreturn-local-addr]
    return string; 
           ^

was ziemlich selbsterklärend ist.

Benutzeravatar von Karl Knechtel
Karl Knechtel

Ich dachte, der Rückgabewert beider Funktionen wäre undefiniert, da sie Daten zurückgeben, die außerhalb des Gültigkeitsbereichs liegen.

Beide Funktionen geben einen Zeiger zurück. Entscheidend ist der Umfang der Referent.

Im function1ist der Referent das Zeichenfolgenliteral "Hello, World!"die eine statische Speicherdauer hat. string ist eine lokale Variable, die auf diese Zeichenfolge zeigt, und konzeptionell wird eine Kopie dieses Zeigers zurückgegeben (in der Praxis vermeidet der Compiler das unnötige Kopieren des Werts).

Im function2konzeptionell ist der Referent das lokale Array string, die (zur Kompilierzeit) automatisch so dimensioniert wurde, dass sie groß genug ist, um das Zeichenfolgenliteral aufzunehmen (natürlich einschließlich eines Null-Terminators), und mit einer Kopie der Zeichenfolge initialisiert wurde. Die Funktion möchten gibt einen Zeiger auf dieses Array zurück, außer dass das Array eine automatische Speicherdauer hat und daher nach dem Verlassen der Funktion nicht mehr existiert (es ist tatsächlich “außerhalb des Gültigkeitsbereichs”, in geläufigerer Terminologie). Da dies ein undefiniertes Verhalten ist, wird der Compiler kann in der Praxis alle möglichen Dinge tun.

Bedeutet das alles char* sind statisch?

Auch hier müssen Sie zwischen dem Zeiger und dem Referenten unterscheiden. Zeiger zeigen auf Daten; sie selbst “enthalten” die Daten nicht.

Sie haben einen Punkt erreicht, an dem Sie gründlich studieren sollten, was Arrays und Zeiger in C tatsächlich sind – leider ist es ein bisschen chaotisch. Die beste Referenz, die ich spontan anbieten kann, ist Diesim Q&A-Format.

1416020cookie-checkWas ist der Unterschied zwischen der Rückgabe eines char* und eines char[] aus einer Funktion? [duplicate]

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

Privacy policy