webentwicklung-frage-antwort-db.com.de

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

Warum gibt die erste Funktion die Zeichenfolge "Hello, World" zurück, die zweite Funktion gibt jedoch nichts zurück. Ich dachte, der Rückgabewert beider Funktionen wäre undefiniert, da sie Daten liefern, 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;
}
66
Tobs

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ückgekehrt ist.

Während string in der ersten Funktion:

char* string = "Hello, World!";

verweist auf eine Literalzeichenfolge, die statische Speicherdauer hat. Dies 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.

66
El Profesor

Das erste, was Sie über Strings wissen müssen, ist, dass ein String-Literal wirklich ein Array von schreibgeschützten Zeichen ist, dessen Lebensdauer das gesamte Programm übersteigt. Das bedeutet, dass sie niemals den Rahmen verlassen werden. Sie werden während der gesamten Ausführung des Programms immer vorhanden sein.

Die erste Funktion (function1) führt einen Zeiger auf das erste Element eines solchen Arrays zurück.

Mit der zweiten Funktion (function2) sieht die Sache etwas anders aus. Hier ist die Variable string eine local-Variable innerhalb der Funktion. Als solches wird es den Gültigkeitsbereich verlassen und nach der Rückkehr der Funktion aufhören zu existieren. Mit dieser Funktion geben Sie einen Zeiger auf das erste Element dieses Arrays zurück, der jedoch sofort ungültig wird, da er auf etwas verweist, das nicht mehr vorhanden ist. Eine Dereferenzierung (was passiert, wenn Sie sie an printf übergeben) führt zu undefiniertem Verhalten .

26

Beim Codieren in C- oder anderen Stack-basierten Sprachen ist zu beachten, dass die Funktion (und der gesamte lokale Speicher) der Funktion nicht mehr verfügbar sind. Das bedeutet, wenn Sie möchten, dass jemand anderes die Ergebnisse Ihrer Methoden unter harter Arbeit sehen kann, müssen Sie sie an einem Ort ablegen, der nach dem Ende Ihrer Methode noch vorhanden ist. Um dies zu tun, müssen Sie ein Verständnis davon erlangen wo C Sachen und wie speichert. 

Sie wissen wahrscheinlich bereits, wie ein Array in C arbeitet. 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 elfte Element einer Zehn zugreifen möchten Element-Array, niemand wird Sie aufhalten, und solange Sie nicht versuchen, etwas zu schreiben, wird kein Schaden angerichtet. Was Sie möglicherweise nicht wissen, ist, dass C diese Idee auf die Art und Weise erweitert, wie Funktionen und Variablen verwendet werden. Eine Funktion ist nur ein Speicherbereich auf einem Stack, der bei Bedarf geladen wird, und der Speicher für seine Variablen ist nur ein Versatz von diesem Speicherort. Ihre Funktion hat einen Zeiger auf eine lokale Variable zurückgegeben, insbesondere die Adresse einer Position auf dem Stapel, die das 'H' von 'Hello World\n\0' enthält. Als Sie jedoch eine andere Funktion (die Druckmethode) aufgerufen haben, war dieser Speicher vorhanden von der Druckmethode wieder verwendet, um das zu tun, was sie benötigt. Sie können dies leicht genug sehen (NICHT IN PRODUKTIONSCODE TUN !!!) 

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'!
7
Paul Smith

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

Nein, das ist nicht der Fall. 

In der Funktion function1 geben Sie einen Zeiger auf ein String-Literal zurück. Das Zurückgeben eines Zeigers auf ein Zeichenfolgenliteral ist in Ordnung, weil Zeichenfolgenliterale statische Speicherdauer haben. Dies gilt jedoch nicht für automatische lokale Variable

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

return string; 

gibt einen Zeiger auf eine automatische lokale Variable zurück. Sobald die Funktion zurückkehrt, ist die Variable string nicht mehr vorhanden. Das Rückführen des zurückgegebenen Zeigers führt zu undefiniertem Verhalten. 

5
haccks

"Hello, World!" ist ein Stringliteral, das eine statische Speicherdauer hat. Das Problem liegt also an anderer Stelle. Ihre erste Funktion gibt den Wert value von string zurück, was in Ordnung ist. Die zweite Funktion gibt jedoch das address einer lokalen Variablen zurück (string ist das gleiche wie &string[0]), was zu undefiniertem Verhalten führt. Ihre zweite printf-Anweisung konnte nichts drucken oder "Hallo, Welt!" Oder etwas anderes. Auf meiner Maschine bekommt das Programm nur einen Segmentierungsfehler.

Schauen Sie sich immer die Meldungen Ihrer Compiler-Ausgaben an. Für Ihr Beispiel gibt gcc Folgendes an:

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

das ist ziemlich selbsterklärend.

1

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

Beide Funktionen geben einen Zeiger zurück. Was zählt, ist der Geltungsbereich des referent.

In function1 ist der Verweis das Zeichenfolgenliteral "Hello, World!", das 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, dass der Wert unnötig kopiert wird).

In function2 ist der Verweis konzeptionell das lokale Array string, das automatisch (zur Kompilierzeit) so bemessen wurde, dass es groß genug ist, um das Zeichenfolgenliteral zu enthalten (natürlich einschließlich eines Nullabschlusszeichens) und mit einer Kopie der Zeichenfolge initialisiert wurde . Die Funktion würde einen Zeiger auf dieses Array zurückgeben, mit der Ausnahme, dass das Array eine automatische Speicherdauer hat und daher nach dem Beenden der Funktion nicht mehr vorhanden ist (in der Tat ist es tatsächlich "außerhalb des Gültigkeitsbereichs"). Da dies ein undefiniertes Verhalten ist, kann der Compiler in der Praxis alle möglichen Dinge tun .

Bedeutet das, dass alle char* statisch sind?

Wieder müssen Sie zwischen dem Zeiger und dem Verweis unterscheiden. Zeiger zeigen auf Daten; Sie "enthalten" die Daten nicht selbst.

Sie sind an einem Punkt angelangt, an dem Sie genau studieren sollten, welche Arrays und Zeiger sich tatsächlich in C befinden - leider ist das ein bisschen Chaos. Die beste Referenz, die ich ohne weiteres anbieten kann, ist this , im Q & A-Format.

0
Karl Knechtel