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: