Verständnisproblem mit Pointern
-
Vielleicht hilft Dir folgendes:
(Variiert aber von Kompiler zu Kompiler,
daher das nur als Lehrmittel zu verstehen)Es gibt zwei Arten von Speicher, einmal der Stack und der Heap.
Der Stack ist der "Code" und "Daten(segment)" Speicher, der Heap
ist zumeistens der RAM.Wenn du eine Variable in einer Funktion definierst, also
void foo(void){ int x=0; //...
Wird dieser nicht wirklich IN der Funktion vorhanden sein. Der Kompiler schafft sich ein bisschen platz auf dem Stack. Und in diesem Platz wird der Wert 0 gespeichert.
Konkret: x ist ein platzhalter für eine Addresse im Datensegment, z.B. Addresse der Funktion - 32 bit (z.B. 0x11111111-0x01=0x11111110,.. ist ja ein 32 bit system... d.h. ein schritt im Offset bedeutet -32 bit)
Also im prinzip auch schon ein Zeiger auf eine Addresse VOR dem Funktionskopf (oder woanders auf m stack)
Definierst du nun Zwei variablen:
void foo(void){ int x=0; int * zeiger; //...
So liegt der Wert der x Variablen bei: Addresse der Funktion - 64 bit
Und die des Zeigers: Addresse der Funktion - 32 bit
Dabei spielt es noch keine Rolle ob die Variable zeiger ein Zeiger ist oder nicht.Macht man aber folgendes:
zeiger=&x;
Nun interpretiert der Kompiler die Variable zeiger immer noch als Variable,
dieser legt auf der Addresse: Addresse der Funktion -32 bit, den Wert:
Addresse der Funktion - 64 bit.Nun damit die Wirkung des Zeigers voll entfaltet werden kann:
int foo(void){ int x=0; int * zeiger; zeiger=&x; *zeiger=1; //das ist neu return x; }
Also hier interpretiert der Kompiler nun den Wert der Variablen Zeiger als eine
Addresse auf der dieser Arbeiten kann. D.h. die 1 wird nun in dem Speicher der
Variablen x abgelegt. Denn die Addresse von x ist ja der Wert der Variablen zeiger.
-
zeusosc schrieb:
Es gibt zwei Arten von Speicher, einmal der Stack und der Heap.
Der Stack ist der "Code" und "Daten(segment)" Speicher, der Heap
ist zumeistens der RAM.Der Stack ist auch RAM und wird nach dem LIFO-Prinzip* verwaltet.
Auf dem Stack sind Rücksprungadressen und (oft, aber nicht zwingend) Funktionsargumente und funktionslokale Variablen. Code liegt normalerweise in einem schreibgeschützten Bereich, muß also nicht zwangsläufig im RAM sein. Und der Heap ist das, was mit malloc(), realloc() und free() bedient wird.
-
int foo(void){ int x=0; int * zeiger; zeiger=&x; *zeiger=1 return x; }
seh ich das richtig das ich zeiger halt nur ohne stern sehe, wenn sie die adresse einer variablen zugeordnet bekommen, weil es ansonsten keinen sinn macht? So wie oben in dem code gegeben?
ich werde niemals etwas findet wie:
int * zeiger; zeiger=x;
oder?
-
Ich glaube ja dass das alles viel zu kompliziert ist. Bessere finde ich erstmal überhaupt nicht zwischen Variablen und Zeigern zu unterscheiden. Es gibt einfach nur Variablen, ein kleiner definierter Speicherbereich irgendwo egal ob Heap oder Stack. Eine Variable hat demzufolge eine Adresse und einen Wert (auch Zeiger).
Was man mit einer Variablen anstellen kann, also die Menge an Operationen die einem zur Verfügung stehen, bestimmt der Typ. Typen könnte man prinzipell auch weglassen. Das ist nur eine Interpretation der Daten.
Nen Zeiger ist also ne Variable vom Type Zeiger (mal so gesprochen). Folglich hat es Sinn den *-Operator zu definieren. Der referenziert nun das Objekt was sich an der Adresse findet, die der Zeiger als Variable speichert.
Kann es sein dass das Thema nur immer wieder Probleme bereitet weil fast jedes Buch das Thema als schwer ankündigt? Man somit falsch vorkonditioniert wird.
Auch versteh ich das vllt die Trennung von Typ und Zeiger bei solchen Deklarationen irreführend ist.
int x, *p;
der Typ von p ist für mich immer int* und die Variable heisst p.
(Edit: Natürlich kann man auch sagen p einmal derefernziert ist vom Typ int, mir ist das klar)Wenn man an anderer Stelle sowas macht.
x = *p;
dann ist das * hier ein unitärer Operator hat nix mit dem * oben in der Deklaration zu tun. Dieser unitäre Operator * hier liefert nun einfach den Wert des Objekts zurück, das sich an der Adresse befindet die in p gespeichert ist (und wird hier als int interpretiert weil p vom Typ int* ist).
Ich finde wenn man da mit dieser Denkweise ran geht ist das einfach zu verstehen und man hat auch keine Probleme mit z.B:
int****************** p;
-
Rammsteiner schrieb:
int foo(void){ int x=0; int * zeiger; zeiger=&x; *zeiger=1 return x; }
seh ich das richtig das ich zeiger halt nur ohne stern sehe, wenn sie die adresse einer variablen zugeordnet bekommen, weil es ansonsten keinen sinn macht? So wie oben in dem code gegeben?
ich werde niemals etwas findet wie:
int * zeiger; zeiger=x;
oder?
Japp weil genau das ihre Aufgabe ist. Adressen speichern.
-
also nochmal zusammenfassend.
Pointer sind variablen denen speicheradressen zugeordnet werden. Sie können keine Werte aufnehmen sondern nur auf die Werte zeigen die an dieser adresse stehen.
Ich benutze Pointer um beispielsweise mit hilfe einer Funktion mehrere Werte zurückzugeben, da eine Funktion ja bekanntlich nur einen Wert zurückliefern kann.
Funktionen, die variablen übergeben bekommen, bekommen eine kopie des wertes übergeben und arbeiten mit dieser kopie. Das nennt man dann**
call by value
**. Diese Kopie hat keinen Einfluss auf den Oirginalwert der Variablen.Funktionen, die Pointer bzw Variablen die auf eine speicheradresse zeigen übergeben bekommen, arbeiten mit den echten werten die auf diesen Speicheradressen stehen. Somit können diese verändert werden. Dies nennt man dann**
call by reference
**.#include <stdio.h> void funktion(int *zeiger){ //Der Funktion wird hier gesagt, das es sich bei ihrem Übergabeparameter um einen Pointer handelt. y=*zeiger; //Hier wird der Wert auf den der Pointer zeigt y zugewiesen. } int main(){ int *zeiger; //Pointer wird deklariert und bekommt den Namen Zeiger. x=1; y=0; zeiger=&x; //Pointer bekommt die Adresse von x. Dieser Pointer zeigt nun auf die Adresse von x. void funktion(zeiger); //Die Funktion bekommt die Adresse auf die der Pointer zeigt übergeben. printf("y=%i", y); }
Die Ausgabe müsste somit sein y=1!
-
Z schrieb:
zeusosc schrieb:
Es gibt zwei Arten von Speicher, einmal der Stack und der Heap.
Der Stack ist der "Code" und "Daten(segment)" Speicher, der Heap
ist zumeistens der RAM.Der Stack ist auch RAM und wird nach dem LIFO-Prinzip* verwaltet.
Auf dem Stack sind Rücksprungadressen und (oft, aber nicht zwingend) Funktionsargumente und funktionslokale Variablen. Code liegt normalerweise in einem schreibgeschützten Bereich, muß also nicht zwangsläufig im RAM sein. Und der Heap ist das, was mit malloc(), realloc() und free() bedient wird.Danke für deine Richtigstellung, sollte ich hinschreiben x~>ESP-0x01 ?
Der Heap muss auch nicht zwangsläufig im RAM sein und das der Callstack nach
Lifo arbeitet ist mir zwar klar, aber für den lernwilligen nicht nütze.Ansonsten kann er bei interesse ja mal hier
http://www.avr-modelleisenbahn.de/atmega8/2-5-stack-pointer-atmega8.htm
nachschauen,..
http://en.wikipedia.org/wiki/Call_stackgrüße
-
Nicht ganz,.. so müsste es sein...
#include <stdio.h> void funktion(int *zeiger, int &y){ //Der Funktion wird hier gesagt, das es sich bei ihrem Übergabeparameter um einen Pointer handelt. y=*zeiger; //Hier wird der Wert auf den der Pointer zeigt y zugewiesen. } int main(){ int *zeiger; //Pointer wird deklariert und bekommt den Namen Zeiger. x=1; y=0; zeiger=&x; //Pointer bekommt die Adresse von x. Dieser Pointer zeigt nun auf die Adresse von x. void funktion(zeiger,y); //Die Funktion bekommt die Adresse auf die der Pointer zeigt übergeben. printf("y=%i", y); }
-
Rammsteiner schrieb:
also nochmal zusammenfassend.
Pointer sind variablen denen speicheradressen zugeordnet werden. Sie können keine Werte aufnehmen sondern nur auf die Werte zeigen die an dieser adresse stehen.
Ich würde eher sagen: Sie können Werte aufnehmen, diese sind als Adressen im Speicher zu interpretieren.
Rammsteiner schrieb:
Ich benutze Pointer um beispielsweise mit hilfe einer Funktion mehrere Werte zurückzugeben, da eine Funktion ja bekanntlich nur einen Wert zurückliefern kann.
Ob das klappt hängt davon ab ob der Speicher auf den der Pointer Zeigt nach dem Funktionsaufruf weiterhin Gültigkeit hat (Thema Heap/Stack). Struct wären hier besser geeignet denke ich.
Edit: Ja, hab dich erst falsch verstanden.Rammsteiner schrieb:
Funktionen, die variablen übergeben bekommen, bekommen eine kopie des wertes übergeben und arbeiten mit dieser kopie. Das nennt man dann**
call by value
**. Diese Kopie hat keinen Einfluss auf den Oirginalwert der Variablen.Gilt auch für Pointer. Man könnte sagen Call by Reference gibt es in C nich wirklich.
Rammsteiner schrieb:
Funktionen, die Pointer bzw Variablen die auf eine speicheradresse zeigen übergeben bekommen, arbeiten mit den echten werten die auf diesen Speicheradressen stehen. Somit können diese verändert werden. Dies nennt man dann**
call by reference
**.Nein sie arbeiten auch mit der Kopie des Wertes des Pointers. Nur verbirgt sich dahinter eine Adresse. * dereferenziert also zum selben Objekt.
int x = 10; void funktion(int* zeiger) { zeiger = &x; } int main() { int y=6, *p; p = &y; funktion(p); printf("y=%d", *p); return 0; }
Ausgabe: y=6. p wird eben nicht auf x gebogen (call by value).
Rammsteiner schrieb:
#include <stdio.h> void funktion(int *zeiger){ //Der Funktion wird hier gesagt, das es sich bei ihrem Übergabeparameter um einen Pointer handelt. y=*zeiger; //Hier wird der Wert auf den der Pointer zeigt y zugewiesen. }
y ist nich deklariert. Sollte nicht kompilieren.
Rammsteiner schrieb:
int main(){ int *zeiger; //Pointer wird deklariert und bekommt den Namen Zeiger. x=1; y=0; zeiger=&x; //Pointer bekommt die Adresse von x. Dieser Pointer zeigt nun auf die Adresse von x. void funktion(zeiger); //Die Funktion bekommt die Adresse auf die der Pointer zeigt übergeben. printf("y=%i", y); }
Die Ausgabe müsste somit sein y=1!
Nein die Ausgabe ist y=0, da y in "main" nicht y in "funktion" ist.
-
Rammsteiner schrieb:
Die Ausgabe müsste somit sein y=1!
Die Ausgabe ist 0, da das y aus funktion() mit dem y aus main() gar nichts zu tun hat.
Es sei denn, x und y sind global. Du hast sie nirgends definiert.
Und in main() reicht dann
funktion(zeiger);
(ohne void davor)@zeusosc
Das ist hier C (nicht C++), darum mach mal das & vor dem y weg.
-
int x=1; int y=0; void funktion(int *zeiger){ y=*zeiger; } int main(){ int *zeiger; zeiger=&x; funktion(zeiger); printf("y=%i", y); }
Jetz müsste die die Ausgabe aber y=1 sein
...
Hmm, kann mir einer denn verraten was denn call by reference und call by value in c ist? Bitte nicht zu technisch werden. Ich muss irgendwie nur die Klausur nächsten Freitag schaffen falls eine Frage in die Richtung kommt was man darunter versteht ... Ich dachte ich habe das mit call by reference und call by value richtig verstanden so wie ich das oben egschrieben habe, aber wie es aussieht istd em nicht so ...
-
Huch, ich hoffe ich habe dich nicht zu dolle verwirrt.
Ich denke du hast das schon verstanden mit Call by Value und Call by Reference.
Call by Value: Kopie der Variablen
Call by Reference: Kopie eines Zeigers auf die Variable (Referenze auf etwas).
also zb
void Plus(int a, int b, int *c) { *c = a + b; // und jetzt hab ich auch erst gescheckt was du mit mehreren Rückgabewerten meintest :) } int main() { int a=1,b=2,c; Plus(a, b, &c); printf("c=%d, c); // Ausgabe: c=3 }
a und b by value und c by refrerence.
-
Call bei Reference hast du schon richtig verstanden, nur übergibst du ja wissentlich die Adresse von der Variablen.
Das macht der Compiler nicht versteckt wie z.B. in Pascal oder C++.Du übergibst als Wert die Adresse. Daher ist auch das ein Call by Value (halt der Adresse).
Wenn in C von 'Call bei Reference' die Rede ist, ist damit die Übergabe der Adresse gemeint.
-
hmm wäre einer von euch vielleicht mal so nett und würde mir eine klausuraufgabe stellen in der ich irgendetwas mit zeigern machen muss? In dem Buch was ich hier habe sind die aufgaben irgendwie zu einfach ...
Ich selbst bin zu unkreativ um mir eigene sachen auszudenken. ich wüsste nicht wie ich das mit den Zeigern weiter festigen kann.