localen Zeiger zurückgeben

BLR

Erfahrenes Mitglied
Hallo zusammen,

ich bin neu in c++ und habe eine Frage bezüglich folgender Sache:

C++:
int* bam(){

  int x = 43;
  cout << "Wert von bam in der Funktion: " << x << "\n";
  cout << "Adresse von dem Wert in der Funktion: " << &x << "\n";
  return &x;
}

int main(int argc, char** argv) {
  
    int *hallo = bam();
    cout << "hallo-zeiger zeigt nicht mehr auf den Wert von der Funktion: " << *hallo << "\n";
    cout << "Adresse wo sich dieser Wert befindet: " << hallo << "\n";

Mein gcc-Compiler sagt mir, wo ich der x die 43 zuweise: address of local variable ‘x’ returned [enabled by default].

So wie ich das verstehe, wird die Adresse von der x-Variable nach dem abarbeiten der Methoe bam wieder freigegeben, worin sich immer noch der Wert 43 befindet.
Nun merke ich mir diese Adresse im Zeiger "hallo" um auf den Wert 43 zugreifen zu können und das funktioniert auch, bei der ersten cout-Ausgabe wird die 43 komischeweise angezeigt, also wo ist das Problem???
 
Hi

Wie du sagst wird der Speicher von x am bam-Ende wieder freigegeben.
Die Adresse wird ganz normal returnt, und du könntest hallo (also den Adresswert) ganz
normal verwenden (auch wenn es nicht viel Sinn macht), nur *hallo ist nicht erlaubt.

Und das da 43 noch drinsteht ist einfach Glück (oder Pech, wie man es sehen will).
Ein freigegebener Speicherplatz kann intern für etwas Anderes benutzt werden.
"Kann", nicht "Muss". Ob und wann der Platz einen neuen Besitzer bekommt bzw.
ob/wann etwas reingespeichert wird ist so nicht vorhersagbar.

Vom Freigabezeitpunkt bis zur Konsolenausgabe hats einfach noch keine
andere Verwendung gegeben, deswegen steht der alte Wert noch drin
(es gibt kein "leer" im Speicher). Aber es kann eben keiner garantieren,
dass das immer so sein wird. Man kann sich nicht drauf verlassen und
genau deswegen gibts die Warnung.


(Hui, gute 7 Jahre angemeldet und 7000 Beiträge :) )
 
Ahh vielen Dank, sehr gut.
Wie könnte man das Problem umgehen?
Sprich, wenn die Methode "bam" irgendwas berechnet und es an eine andere Methode zurückgibt, wie könnte man das "sicher" gestalten?
 
Da gibts sehr viele Möglichkeiten... ein paar davon
(nicht geordnet von bester zu schlechtester oder so)

Für Fälle wie bei deinem Beispiel: Einfach den Wert selber zurückgeben :)
C++:
int bam(){
	int x = 43;
	cout << "Wert von bam in der Funktion: " << x << "\n";
	cout << "Adresse von dem Wert in der Funktion: " << &x << "\n";
	return x;
}
...
int hallo = bam();
Geht halt nur in einfachen Fällen so problemlos.
Probleme wären mehrere Werte, Klassen, Threads...


(Nicht direkt auf deinen Code bezogen):
Wenn mehrere Variablen zusammen zurückgegeben werden sollen könnte man zB.
eine struct dafür machen. Kann natürlich nicht nur für return, sondern auch in
die andere Richtung (Parameter eben) verwendet werden. Nachteil: Macht nur
wirklich Sinn, wenn diese Variablen oft zusammen vorkommen, also wenn man
die Struktur für viele Funktionen etc. verwenden kann. Für jede Funktion eigene
Strukts machen macht alle nur unübersichtlich und mühsam
C++:
typedef struct _str
{
	int var1;
	int var2,
	float var3;
} str;

str bam(){
	str x;
	x.var1 = 4;
	...
	return x;
}
...
str hallo = bam();


Die direkteste Alternative:
Statt die Adresse einer lokalen Funktionsvariable zurückgeben genau umgekehrt
vorgehen: Die Variable da machen, wo man die Funktion aufruft, und die Adresse
als Parameter zur Funktion hingeben. Die Variablen der aufrufenden Stelle werden
ja nicht gelöscht, solange auf das Funktionsende gewartet wird... Auch so sind mehrere
Funktionergebnisse möglich (und wenn man will auch nochmit structs kombinierbar)
C++:
void bam(int a, int b, int *plus, int *minus, int *mult){
	*plus = a+b;
	*minus = a+b;
	*mult = a*b;
}

...

int a,b,c;
bam(4, 20, &a, &b, &c);
//Ergebnisse jetzt in a, b, c


Eine kleine Abwandlung zum vorigen: Weniger * und & tippen,
aber dafür nur in C++ möglich (das obere geht auch in C so):
Referenzen
C++:
void bam(int a, int b, int &plus, int &minus, int &mult){
	plus = a+b;
	minus = a+b;
	mult = a*b;
}

...

int a,b,c;
bam(4, 20, a, b, c);
//Ergebnisse jetzt in a, b, c


Wirkliches Pointer-Zurückgeben mit einem Schönheitsproblem: Bei einem "int x = 43;"
muss man das Speicherreservieren und -freigeben nicht extra in den Code schreiben.
Angelegt wird eben durch "int x", freigegeben wird am Ende des {}-Blocks, in dem x gemacht
wurde (deswegen eben spätetens am Funktionende). Man kann aber auch mit malloc bzw.
new selbst Speicher anfordern (und einen Pointer haben, mit dem man zu seinem Speicher findet)
Das Zurückgeben der Adresse wäre damit in Ordnung.
Das Problem ist, dass jeder selbst angelegte Speicher auch wieder selbst weggeräumt werden muss,
was hier ein free/delete außerhalb der Funktion bedeutet. Und wenn man die Funktion aufruft,
ohne den inneren Code gearde vor Augen zu haben, kann man schon mal vergessen, dass man das
Ergebnis nicht nur verwenden, sondern auch freigeben sollte. Deswegen ziemlich unbeliebte Lösung.
Die Variante mit new/delete für C++ (malloc/free gehen in C):
C++:
int *bam(){
	int *x = new int();
	*x = 4;
	return x;
}
...
int *y = bam();
// *y ist 4
delete y;


Und, um beim selben Prinzip zu bleiben: Mit C++-Klassen und deren Features kann
man sich etwas zusammenbasteln, dass man dieses delete eben nicht selber aufrufen
muss (ganz grobes Prinzip: eine Klasse zum Speichern des Pointers in sich, und
der Destruktor macht delete). Wegen der Nützlichkeit davon gibt es
so eine Klasse auch schon fertig:
C++:
std::unique_ptr<int> bam(){
	std::unique_ptr<int> x(new int());
	*x = 4;
	return x;
}
...
std::unique_ptr<int> y = bam();
// *y ist 4
//kein "delete y;" nötig
Siehe auch shared_ptr usw.
 
Vielen vielen Dank für die Mühe.
Ich finde deine direkte Alternative am besten, obwohl einen Zeiger auf dem Heap zu erzeugen und den Speicher freizugeben, (der Wert im Speicher kann ja noch für eine unbestimmte Zeit drin sein) am besten.
 
Direkte Variante war, in der Argumentenliste die Referenz zu übergeben... ;)

Obwohl mir da noch eine Frage einfällt und zwar

C++:
int *hallo = bam();
der hallo-Zeiger zeigt auf die Adresse auf dem Stack, wo sich vielleicht immer noch der Wert von der aufgerufen Funktion "bam" befindent oder nicht.
Als nächstes mache ich folgendes:
C++:
*hallo = 6
Ich hätte mir jetzt gedacht, da ich ja nicht explizit mit "new" oder einfacher Zuweisung wie: int* zZ = &z
den Speicher allokiere, schreibe ich den Wert 6 einfach irgendwo in den Speicher rein (aber kein Heap, da kein new)
Komischeweise zeigt er mir aber immer noch die Adresse an, die von "bam" zurückgegeben wurde.

1. Überschreibe ich somit explizit den Wert "43" mit "6" in die immer noch zurückgegebene Adresse
oder
2. Landet die "6" zufälligerweise auf dieser zurückgegebener Adresse, und er hätte die "6" auch an eine andere Stelle im Speicher reinschreiben können?
 
hallo zeigt auf eine Speicheradresse. *hallo ist der Wert an sich. Somit überschreibt *hallo = 6 den ursprünglichen Wert. Dahingegen würde hallo = xyz den Zeiger überschreiben, aber nicht den originalen Wert.
 
Zuletzt bearbeitet:
*hallo = xyz
würde doch genau so wie
*hallo = 6 den ursprünglichen Wert überschreiben, das ist doch das selbe.
Ok, das bedeutet jedenfalls, dass ich hier explizit auf die freigegebene Speicheradresse im Stack schreibe (obwohl das gar nicht offiziel allokiert wurde, somit dürfte man das nicht machen) und diese Speicheradresse im Stack wurde nicht einfach zufällig gewählt.
 
Oh, beim zweiten Mal hatte ich aus Versehen das Sternchen beibehalten. Ich habe es oben korrigiert.

Die Frage ist, auf welche Speicheradresse der Zeiger zeigt und ob diese "schreibbar" ist. Die Speicheradresse könnte beispielsweise außerhalb des Bereichs des aktuellen Programms liegen. Dann würde du eine Memory Access Violation Fehlermeldung (vom Betriebssystem) bekommen.
 

Neue Beiträge

Zurück