Revision [041af11]
Letzte Änderung am 2020-05-26 21:46:54 durch Oksana Neopagitova
ADDITIONS
![image](/uploads/ProzProg9Zeiger/BMBF_Logo_klein.jpg?width=200)

## Prozedurale Programmierung - Kapitel 9 - Zeiger

_<span class="right">Inhalte von Dr. E. Nadobnyh</p>_


## 9.1. Zeiger auf eine Variable


#### Grundidee

C/C++ enthält ein umfangreiches Zeigerkonzept, welches einen direkten Zugriff auf den Speicherplatz ermöglicht.

Ein <span style="text-decoration:underline;">Zeiger </span>ist eine Variable, die eine Adresse einer Variablen oder einer Funktion enthält.

**Synonyme**: Pointer, Pointervariable, Zeigervariable.

Man kann auf eine adressierte Variable oder Funktion indirekt über einen Zeiger zugreifen. Ein Zeiger adressiert eine Speicherstelle und gibt mit dem Typ an, wie diese Speicherstelle zu verwenden ist.


Bei Zeigern sind immer zwei unterschiedliche Typen verwickelt:

1) Zeigertyp (Pointertyp, Adresstyp) ist eigenen Datentyp des Zeigers und der Adresse.

2) Basistyp ist Typ der Variablen (des Objektes), auf die der Zeiger &#8220;zeigt&#8220;.

In C werden Zeiger auf eine Variable, auf eine Funktion, auf ein Feld und auf ein Zeiger definiert.



#### Zeiger auf eine Variable. Definition

Ein Zeiger wird wie jede andere Variable definiert: T* name;

Mit T* wird der Datentyp des Zeigers und mit T der Datentyp der adressierten Variable (Basistyp) bezeichnet.

**Beispiel**: int* p1; float* p2;
>>
>> Die Zeigervariablen p1 und p2 haben die Datentypen &#8222;Zeiger auf int&#8220; und &#8222;Zeiger auf float&#8220;.

Mit der Definition wird der Speicherplatz für den Zeiger reserviert. Ein Zeiger belegt bei einem 32-bit-Betriebs-system immer 4 Bytes.

Ein <span style="text-decoration:underline;">uninitialisierter </span>Zeiger wird wie jede andere Variable mit dem zufälligen Wert oder mit 0 initialisiert. Die Verwendung des uninitialisierten Zeigers verursacht einen Fehler, der oft schwer zu finden ist.



#### Initialisierter Zeiger

Wie jede andere Variable erhält ein Zeiger einen sinnvollen Wert bei der Definition oder durch die Zuweisung.

Eine Variablenadresse kann z.B. mit dem Adressoperator erhalten werden.

**Beispiel**: int a=3; int* p=&a;

![image](/uploads/ProzProg9Zeiger/pp55.gif?width=500)

Nach der Zeigerinitialisierung sagt man "p <span style="color:blue;">zeigt</span> auf a".

&rArr; Demo 1.



#### Nullzeiger

Ein Zeiger, der die Adresse NULL enthält, wird Nullzeiger (Nullpointer) genannt. Er zeigt auf kein gültiges Datenobjekt.

Es ist sinnvoll, Zeigervariablen mit NULL zu initialisieren, z.B.: <span style="color:blue;">double* p=NULL; </span>

NULL ist als eine Adresse mit dem Wert 0 in der Standardbibliothek definiert.

Man sollt auch immer NULL statt den Zahlenwert 0 verwenden, denn es ist nicht sicher, ob die Länge einer Integer-Variablen auch der Länge einer Adresse entspricht.



#### Inhaltsoperator

Der Inhaltsoperator (Dereferenzierungsoperator, Verweisoperator, Indirektionsoperator) wird mittels * bezeichnet.

Er bildet ein Sprachkonstrukt *x, der als ein Verweis benannt wird. Statt x kann eine Adresse, ein Zeigername, ein Feldname usw. verwendet werden.

Die Ausführung eines Inhaltsoperators ist vom Kontext abhängig:

1) Wenn ein Verweis in einem Ausdruck steht, wird er auswertet, d.h. durch ein Wert ersetzt.
2) Wenn ein Verweis links vom Gleichheitszeichen steht, wird er als Zuweisung interpretiert.

Achtung: Begriffsverwirrung bei Verweis vs. Referenz, Zeiger, Punkt-Operator!



#### Impliziter Inhaltsoperator

Zur Laufzeit wird für jede Variable eine Adresse zugeordnet. Dabei wird der Variablenname durch den Verweis ersetzt.

Wenn der Variable a die Adresse 0012FF88 zugeordnet wird, dann kann z.B. die Zuweisung <span style="color:blue;"> a=a+4;</span> zur Laufzeit folgendermaßen aussehen:

![image](/uploads/ProzProg9Zeiger/pp56.gif?width=450)

Der Verweis *(0012FF88) enthält den impliziten Inhaltsoperator * .



#### Inhaltsoperator und Zeiger

Die Verwendung des Inhaltsoperators *p bedeutet einen lesenden oder schreibenden Zugriff auf die Variable, deren Adresse im Zeiger p enthalten ist.

**Beispiel**:

%%(cpp)
>>int a=3, tmp;
>>int* p = &a; _statt a kann *p benutzt werden
>>tmp = *p;_ lesender Zugriff auf die Variable a
>>*p=5; _schreibender Zugriff auf die Variable
%%

Achtung: Das Zeichen * in der Zeiger-Definition int* ist ein Teil der Typbezeichnung aber kein Inhaltsoperator.

&rArr; Demo 2



#### Inhaltsoperator und Priorität

Bei der Verwendung des Operators * muß man die Priorität und Assoziativität von Operatoren genau beachten. Dies erscheint zunächst etwas schwierig, da dieser Operator ungewohnt ist.

**Beispiele**:

%%(cpp)
int x=3;
int* px;_ px ist ein Zeiger auf int
px = &x; _in px die Adresse von x speichern
*px;_ Wert der Variablen x
*px + 1; _Wert von x plus 1
*(px+1);_ Adresse in px erhöhen.
>>>>>> _Inhalt der Nachbar-Variable
*px += 1;_ Inhalt von x erhöhen
(*px)++; _Inhalt von x inkrementieren
*px++;_ wie *(px++); wegen Assoziativität
*++px; _wie *(++px); wegen Assoziativität
%%



## 9.2. Zeiger auf eine Variable. Details


#### Adresstyp T*

Jede Adresse hat einen bestimmten Typ, der als Adresstyp (auch Zeigertyp) bezeichnet wird.

Ein Adressoperator liefert eine Adresse vom Typ T*, wenn sein Argument vom Typ T ist.

Im folgenden Beispiel hat die Adresse den Typ <span style="color:blue;">int*</span> :

int a=3; printf("%p", &a);_ <span style="color:blue;">0012FF88</span>

Einem Zeiger kann eine Adresse zugewiesen werden, die den gleichen Zeigertyp hat.

T a; T* p = &(a);

Ein Inhaltsoperator liefert einen Wert vom Typ T, wenn sein Argument vom Typ T* ist.

T* p; T a = *(p);

Legende: T &#8211; Datentyp, T* - Adresstyp.



#### Konvertierung von Adresstypen

Synonym: Typumwandlung von Zeigertypen.

Den Typ einer Adresse kann konvertiert werden. Der binäre Code der Adresse bleibt dabei unverändert. Die Konvertierung von Zeigertypen verwendet man z.B. in der objektorientierten Programmierung.

Eine implizite Zeigertypen-Konvertierung von eingebauten Datentypen ist nicht erlaubt, z.B.:

>>> short b=4; short* pb=&b;
>>> int* pa=pb; _Fehler: Konvertierung nicht möglich

Eine explizite Zeigertypen-Konvertierung ist nahezu immer ein logischer Fehler, z.B.:

>>> pa=(int*)pb;_ keine Fehlermeldung

&rArr; Demo 3.



#### Zeiger und Konstanten

Wenn man einen Zeiger p benutzt, sind zwei Variablen beteiligt: der Zeiger und die Variable, auf die er zeigt (dereferenzierte Variable, Objekt).

![image](/uploads/ProzProg9Zeiger/pp57.gif?width=350)

Es gibt drei Möglichkeiten const zu verwenden:

1. Zeiger auf konstante Variable, z.B.:

>>const int* <span style="color:blue;"> p</span>;
>>int const* <span style="color:blue;"> p</span>;
>>>
Eine Veränderung des Zeigers ist möglich.

2. Konstanter Zeiger auf Variable, z.B.:
>> int* <span style="color:blue;">const p </span>;
>>>>>
Eine Veränderung der Variablen ist möglich.

3. Konstanter Zeiger auf konstante Variable, z.B.:

>>const int* <span style="color:blue;">const p</span>;
>>int const* <span style="color:blue;">const p</span>;



#### Zeiger als Parameter

Ein Zeiger als Parameter stellt einen IN-OUT-Parameter dar. Die adressierte Variable kann in der Funktion gelesen und beschrieben werden. Eine Übergabe per Zeiger ist ähnlich wie call-by-reference.

Syntax für Prototyp und Aufruf:
>>>void f(T* p); T a; f(<span style="color:blue;">&a</span> ); T- Datentyp (Basistyp).

Diese Übergabe ist aber tatsächlich call-by-value. Der Parameter p wird beim Aufruf mit dem Wert des Arguments, d.h. mit der Adresse der Variablen a initialisiert.

**Beispiele**

%%(cpp)
int main()
{ int x=3, y;
>> scanf(&#8220;%i&#8220;, &y);
>> swap(&x, &y);
>> return 0;
}

void swap(int* x, int* y)
{ int h = *x;
>>*x = *y;
>>*y = h;
}
%%



#### Rückgabe per Zeiger

**Synonym:** Zeiger als Rückgabewert

Eine Rückgabe per Zeiger ist ähnlich wie Rückgabe per Referenz. Der Aufrufer erhält die Adresse der Variablen (des Objektes) und kann direkt mit dem Original arbeiten.

Syntax für Prototyp und Aufruf:
>>> T* f( ); T* a = f(); T- Datentyp.


**Beispiel**:

%%(cpp)
int main()
{ int* a=f();
>>return 0;
}

int b=5;

int* f( )
{ return &b;
}
%%

Eine Rückgabe per Zeiger ist tatsächlich die Rückgabe per Wert. Eine Adresse von Typ T* wird zurückgeliefert und kann einem Zeiger zugewiesen werden.

Bei der Manipulationen mit &#8222;großen&#8220; Variablen (Objekten) kann man die Kopierarbeit reduzieren, wenn man statt Objekten Zeiger verwendet.

&rArr; Demo 4.

**Gefahren**bei der Rückgabe per Zeiger:
Die Adresse von lokalen Variablen (Objekte) dürfen nicht zurückgegeben werden, weil sie nach dem Rückkehr verschwunden sind.

**Negatives Beispiel :**
Die lokale Variable existiert nur während der Funktionsausführung. Die Ausgabe ist unbestimmt, sie könnte zufällig auch 1234 lauten.

%%(cpp)
int main(void)
{ int* p = f();
printf("%d", *p);
return 0;
}
int* f(void)
{ int x = 1234;
>>return (&x);
}
%%



## 9.3. Adressarithmetik


#### Adressarithmetik

Als Adressarithmetik (Zeigerarithmetik, pointer arithmetic) bezeichnet man spezifische Adressoperationen mit beschränkte Möglichkeiten. Schwerpunkt ist die Behandlungvon Feldern.

Es sind natürlich nur Operationen erlaubt, die zu sinnvollen Ergebnissen führen:
1. Inkrement und Dekrement: ++, --.
2. Addition und Subtraktion mit ganzzahligem Wert.
3. Zeigersubtraktion.
4. Zeigervergleich: >, >=, <, <=, != und
##### .
5. Alle anderen Operationen sind verboten.

Zeigersubtraktion und Vergleich ist nur dann sinnvoll, wenn beide Zeiger auf <span style="color:blue;">Elemente des gleichen Feldes</span> zeigen.

Anmerkung: Arithmetische Operationen sind nicht erlaubt bei Zeigern auf Funktionen.



#### Name eines Feldelementes

In einem eindimensionalen Feld <span style="color:blue;">T a[N];</span> ist der Name eines Feldelementes aus einem Feldnamen a und einem Index i zusammengestellt: <span style="color:blue;">a[i]</span>
>>>>>>>>>>>>
Für eingebaute Datentypen T transformiert der Compiler diesen Namen zu äquivalenter Form:

a[i] &rArr; *(a + i).

Hier sind beteiligt:
* - ein Inhaltsoperator und
+ - eine Summe der Adressarithmetik.

Beispiel: int a[2], b[2]; int i=1;

a[i]=5; &rArr; *(a + i) = 5;

a[0] = b[0]; &rArr; *(a+0)=*(b+0); &rArr; *a=*b;



Die Addition der Adressarithmetik ist eine spezifische und wichtige Operation.

Die Adresse des einzelnen Elementes a[i] wird vom Compiler nach der folgenden Formel berechnet:

>>>>>>>> (int)a + i *x

**Legende**:
+ und * -normale Summe und Multiplikation, x -die Anzahl von Bytes, die Datentyp T des Feldelementes besitzt, x= sizeof(T).

&rArr; Demo 5.

In einem zweidimensionalen Feld T a[N][M]; wird der Elementnamen a[i][j] vom Compiler zu äquivalenter Form umformuliert :

a[i][j] *(*(a + i) + j)

**Legende**:
* - ein Inhaltsoperator und
+ - eine Summe der Adressarithmetik.

Die Adresse des einzelnen Elementes a[i][j] wird vom Compiler nach der folgenden Formel berechnet:

(int)a + i*M*x + j*x

**Legende**:
+ und * -normale Summe und Multiplikation,
x -die Anzahl von Bytes, die den Datentyp T besitzt: x= sizeof(T).



## 9.4. Zeiger und eindimensionale Felder



#### Zeiger auf Feldelement

Zwischen Zeiger und Felder besteht in C/C++ eine enge Verwandtschaft.

1) Ein Feld T a[N] hat Elemente vom Datentyp T.
2) Ein Feldname ist eine Adresse auf erstes Element des Feldes und hat den Datentyp T*.
3) Ein Zeiger T* p kann als Zeiger auf ein Feldelement des Feldes T a[N] benutzt werden.
4) Folgende Paare sind äquivalent:

<table class="data" width="500px" style="border:0">
<tr>
<td>
Adresse auf das ersten Element
</td>
<td>
a
</td>
<td>
&a[0]

</td>
</tr>
<tr>
<td>
Zeiger-Zuweisung
</td>
<td>
p=a
</td>
<td>
p=&a[0]

</td>
</tr>
<tr>
<td>
Zugriff auf Element über den Feldnamen
</td>
<td>
a[i]
</td>
<td>
*(a+i)

</td>
</tr>
<tr>
<td>
Zugriff auf Element über den Zeiger
</td>
<td>
p[i]
</td>
<td>
*(p+i)
</td>
</tr>
</table>


Zeiger auf Feldelemente können bei der Übergabe eines Feldes als Parameter verwendet werden.

Dafür gibt es drei verschiedenen Schreibweise, die äquivalent sind:

>>> void f( T a[n]); void f( T a[ ]); void f( T* a);

%%(cpp)
int main()
{ int b[4]; f( b, 4);
>>return 0;
}

void f( int* a, int n);
void f( int a[4], int n);
void f( int a[ ], int n);
%%

**Vorsicht**: Zwei Definitionen haben gleiche Syntax und können nur durch Kontext unterschieden werden:
>>* int b[4] steht nicht in der Parameterliste. Hier werden vier Variablen angelegt.
>>* int a[4] steht in der Parameterliste. Hier wird ein Zeiger definiert.



#### Zeiger als Laufvariable

Ein Zeiger kann elegant als eine Laufvariable einer Schleife bei der Feld-Bearbeitung verwendet werden.

Nach der folgende Addition enthält der Zeiger p die Adresse des Feldelementes a[i]:

T a[N]; T* p=a; p+=i ;

**Beispiel**:

%%(cpp)
const int n=4; int a[n];
int sum=0;
for(int* p=a; p<a+n; p++)
{ _Zeiger p als Laufvariable
>>> sum+=*p;
}
%%



#### Zeiger und Zeichenkette

Zeichenketten (nullterminierte C-Strings) sind Felder vom Typ char[ ], wobei das letzte Zeichen immer das Nullzeichen &#8216;\\0&#8216; sein muss.

Zeichenketten können unterschiedlich definiert werden:

1) Ein benanntes char-Feld a, das mittels der Zeichen initialisiert wird, z.B.:
>>char a[ ] = {&#8216;a&#8216;, &#8216;b&#8216;, &#8216;c&#8216;, &#8216;\\0&#8216;}; oder char a[ ] = &#8220;abc&#8220;;

2)Ein namenloses char-Feld (Stringliteral) und ein Zeiger p der mit der Feldadresse initialisiert wird, z.B.:
>>const char* p = &#8220;qwert&#8220;;

Besonderheit: Bei einigen Systemen sind Stringliterale schreibgeschützt und daher muss der Zeiger als const definiert werden.

&rArr; Demo 6 und 7



## 9.5. Besondere Zeiger


#### Zeiger auf Zeiger

Da Zeigervariablen selbst Datenobjekte sind, kann auch auf sie über Zeiger zugegriffen werden.

![image](/uploads/ProzProg9Zeiger/pp58.gif?width=450)

**Syntax:**
T **p;_ T - Datentyp**Beispiel**:

%%(cpp)
int main()
{ int x= 10;
>>int* px= &x;
>>int** ppx= &px;

>>int y= **ppx; _Zugriff auf x
>>return 0;
}
%%



### Adresstyp T**

Ein Adressoperator liefert eine Adresse vom Typ T**, wenn sein Argument vom Typ T* ist.

T a; T* p= &a; T** pp= &p;

Ein Inhaltsoperator liefert eine Adresse vom Typ T*, wenn sein Argument vom Typ T** ist.

T* p= *pp; T a= *p;

Legende: T &#8211; Datentyp, T **und T* - Adresstypen.**Beispiel**:

%%(cpp)
>>int x= 10;
>>int* px1= &x;
>>int** ppx= &px1;
>>int* px2= *ppx;
>>int y= *px2;_ Zugriff auf x
%%



#### Zeiger auf ein Feld

Ein zweidimensionales Feld T aa[N][M] ist ein eindimensionales Feld mit N Zeilen. Jede Zeile ist selbst ein eindimensionales Feld. T- Typ eines Feldelementes. Der Datentyp einer Zeile ist T [ ][M]. Der Feldname aa ist eine Adresse vom Typ der Zeile.

C/C++ hat einen Zeiger auf ein Feld (Feldzeiger) vom Typ T[][M]. Dieser Zeiger kann hauptsächlich bei der Parameterübergabe verwendet werden, z.B.:

%%(cpp)
void f(int pp[ ][M])
{ cout sizeof(*pp)endl;
>> cout sizeof(int)*M;
}

int main()
{ int aa[N][M]; f(aa);
>>return 0;
} %%

**Äquivalente Schreibweise:**
void f(T pp[ ][M]); void f(T pp[N][M]); void f(T(*pp)[M]);



#### Adresstyp T [][M]

Ein Inhaltsoperator liefert eine Adresse vom Typ T[], wenn sein Argument vom Typ T [][M] ist.

void f( T pp[][M], T p[] ) { p = *pp; }

**Legende**:
T &#8211;Datentyp, T[][M] und T[] -Adresstypen.

Umgekehrt funktioniert es nicht. Ein Adressoperator liefert eine Adresse vom Typ T**, wenn sein Argument vom Typ
T[] ist: T** zz = &p;

&rArr; Demo 8.



#### Zeiger auf Funktionen

>>* Jede kompilierte Funktion belegt einen Speicherbereich und besitzt ein Eingangspunkt.
>>* Der Name einer Funktion ist eine symbolische Bezeichnung der Adresse des Eingangspunktes (Funktionsadresse).
>>* In C/C++ gibt es einen Zeiger auf Funktionen (Funktionszeiger), der die Funktionsadresse enthalten kann.
>>* Ein Funktionszeiger kann verwendet werden, um die Übergabe einer Funktion als Parameter zu programmieren.
>>* Der Funktionszeiger wird dabei als ein formaler Parameter verwendet.
>>* Der Name einer Funktion wird dabei als ein aktueller Parameter verwendet.

**Syntax**:
T (*fp) (Parameterliste);

**Legende**:
fp &#8211; Funktionszeiger, T &#8211;Rückgabetyp.

**Beispiel:**

%%(cpp)
int main()
{
>> int (* fp) (int);
>> fp = f2;
>> cout f1(fp);
>> return 0;
}

int f2( int n ){ return n*2; }
int f3( int n ){ return n*3; }

int f1( int (*z) (int) )
{ return z(100);
}
%%


#### Komplexe Zeiger

![image](/uploads/ProzProg9Zeiger/pp59.gif?width=500)


**Eigenschaften:**

1) Es entsteht kein neuer Datentyp.

2) Kein Speicherplatz wird reserviert.

3) Für beliebige Datentypen T anwendbar.

4) Als Konvention in C sollten neue Typennamen gesamt oder mit dem erstem Buchstaben großgeschrieben werden.

5) Neue Typnamen dienen zur Verbesserung der Lesbarkeit von Programmen.

6) typedef &#8211;Vereinbarungen sollen in Headerdateien isoliert werden. Dann können Portabilitäts- und Wartungsprobleme vereinfacht werden.



## 9.6. Zeigerfelder


#### Grundidee

Zeigerfelder (Zeigervektoren, Pointer-Arrays) sind Felder deren Elemente Zeiger sind.

Syntax: T* a[M]; T- Datentyp,

Für viele Fälle ist die Arbeit mit Zeigern effektiver, als umfangreiche Datenbestände zu bearbeiten, z. B. werden beim Sortieren nur die Zeiger und nicht die Variablen (Objekte, Datenbestände) von &#8222;großen&#8220; Datentypen selbst sortiert.

![image](/uploads/ProzProg9Zeiger/pp60.gif?width=500)



#### Ausgefranste Felder

Ausgefranste Felder (ragged arrays) sind mehrdimensionale Felder, deren Elemente wiederum aus Feldern (Teilfeldern) bestehen. Die einzelnen Teilfelder können unterschiedlich lang sein.

Ein ausgefranstes Feld wird mit Hilfe des Zeigerfeldes aufgefasst, wenn die Elemente des Zeigerfeldes wiederum Adressen von Teilfeldern (Zeilen) sind.

**Beispiel**:
char a [4], b [2], c [4]; _Teilfelder
char* Z[3] = {a, b, c};_ Zeigerfeld

![image](/uploads/ProzProg9Zeiger/pp61.gif?width=500)



#### Eigenschaften von ausgefransten Felder

1) Mehrdimensionale Felder und ausgefranste Felder werden gleichartig verwendet:

>>>Z[i] -enthält die Adresse auf (i+1)-tes Teilfeld,
>>>Z[i]+j -ist eine Adresse auf das (j+1)-te Element des (i+1)-ten Teilfeldes,
>>>Z [i][j] -enthält das (j+1)-te Element des (i+1)-ten Teilfeldes.

2) Teilfelder können genau nach Bedarf reserviert werden, z.B. dreieckiges Feld.

3) Der Nachteil ist der zusätzliche Speicherbedarf für das Zeigerfeld.

&rArr; Demo 9.



#### Kommandozeilenparameter

C/C++ bietet die Möglichkeit, beim Programmstart Kommandozeilenparameter an das Programm main zu übergeben:

void main(int argc, char *argv[]){...}
>>>>
>>>>
Die einzelnen Parameter sind nach dem Programmnamen jeweils durch Leerzeichen getrennt anzugeben.

Die main-Funktion übernimmt Kommandozeilen durch folgende Parameter:
>>* int argc -die Argumentenanzahl (argument count),
>>* char *argv[] -ein Zeigerfeld auf C-Strings (arg. vector),
>>* argv[0] ist ein Pointer auf den Programmnamen selbst,
>>* argv[1] ist ein Pointer auf den ersten echten Kommandozeilen-Parameter,
>>* argv[argc-1] ist ein Pointer auf den letzten Parameter,
>>* argc > 1 wegen dem Programmnamen.

&rArr;Demo 10.



## 9.7. Dynamische Speicherreservierung


#### Grundidee

Es ist nicht immer möglich, den Speicherplatz exakt vorher zu planen, z.B. wenn die Größe eines Feldes nur zur Laufzeit bekannt ist. Es ist unökonomisch, den maximalen Speicherplatz zu reservieren.

C++ bietet zwei Operatoren:
>>* new -reserviert den Speicherplatz genau in der richtigen Menge und zum richtigen Zeitpunkt,
>>* delete -gibt den Speicherplatz frei, wenn er nicht mehr benötigt wird.

Dynamische Datenobjekte (Variablen, Felder) werden auch nicht über einen Namen, sondern nur über ihre Adresse angesprochen.
Diese Adresse entsteht bei der Speicherbelegung und wird normalerweise einem Zeiger zugewiesen.



#### Speicherreservierung

Für die dynamische Speicherverwaltung gibt es einen großen Adressbereich, der als Heap bezeichnet wird.

![image](/uploads/ProzProg9Zeiger/pp62.gif?width=500)

Der Zeiger p enthält die Adresse auf die namenlose int&#8211;Variable.

Die Lebensdauer dynamisch reservierter Datenobjekte ist nicht an die Ausführungszeit eines Blocks gebunden.

Mit dem Operator new[ ] kann ein Feld dynamisch reserviert werden:

**Syntax**: T* p = new T [N]; T-Datentyp.

**Beispiel**: double* p= new double[10];

Der Zugriff auf die Feldelemente geschieht genau wie beim nicht dynamischen Feld:

%%(cpp)
double a[10];
a[4]= 4.4; oder *(a+3)= 3.3; _a- ist die Adresse
p[4]= 4.4; oder *(p+3)= 3.3;_ p- enthält die Adresse
%%

&rArr; Demo 11



#### Speicherfreigabe

**Synonym**: Löschen oder Zerstörung des Datenobjektes.

Der delete- Operator gibt den reservierten Platz frei, damit er bei neuen Reservierungen wieder belegt werden kann.

**Syntax**:
delete p; oder delete[] p;
p - Zeiger auf reserviertes Datenobjekt.

Wenn dynamische Datenobjekte nicht mehr gebraucht werden, müssen sie freigegeben werden. Ein nicht mehr zugängliches Datenobjekt wird als Speicherleck (memory leak) bezeichnet.

**Beispiel**: int* p= new int; p= new int;

Wenn ein Programm mit dem Speicherleck immer mehr Speicher reserviert, wird bei genügend langer Laufzeit an erschöpften Systemreserven scheitern.

&rArr; Demo 12
DELETIONS
![image](/uploads/ProzProg9Zeiger/BMBF_Logo_klein.jpg?width=200)
# Prozedurale Programmierung - Kapitel 9 - Zeiger
_<span class="right">Inhalte von Dr. E. Nadobnyh</p>_
## 9.1. Zeiger auf eine Variable
#
#### Grundidee

C/C++ enthält ein umfangreiches Zeigerkonzept, welches einen direkten Zugriff auf den Speicherplatz ermöglicht.

Ein <span style="text-decoration:underline;">Zeiger </span>ist eine Variable, die eine Adresse einer Variablen oder einer Funktion enthält.

**Synonyme**: Pointer, Pointervariable, Zeigervariable.

Man kann auf eine adressierte Variable oder Funktion indirekt über einen Zeiger zugreifen. Ein Zeiger adressiert eine Speicherstelle und gibt mit dem Typ an, wie diese Speicherstelle zu verwenden ist.


Bei Zeigern sind immer zwei unterschiedliche Typen verwickelt:

1) Zeigertyp (Pointertyp, Adresstyp) ist eigenen Datentyp des Zeigers und der Adresse.

2) Basistyp ist Typ der Variablen (des Objektes), auf die der Zeiger &#8220;zeigt&#8220;.

In C werden Zeiger auf eine Variable, auf eine Funktion, auf ein Feld und auf ein Zeiger definiert.

#### Zeiger auf eine Variable. Definition

Ein Zeiger wird wie jede andere Variable definiert: T* name;

Mit T* wird der Datentyp des Zeigers und mit T der Datentyp der adressierten Variable (Basistyp) bezeichnet.

**Beispiel**: int* p1; float* p2;
>>
>> Die Zeigervariablen p1 und p2 haben die Datentypen &#8222;Zeiger auf int&#8220; und &#8222;Zeiger auf float&#8220;.

Mit der Definition wird der Speicherplatz für den Zeiger reserviert. Ein Zeiger belegt bei einem 32-bit-Betriebs-system immer 4 Bytes.

Ein <span style="text-decoration:underline;">uninitialisierter </span>Zeiger wird wie jede andere Variable mit dem zufälligen Wert oder mit 0 initialisiert. Die Verwendung des uninitialisierten Zeigers verursacht einen Fehler, der oft schwer zu finden ist.
#### Initialisierter Zeiger

Wie jede andere Variable erhält ein Zeiger einen sinnvollen Wert bei der Definition oder durch die Zuweisung.

Eine Variablenadresse kann z.B. mit dem Adressoperator erhalten werden.

**Beispiel**: int a=3; int* p=&a;
![image](/uploads/ProzProg9Zeiger/pp55.gif?width=500)
Nach der Zeigerinitialisierung sagt man "p <span style="color:blue;">zeigt</span> auf a".
&rArr; Demo 1.
#### Nullzeiger

Ein Zeiger, der die Adresse NULL enthält, wird Nullzeiger (Nullpointer) genannt. Er zeigt auf kein gültiges Datenobjekt.

Es ist sinnvoll, Zeigervariablen mit NULL zu initialisieren, z.B.: <span style="color:blue;">double* p=NULL; </span>

NULL ist als eine Adresse mit dem Wert 0 in der Standardbibliothek definiert.

Man sollt auch immer NULL statt den Zahlenwert 0 verwenden, denn es ist nicht sicher, ob die Länge einer Integer-Variablen auch der Länge einer Adresse entspricht.
#### Inhaltsoperator

Der Inhaltsoperator (Dereferenzierungsoperator, Verweisoperator, Indirektionsoperator) wird mittels * bezeichnet.

Er bildet ein Sprachkonstrukt *x, der als ein Verweis benannt wird. Statt x kann eine Adresse, ein Zeigername, ein Feldname usw. verwendet werden.

Die Ausführung eines Inhaltsoperators ist vom Kontext abhängig:
1) Wenn ein Verweis in einem Ausdruck steht, wird er auswertet, d.h. durch ein Wert ersetzt.
2) Wenn ein Verweis links vom Gleichheitszeichen steht, wird er als Zuweisung interpretiert.

Achtung: Begriffsverwirrung bei Verweis vs. Referenz, Zeiger, Punkt-Operator!
#### Impliziter Inhaltsoperator

Zur Laufzeit wird für jede Variable eine Adresse zugeordnet. Dabei wird der Variablenname durch den Verweis ersetzt.

Wenn der Variable a die Adresse 0012FF88 zugeordnet wird, dann kann z.B. die Zuweisung <span style="color:blue;"> a=a+4;</span> zur Laufzeit folgendermaßen aussehen:

![image](/uploads/ProzProg9Zeiger/pp56.gif?width=450)
Der Verweis *(0012FF88) enthält den impliziten Inhaltsoperator * .

#### Inhaltsoperator und Zeiger

Die Verwendung des Inhaltsoperators *p bedeutet einen lesenden oder schreibenden Zugriff auf die Variable, deren Adresse im Zeiger p enthalten ist.

**Beispiel**:
%%(cpp)
>>int a=3, tmp;
>>int* p = &a; _statt a kann *p benutzt werden
>>tmp = *p;_ lesender Zugriff auf die Variable a
>>*p=5; _schreibender Zugriff auf die Variable
%%
Achtung: Das Zeichen * in der Zeiger-Definition int* ist ein Teil der Typbezeichnung aber kein Inhaltsoperator.

&rArr; Demo 2
#### Inhaltsoperator und Priorität

Bei der Verwendung des Operators * muß man die Priorität und Assoziativität von Operatoren genau beachten. Dies erscheint zunächst etwas schwierig, da dieser Operator ungewohnt ist.
**Beispiele**:

%%(cpp)
int x=3;
int* px;_ px ist ein Zeiger auf int
px = &x; _in px die Adresse von x speichern
*px;_ Wert der Variablen x
*px + 1; _Wert von x plus 1
*(px+1);_ Adresse in px erhöhen.
>>>>>> _Inhalt der Nachbar-Variable
*px += 1;_ Inhalt von x erhöhen
(*px)++; _Inhalt von x inkrementieren
*px++;_ wie *(px++); wegen Assoziativität
*++px; _wie *(++px); wegen Assoziativität
%%

## 9.2. Zeiger auf eine Variable. Details
#### Adresstyp T*
Jede Adresse hat einen bestimmten Typ, der als Adresstyp (auch Zeigertyp) bezeichnet wird.

Ein Adressoperator liefert eine Adresse vom Typ T*, wenn sein Argument vom Typ T ist.

Im folgenden Beispiel hat die Adresse den Typ <span style="color:blue;">int*</span> :
int a=3; printf("%p", &a);_ <span style="color:blue;">0012FF88</span>

Einem Zeiger kann eine Adresse zugewiesen werden, die den gleichen Zeigertyp hat.

T a; T* p = &(a);

Ein Inhaltsoperator liefert einen Wert vom Typ T, wenn sein Argument vom Typ T* ist.

T* p; T a = *(p);

Legende: T &#8211; Datentyp, T* - Adresstyp.
#### Konvertierung von Adresstypen

Synonym: Typumwandlung von Zeigertypen.

Den Typ einer Adresse kann konvertiert werden. Der binäre Code der Adresse bleibt dabei unverändert. Die Konvertierung von Zeigertypen verwendet man z.B. in der objektorientierten Programmierung.

Eine implizite Zeigertypen-Konvertierung von eingebauten Datentypen ist nicht erlaubt, z.B.:

>>> short b=4; short* pb=&b;
>>> int* pa=pb; _Fehler: Konvertierung nicht möglich

Eine explizite Zeigertypen-Konvertierung ist nahezu immer ein logischer Fehler, z.B.:

>>> pa=(int*)pb;_ keine Fehlermeldung

&rArr; Demo 3.
#### Zeiger und Konstanten

Wenn man einen Zeiger p benutzt, sind zwei Variablen beteiligt: der Zeiger und die Variable, auf die er zeigt (dereferenzierte Variable, Objekt).
![image](/uploads/ProzProg9Zeiger/pp57.gif?width=350)
Es gibt drei Möglichkeiten const zu verwenden:
1. Zeiger auf konstante Variable, z.B.:
>>const int* <span style="color:blue;"> p</span>;
>>int const* <span style="color:blue;"> p</span>;
>>>
Eine Veränderung des Zeigers ist möglich.

2. Konstanter Zeiger auf Variable, z.B.:
>> int* <span style="color:blue;">const p </span>;
>>>>>
Eine Veränderung der Variablen ist möglich.

3. Konstanter Zeiger auf konstante Variable, z.B.:
>>const int* <span style="color:blue;">const p</span>;
>>int const* <span style="color:blue;">const p</span>;
#### Zeiger als Parameter

Ein Zeiger als Parameter stellt einen IN-OUT-Parameter dar. Die adressierte Variable kann in der Funktion gelesen und beschrieben werden. Eine Übergabe per Zeiger ist ähnlich wie call-by-reference.

Syntax für Prototyp und Aufruf:
>>>void f(T* p); T a; f(<span style="color:blue;">&a</span> ); T- Datentyp (Basistyp).

Diese Übergabe ist aber tatsächlich call-by-value. Der Parameter p wird beim Aufruf mit dem Wert des Arguments, d.h. mit der Adresse der Variablen a initialisiert.
**Beispiele**
%%(cpp)
int main()
{ int x=3, y;
>> scanf(&#8220;%i&#8220;, &y);
>> swap(&x, &y);
>> return 0;
}
void swap(int* x, int* y)
{ int h = *x;
>>*x = *y;
>>*y = h;
}
%%
#### Rückgabe per Zeiger

**Synonym:** Zeiger als Rückgabewert

Eine Rückgabe per Zeiger ist ähnlich wie Rückgabe per Referenz. Der Aufrufer erhält die Adresse der Variablen (des Objektes) und kann direkt mit dem Original arbeiten.

Syntax für Prototyp und Aufruf:
>>> T* f( ); T* a = f(); T- Datentyp.


**Beispiel**:
%%(cpp)
int main()
{ int* a=f();
>>return 0;
}
int b=5;
int* f( )
{ return &b;
}
%%
Eine Rückgabe per Zeiger ist tatsächlich die Rückgabe per Wert. Eine Adresse von Typ T* wird zurückgeliefert und kann einem Zeiger zugewiesen werden.

Bei der Manipulationen mit &#8222;großen&#8220; Variablen (Objekten) kann man die Kopierarbeit reduzieren, wenn man statt Objekten Zeiger verwendet.

&rArr; Demo 4.
**Gefahren**bei der Rückgabe per Zeiger:
Die Adresse von lokalen Variablen (Objekte) dürfen nicht zurückgegeben werden, weil sie nach dem Rückkehr verschwunden sind.

**Negatives Beispiel :**
Die lokale Variable existiert nur während der Funktionsausführung. Die Ausgabe ist unbestimmt, sie könnte zufällig auch 1234 lauten.
%%(cpp)
int main(void)
{ int* p = f();
printf("%d", *p);
return 0;
}
int* f(void)
{ int x = 1234;
>>return (&x);
}
%%
## 9.3. Adressarithmetik
#### Adressarithmetik

Als Adressarithmetik (Zeigerarithmetik, pointer arithmetic) bezeichnet man spezifische Adressoperationen mit beschränkte Möglichkeiten. Schwerpunkt ist die Behandlungvon Feldern.

Es sind natürlich nur Operationen erlaubt, die zu sinnvollen Ergebnissen führen:
1. Inkrement und Dekrement: ++, --.
2. Addition und Subtraktion mit ganzzahligem Wert.
3. Zeigersubtraktion.
4. Zeigervergleich: >, >=, <, <=, != und
##### .
5. Alle anderen Operationen sind verboten.

Zeigersubtraktion und Vergleich ist nur dann sinnvoll, wenn beide Zeiger auf <span style="color:blue;">Elemente des gleichen Feldes</span> zeigen.

Anmerkung: Arithmetische Operationen sind nicht erlaubt bei Zeigern auf Funktionen.
#### Name eines Feldelementes

In einem eindimensionalen Feld <span style="color:blue;">T a[N];</span> ist der Name eines Feldelementes aus einem Feldnamen a und einem Index i zusammengestellt: <span style="color:blue;">a[i]</span>
>>>>>>>>>>>>
Für eingebaute Datentypen T transformiert der Compiler diesen Namen zu äquivalenter Form:
a[i] &rArr; *(a + i).
Hier sind beteiligt:
* - ein Inhaltsoperator und
+ - eine Summe der Adressarithmetik.

Beispiel: int a[2], b[2]; int i=1;

a[i]=5; &rArr; *(a + i) = 5;

a[0] = b[0]; &rArr; *(a+0)=*(b+0); &rArr; *a=*b;

Die Addition der Adressarithmetik ist eine spezifische und wichtige Operation.

Die Adresse des einzelnen Elementes a[i] wird vom Compiler nach der folgenden Formel berechnet:

>>>>>>>> (int)a + i *x

**Legende**:
+ und * -normale Summe und Multiplikation, x -die Anzahl von Bytes, die Datentyp T des Feldelementes besitzt, x= sizeof(T).

&rArr; Demo 5.
In einem zweidimensionalen Feld T a[N][M]; wird der Elementnamen a[i][j] vom Compiler zu äquivalenter Form umformuliert :
a[i][j] *(*(a + i) + j)
**Legende**:
* - ein Inhaltsoperator und
+ - eine Summe der Adressarithmetik.

Die Adresse des einzelnen Elementes a[i][j] wird vom Compiler nach der folgenden Formel berechnet:
(int)a + i*M*x + j*x

**Legende**:
+ und * -normale Summe und Multiplikation,
x -die Anzahl von Bytes, die den Datentyp T besitzt: x= sizeof(T).
## 9.4. Zeiger und eindimensionale Felder
#### Zeiger auf Feldelement

Zwischen Zeiger und Felder besteht in C/C++ eine enge Verwandtschaft.

1) Ein Feld T a[N] hat Elemente vom Datentyp T.
2) Ein Feldname ist eine Adresse auf erstes Element des Feldes und hat den Datentyp T*.
3) Ein Zeiger T* p kann als Zeiger auf ein Feldelement des Feldes T a[N] benutzt werden.
4) Folgende Paare sind äquivalent:
<table class="data" width="500px" style="border:0">
>><tr>
>>><td>
>>>>Adresse auf das ersten Element
>>></td>
>>><td>
>>>>a
>>></td>
>>><td>
>>>>&a[0]
>>></td>
>></tr>
>><tr>
>>><td>
>>>>Zeiger-Zuweisung
>>></td>
>>><td>
>>>>p=a
>>></td>
>>><td>
>>>>p=&a[0]
>>></td>
>></tr>
>><tr>
>>><td>
>>>>Zugriff auf Element über den Feldnamen
>>></td>
>>><td>
>>>>a[i]
>>></td>
>>><td>
>>>>*(a+i)
>>></td>
>></tr>
>><tr>
>>><td>
>>>>Zugriff auf Element über den Zeiger
>>></td>
>>><td>
>>>>p[i]
>>></td>
>>><td>
>>>>*(p+i)
>>></td>
>></tr>
</table>
Zeiger auf Feldelemente können bei der Übergabe eines Feldes als Parameter verwendet werden.

Dafür gibt es drei verschiedenen Schreibweise, die äquivalent sind:
>>> void f( T a[n]); void f( T a[ ]); void f( T* a);
%%(cpp)
int main()
{ int b[4]; f( b, 4);
>>return 0;
}
void f( int* a, int n);
void f( int a[4], int n);
void f( int a[ ], int n);
%%
**Vorsicht**: Zwei Definitionen haben gleiche Syntax und können nur durch Kontext unterschieden werden:
>>* int b[4] steht nicht in der Parameterliste. Hier werden vier Variablen angelegt.
>>* int a[4] steht in der Parameterliste. Hier wird ein Zeiger definiert.
#### Zeiger als Laufvariable

Ein Zeiger kann elegant als eine Laufvariable einer Schleife bei der Feld-Bearbeitung verwendet werden.

Nach der folgende Addition enthält der Zeiger p die Adresse des Feldelementes a[i]:

T a[N]; T* p=a; p+=i ;
**Beispiel**:
%%(cpp)
const int n=4; int a[n];
int sum=0;
for(int* p=a; p<a+n; p++)
{ _Zeiger p als Laufvariable
>>> sum+=*p;
}
%%
#### Zeiger und Zeichenkette

Zeichenketten (nullterminierte C-Strings) sind Felder vom Typ char[ ], wobei das letzte Zeichen immer das Nullzeichen &#8216;\\0&#8216; sein muss.

Zeichenketten können unterschiedlich definiert werden:
1) Ein benanntes char-Feld a, das mittels der Zeichen initialisiert wird, z.B.:
>>char a[ ] = {&#8216;a&#8216;, &#8216;b&#8216;, &#8216;c&#8216;, &#8216;\\0&#8216;}; oder char a[ ] = &#8220;abc&#8220;;

2)Ein namenloses char-Feld (Stringliteral) und ein Zeiger p der mit der Feldadresse initialisiert wird, z.B.:
>>const char* p = &#8220;qwert&#8220;;

Besonderheit: Bei einigen Systemen sind Stringliterale schreibgeschützt und daher muss der Zeiger als const definiert werden.

&rArr; Demo 6 und 7
## 9.5. Besondere Zeiger
#### Zeiger auf Zeiger

Da Zeigervariablen selbst Datenobjekte sind, kann auch auf sie über Zeiger zugegriffen werden.
![image](/uploads/ProzProg9Zeiger/pp58.gif?width=450)
**Syntax:**
T **p;_ T - Datentyp**Beispiel**:
%%(cpp)
int main()
{ int x= 10;
>>int* px= &x;
>>int** ppx= &px;

>>int y= **ppx; _Zugriff auf x
>>return 0;
}
%%
### Adresstyp T**

Ein Adressoperator liefert eine Adresse vom Typ T**, wenn sein Argument vom Typ T* ist.

T a; T* p= &a; T** pp= &p;

Ein Inhaltsoperator liefert eine Adresse vom Typ T*, wenn sein Argument vom Typ T** ist.

T* p= *pp; T a= *p;

Legende: T &#8211; Datentyp, T **und T* - Adresstypen.**Beispiel**:
%%(cpp)
>>int x= 10;
>>int* px1= &x;
>>int** ppx= &px1;
>>int* px2= *ppx;
>>int y= *px2;_ Zugriff auf x
%%
#### Zeiger auf ein Feld
Ein zweidimensionales Feld T aa[N][M] ist ein eindimensionales Feld mit N Zeilen. Jede Zeile ist selbst ein eindimensionales Feld. T- Typ eines Feldelementes. Der Datentyp einer Zeile ist T [ ][M]. Der Feldname aa ist eine Adresse vom Typ der Zeile.
C/C++ hat einen Zeiger auf ein Feld (Feldzeiger) vom Typ T[][M]. Dieser Zeiger kann hauptsächlich bei der Parameterübergabe verwendet werden, z.B.:
%%(cpp)
void f(int pp[ ][M])
{ cout sizeof(*pp)endl;
>> cout sizeof(int)*M;
}
int main()
{ int aa[N][M]; f(aa);
>>return 0;
} %%
**Äquivalente Schreibweise:**
void f(T pp[ ][M]); void f(T pp[N][M]); void f(T(*pp)[M]);
#### Adresstyp T [][M]

Ein Inhaltsoperator liefert eine Adresse vom Typ T[], wenn sein Argument vom Typ T [][M] ist.

void f( T pp[][M], T p[] ) { p = *pp; }

**Legende**:
T &#8211;Datentyp, T[][M] und T[] -Adresstypen.

Umgekehrt funktioniert es nicht. Ein Adressoperator liefert eine Adresse vom Typ T**, wenn sein Argument vom Typ
T[] ist: T** zz = &p;

&rArr; Demo 8.
#### Zeiger auf Funktionen

>>* Jede kompilierte Funktion belegt einen Speicherbereich und besitzt ein Eingangspunkt.
>>* Der Name einer Funktion ist eine symbolische Bezeichnung der Adresse des Eingangspunktes (Funktionsadresse).
>>* In C/C++ gibt es einen Zeiger auf Funktionen (Funktionszeiger), der die Funktionsadresse enthalten kann.
>>* Ein Funktionszeiger kann verwendet werden, um die Übergabe einer Funktion als Parameter zu programmieren.
>>* Der Funktionszeiger wird dabei als ein formaler Parameter verwendet.
>>* Der Name einer Funktion wird dabei als ein aktueller Parameter verwendet.

**Syntax**:
T (*fp) (Parameterliste);

**Legende**:
fp &#8211; Funktionszeiger, T &#8211;Rückgabetyp.
**Beispiel:**
%%(cpp)
int main()
{
>> int (* fp) (int);
>> fp = f2;
>> cout f1(fp);
>> return 0;
}
int f2( int n ){ return n*2; }
int f3( int n ){ return n*3; }
int f1( int (*z) (int) )
{ return z(100);
}
%%
#### Komplexe Zeiger
![image](/uploads/ProzProg9Zeiger/pp59.gif?width=500)
**Eigenschaften:**

1) Es entsteht kein neuer Datentyp.

2) Kein Speicherplatz wird reserviert.

3) Für beliebige Datentypen T anwendbar.

4) Als Konvention in C sollten neue Typennamen gesamt oder mit dem erstem Buchstaben großgeschrieben werden.

5) Neue Typnamen dienen zur Verbesserung der Lesbarkeit von Programmen.

6) typedef &#8211;Vereinbarungen sollen in Headerdateien isoliert werden. Dann können Portabilitäts- und Wartungsprobleme vereinfacht werden.
## 9.6. Zeigerfelder
#### Grundidee

Zeigerfelder (Zeigervektoren, Pointer-Arrays) sind Felder deren Elemente Zeiger sind.

Syntax: T* a[M]; T- Datentyp,

Für viele Fälle ist die Arbeit mit Zeigern effektiver, als umfangreiche Datenbestände zu bearbeiten, z. B. werden beim Sortieren nur die Zeiger und nicht die Variablen (Objekte, Datenbestände) von &#8222;großen&#8220; Datentypen selbst sortiert.
![image](/uploads/ProzProg9Zeiger/pp60.gif?width=500)
#### Ausgefranste Felder

Ausgefranste Felder (ragged arrays) sind mehrdimensionale Felder, deren Elemente wiederum aus Feldern (Teilfeldern) bestehen. Die einzelnen Teilfelder können unterschiedlich lang sein.

Ein ausgefranstes Feld wird mit Hilfe des Zeigerfeldes aufgefasst, wenn die Elemente des Zeigerfeldes wiederum Adressen von Teilfeldern (Zeilen) sind.

**Beispiel**:
char a [4], b [2], c [4]; _Teilfelder
char* Z[3] = {a, b, c};_ Zeigerfeld
![image](/uploads/ProzProg9Zeiger/pp61.gif?width=500)
#### Eigenschaften von ausgefransten Felder

1) Mehrdimensionale Felder und ausgefranste Felder werden gleichartig verwendet:
>>>Z[i] -enthält die Adresse auf (i+1)-tes Teilfeld,
>>>Z[i]+j -ist eine Adresse auf das (j+1)-te Element des (i+1)-ten Teilfeldes,
>>>Z [i][j] -enthält das (j+1)-te Element des (i+1)-ten Teilfeldes.

2) Teilfelder können genau nach Bedarf reserviert werden, z.B. dreieckiges Feld.

3) Der Nachteil ist der zusätzliche Speicherbedarf für das Zeigerfeld.

&rArr; Demo 9.
#### Kommandozeilenparameter

C/C++ bietet die Möglichkeit, beim Programmstart Kommandozeilenparameter an das Programm main zu übergeben:
void main(int argc, char *argv[]){...}
>>>>
>>>>
Die einzelnen Parameter sind nach dem Programmnamen jeweils durch Leerzeichen getrennt anzugeben.

Die main-Funktion übernimmt Kommandozeilen durch folgende Parameter:
>>* int argc -die Argumentenanzahl (argument count),
>>* char *argv[] -ein Zeigerfeld auf C-Strings (arg. vector),
>>* argv[0] ist ein Pointer auf den Programmnamen selbst,
>>* argv[1] ist ein Pointer auf den ersten echten Kommandozeilen-Parameter,
>>* argv[argc-1] ist ein Pointer auf den letzten Parameter,
>>* argc > 1 wegen dem Programmnamen.

&rArr;Demo 10.
## 9.7. Dynamische Speicherreservierung
#### Grundidee

Es ist nicht immer möglich, den Speicherplatz exakt vorher zu planen, z.B. wenn die Größe eines Feldes nur zur Laufzeit bekannt ist. Es ist unökonomisch, den maximalen Speicherplatz zu reservieren.

C++ bietet zwei Operatoren:
>>* new -reserviert den Speicherplatz genau in der richtigen Menge und zum richtigen Zeitpunkt,
>>* delete -gibt den Speicherplatz frei, wenn er nicht mehr benötigt wird.

Dynamische Datenobjekte (Variablen, Felder) werden auch nicht über einen Namen, sondern nur über ihre Adresse angesprochen.
Diese Adresse entsteht bei der Speicherbelegung und wird normalerweise einem Zeiger zugewiesen.
#### Speicherreservierung

Für die dynamische Speicherverwaltung gibt es einen großen Adressbereich, der als Heap bezeichnet wird.
![image](/uploads/ProzProg9Zeiger/pp62.gif?width=500)
Der Zeiger p enthält die Adresse auf die namenlose int&#8211;Variable.

Die Lebensdauer dynamisch reservierter Datenobjekte ist nicht an die Ausführungszeit eines Blocks gebunden.
Mit dem Operator new[ ] kann ein Feld dynamisch reserviert werden:

**Syntax**: T* p = new T [N]; T-Datentyp.

**Beispiel**: double* p= new double[10];

Der Zugriff auf die Feldelemente geschieht genau wie beim nicht dynamischen Feld:

%%(cpp)
double a[10];
a[4]= 4.4; oder *(a+3)= 3.3; _a- ist die Adresse
p[4]= 4.4; oder *(p+3)= 3.3;_ p- enthält die Adresse
%%
&rArr; Demo 11
#### Speicherfreigabe

**Synonym**: Löschen oder Zerstörung des Datenobjektes.

Der delete- Operator gibt den reservierten Platz frei, damit er bei neuen Reservierungen wieder belegt werden kann.

**Syntax**:
delete p; oder delete[] p;
p - Zeiger auf reserviertes Datenobjekt.

Wenn dynamische Datenobjekte nicht mehr gebraucht werden, müssen sie freigegeben werden. Ein nicht mehr zugängliches Datenobjekt wird als Speicherleck (memory leak) bezeichnet.

**Beispiel**: int* p= new int; p= new int;

Wenn ein Programm mit dem Speicherleck immer mehr Speicher reserviert, wird bei genügend langer Laufzeit an erschöpften Systemreserven scheitern.

&rArr; Demo 12
***
CategoryProzeduraleProgrammierung
Revision [cbaf74e]
Bearbeitet am 2017-08-16 15:46:14 von ClaudiaMichel
ADDITIONS
CategoryProzeduraleProgrammierung
DELETIONS
CategoryDelete
Revision [03f8fa9]
Bearbeitet am 2016-09-27 12:40:29 von NicoleHennemann
ADDITIONS
CategoryDelete
DELETIONS
CategoryProzProg
Revision [360cb5e]
Die älteste bekannte Version dieser Seite wurde von RonnyGertler am 2013-03-28 20:50:44 erstellt
ADDITIONS
![image](/uploads/ProzProg9Zeiger/BMBF_Logo_klein.jpg?width=200)
# Prozedurale Programmierung - Kapitel 9 - Zeiger
_<span class="right">Inhalte von Dr. E. Nadobnyh</p>_
## 9.1. Zeiger auf eine Variable
#
#### Grundidee

C/C++ enthält ein umfangreiches Zeigerkonzept, welches einen direkten Zugriff auf den Speicherplatz ermöglicht.

Ein <span style="text-decoration:underline;">Zeiger </span>ist eine Variable, die eine Adresse einer Variablen oder einer Funktion enthält.

**Synonyme**: Pointer, Pointervariable, Zeigervariable.

Man kann auf eine adressierte Variable oder Funktion indirekt über einen Zeiger zugreifen. Ein Zeiger adressiert eine Speicherstelle und gibt mit dem Typ an, wie diese Speicherstelle zu verwenden ist.


Bei Zeigern sind immer zwei unterschiedliche Typen verwickelt:

1) Zeigertyp (Pointertyp, Adresstyp) ist eigenen Datentyp des Zeigers und der Adresse.

2) Basistyp ist Typ der Variablen (des Objektes), auf die der Zeiger &#8220;zeigt&#8220;.

In C werden Zeiger auf eine Variable, auf eine Funktion, auf ein Feld und auf ein Zeiger definiert.

#### Zeiger auf eine Variable. Definition

Ein Zeiger wird wie jede andere Variable definiert: T* name;

Mit T* wird der Datentyp des Zeigers und mit T der Datentyp der adressierten Variable (Basistyp) bezeichnet.

**Beispiel**: int* p1; float* p2;
>>
>> Die Zeigervariablen p1 und p2 haben die Datentypen &#8222;Zeiger auf int&#8220; und &#8222;Zeiger auf float&#8220;.

Mit der Definition wird der Speicherplatz für den Zeiger reserviert. Ein Zeiger belegt bei einem 32-bit-Betriebs-system immer 4 Bytes.

Ein <span style="text-decoration:underline;">uninitialisierter </span>Zeiger wird wie jede andere Variable mit dem zufälligen Wert oder mit 0 initialisiert. Die Verwendung des uninitialisierten Zeigers verursacht einen Fehler, der oft schwer zu finden ist.
#### Initialisierter Zeiger

Wie jede andere Variable erhält ein Zeiger einen sinnvollen Wert bei der Definition oder durch die Zuweisung.

Eine Variablenadresse kann z.B. mit dem Adressoperator erhalten werden.

**Beispiel**: int a=3; int* p=&a;
![image](/uploads/ProzProg9Zeiger/pp55.gif?width=500)
Nach der Zeigerinitialisierung sagt man "p <span style="color:blue;">zeigt</span> auf a".
&rArr; Demo 1.
#### Nullzeiger

Ein Zeiger, der die Adresse NULL enthält, wird Nullzeiger (Nullpointer) genannt. Er zeigt auf kein gültiges Datenobjekt.

Es ist sinnvoll, Zeigervariablen mit NULL zu initialisieren, z.B.: <span style="color:blue;">double* p=NULL; </span>

NULL ist als eine Adresse mit dem Wert 0 in der Standardbibliothek definiert.

Man sollt auch immer NULL statt den Zahlenwert 0 verwenden, denn es ist nicht sicher, ob die Länge einer Integer-Variablen auch der Länge einer Adresse entspricht.
#### Inhaltsoperator

Der Inhaltsoperator (Dereferenzierungsoperator, Verweisoperator, Indirektionsoperator) wird mittels * bezeichnet.

Er bildet ein Sprachkonstrukt *x, der als ein Verweis benannt wird. Statt x kann eine Adresse, ein Zeigername, ein Feldname usw. verwendet werden.

Die Ausführung eines Inhaltsoperators ist vom Kontext abhängig:
1) Wenn ein Verweis in einem Ausdruck steht, wird er auswertet, d.h. durch ein Wert ersetzt.
2) Wenn ein Verweis links vom Gleichheitszeichen steht, wird er als Zuweisung interpretiert.

Achtung: Begriffsverwirrung bei Verweis vs. Referenz, Zeiger, Punkt-Operator!
#### Impliziter Inhaltsoperator

Zur Laufzeit wird für jede Variable eine Adresse zugeordnet. Dabei wird der Variablenname durch den Verweis ersetzt.

Wenn der Variable a die Adresse 0012FF88 zugeordnet wird, dann kann z.B. die Zuweisung <span style="color:blue;"> a=a+4;</span> zur Laufzeit folgendermaßen aussehen:

![image](/uploads/ProzProg9Zeiger/pp56.gif?width=450)
Der Verweis *(0012FF88) enthält den impliziten Inhaltsoperator * .

#### Inhaltsoperator und Zeiger

Die Verwendung des Inhaltsoperators *p bedeutet einen lesenden oder schreibenden Zugriff auf die Variable, deren Adresse im Zeiger p enthalten ist.

**Beispiel**:
%%(cpp)
>>int a=3, tmp;
>>int* p = &a; _statt a kann *p benutzt werden
>>tmp = *p;_ lesender Zugriff auf die Variable a
>>*p=5; _schreibender Zugriff auf die Variable
%%
Achtung: Das Zeichen * in der Zeiger-Definition int* ist ein Teil der Typbezeichnung aber kein Inhaltsoperator.

&rArr; Demo 2
#### Inhaltsoperator und Priorität

Bei der Verwendung des Operators * muß man die Priorität und Assoziativität von Operatoren genau beachten. Dies erscheint zunächst etwas schwierig, da dieser Operator ungewohnt ist.
**Beispiele**:

%%(cpp)
int x=3;
int* px;_ px ist ein Zeiger auf int
px = &x; _in px die Adresse von x speichern
*px;_ Wert der Variablen x
*px + 1; _Wert von x plus 1
*(px+1);_ Adresse in px erhöhen.
>>>>>> _Inhalt der Nachbar-Variable
*px += 1;_ Inhalt von x erhöhen
(*px)++; _Inhalt von x inkrementieren
*px++;_ wie *(px++); wegen Assoziativität
*++px; _wie *(++px); wegen Assoziativität
%%

## 9.2. Zeiger auf eine Variable. Details
#### Adresstyp T*
Jede Adresse hat einen bestimmten Typ, der als Adresstyp (auch Zeigertyp) bezeichnet wird.

Ein Adressoperator liefert eine Adresse vom Typ T*, wenn sein Argument vom Typ T ist.

Im folgenden Beispiel hat die Adresse den Typ <span style="color:blue;">int*</span> :
int a=3; printf("%p", &a);_ <span style="color:blue;">0012FF88</span>

Einem Zeiger kann eine Adresse zugewiesen werden, die den gleichen Zeigertyp hat.

T a; T* p = &(a);

Ein Inhaltsoperator liefert einen Wert vom Typ T, wenn sein Argument vom Typ T* ist.

T* p; T a = *(p);

Legende: T &#8211; Datentyp, T* - Adresstyp.
#### Konvertierung von Adresstypen

Synonym: Typumwandlung von Zeigertypen.

Den Typ einer Adresse kann konvertiert werden. Der binäre Code der Adresse bleibt dabei unverändert. Die Konvertierung von Zeigertypen verwendet man z.B. in der objektorientierten Programmierung.

Eine implizite Zeigertypen-Konvertierung von eingebauten Datentypen ist nicht erlaubt, z.B.:

>>> short b=4; short* pb=&b;
>>> int* pa=pb; _Fehler: Konvertierung nicht möglich

Eine explizite Zeigertypen-Konvertierung ist nahezu immer ein logischer Fehler, z.B.:

>>> pa=(int*)pb;_ keine Fehlermeldung

&rArr; Demo 3.
#### Zeiger und Konstanten

Wenn man einen Zeiger p benutzt, sind zwei Variablen beteiligt: der Zeiger und die Variable, auf die er zeigt (dereferenzierte Variable, Objekt).
![image](/uploads/ProzProg9Zeiger/pp57.gif?width=350)
Es gibt drei Möglichkeiten const zu verwenden:
1. Zeiger auf konstante Variable, z.B.:
>>const int* <span style="color:blue;"> p</span>;
>>int const* <span style="color:blue;"> p</span>;
>>>
Eine Veränderung des Zeigers ist möglich.

2. Konstanter Zeiger auf Variable, z.B.:
>> int* <span style="color:blue;">const p </span>;
>>>>>
Eine Veränderung der Variablen ist möglich.

3. Konstanter Zeiger auf konstante Variable, z.B.:
>>const int* <span style="color:blue;">const p</span>;
>>int const* <span style="color:blue;">const p</span>;
#### Zeiger als Parameter

Ein Zeiger als Parameter stellt einen IN-OUT-Parameter dar. Die adressierte Variable kann in der Funktion gelesen und beschrieben werden. Eine Übergabe per Zeiger ist ähnlich wie call-by-reference.

Syntax für Prototyp und Aufruf:
>>>void f(T* p); T a; f(<span style="color:blue;">&a</span> ); T- Datentyp (Basistyp).

Diese Übergabe ist aber tatsächlich call-by-value. Der Parameter p wird beim Aufruf mit dem Wert des Arguments, d.h. mit der Adresse der Variablen a initialisiert.
**Beispiele**
%%(cpp)
int main()
{ int x=3, y;
>> scanf(&#8220;%i&#8220;, &y);
>> swap(&x, &y);
>> return 0;
}
void swap(int* x, int* y)
{ int h = *x;
>>*x = *y;
>>*y = h;
}
%%
#### Rückgabe per Zeiger

**Synonym:** Zeiger als Rückgabewert

Eine Rückgabe per Zeiger ist ähnlich wie Rückgabe per Referenz. Der Aufrufer erhält die Adresse der Variablen (des Objektes) und kann direkt mit dem Original arbeiten.

Syntax für Prototyp und Aufruf:
>>> T* f( ); T* a = f(); T- Datentyp.


**Beispiel**:
%%(cpp)
int main()
{ int* a=f();
>>return 0;
}
int b=5;
int* f( )
{ return &b;
}
%%
Eine Rückgabe per Zeiger ist tatsächlich die Rückgabe per Wert. Eine Adresse von Typ T* wird zurückgeliefert und kann einem Zeiger zugewiesen werden.

Bei der Manipulationen mit &#8222;großen&#8220; Variablen (Objekten) kann man die Kopierarbeit reduzieren, wenn man statt Objekten Zeiger verwendet.

&rArr; Demo 4.
**Gefahren**bei der Rückgabe per Zeiger:
Die Adresse von lokalen Variablen (Objekte) dürfen nicht zurückgegeben werden, weil sie nach dem Rückkehr verschwunden sind.

**Negatives Beispiel :**
Die lokale Variable existiert nur während der Funktionsausführung. Die Ausgabe ist unbestimmt, sie könnte zufällig auch 1234 lauten.
%%(cpp)
int main(void)
{ int* p = f();
printf("%d", *p);
return 0;
}
int* f(void)
{ int x = 1234;
>>return (&x);
}
%%
## 9.3. Adressarithmetik
#### Adressarithmetik

Als Adressarithmetik (Zeigerarithmetik, pointer arithmetic) bezeichnet man spezifische Adressoperationen mit beschränkte Möglichkeiten. Schwerpunkt ist die Behandlungvon Feldern.

Es sind natürlich nur Operationen erlaubt, die zu sinnvollen Ergebnissen führen:
1. Inkrement und Dekrement: ++, --.
2. Addition und Subtraktion mit ganzzahligem Wert.
3. Zeigersubtraktion.
4. Zeigervergleich: >, >=, <, <=, != und
##### .
5. Alle anderen Operationen sind verboten.

Zeigersubtraktion und Vergleich ist nur dann sinnvoll, wenn beide Zeiger auf <span style="color:blue;">Elemente des gleichen Feldes</span> zeigen.

Anmerkung: Arithmetische Operationen sind nicht erlaubt bei Zeigern auf Funktionen.
#### Name eines Feldelementes

In einem eindimensionalen Feld <span style="color:blue;">T a[N];</span> ist der Name eines Feldelementes aus einem Feldnamen a und einem Index i zusammengestellt: <span style="color:blue;">a[i]</span>
>>>>>>>>>>>>
Für eingebaute Datentypen T transformiert der Compiler diesen Namen zu äquivalenter Form:
a[i] &rArr; *(a + i).
Hier sind beteiligt:
* - ein Inhaltsoperator und
+ - eine Summe der Adressarithmetik.

Beispiel: int a[2], b[2]; int i=1;

a[i]=5; &rArr; *(a + i) = 5;

a[0] = b[0]; &rArr; *(a+0)=*(b+0); &rArr; *a=*b;

Die Addition der Adressarithmetik ist eine spezifische und wichtige Operation.

Die Adresse des einzelnen Elementes a[i] wird vom Compiler nach der folgenden Formel berechnet:

>>>>>>>> (int)a + i *x

**Legende**:
+ und * -normale Summe und Multiplikation, x -die Anzahl von Bytes, die Datentyp T des Feldelementes besitzt, x= sizeof(T).

&rArr; Demo 5.
In einem zweidimensionalen Feld T a[N][M]; wird der Elementnamen a[i][j] vom Compiler zu äquivalenter Form umformuliert :
a[i][j] *(*(a + i) + j)
**Legende**:
* - ein Inhaltsoperator und
+ - eine Summe der Adressarithmetik.

Die Adresse des einzelnen Elementes a[i][j] wird vom Compiler nach der folgenden Formel berechnet:
(int)a + i*M*x + j*x

**Legende**:
+ und * -normale Summe und Multiplikation,
x -die Anzahl von Bytes, die den Datentyp T besitzt: x= sizeof(T).
## 9.4. Zeiger und eindimensionale Felder
#### Zeiger auf Feldelement

Zwischen Zeiger und Felder besteht in C/C++ eine enge Verwandtschaft.

1) Ein Feld T a[N] hat Elemente vom Datentyp T.
2) Ein Feldname ist eine Adresse auf erstes Element des Feldes und hat den Datentyp T*.
3) Ein Zeiger T* p kann als Zeiger auf ein Feldelement des Feldes T a[N] benutzt werden.
4) Folgende Paare sind äquivalent:
<table class="data" width="500px" style="border:0">
>><tr>
>>><td>
>>>>Adresse auf das ersten Element
>>></td>
>>><td>
>>>>a
>>></td>
>>><td>
>>>>&a[0]
>>></td>
>></tr>
>><tr>
>>><td>
>>>>Zeiger-Zuweisung
>>></td>
>>><td>
>>>>p=a
>>></td>
>>><td>
>>>>p=&a[0]
>>></td>
>></tr>
>><tr>
>>><td>
>>>>Zugriff auf Element über den Feldnamen
>>></td>
>>><td>
>>>>a[i]
>>></td>
>>><td>
>>>>*(a+i)
>>></td>
>></tr>
>><tr>
>>><td>
>>>>Zugriff auf Element über den Zeiger
>>></td>
>>><td>
>>>>p[i]
>>></td>
>>><td>
>>>>*(p+i)
>>></td>
>></tr>
</table>
Zeiger auf Feldelemente können bei der Übergabe eines Feldes als Parameter verwendet werden.

Dafür gibt es drei verschiedenen Schreibweise, die äquivalent sind:
>>> void f( T a[n]); void f( T a[ ]); void f( T* a);
%%(cpp)
int main()
{ int b[4]; f( b, 4);
>>return 0;
}
void f( int* a, int n);
void f( int a[4], int n);
void f( int a[ ], int n);
%%
**Vorsicht**: Zwei Definitionen haben gleiche Syntax und können nur durch Kontext unterschieden werden:
>>* int b[4] steht nicht in der Parameterliste. Hier werden vier Variablen angelegt.
>>* int a[4] steht in der Parameterliste. Hier wird ein Zeiger definiert.
#### Zeiger als Laufvariable

Ein Zeiger kann elegant als eine Laufvariable einer Schleife bei der Feld-Bearbeitung verwendet werden.

Nach der folgende Addition enthält der Zeiger p die Adresse des Feldelementes a[i]:

T a[N]; T* p=a; p+=i ;
**Beispiel**:
%%(cpp)
const int n=4; int a[n];
int sum=0;
for(int* p=a; p<a+n; p++)
{ _Zeiger p als Laufvariable
>>> sum+=*p;
}
%%
#### Zeiger und Zeichenkette

Zeichenketten (nullterminierte C-Strings) sind Felder vom Typ char[ ], wobei das letzte Zeichen immer das Nullzeichen &#8216;\\0&#8216; sein muss.

Zeichenketten können unterschiedlich definiert werden:
1) Ein benanntes char-Feld a, das mittels der Zeichen initialisiert wird, z.B.:
>>char a[ ] = {&#8216;a&#8216;, &#8216;b&#8216;, &#8216;c&#8216;, &#8216;\\0&#8216;}; oder char a[ ] = &#8220;abc&#8220;;

2)Ein namenloses char-Feld (Stringliteral) und ein Zeiger p der mit der Feldadresse initialisiert wird, z.B.:
>>const char* p = &#8220;qwert&#8220;;

Besonderheit: Bei einigen Systemen sind Stringliterale schreibgeschützt und daher muss der Zeiger als const definiert werden.

&rArr; Demo 6 und 7
## 9.5. Besondere Zeiger
#### Zeiger auf Zeiger

Da Zeigervariablen selbst Datenobjekte sind, kann auch auf sie über Zeiger zugegriffen werden.
![image](/uploads/ProzProg9Zeiger/pp58.gif?width=450)
**Syntax:**
T **p;_ T - Datentyp**Beispiel**:
%%(cpp)
int main()
{ int x= 10;
>>int* px= &x;
>>int** ppx= &px;

>>int y= **ppx; _Zugriff auf x
>>return 0;
}
%%
### Adresstyp T**

Ein Adressoperator liefert eine Adresse vom Typ T**, wenn sein Argument vom Typ T* ist.

T a; T* p= &a; T** pp= &p;

Ein Inhaltsoperator liefert eine Adresse vom Typ T*, wenn sein Argument vom Typ T** ist.

T* p= *pp; T a= *p;

Legende: T &#8211; Datentyp, T **und T* - Adresstypen.**Beispiel**:
%%(cpp)
>>int x= 10;
>>int* px1= &x;
>>int** ppx= &px1;
>>int* px2= *ppx;
>>int y= *px2;_ Zugriff auf x
%%
#### Zeiger auf ein Feld
Ein zweidimensionales Feld T aa[N][M] ist ein eindimensionales Feld mit N Zeilen. Jede Zeile ist selbst ein eindimensionales Feld. T- Typ eines Feldelementes. Der Datentyp einer Zeile ist T [ ][M]. Der Feldname aa ist eine Adresse vom Typ der Zeile.
C/C++ hat einen Zeiger auf ein Feld (Feldzeiger) vom Typ T[][M]. Dieser Zeiger kann hauptsächlich bei der Parameterübergabe verwendet werden, z.B.:
%%(cpp)
void f(int pp[ ][M])
{ cout sizeof(*pp)endl;
>> cout sizeof(int)*M;
}
int main()
{ int aa[N][M]; f(aa);
>>return 0;
} %%
**Äquivalente Schreibweise:**
void f(T pp[ ][M]); void f(T pp[N][M]); void f(T(*pp)[M]);
#### Adresstyp T [][M]

Ein Inhaltsoperator liefert eine Adresse vom Typ T[], wenn sein Argument vom Typ T [][M] ist.

void f( T pp[][M], T p[] ) { p = *pp; }

**Legende**:
T &#8211;Datentyp, T[][M] und T[] -Adresstypen.

Umgekehrt funktioniert es nicht. Ein Adressoperator liefert eine Adresse vom Typ T**, wenn sein Argument vom Typ
T[] ist: T** zz = &p;

&rArr; Demo 8.
#### Zeiger auf Funktionen

>>* Jede kompilierte Funktion belegt einen Speicherbereich und besitzt ein Eingangspunkt.
>>* Der Name einer Funktion ist eine symbolische Bezeichnung der Adresse des Eingangspunktes (Funktionsadresse).
>>* In C/C++ gibt es einen Zeiger auf Funktionen (Funktionszeiger), der die Funktionsadresse enthalten kann.
>>* Ein Funktionszeiger kann verwendet werden, um die Übergabe einer Funktion als Parameter zu programmieren.
>>* Der Funktionszeiger wird dabei als ein formaler Parameter verwendet.
>>* Der Name einer Funktion wird dabei als ein aktueller Parameter verwendet.

**Syntax**:
T (*fp) (Parameterliste);

**Legende**:
fp &#8211; Funktionszeiger, T &#8211;Rückgabetyp.
**Beispiel:**
%%(cpp)
int main()
{
>> int (* fp) (int);
>> fp = f2;
>> cout f1(fp);
>> return 0;
}
int f2( int n ){ return n*2; }
int f3( int n ){ return n*3; }
int f1( int (*z) (int) )
{ return z(100);
}
%%
#### Komplexe Zeiger
![image](/uploads/ProzProg9Zeiger/pp59.gif?width=500)
**Eigenschaften:**

1) Es entsteht kein neuer Datentyp.

2) Kein Speicherplatz wird reserviert.

3) Für beliebige Datentypen T anwendbar.

4) Als Konvention in C sollten neue Typennamen gesamt oder mit dem erstem Buchstaben großgeschrieben werden.

5) Neue Typnamen dienen zur Verbesserung der Lesbarkeit von Programmen.

6) typedef &#8211;Vereinbarungen sollen in Headerdateien isoliert werden. Dann können Portabilitäts- und Wartungsprobleme vereinfacht werden.
## 9.6. Zeigerfelder
#### Grundidee

Zeigerfelder (Zeigervektoren, Pointer-Arrays) sind Felder deren Elemente Zeiger sind.

Syntax: T* a[M]; T- Datentyp,

Für viele Fälle ist die Arbeit mit Zeigern effektiver, als umfangreiche Datenbestände zu bearbeiten, z. B. werden beim Sortieren nur die Zeiger und nicht die Variablen (Objekte, Datenbestände) von &#8222;großen&#8220; Datentypen selbst sortiert.
![image](/uploads/ProzProg9Zeiger/pp60.gif?width=500)
#### Ausgefranste Felder

Ausgefranste Felder (ragged arrays) sind mehrdimensionale Felder, deren Elemente wiederum aus Feldern (Teilfeldern) bestehen. Die einzelnen Teilfelder können unterschiedlich lang sein.

Ein ausgefranstes Feld wird mit Hilfe des Zeigerfeldes aufgefasst, wenn die Elemente des Zeigerfeldes wiederum Adressen von Teilfeldern (Zeilen) sind.

**Beispiel**:
char a [4], b [2], c [4]; _Teilfelder
char* Z[3] = {a, b, c};_ Zeigerfeld
![image](/uploads/ProzProg9Zeiger/pp61.gif?width=500)
#### Eigenschaften von ausgefransten Felder

1) Mehrdimensionale Felder und ausgefranste Felder werden gleichartig verwendet:
>>>Z[i] -enthält die Adresse auf (i+1)-tes Teilfeld,
>>>Z[i]+j -ist eine Adresse auf das (j+1)-te Element des (i+1)-ten Teilfeldes,
>>>Z [i][j] -enthält das (j+1)-te Element des (i+1)-ten Teilfeldes.

2) Teilfelder können genau nach Bedarf reserviert werden, z.B. dreieckiges Feld.

3) Der Nachteil ist der zusätzliche Speicherbedarf für das Zeigerfeld.

&rArr; Demo 9.
#### Kommandozeilenparameter

C/C++ bietet die Möglichkeit, beim Programmstart Kommandozeilenparameter an das Programm main zu übergeben:
void main(int argc, char *argv[]){...}
>>>>
>>>>
Die einzelnen Parameter sind nach dem Programmnamen jeweils durch Leerzeichen getrennt anzugeben.

Die main-Funktion übernimmt Kommandozeilen durch folgende Parameter:
>>* int argc -die Argumentenanzahl (argument count),
>>* char *argv[] -ein Zeigerfeld auf C-Strings (arg. vector),
>>* argv[0] ist ein Pointer auf den Programmnamen selbst,
>>* argv[1] ist ein Pointer auf den ersten echten Kommandozeilen-Parameter,
>>* argv[argc-1] ist ein Pointer auf den letzten Parameter,
>>* argc > 1 wegen dem Programmnamen.

&rArr;Demo 10.
## 9.7. Dynamische Speicherreservierung
#### Grundidee

Es ist nicht immer möglich, den Speicherplatz exakt vorher zu planen, z.B. wenn die Größe eines Feldes nur zur Laufzeit bekannt ist. Es ist unökonomisch, den maximalen Speicherplatz zu reservieren.

C++ bietet zwei Operatoren:
>>* new -reserviert den Speicherplatz genau in der richtigen Menge und zum richtigen Zeitpunkt,
>>* delete -gibt den Speicherplatz frei, wenn er nicht mehr benötigt wird.

Dynamische Datenobjekte (Variablen, Felder) werden auch nicht über einen Namen, sondern nur über ihre Adresse angesprochen.
Diese Adresse entsteht bei der Speicherbelegung und wird normalerweise einem Zeiger zugewiesen.
#### Speicherreservierung

Für die dynamische Speicherverwaltung gibt es einen großen Adressbereich, der als Heap bezeichnet wird.
![image](/uploads/ProzProg9Zeiger/pp62.gif?width=500)
Der Zeiger p enthält die Adresse auf die namenlose int&#8211;Variable.

Die Lebensdauer dynamisch reservierter Datenobjekte ist nicht an die Ausführungszeit eines Blocks gebunden.
Mit dem Operator new[ ] kann ein Feld dynamisch reserviert werden:

**Syntax**: T* p = new T [N]; T-Datentyp.

**Beispiel**: double* p= new double[10];

Der Zugriff auf die Feldelemente geschieht genau wie beim nicht dynamischen Feld:

%%(cpp)
double a[10];
a[4]= 4.4; oder *(a+3)= 3.3; _a- ist die Adresse
p[4]= 4.4; oder *(p+3)= 3.3;_ p- enthält die Adresse
%%
&rArr; Demo 11
#### Speicherfreigabe

**Synonym**: Löschen oder Zerstörung des Datenobjektes.

Der delete- Operator gibt den reservierten Platz frei, damit er bei neuen Reservierungen wieder belegt werden kann.

**Syntax**:
delete p; oder delete[] p;
p - Zeiger auf reserviertes Datenobjekt.

Wenn dynamische Datenobjekte nicht mehr gebraucht werden, müssen sie freigegeben werden. Ein nicht mehr zugängliches Datenobjekt wird als Speicherleck (memory leak) bezeichnet.

**Beispiel**: int* p= new int; p= new int;

Wenn ein Programm mit dem Speicherleck immer mehr Speicher reserviert, wird bei genügend langer Laufzeit an erschöpften Systemreserven scheitern.

&rArr; Demo 12
***
CategoryProzProg