PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Zeiger: wofür braucht man die?



Darkthief
23.10.2008, 16:27
Hi Leute.
Ich befasse mich gerade mit C++.
habe vorher auch PHP/MySQL und Java gemacht.
Jetzt bin ich hier auf Zeiger gestoßen.
Ich habe zwar verstanden, was sie machen und wofür man sie verwenden kann.
Aber wofür braucht man die jetzt eigentlich?
Gibt es irgendeinen fall, wo man einen Zeiger benutzen müsste?
Ich meine Java kommt doch auch ganz gut ohne Zeiger aus.

Am besten wäre ein Beispiel, kann c++ Code lesen.
Und ich will nochmal betonen, das ich die Funktionsweise eines Zeigers verstanden habe.

f!l3st0rm
23.10.2008, 17:13
http://www.hs-augsburg.de/~sandman/c_von_a_bis_z/c_000Vorwort_000.htm

-> Kapitel Zeiger.


Ich habe zwar verstanden, was sie machen und wofür man sie verwenden kann.
Aber wofür braucht man die jetzt eigentlich?

Bischen komisch, les Dir lieber nochmal durch, dann verstehst Du es auch.

Sprachen in denen es "keine Zeiger aus Programmierersicht" gibt kommen nicht unbedingt ohne Zeiger aus. Im Hintergrund werden auch oft Pointer verwendet.

LudenJupp
23.10.2008, 17:22
Ein sehr einfaches Beispiel wäre eine lineare Liste.
Jedes Element dieser Liste besitzt 3 (oder mehr) "Felder".
1: Zeiger auf das Element davor
2: Inhalt
3: Zeiger auf das Element danach
Allgemein sind sie sehr hilfreich bzw. notwendig um bestimmte, günstige Datenstrukturen zu erstellen.
Ein Baum eignet z.B. gut für eine relativ schnelle Suche. (http://www.activevb.de/tutorials/tut_zeiger/zeiger5.html)

Pekel
23.10.2008, 18:08
Markieren Zeiger nicht auch das Ende von Feldern?

mo0wm
23.10.2008, 20:09
Ganz einfach,
um eine Klasse an eine Funktion zu übergeben ist viel technischer Aufwand nötig, wenn man nur die Adresse einer Klasse übergibt geht das fix.


Durch Zeiger kann man auch mehrere Variablen/Klassen in einer Funktion verändern, indem man sie über die Parameter angibt, und nach der Funktion sind die Variablen dann "verändert".

f!l3st0rm
23.10.2008, 20:16
mo0wm, zur Vollstädigkeit wärn hier die Stichwörter "Call By Value" und "Call By Reference" noch zu erwähnen.

Call By Value: Übergibt eine Kopie (Variable / Whatever). Es wird also mit einer Kopie gearbeitet.

Call By Reference: Es wird nur die Adresse übergeben - man arbeitet mit dem Original.

Deathskull
23.10.2008, 20:42
Nicht zu vergessen ist bitte auch, das man damit den Heap Speicher nutzen kann! - was sich bei großen Programmen wirklich bemerkbar macht. (und auf den Heap zugreifen geht auch [fast] nur mit Zeigern )
Aber keine Sorge Darkthief, ich glaube kein C++ - Beginner hat den Verwendungszweck von Zeigern am Anfang verstanden, ist echt so :)
Ich weiß noch wie ich da saß, und mich fragte wozu ich diese scheiß Dinger überhaupt brauchen könnte^^
Am besten einfach alles so hinnehmen, das Thema Zeiger gründlich lernen und ab jetzt bei jeder neu gelernten Sache darüber nachdenken, wie Zeiger diese vereinfachen oder verbessern könnten. Damit kommt dann die "Erleuchtung" von selbst nach einiger Zeit :wink:

Gruß Death

blackberry
24.10.2008, 01:08
Zeiger sind zur Manipulation von Speicherblöcken gedacht, wobei theoretisch jede beliebige Adresse manipuliert werden kann (praktisch wird dies natürlich von den momentan auf dem Markt erhältlichen Betriebsystemen, die im "Safe-Mode" laufen, überwacht und ggf. verhindert).

Ein gutes Beispiel ist wenn du einen Integer hast (lokal vereinbart) und diesen in einer Funktion verwenden willst und danach die Variable mit dem Inhalt haben willst, den er am Ende der Funktion A (nennen wir sie einfach mal A) besaß.

Für einen "normalen" Funktionsaufruf werden die Parameter (meißtens in umgekehrter Reihenfolge) auf den Stack "gepusht" ([Intel-]ASM: push <WERT/REGISTER/ADRESSE> : legt <WERT/...> auf den Stack) und dann von den Funktionen "gepopt" ([Intel-]ASM: pop <REGISTER/ADRESSE> : holt den nächsten Wert vom Stack, größe wird durch die größe des Parameters bestimmt).

Das wiederum führt dazu, dass die Funktionen einen Lokalen wert erhalten -> eine Änderung des Integers I, in Funktion A, hat keine Änderung des Integers I in der Funktion main zu folge.

Beispiel 1; Code:

#include <stdio.h>


void A(int i)
{
i++;
}

int main(void)
{
int i = 1;
printf("I besitzt den Wert %d\n", i);
A(i);
printf("I besitzt den Wert %d\n", i);
return 0;
}

Beispiel 1; Ausgabe:

blackberry@system:~/Desktop$ gcc a.c -o a; ./a
I besitzt den Wert 1
I besitzt den Wert 1

Nun sagen wir mal, wir übergeben anstelle des Integers I einen "Zeiger auf I".

Beispiel 2; Code:

#include <stdio.h>


void A(int *i)
{
(*i)++;
}

int main(void)
{
int i = 1;
printf("I besitzt den Wert %d\n", i);
A(&i);
printf("I besitzt den Wert %d\n", i);
return 0;
}

Beispiel 2; Ausgabe:

blackberry@system:~/Desktop$ gcc a.c -o a; ./a
I besitzt den Wert 1
I besitzt den Wert 2

Die Klammern bei "(*i)++;" sind in dem Fall notwendig, weil der Inkrementationsoperator in der Hirarchie höher steht als der unäre Operator, was ohne Klammern (also so: "*i++;") eine Inkrementation des Zeigers und anschießende Anwendung des unären Operatoren zur Folge hätte -> keine Wertänderung. Mit den Klammern wird zuerst auf den Speicherwert von I zugegriffen und dieser dann um eins erhöht.
Zu bemerken ist dementsprechend auch, dass Zeiger wiederum Variablen sind und eine Änderung des Zeigers, ähnlich wie bei Beispiel 1, keinen Effekt auf den Zeiger in einer anderen Funktion hat (dafür wäre ein Zeiger auf einen Zeiger auf ... notwendig).

Also ein sehr großer nutzen von Zeigern besteht darin, dass du bei einer Funktion *indirekt* mehr als einen Wert, mit verschiedenen Datentypen zurückgeben kannst.
Als Praxisbeispiel schau dir doch mal MSDN an (zB. http://msdn.microsoft.com/en-us/library/aa364992(VS.85).aspx ) - du wirst bei den Parametern sicher sehr viele "_out DWORD ..." finden. (DWORD ist ein typedef für "unsigned long int")

Zum Thema "Java kommt auch ohne aus": Java kommt im Vergleich zu C++ mit sehr wenig aus (und hat meiner Meinung nach die Objektorientierung ein bisschen zu weit in den Hals bekommen). Nicht umsonst wird deshalb auch bei der Programmierung von Computerspielen und Ähnlichem C++, Java vorgezogen.


mfG. BlackBerry

Darkthief
24.10.2008, 10:07
danke leute,
ihr letzten vier habt mir sehr geholfen.
ich versteh nur nicht ganz, warum (*i)++; den wert, und nicht die adresse ändert.
Eigentlich müsste doch *i eine hex zahl (die adresse von i) ausgeben und diese dann inkrementiert werden, also *i würde auf den falschen byte zeigen, oder?
oder ist *i in diesem fall sozusagen *(*i)?

blackberry
24.10.2008, 13:09
ich versteh nur nicht ganz, warum (*i)++; den wert, und nicht die adresse ändert.

Um mal -[RiDER]- zu imitieren bringe ich hier mal ein Zitat aus der deutschen Übersetzung
des Buches "The C Programming Language" (deutscher Titel: "Programmieren in C")
von Brian W. Kernighan und Dennis M. Ritchie (zweite Auflage):
"Der unäre Operator * dient für Verweise und liefert das Objekt oder die Funktion,
auf die sein Operand zeigt. Das Resultat ist ein L-Wert, wenn der Operand ein Zeiger auf
ein Objekt ist, dessen Typ arithmetisch, eine Struktur, eine Union oder ein Zeigertyp ist.
Hat der Operand den Typ 'Zeiger auf T', so hat das Resultat den Typ T." (S. 197, Anhang "A.7.4.3 Inhaltsoperator")

In diesem Fall ist "Zeiger auf T" ein "Zeiger auf Int". Wie im Zitat aufgeführt
gibt "*(Zeiger auf T)" als Resultat den Typ T, also in unserem Fall "int".
Dieser von dem unären Inhaltsoperator zurückgegebene Wert benutzt die Speicheradresse,
die in "Zeiger auf Int" beschrieben wurde. Eine Änderung
dieses Wertes und somit dem Inhalt seiner Speicherzelle hat darum auch eine Änderung
aller Werte zufolge, die diese Speicherzelle mit dem Wert teilen.
Dies betrifft die Variable "i" in "int main(void)".

Die Änderung dieses Wertes wird in unserem Beispiel durch eine simple Inkrementation
durchgeführt.
Auch möglich wäre folgende Syntax:
*i += 1;
bzw.
*i = *i + 1;


Eigentlich müsste doch *i eine hex zahl (die adresse von i) ausgeben

Wie gesagt: in Funktion A ist "I" bereits ein Zeiger und speichert somit bereits eine Adresse
(genauer gesagt: die Adresse von "int i" in der main-Funktion).
Somit ergibt sich, dass "i" und "&(*i)" äquivalent sind.


Ich hoffe, das hat dir geholfen :)
mfG. BlackBerry

-[RiDER]-
25.10.2008, 12:34
Hi :D

In dem ersten Buch über C, das ich gelesen habe stand, was ich gut einleuchtend fand. Es ist vielleicht nicht sooooo fachmännisch, aber mir hats geholfen. Ich kanns grad nicht zitieren (GreetZ @ BlackBerry :D ), aber sinngemäß lautete es etwa wie folgt (wir gehen davon aus, dass i als Zeiger auf irgendeine Variable von irgendeinem Typen (von mir aus T) deklariert ist, sagen wir foo):

&i gibt die Adresse des Zeigers an (den Ort, wo sich der Zeiger im Speicher befindet).
i gibt die Adresse an, auf die der Zeiger i zeigt (den Ort, wo foo im Speicher steht).
*i gibt den Wert der Variable an, auf die i zeigt, also den Wert von foo.

Als Bild vielleicht so, wobei alles außer "Wert von foo" (was der Wert der Variable sein soll, auf die i zeigt) Adressen sind:

+----+
| |
| &i |--+
| | | +---+
+----+ | | |
+-->| i |--+
| | | +----+
+---+ | | |
+-->| *i |----+
| | |
+--------------+ +----+ |
| | |
| Wert von foo |<-------------+
| |
+--------------+
Wenn man sich das so vorstellt, kann man sich auch denken, wie man den Kram verschachteln kann, z.B. als &((*)(&*i)), zumindest isses bei mir so :)

Vielleicht hilfts... ;)
GreetZ RiDER :D :D :D

EDiT: Vielleicht noch zu erwähnen, dass i == &foo und &*i == &foo, also i == &*i ist. :D
Und *&i == &*i, wobei das erste bedeutet: "Wert dessen, was in 'Adresse von i' (also &i) steht", und das zweite: "Adresse des 'Ortes, an dem der Wert der Variable, auf die i zeigt (also foo)' (also *i) steht" und beides entspricht i.
Man kann also beliebig auf irgendwelchen Ebenen herumhopsen, die sogar noch nichtmal existieren müssen, so z.B. &&&&&*****i == i, fast schon wie mit Brüchen rechnen... und ich laber schon wieder so fett...

Darkthief
26.10.2008, 19:03
yap, das Buch aus dem du zitiert hast hab ich auch.
Von daher hats nicht wirklich geholfen.
das der unäre Operator den inhalt der Variable auf die gezeigt wird ausgibt und der & Operator die Adresse des Zeigers ausgibt hab ich verstanden.
Was ich jetzt nur wissen wollte:
ist
(*zi) += 5;
dasselbe wie
i += 5;
?

außerdem:
(*i)++;
dürfte doch garnichts verändern, da ja das postfix increment verwendet
wird.

edit:
ich habs grat getestet und versteh jetzt echt nicht, warum (*i)++ was verändert und i++ nicht -.-

blackberry
27.10.2008, 18:26
Was ich jetzt nur wissen wollte:
ist
(*zi) += 5;
dasselbe wie
i += 5;
?

Wenn ich davon ausgehen darf, dass "zi" ein Zeiger auf "i" ist, dann sind beide oben beschriebenen Ausdrücke äquivalent.


ich habs grat getestet und versteh jetzt echt nicht, warum (*i)++ was verändert und i++ nicht -.-

Ich hab's selbstverständlich auch getestet bevor ich es gepostet habe (kompilieren kostet mich sowieso nur einen Befehl im Terminal).


außerdem:
(*i)++;
dürfte doch garnichts verändern, da ja das postfix increment verwendet
wird.

Wir fassen zusammen:
zi++;
erhöht den Zeiger um eins.
ASCII-Grafik (RiDER ist mein Idol :D)

---+ zi (vom Typ int *) zeigt auf...
|
_ # _ _ _ _ _ Speicher

zi++;

-----+ zi (vom Typ int *) zeigt auf...
|
_ _ # _ _ _ _ _ Speicher

*zi;
Gibt eine Integer-Variable zurück, welche den Speicherbereich mit "i" Teilt.
Eine Veränderung dieses Speicherbereies (zB. durch inkrementation) hat eine Änderung aller Variablen zu folge, die diesen Speicherbereich teilen.

Kurz gesagt:
*zi und i sind äquivalent, weil sie sich den selben Speicherbereich teilen.


PS: sorry für die späte Antwort - der Topic ist irgendwie unter den anderen untergegangen :)


mfG. BlackBerry

Darkthief
27.10.2008, 23:13
ok, aber bei deinen beiden codeschnipseln oben hat i++ i nicht bleibend verändert (*i)++ aber schon.

zu deiner ascii grafik:
würde das bedeuten, das zi++ (ja, zi ist bei mir ein zeiger) den zeiger auf einen anderen punkt im speicher zeigen lässt, er also nichtmehr auf i zeigt? (angenommen er hat vorher auf i gezeigt)

blackberry
27.10.2008, 23:36
ok, aber bei deinen beiden codeschnipseln oben hat i++ i nicht bleibend verändert (*i)++ aber schon.

zu deiner ascii grafik:
würde das bedeuten, das zi++ (ja, zi ist bei mir ein zeiger) den zeiger auf einen anderen punkt im speicher zeigen lässt, er also nichtmehr auf i zeigt? (angenommen er hat vorher auf i gezeigt)

Ich gehe mal zuerst auf den zweiten Abschnitt ein:
nehmen wir an wir haben folgenden code:

int i = 1234;
int *zi = &i;
Dann speichert zi die Adresse von i.
Inkrementieren wir nun zi (also: zi++; bzw ++zi;) ergibt sich folgender Zusammenhang:
zi = &i + 1;
zi zeigt also auf die Adresse von i plus eins.

Zu deiner ersten Frage:
gehen wir wieder von der Funktion A aus meinem ersten Beispiel (eigentlich das weite, aber in meinem ersten post in diesem Topic...) aus, wobei wir mal der Einheitlichkeit zu liebe von einem Parameternamen zi ausgehen.
Es ergibt sich folgender Funktionsprototyp:
void A(int *zi);
zi ist also eine Variable mit dem Gültigkeitsbereich void A(int *), ist also nur innerhalb von A gültig und wird sich nach dem Rücksprung zum Aufrufer der Funktion wieder "auflösen", sprich seinen Wert verlieren.
Eine Inkrementation verändert also keine anderen Variablen, weil diese Variable (Zeiger auf i) einen eigenen Speicherbereich besitzt (welcher die Adresse von i enthält).
Dem zufolge verändert "zi++;" die Variable in der main-Funktion nicht.


mfG. BlackBerry

Darkthief
28.10.2008, 00:16
ok danke.
Das mit dem Gültigkeitsbereich hatte ich vergessen bzw. war mir da nie so sicher.

jetzt bleibt nurnoch meine Frage:
ist i++ dasselbe wie ++i ?

-[RiDER]-
28.10.2008, 12:43
Hi :D

Ich gehe mal zuerst auf den zweiten Abschnitt ein:
nehmen wir an wir haben folgenden code:

int i = 1234;
int *zi = &i;
Dann speichert zi die Adresse von i.
Inkrementieren wir nun zi (also: zi++; bzw ++zi;) ergibt sich folgender Zusammenhang:
zi = &i + 1;
zi zeigt also auf die Adresse von i plus eins.
Nope.

zi zeigt dann auf die Adresse von i plus sizeof(int) (wenn i wie bei Dir vom Typ int ist), also normalerweise vier. ;)
Die Operation heißt also zi = &i + sizeof(int); oder in diesem Fall vielleicht sogar zi = &i + sizeof(i);, letzteres ist aber eher unwahrscheinlich...


jetzt bleibt nurnoch meine Frage:
ist i++ dasselbe wie ++i ?
Nein.

if(++i == i + 1)
/* true, es wird zuerst das Inkrement ausgeführt und dann der Ausdruck überprüft */
if(i++ == i + 1)
/* false, es findet zuerst die Überprüfung (i == i + 1) statt und dann das Inkrement */
Genauso gilt:
int i = 0, j = 0;
printf("%d", i++); // liefert 0
printf("%d", ++j); // liefert 1

GreetZ RiDER :D :D :D