IT-Academy Logo
Sign Up Login Help
Home - Programmieren - C++ - Zeiger in C



Zeiger in C

Zeiger (oder englisch "pointer") sind ein sehr mächtiges Element der Programmiersprache C, sie ermöglichen komplexe Datenstrukturen, geringen Speicherbedarf und eine hohe Ausführungsgeschwindigkeit. Sie sind jedoch nicht ganz einfach in der Anwendung, Fehler in der Handhabung mit Zeigern können Fehler in der Programmausführung zur Folge haben.


Autor: Patrick Bucher (paedubucher)
Datum: 15-12-2007, 21:18:57
Referenzen: C - kurz und gut, O'Reilly-Verlag
Schwierigkeit: Fortgeschrittene
Ansichten: 10653x
Rating: 7 (1x bewertet)

Hinweis:

Für den hier dargestellte Inhalt ist nicht der Betreiber der Plattform, sondern der jeweilige Autor verantwortlich.
Falls Sie Missbrauch vermuten, bitten wir Sie, uns unter missbrauch@it-academy.cc zu kontaktieren.

[Druckansicht] [Als E-Mail senden] [Kommentar verfassen]



Ein Zeiger kann eine Adresse des Speichers aufnehmen, hinter welcher sich eine Variable oder eine Funktion befindet. Ein Zeiger muss immer den gleichen Typ haben, wie das Element auf das er zeigt. In Wirklichkeit ist aber jeder Zeiger vom Datentyp int, da Speicheradressen immer als Ganzzahlen angegeben werden.

Die Deklaration

Ein Zeiger wird sehr ähnlich wie eine gewöhnliche Variable deklariert. Es gibt nur den Unterschied, dass vor dem Variablennamen ein Stern (*) stehen muss:

[Datentyp] *[Variablenname] = [Initialisierungswert];

Dazu ein Beispiel:

int *f, *g;

Zeiger können auch in derselben Zeile wie normale Variablen des gleichen Typs deklariert werden. Dabei kann auch zwischen Zeigern und normalen Werten abgewechselt werden.

int a = 0, b = 0, *c, d = 0, *e;

Falls man dem Zeiger nicht schon bei der Deklaration auf eine Variable zeigen lassen will, sollte man ihn unbedingt mit dem Wert 0 belegen. Es kommen nur die Werte 0, oder direkt eine Adresse einer Variablen in Frage - andere Werte führen zu Fehlern!

Die Initialisierung

Zeiger können wie oben beschrieben auf Speicherzellen verweisen. Dies ist möglich, indem in einem Zeiger die Nummer der Speicherzelle angegeben wird, auf die gezeigt werden soll. Da man den Inhalt seines Arbeitsspeichers aber in der Regel nicht kennt, macht es wenig Sinn, diese Speicheradressen direkt als Zahlen in den Code einzufügen. Die Speicherzelle könnte möglicherweise bereits verwendet werden oder gar nicht existieren.

float *p = 194324; // sollte vermieden werden

Eine Ausnahme bildet der numerische Wert 0. Dieser steht für einen Zeiger ins "leere", es wird nicht wirklich auf den Speicher verwiesen.

Mit einem Zeiger der auf 0 zeigt kann man jedoch noch nicht viel anfangen, er muss zuerst auf eine Variable verweisen. Zeiger können bekanntlich nur Speicheradressen aufnehmen. Damit man an die Speicheradresse einer Variablen herankommt, verwendet man den Adressoperator (&). Dieser gibt die Speicheradresse zurück, welche man nun in einer Zeigervariable abspeichern kann.

int a = 5, *p = 0;
p = &a;

Der Zeiger p (vom Datentyp int) verweist nun auf die Variable a (ebenfalls ein int).

Der Zugriff

Der eigentliche Wert eines Zeigers ist eigentlich selten bis nie von Interesse, wichtiger ist der Wert der Variablen, auf welche der Zeiger verweist. Um auf den Wert zuzugreifen, auf welchen der Zeiger verweist, kommt erneut der Stern zum Einsatz:

int a = 5, *p = 0;
p = &a;
printf("%i", p); // gibt die Speicheradresse von a aus
printf("%i", *p); // gibt 5 aus

Sobald der Stern dafür eingesetzt wird, um auf den Wert hinter einen Zeiger zuzugreifen, wird dieser als Dereferenzierungsoperator bezeichnet. Der Wert kann mithilfe des Dereferenzierungsoperator nicht nur gelesen, sondern auch überschrieben werden:

int a = 5, *p = &a;
printf("%i", *p); // gibt 5 aus
*p = 16
printf("%i", *p); // gibt 16 aus
printf("%i", a); // gibt auch 16 aus - der Zeiger hat auf a zugegriffen!

Ein Zeiger kann während seiner Lebensdauer auf verschiedene Variablen zeigen, d.h. ihm kann während der Programmausführung eine neue Speicheradresse zugewiesen werden.

int a = 5, b = 7, *p = &a;
printf("%i", *p); // gibt 5 aus
p = &b;
printf("%i", *p); // gibt 7 aus

Eine Merkregel

Der Dereferenzierungsoperator (*) wird bei der Initialisierung der Zeigervariable, sowie beim Lese- und Schreibzugriff auf einen Zeiger benötigt. Bei einem Schreibzugriff auf einen Zeiger ohne den Dereferenzierungsoperator muss eine Adresse angegeben werden. Sobald aber der Dereferenzierungsoperator verwendet wird, erfolgt der Zugriff auf den Wert, auf den die Zeigervariable verweist!

int *a, b;
a = &b; // Zugriff auf die Adresse
*a = 50; // Zugriff auf den Wert "hinter" dem Zeiger 

Bei der Initialisierung muss der Dereferenzierungsoperator immer angegeben werden.

int a = 15;
int *b = &a;
int *c = 0;

Zur Erinnerung, neben einer Variablenadresse darf ein Zeiger nur mit dem Wert 0 belegt werden, alle anderen Werte führen zu Problemen!

Zeiger auf Arrays

Zeiger können nicht nur auf eine einzelne Variable zeigen, man kann sie auch zur Referenzierung auf ganze Arrays verwenden. Zeiger werden oft dazu benutzt, um durch ein Array durch zu iterieren. Die Instanziierung von einem Zeiger auf ein Array erfolgt wie bei einer normalen Variable:

int field[3] = {16, 85, 34};
int *p = field[0]; // auf das erste Element verweisen

Um auf das erste Element zu verweisen, muss der Index nicht zwingend angegeben werden. Der Arrayname steht immer für eine Referenz auf das Element 0.

Das Interessante an Zeiger auf Arrays ist, dass man durch ein Array iterieren kann. Um auf das nächste Element zu kommen, muss nur der Zeiger um 1 erhöht werden. Dabei spielt es keine Rolle, ob das Array vom Typ double oder etwa int ist, beim Inkrementieren wird immer auf das nächste Arrayelement gesprungen.

int field[3] = {16, 85, 34};
int *p = field[0]; // *p = 16
p++; // *p = 85
p++; // *p = 34
p++; // Fehler, Überschreitung der Arraygrenze!

Obiges Beispiel zeigt, dass man immer darauf achten sollte, dass man nicht über die Arraygrenze hinaus iteriert.

Der Zugriff auf den Wert hinter dem Zeiger erfolgt wie bei Zeigern auf normale Variablen. Ob man mit dem Zeiger durch ein Array iteriert oder direkt auf eine einfache Variable verweist - im Hintergrund wird immer nur auf eine Variable verwiesen.

Ein Array kann auch rückwärts durch iteriert werden, dazu muss der Zeiger jeweils um den Wert 1 verringert werden:

int field[3] = {16, 85, 34};
int *p = field[2]; // *p = 34
p--; // *p = 85
p--; // *p = 16
p--; // Fehler, Unterschreitung der Arraygrenze!

Auch hier muss man die Arraygrenzen beachten!

Wenn man Zeiger per Addition und Subtraktion auf andere Variablen zeigen lässt, ist die Rede von Zeigerarithmetik. Diese ist jedoch nur mit der Addition und der Subtraktion möglich. Zeiger dürfen auf keinen Fall multipliziert oder dividiert werden.

Zeiger auf Strukturen

Mit Zeigern kann nicht nur auf einfache Variablen und Arrays verwiesen werden, auch der Zugriff auf Strukturen ist möglich. Dies kann ein Zeiger auf ein einzelnes Strukturelement, oder gleich auf die ganze Struktur sein. Für alle Beispiele in diesem Abschnitt wird folgende Struktur verwendet:

struct processor
{
  int taktfrequenz;
  float preis;
};

Das Zeigen auf ein einzelnes Strukturelement erfolgt gleich wie auf eine einzelne Variable, das Element muss aber über den Strukturnamen aufgelöst werden:

struct processor pentium4;
int *p;
pentium4.taktfrequenz = 2400;
p = &pentium4.taktfrequenz; // *p = 2400

Dabei muss der Zeiger wie immer unbedingt mit dem gleichen Datentyp deklariert worden sein, wie das Strukturelement, auf das er zeigen soll. Es spielt dabei keine Rolle, wie die Struktur genau aussieht - wichtig ist nur der Datentyp des Elements!

Mit Zeigern kann man wie oben erwähnt auch auf ganze Strukturen verweisen. Hier muss der Zeiger nun mit dem gleichen Datentyp deklariert werden, wie die Struktur selber:

struct processor pentium4, *pt;
pentium4.taktfrequenz = 2400;
pentium4.preis = 179.0;
pt = &pentium4;

Bisher ist alles gleich abgelaufen, wie beim Zeigen auf normale Variablen eines primitiven Datentyps. Der Zugriff auf die einzelnen Elemente der Struktur bietet den ersten Unterschied. Da der Punktoperator eine höhere Priorität als der Dereferenzierungsoperator hat, muss man den Zeiger mit runden Klammern umgeben:

pt.preis = 159; // Fehler
(*pt).preis = 159;

Da diese Variante etwas umständlich ist, wurde der Pfeiloperator (->) geschaffen. Dieser wird wie folgt angewendet:

pt->preis = 159; // äquivalent zu (*pt).preis = 159;


[back to top]



Userdaten
User nicht eingeloggt

Gesamtranking
Werbung
Datenbankstand
Autoren:04508
Artikel:00815
Glossar:04116
News:13565
Userbeiträge:16552
Queueeinträge:06245
News Umfrage
Ihre Anforderungen an ein Online-Zeiterfassungs-Produkt?
Mobile Nutzung möglich (Ipone, Android)
Externe API Schnittstelle/Plugins dritter
Zeiterfassung meiner Mitarbeiter
Exportieren in CSV/XLS
Siehe Kommentar



[Results] | [Archiv] Votes: 1147
Comments: 0