So geben Sie ein Zeichenfolgenliteral von einer Funktion zurück

6

Ich bin immer verwirrt, wenn ich ein String-Literal oder eine Zeichenkette aus einer Funktion zurücksende. Mir wurde gesagt, dass es einen Speicherverlust geben könnte, weil Sie nicht wissen, wann der Speicher gelöscht wird?

Zum Beispiel im Code unten, wie man foo() implementiert, damit die Ausgabe des Codes "Hello World" ist?

%Vor%

Auch wenn der Rückgabetyp von foo() nicht ungültig ist, Sie aber char* zurückgeben können, was sollte es sein?

    
skydoor 20.03.2010, 01:30
quelle

4 Antworten

9

Ich gehe davon aus, dass wir main nicht ändern können. Damit Ihr Programm ohne Leck funktioniert, benötigen Sie etwas, um statischen Speicher zu haben:

%Vor%

Aber wirklich, das ist kein sehr konventioneller C ++ Code. Sie würden std::string und std::cout verwenden, damit Sie sich keine Sorgen um den Speicher machen müssen:

%Vor%

Wenn Sie sich fragen, ob etwas manuell aufgehoben werden muss, wird es falsch gemacht.

    
GManNickG 20.03.2010, 01:34
quelle
5

Da die alte Verwendung von char * veraltet ist, können Sie nicht einfach eine Zeichenkette verwenden?

const char* func1 () {return "string literal";}

string func2 () {return "another string literal";}

Beide funktionieren ohne Compilerwarnungen.

Jedoch

char* func3 () {return "yet another string literal";}

wird überhaupt nicht kompiliert. Noch wird

char* func4 () {return &"a ref to a string literal?";}

Stroustrup sagt in "Die C ++ Programmiersprache" (Dritte Ausgabe):

"Ein String-Literal wird statisch zugewiesen, so dass es sicher ist, einen String von einer Funktion zurückzugeben.

%Vor%

Der Speicher, der Bereichsfehler enthält, wird nach einem Aufruf von error_messages () nicht gelöscht. "

So wird jedes Zeichenfolgenliteral in einem Programm in seinem eigenen kleinen Speicherstück zugewiesen, das für die Dauer des Programms dauert (d. h. statisch zugewiesen ist). Durch das Setzen des const vor dem char * weiß der Compiler, dass Sie das kleine Speicherstück des Stringliterals, das gefährlich sein könnte, nicht ändern wollen (und nicht ändern können), sodass diese Zuweisung trotz der Umwandlung von Zeichenfolgenliteral in char * verschoben wird. ist veraltet.

Wenn Sie stattdessen zu einer Zeichenfolge zurückkehren, müssen Sie das Zeichenfolgenliteral in ein Objekt vom Typ string kopieren, für das der Aufrufer verantwortlich ist.

In beiden Fällen gibt es keine Speicherlecks: Jedes String-Literal erhält seinen eigenen Speicher, der beim Beenden des Programms bereinigt wird; return to const char * gibt einen Zeiger auf das Speicherstück eines Literals zurück (wissend, dass man es nicht ändern kann); und die Rückkehr zu einem String macht eine Kopie zu einem String-Objekt, das im Code des Anrufers existiert, der vom Anrufer bereinigt wird.

Obwohl es ein wenig hässlich in Bezug auf die Notation scheint, vermute ich, dass sie den const char * zurückgelassen haben, um die billige Alternative (ohne Kopien) zu behalten.

    
RMS 16.06.2012 15:12
quelle
4
  

Ich bin immer verwirrt, wenn ich ein String-Literal oder eine Zeichenkette von einer Funktion zurückgeben möchte.

Unveränderbare, literale Zeichenfolge

Wie ich es verstehe, können Sie ein String-Literal direkt ausgeben, wenn der Rückgabetyp const deklariert ist, um zu erklären, dass die Zeichenfolge nicht geändert werden soll. Das bedeutet, dass Sie sich keine Gedanken über die Lebensdauer der String- / Speicherlecks machen müssen.

Veränderbare, nicht literale Zeichenfolge

Wenn Sie jedoch eine Zeichenfolge benötigen, die Sie direkt ändern können, müssen Sie die Lebensdauer der Zeichenfolge und die Größe der Speicherzuordnung, in der sie gespeichert ist, berücksichtigen. Dies wird zu einem Problem, da Sie nicht mehr den gleichen Speicher, der eine Zeichenfolge enthält, für jeden Aufruf der Funktion zurückgeben können, da eine vorherige Verwendung den Inhalt dieses Speichers geändert haben könnte und / oder immer noch verwendet werden kann. Daher muss ein neuer Speicherabschnitt zugewiesen werden, um die zurückgegebene Zeichenfolge zu speichern.

Hier liegt das Potenzial für ein Leck, und es muss entschieden werden, wo die Zuteilung und die Aufhebung der Zuteilung erfolgen soll. Sie könnten veranlassen, dass die Funktion selbst den Speicher und den Zustand in der Dokumentation zuweist, dass dies geschieht, und darin festlegen, dass der Aufrufer den Speicher freigeben muss, wenn er nicht mehr benötigt wird (Verhindern eines Lecks). Dies bedeutet, dass die Funktion einfach ein char * zurückgeben kann.

Die andere Option besteht darin, etwas Speicher an die Funktion zu übergeben, die vom Aufrufer zugewiesen wurde, und die Funktion speichert die Zeichenfolge in diesem Speicher. In diesem Fall weist der Aufrufer beides zu und ist dafür verantwortlich, diesen Speicher freizugeben.

Schließlich habe ich erwähnt, dass die Größe des Speichers und der Zeichenfolge verwaltet werden muss, wenn eine veränderbare Zeichenfolge verwendet wird. Die Zuweisung muss sowohl für die anfänglich von der Funktion festgelegte Zeichenfolge als auch für alle Änderungen, die nach der Funktion vorgenommen werden, groß genug sein, bevor der Speicher freigegeben wird. Wenn Sie dies nicht richtig tun, kann dies zu einem Pufferüberlauf führen, indem Sie eine lange Zeichenfolge schreiben, die in den ursprünglich zugewiesenen Speicher passt. Dies ist äußerst gefährlich für die Gesundheit und Sicherheit Ihres Programms. Dies kann zu Fehlern und Sicherheitslücken führen, die extrem schwer zu erkennen sind (da die Fehlerursache - der Überlauf - weit von den Symptomen entfernt sein kann, die beim Ausfall des Programms auftreten).

    
Mike Tunnicliffe 20.03.2010 02:08
quelle
2

In etwa so:

%Vor%

Dann nenne es so:

%Vor%

Wie im Kommentar erwähnt, sei vorsichtig, dass der String, den du speicherst, kleiner ist als der Puffer oder du bekommst einen ... Stack-Overflow! (Wortspiel beabsichtigt)

    
Nathan Osman 20.03.2010 01:37
quelle

Tags und Links