PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C++ Pointer-Rätsel


Threadstarter
2012-08-27, 18:04:25
int* foo(int* &referenceToPointerToInt)
{
int tmp = 3;
referenceToPointerToInt = &tmp;
return &tmp;
}

int main(int argc, char* argv[])
{
int tmp = 2;
int* pointerToInt_Argument = &tmp;
int* pointerToInt_Returned = foo(pointerToInt_Argument);

std::cout << "arg: " << pointerToInt_Argument << " " << *pointerToInt_Argument << std::endl;
std::cout << "ret: " << pointerToInt_Returned << " " << *pointerToInt_Returned << std::endl;

return 0;
}


Output:

arg: 0023FE08 3
ret: 0023FE08 1450931176


WTF?
Mit letzterem Ergebnis hätte ich ja gerechnet, denn tmp innerhalb der Funktion existiert zum Zeitpunkt des cout's ja nicht mehr, weshalb der Pointer irgend einen Speicher dereferenziert und somit die Ausgabe eben '1450931176' ist.

Aber warum ist das Ergebnis des übergebenen Pointers denn bitte noch immer '3', obwohl die Speicheradresse die selbe ist?

Gast
2012-08-27, 18:54:43
Weil std::cout den Speicher verändert. Wenn du vor

std::cout << "arg: " << pointerToInt_Argument << " " << *pointerToInt_Argument << std::endl;

z.B. noch "Hello World" ausgibst ist die erste Ausgabe auch nicht mehr 3.

Threadstarter
2012-08-27, 19:46:27
Weil std::cout den Speicher verändert. Wenn du vor

std::cout << "arg: " << pointerToInt_Argument << " " << *pointerToInt_Argument << std::endl;

z.B. noch "Hello World" ausgibst ist die erste Ausgabe auch nicht mehr 3.

Tatsächlich ... weshalb ist dem so?

Exxtreme
2012-08-27, 19:59:54
Tatsächlich ... weshalb ist dem so?
Sobald die Funktion foo verlassen wird ist die lokale Variable tmp = 3 nicht mehr definiert und kann überschrieben werden. std::cout überschreibt das Ding wohl.

Gast
2012-08-27, 20:02:19
Tatsächlich ... weshalb ist dem so?

Deine Zeiger weisen alle auf Variablen, welche innerhalb der Funktion "foo" auf dem Keller alloziert wurden.
Per Definition ist dieser Speicher nur gültig solange du dich in der genannten Funktion befindest.
Deine Zeigen weisen also auf undefinierten Regionen. Die Speicherzellen können das gewünschte enthalten, oder im Falle beim aufrufen einer weiteren Funktion, "zufällige" Werte.

Dr.Doom
2012-08-27, 20:06:43
int* foo(int* &referenceToPointerToInt)
{
int tmp = 3;
referenceToPointerToInt = &tmp;
return &tmp;
}


Aber warum ist das Ergebnis des übergebenen Pointers denn bitte noch immer '3', obwohl die Speicheradresse die selbe ist?*grübel* Ich bin jetzt nicht der C-Guru, aber in der Funktion lässt man referenceToPointerToInt doch nur auf eine andere Speicheradresse weisen (an der "3" steht"). Die Adresse, an der der Zeiger selber steht, wird nicht verändert und hat noch den Wert aus main von vor dem Funktionsaufruf. :confused:

Ectoplasma
2012-08-27, 20:41:29
... in der Funktion lässt man referenceToPointerToInt doch nur auf eine andere Speicheradresse weisen (an der "3" steht").

Wieso auf eine "andere" Adresse? referenceToPointerToInt zeigt auf tmp und tmp liegt auf dem Stack. Die Adresse von tmp ändert sich natürlich nicht, auch
nicht nach Verlassen von foo. Was sich aber ändert ist der Wert an der Adressposition von tmp. Spätestens nach dem ersten Aufruf von std::cout ist die 3 nicht mehr vorhanden.

referenceToPointerToInt ist eine Referenz auf einen Pointer. Das ist etwas anderes als ein einfacher Pointer.

Marscel
2012-08-27, 21:08:41
Etwas einfacher ausgedrückt, du rufst ja ne Funktion auf, operator<<. Deren Stackframe - oder der weiterer interner Aufrufe - macht einfach da weiter, wo gerade noch foo ausgeführt wurde. Und an irgendwo in dessen Interna wird dann etwas später an der Stelle, wo die 3 war, was anderes hingeschrieben, sodass es beim nächsten Mal futsch ist, also was anderes da steht.

Dr.Doom
2012-08-27, 21:12:35
Ok, ich bin verwirrt, den Zeigerkram werde ich wohl nie stehen. :redface:

Marscel
2012-08-27, 21:23:00
Ok, ich bin verwirrt, den Zeigerkram werde ich wohl nie stehen. :redface:

Stell dir vor, du übergibst einer Funktion einen regulären Pointer, heißt: du kopierst den Wert, also die Adresse, ins Argument. In dieser Funktion benutzt du dann z.B. realloc(). Das kann dazu führen, dass der Kram an eine andere Stelle kopiert wird, sodass der Zeiger nun woanders hin zeigt. Davon kriegt die aufrufende Funktion aber nichts mit, weil die eine Kopie vom Zeiger übergeben hat, nicht ihn selbst. Wenn man den danach nun benutzt, passieren komische Dinge.

Lösung: anstatt einer Adresskopie übergibst du die Adresse, an der der Pointer sitzt (in C mit int**, oder in C++ auch mit int*&), so brauchst du keine Rückgabwerte, sondern modifizierst ihn global.

In dem Beispiel wird also die Adresse, die in pointerToInt_Argument steht, im Aufruf von von foo geändert. Weil referenceToPointerToInt ja ein Alias davon ist. In den wird dann die Adresse des stack-lokalen tmp gelegt.

Exxtreme
2012-08-27, 21:43:42
Ok, ich bin verwirrt, den Zeigerkram werde ich wohl nie stehen. :redface:
Das ist eigentlich kein Zeiger-Problem sondern ein "Wann ist eine Variable gültig/definiert?"-Problem. Die Variable tmp ist ausschliesslich innerhalb der Funktion foo() gültig. Sie wird innerhalb von foo() angelegt und sobald foo() zuende ist, wird die Speicheradresse von tmp zum Abschuss freigegeben. Dann kommt das erste std::cout und überschreibt die Adresse, an der tmp früher war mit was anderem. Dummerweise wissen davon die Zeiger/Referenzen nix. Die zeigen immer noch auf die Adresse von tmp, in der jetzt was völlig anderes stehen kann.