Zeiger

  • Themenstarter Themenstarter gelöscht
  • Beginndatum Beginndatum
G

gelöscht

Guest
Hi @ all

Ich habe mir vor drei Tagen ein Buch über C zugelegt.
Nun bin ich am Kapitel Zeiger angelangt.
Ich verstehe zwar wie man sie deklariert und initialisiert, aber ich habe keinen blassen Schimmer wozu die gut sein sollten.
Wäre schön wenn ihr mir weiterhelfen könntet.
 
Mit Zeigern bekommt man den Speicherort und -inhalt eines Objektes. Auf die im RAM abgelegten Objekte oder Variablen kann man mit Zeigern zugreifen, ohne sie kopieren zu muessen.
Beim sog. Call-by-Reference werden bei Funktionen oft Zeiger eingesetzt, da das Objekt direkt veraendert wird.
Wird etwas "normal" an eine Funktion uebergeben, nimmt man das Call-by-Value Verfahren, bei dem die Variablen kopiert werden, die eigentlichen Werte der uebergebenen Variablen aber nicht beruehrt werden...


Ich hoffe mal, Nornagest o.A. habens etwas besser formuliert...
 
ch hoffe mal, Nornagest o.A. habens etwas besser formuliert...
Na dann will ichs mal versuchen :)

Also wie Nookie schon gesagt hat enthält ein Zeiger eien Speicheradresse.

In der Schule hatten wir Zeiger damals im Zusammenhang mit Listen, weil man da merkt wofür man sie braucht und wie sie funktionieren. (kannst ja mal den Datentyp Liste impementieren, sollte ne schöne Übung sein :) )

Zeiger braucht man z.B., wenn man dynamisch mit Speicher hantiert.
Du kannst z.B. wenn du einen String einlesen willst einen relativ großen Buffer bereitstellen und wenn du dann die genaue Länge des Strings hast einen neuen Buffer von der betreffenden Länge bereitstellen und über einen Zeiger darauf zugreifen.
Oder bei einer Liste muss man, wenn man ein neues Element einfügt nur ein wenig mit den Zeigern jonglieren, anstatt größere Kopieraktionen anzufangen.

Und wie Nookie auch schon gesagt braucht man sie natürlich für Call by Reference, was sehr praktisch sein kann, wenn man entweder wie Nookie erwähnt hat Variablen direkt in einer Funktion verändern will anstatt nur mit Kopie (wie bei Call by Value) zu arbeiten. Außerdem kann es sehr resourcenschonend sien, wenn man z.B. sehr große Objekte übergibt, da diese bei Call by Value kopiert werden müsssen, während man bei Call by Reference nur entsprechende Zeiger übergibt.
 
ok danke...

@nornagest
Was meinst du denn mit den Listen???
 
Was bei Funktionen nicht ganz klar wurde:

Wenn du zb. aus der funktion main einen wert hast, der geändert werden muss, aber das so viel code bedeutet das du diesen code in eine Funktion auslagern willst geht das auch nur über zeiger.

Du übergibts funktion B einen Zeiger auf einen Wert in Funktion A. So kannst du in B diesen Wert beliebig ändern und musst auch aus B herraus keine Rückgabe liefern.

mfg
 
Moin,

Pointer und Listen haben insofern miteinander zu tun, daß in einer verketteten Liste ein Pointer in einem Element der Liste auf das nächste Element dieser Liste zeigt. So bastelst du dir dynamische Datentypen.

Ein Beispiel:

Du willst Adressen speichern. Könnte man ja so machen:

char adressen[256][80];

und mit

strcpy(adressen[3], "Fritze#Müller#Heimchenweg 12#04711 Adorf");

die Werte eintragen.

Nachteil dabei ist, du bist auf 80 Bytes pro Adresse beschränkt. Hast du eine längere, paßt sie nicht rein. Hast du eine kürzere Adresse, verschwendest du Platz.

Also macht man das dynamisch mit Pointern:

typedef struct adressen_struct{
char *adresse; /* <= Pointer auf Character-Feld für die Adresse */
void *next; /* <== Pointer auf das nächste Element der Liste */
} adrstruct;

adrstruct *adr; /* Der Pointer auf die gesamte Liste */
adrstruct *adr2; /* und noch ein zweiter zum zwischenspeichern `*/

adr=malloc(sizeof(adrstrucct)); /* Legt ein Element an, der Pointer adr zeigt auf den Speicherbereich */

adr->adresse=malloc(strlen(neue_adresse)+1); /* Platz für die neue Adresse in der wirklichen Länge */
strcpy(adr->adresse, neue_adresse);
adr->next=NULL; /* Hier ist die Liste zu ende. */

adr2=malloc(sizeof(adrstruct));; /* und noch eine adresse */
adr2->adresse=malloc(strlen(neue_adresse)+1); /* Platz für die neue Adresse in der wirklichen Länge */
strcpy(adr2->adresse, neue_adresse);
adr2->next=adr; /* zeigt jetzt auf das 1. Element der Liste */
adr=adr2; /* und vorne in die Liste eingehängt. */

Durchsuchen tut man eine solche Liste dann wie folgt;

adr2=adr;
for(;;){
if(adr2==NULL) break; /* Liste zu ende? Raus! */
printf("Adresse=>%s<\n", adr2->adresse);
adr2=adr2->next; /* zum nächsten (NEXT) Element der Liste springen */
}


Also, man definiert sich einen eigenen Datentyp mit typedef{}. Davon ist noch kein Speicher belegt, aber der Compiler kennt jetzt diese Datentype adrstr.

Mit adrstr *adr; legt man sich einen Pointer an. Der belegt 4 Bytes im Speicher und zeigt zur Zeit in den Wald.

Mit adr=malloc(..) holst man sich ein Stück Speicher (Memory Allocation=malloc) und malloc() liefert einen Zeiger auf das Stück Speicher. adr zeigt jetzt auf dieses Stück, auf dessen Anfangsadresse!!!
Das Stück Speicher ist so groß, wie der selbst definierte Datentyp ( sizeof(adrstr)), also 8 Bytes. 4 Bytes für den Pointer adresse und 4 Bytes für den Poiner next.

mit adr->adresse spricht man die ersten 4 Bytes des Speichers an, auf den der Pointer adr zeigt. Nämlich den, den malloc() für uns reserviert hat.

Zu beachten ist, daß C nicht kontrolliert, ob du über den reservierten Speicher hinausschreibst. Wie denn auch, ein Pointer ist nur ein Zeiger, eine Adresse. Ein Pointer kennt keine Länge.

Also, bei Pointern immer schön darauf achten, daß man nicht mehr speichern will, als das reservierte Stück Speicher wirklich groß ist. Sonst schepperts nämlich, sprich Core-Dump.

Hoffe, dir mehr Klarheit als Verwirrung bereitet zu haben. Wenn nicht, tut's mir leid :D
 
@TimeShock
Normagest wird wohl dynamisch, verkettete Listen meinen. Es gib sehr viele Einsatzbereiche für solche Listen. Ihr Hauptsinn liegt allerdings in der effizienten Speicherverwaltung eines Programms und sie sind immer dann sinnvoll wenn sich die benötigte Speicherplatzgrösse erst während der Laufzeit ergibt.

Die einfachste Form einer solchen Liste würde man folgendermaßen erstellen. Zunächst deklariert man eine neue Klasse bzw einen Strukturtyp. Innerhalb dieser Klasse deklariert man neben den eigentlichen Nutzdaten/Membervariablen nun einen Zeiger, welcher den gleichen Typ hat wie die Klasse selbst. Mit diesem Zeiger kann man nun eine neu angelegte Instanz der Klasse referenzieren. Innerhalb dieser neuen Instanz befindet sich natürlich wieder ein Zeiger an den man weitere Klassen anhängen kann. Auf diese Wiese kann und muss man sich über die Zeiger von Instanz zur Instanz "durchhangeln" und genau darin liegt auch der Nachteil dieser Methode. Die Vorteile liegen in der effizienteren Speicherplatznutzung und in der Tatsache, dass die Elemente dieser Liste nicht durch Kopieren der Nutzdaten sortiert werden, sondern durch "verbiegen" der Zeiger. In der Praxis findet man allerdings häufiger Klassen mit 2 oder mehr Zeigern auf andere Klassen. Man kann halt alle möglichen komplexen Datenstrukturen damit darstellen.

Ich würde dir den Rat geben dir via Google eine Dokument zu suchen, bei der dieses Thema durch eine Grafik veranschaulicht wird.
 
als beispiel für eine doppelt verkettete liste kann man sich glaub ich ganz gut die forward/back-buttons in einem browser vorstellen.. jedes listenelement hat nen wert (die URL) und nen zeiger auf das nächste element (forward) und das vorherige (back). der listen anfang wäre dann die erste url (hat dann natürlich keinen vorgänger)

--theVoid
 
Zurück
Oben