Aus Grundtypen und Benutzer-definierten Typen können mit den Deklarationsoperatoren neue Typen abgeleitet werden:
* | Zeiger |
[] | Array |
& | Referenz |
() | Funktion |
Die Deklaration soll dabei die spätere Anwendung widerspiegeln:
int a[10]; i = a[3]; int *ptr; i = *ptr; int f(int); i = f(99);
Verwirrung entsteht oft dadurch, daß
* und & | Präfix-Operatoren und |
[] und () | Postfix-Operatoren |
int *v[10]; // Array aus 10 int-Zeigern int (*v)[10]; // Zeiger auf Array aus 10 ints
Die meisten Menschen merken sich einfach die Gestalt der häufigsten Typen.
B. Stroustrup
Syntaktisch: wie ein Grundtyp.
Aber: void-Objekte gibt es nicht!
Zweck:
Einer Variablen vom Typ void* können beliebige Zeiger zugewiesen werden. Sie darf jedoch nicht dereferenziert werden.
Zum Zugriff muß die explizite Typumwandlung benutzt werden:
void *f(unsigned int); ... int *iptr = (int*) f(10);
Für die meisten Typen T ist T* ein Zeiger auf T.
B. Stroustrup
Ein T* kann die Adresse eines Objekts vom Typ T enthalten.
Für Arrays und Funktionen ist die Notation komplizierter:
int *p; // Zeiger auf int char **cpp; // Zeiger auf char-Zeiger int (*vp)[7]; // Zeiger auf ein int-Array int (*fp)(t) // Zeiger auf Fkt. "int f(t)"
Die elementare Operation auf Zeigern ist die Dereferenzierung mit dem Operator *:
char c = 'P'; char *cp = &c; // Addresse von c char c2 = *cp; // c2 = 'P' class date {public: long julian;} *dp; cout << (*dp).julian; // Klammern! cout << dp->julian; // dto. aber schoener!
Erlaubt sind
Voraussetzung: Der Zeiger zeigt auf ein ``Datenobjekt'', d.h. nicht auf void oder eine Funktion.
int strlen(char *p) { int l=0; while (*p++) l++; return l; }oder
int strlen(char *p) { char *pos = p; while (*pos++); return (pos-p)+1; }aber nicht
// Ein Zeiger auf "strlen": int (*fp)(char*) = strlen; fp++; // VERBOTEN!
Für einen Typ T ist T[size] ein Array aus size Elementen vom Typ T, indiziert von 0 bis size-1:
float v[100]; // 100 floats int d[24][80]; // 24 80-elementige int-Arrays char *texte[50]; // 50 char-Zeiger date urlaub[29]; // 29 Urlaubstage
Die Länge eines Arrays muß nicht angegeben werden, wenn ein Initialisierer spezifiziert ist:
int v1[] = {1, 2, 3, 4}; // 4 Elemente char c[] = {'a', 'b', 'c'}; // 3 Elemente
Sonderform für char-Arrays:
char c[] = "abc"; // 4 Elemente !!!
Vorsicht: Eine entsprechende Zuweisung ist nicht definiert:
char c[4]; c = "abc"; // FEHLER
Ist der Initialisierer zu kurz, wird mit Nullen von entsprechendem Typ aufgefüllt:
int a[5] = {1,2,3}; // {1,2,3,0,0} char c[5] = "abc" // {'a','b','c','\0','\0'} float y[4][2] = {{1,2}, // |1,2| {3,4}, // |3,4| {5,6}}; // |5,6| // |0,0|
Der Name eines Arrays kann als Zeiger auf das erste Element verwendet werden.
char text[] = "hallo"; char *p = text;ist dasselbe wie
char text[] = "hallo"; char *p = &text[0];
Wird oft in Funktionsaufrufen ausgenutzt:
char text[] = "C++ is Fun"; int length = strlen(text);
übergeben wird der Zeiger auf text[0]
Wert-Parameter-Übergabe (call-by-value) mit Arrays geht nicht!
Arithmetik auf Zeigern hängt vom Typ ab, auf den gezeigt wird:
Wenn p vom Typ T* auf ein Element eines T-Arrays zeigt, liefert p+1 die Adresse des nächsten Elementes.
int main() { double d[2]; double* dp1 = &d[0]; double* dp2 = &d[1]; cout << "Zeigerdifferenz: " << dp2 - dp1; cout << " Adressendifferenz: " << long(dp2) - long(dp1); } }
liefert (Sun4)
Zeigerdifferenz: 1 Adressendifferenz: 8
Das Ergebnis der Pointer-Arithmetik ist undefiniert: