Einführung in C++ (Teil 2)




Arrays:

Arrays sind Datenfelder, in denen sich mehrere Datenelemente desselben Datentyps befinden, wobei auf die einzelnen Daten über einen Index zugegriffen werden kann. Sämtliche Daten haben zwar denselben Namen, können aber eindeutig vom Programm über eine Nummer angesprochen werden. Bei der Deklaration eines Feldes gibt man die Anzahl der Daten an, die das Feld speichern soll, sowie den für sämtliche Daten gültigen Datentyp.

Array-Deklaration:              Datentyp Feldname[Anzahl]

Beispiel für Array-Deklaration:    int a[100]
Man hat dann 100 Variblen vom Typ int: a[0], a[1], ... ,a[99].

Beispiel 1:

// Lese 5 Zahlen ein und gebe sie in umgekehrter Reihenfolge auf dem Bildschirm aus.

#include <iostream.h>

void main()
{
  const int Anzahl = 5;
  int i, Feld[Anzahl];              // Array-Deklaration

  cout << "Gib " << Anzahl << " ganze Zahlen ein: " << endl;
  for(i = 0; i < Anzahl; i++)
  {
    cout << "gib eine Zahl ein: ";
    cin >> Feld[i];
  }
  cout << "Zahlen in umgekehrter Reihenfolge: " << endl;
  for(i = Anzahl-1; i >= 0; i--) cout << " " << Feld[i];
  cout << endl;
}

Output:
Gib 5 ganze Zahlen ein:
Gib eine Zahl ein: 3
Gib eine Zahl ein: 5
Gib eine Zahl ein: 2
Gib eine Zahl ein: 7
Gib eine Zahl ein: 8
Zahlen in umgekehrter Reihenfolge:
8 7 2 5 3


Zweidimensionale Felder (Matrizen):

Eine Matrix kann man auffassen als einen Array, dessen Elemente wieder Arrays sind.

Deklaration:             Datentyp   Feldname[Anzahl1] [Anzahl2]
Beispiel für Matrix-Deklaration:   int Tabelle[2] [2]

Man hat dann 4 Variablen vom Typ int: Tabelle[0][0], Tabelle[0][1], Tabelle[1][0], Tabelle[1][1].

Beispiel 2:

// Programm Matrix1: Ein- und Ausgabe der Matrix:

#include <iostream.h>

void main( )
{
  const int dim1 = 2, dim2 = 3;
  int matrix [dim1] [dim2] = {{1,2,3}, {4,5,6}};
  for (int i = 0; i < dim1; i++)
  {
    for (int j = 0; j < dim2; j++)
    cout << matrix [i] [j] << " ";
    cout << endl;
  }
}

Output:
1 2 3
4 5 6

Beispiel 3:

//Programm Matrix2: Ein- und Ausgabe einer beliebigen 2x2 - Matrix.

#include <iostream.h>

void main( )
{
  int matrix [2] [2];
  for (int i = 0; i < 2; i++)
  {
    for (int j = 0; j < 2; j++)
    {
      cout << "Geben sie das Matrixelement a" << i+1 << j+1 <<" ein: ";
      cin >> matrix [i] [j];
    }
  }
  cout << "Die eingegebene Matrix lautet: " << endl;
  for (int i = 0; i < 2; i++)
  {
    for (int j = 0; j < 2; j++)
    cout << matrix [i] [j] << " ";
    cout << endl;
  }
}

Output:
Geben sie das Matrixelement a11 ein: 2
Geben sie das Matrixelement a12 ein: 5
Geben sie das Matrixelement a21 ein: 8
Geben sie das Matrixelement a22 ein: 9
Die eingegebene Matrix lautet:
2 5
8 9


Zeiger (Pointer)

Zeiger sind Variablen. Sie speichern aber im Gegensatz zu konventionellen Variablen Adressen.

Zeigerdeklaration:             Datentyp   *Variablenname

Um nur einige Vorteile zu nennen:
  1. Mit Zeigern lassen sich dynamische Datenstrukturen konstruieren. Unter einer dynamischen Datenstruktur versteht man eine Datenstruktur, deren Größe und Aufbau sich zur Laufzeit des Programms verändern kann.
  2. Es gibt einen Zusammenhang zwischen Zeigern und Arrays.
  3. Zeiger werden als Parameter von Funktionen verwendet, um Daten der aufrufenden Funktion ändern zu können.

Operator

Bedeutung

&

Adresse

*

Inhalt


Über den Adressoperator & kann man Zeiger auf bereits vorhandene Variablen konstruieren.
Der Inhaltsoperator * liefert den Wert der durch den Zeiger referenzierten Variablen.

Definition und Wertzuweisung von Zeigern:

Die Zuweisung einer Adresse an eine Zeigervariable erfolgt mit Hilfe des Adressoperators &.

Beispiel 4:

//Pointer1

#include <iostream.h>

void main()
{
  int x; int *px, *p;

  p = 0xbffff684;
  x = 5;
  px = &x;

  cout << "Inhalt von p: " << p << endl;
  cout << "Inhalt von Speicherplatz 0xbffff684: " << *p << endl;
  cout << "Adresse von x: " << px << endl;
  cout << "Inhalt von x: " << *px << endl;        //entspricht: cout << "Inhalt von x: " << x << endl;
}

Output:
Inhalt von p: 0xbffff684
Inhalt von Speicherplatz 0xbffff684: 5
Adresse von x: 0xbffff684
Inhalt von x: 5

Die Variable x ist eine gewöhnliche int-Variable und p und px sind Pointervariablen (auch kurz Pointer genannt). Dem Pointer p wird als Wert eine Adresse zugewiesen: p = 0xbffff684. Die int-Variable x erhält den Wert 5. An der Stelle im Speicher des Computers, an der x gespeichert wurde, befindet sich jetzt der Wert 5. Im nächsten Schritt wird dem Zeiger px die Adresse von x zugewiesen. Der Zeiger px zeigt nun also auf die Variable x.

Man kann dies auf 2 Arten veranschaulichen:

Möglichkeit 1:

p = 0xbffff684; x = 5; px = &x:

Möglichkeit 2:

p = 0xbffff684; x = 5; px = &x:


Zugriff auf den Wert der referenzierten Variablen:

Beispiel 5:

//Pointer2

#include <iostream.h>

void main()
{
  int x;
  int *px;
  px = &x;

  x=4;
  cout << "x = " << x << endl;
  *px=2;
  cout << "x = " << x << endl;
}

Output:
x = 4
x = 2

Veranschaulichung:

px = &x; x = 4:

*px = 2:


Zuweisung zweier Zeiger:

Die Zuweisung einer Adresse an einer Zeigervariable kann auch durch Zuweisung einer anderen Zeigervariablen erfolgen.

Beispiel 6:

//Pointer3

#include <iostream.h>

void main()
{
  int x,y;
  int *px,*py;

  x=4;
  y=3;
  px=&x;
  py=&y;
  cout << "px zeigt auf den Wert " << *px << " und " << "py zeigt auf den Wert " << *py << endl;
  px=py;
  cout << "px zeigt auf den Wert " << *px << " und " << "py zeigt auf den Wert " << *py << endl;
}

Output:
px zeigt auf den Wert 4 und py zeigt auf den Wert 3
px zeigt auf den Wert 3 und py zeigt auf den Wert 3

Veranschaulichung:

x = 4; y = 3; px = &x; py = &y:

px = py:

Zusammenhang zwischen Arrays und Pointers

Ein Zeiger kann nicht nur auf eine einzelne Variable, sondern auch auf ein Element einer Folge von Werten (Array) zeigen. Man kann den Namen einer Array-Variablen als Zeiger auf ihr erstes Element auffassen:

int a[100]                                         //Array mit 100 Elementen vom Typ int

a ist identisch mit &a[0]               //Der Bezeichner der Folge, nämlich a, ist gleichbedeutend mit dem ersten Element der Folge.
Somit ist *a identisch mit a[0]

Verallgemeinerung:
a + i entspricht &a[i]
*(a + i) entspricht a[i]

Es gibt also neben der schon bekannten Methode eine zweite, mit der auf die Elemente eines Arrays zugegriffen werden kann (Beispiel 7).

Beispiel 7:

//Pointer4

#include <iostream.h>

void main()
{
  const int Anzahl = 5;
  int i, Feld[Anzahl];

  cout << "Gib " << Anzahl << " ganze Zahlen ein: " << endl;
  for(i = 0; i < Anzahl; i++)
  {
    cout << "gib eine Zahl ein: ";
    cin >> *(Feld+i);
  }
  cout << "Zahlen in umgekehrter Reihenfolge: " << endl;
  for(i = Anzahl-1; i >= 0; i--) cout << " " << *(Feld+i);
  cout << endl;
}

Output:
Gib 5 ganze Zahlen ein:
Gib eine Zahl ein: 3
Gib eine Zahl ein: 5
Gib eine Zahl ein: 2
Gib eine Zahl ein: 7
Gib eine Zahl ein: 8
Zahlen in umgekehrter Reihenfolge:
8 7 2 5 3


Funktionen

Die Unterprogramme in C++ heißen Funktionen. Dadurch entsteht die Möglichkeit eine einmal definierte Anweisungsfolge von einer beliebigen Stelle des Programms zu einem beliebigen Zeitpunkt aus "aufzurufen". Ein solcher Aufruf bewirkt, daß der Programmcode des Unterprogramms ausgeführt und nach dessen Ende mit der nächsten Anweisung hinter der Aufrufstelle fortgefahren wird.

Anmerkung: In C++ wird keine Unterscheidung zwischen Procedures (Subroutinen) und Funktionen gemacht!

Funktionsdefinition Rückgabedatentyp   Funktionsname(Parameterliste)
{
  Anweisungen;
  return Wert;             //nur wenn die Funktion einen Rückgabe-
}                                 //datentyp ungleich void hat.
Funktion-Deklaration Rückgabedatentyp   Funktionsname(Parameterliste)
Funktionsaufruf Funktionsname(Parameterliste)
Beim Funktionsaufruf werden die bei der Funktionsdefinition festgelegten Anweisungen ausgeführt.

Beispiel 8:

//Produkt zweier Zahlen a und b

#include <iostream.h>

int produkt(int a, int b)
{
  return(a*b);
}

void main()
{
  int x, y;

  cout << "Gib zwei ganze Zahlen ein: ";
  cin >> x >> y;
  cout << "Das Produkt von a und b ist " << produkt(x,y) << endl;
}

Output:
Gib zwei ganze Zahlen ein: 4 5
Das Produkt von a und b ist 20

Der Funktionsaufruf geschieht durch produkt(x,y) in der Funktion main. Dabei erfolgt ein Sprung zur Funktion produkt, die das Produkt der Zahlen x und y berechnet und das Ergebnis mittels der Anweisung return an die Funktion main zurückgibt. Das zurückgegebene Ergebnis tritt dann an die Stelle des Funktionsaufrufs.
Die Parameter a und b treten als interne Variablen auf, die durch den Funktionsaufruf die Werte von x und y als Anfangswert erhalten. Die Parameterliste des Funktionsaufrufs erhält also die Namen der Variablen die an die Funktion übergeben werden sollen.

Allgemeines:
Die Funktion produkt hätte auch hinter der Funktion main stehen können. Sie muß dann allerdings zusätzlich in der Funktion main deklariert werden. Dies geschieht durch:

int produkt(int a, int b);

Achtung: Funktionen können nie innerhalb anderer Funktionen stehen!

Beispiel 9:

//Lese 3 ganze Zahlen ein und gib die größte Zahl aus.

#include <iostream.h>

void main()
{
  int Zahl1, Zahl2, Zahl3;
  void groesste_Zahl(int z1, int z2, int z3);
  cout << "Geben Sie bitte drei ganze Zahlen ein: ";
  cin >> Zahl1 >> Zahl2 >> Zahl3;
  groesste_Zahl(Zahl1, Zahl2, Zahl3);
}

void groesste_Zahl(int z1, int z2, int z3)
{
  if (z2 > z1) z1=z2;
  if (z3 > z1) z1=z3;
  cout << "Die größste Zahl ist: " << z1 << endl;
}

Output:
Geben Sie bitte drei ganze Zahlen ein: 2 8 1
Die größste Zahl ist: 8

Gültigkeitsbereiche von Variablen

Der Funktionskörper ist ein Block (durch geschweifte Klammern { } begrenztes Programmstück). Alle Variablen einer Funktion existieren somit nur in diesem Block, sind also anderen Funktionen völlig unbekannt. Diese Variablen bezeichnet man als lokale Variablen. Die in der Parameterliste aufgeführten Variablen nehmen eine Sonderstellung ein. Sie werden innerhalb der Funktion wie lokale Variablen betrachtet und stellen (von außen gesehen) die Datenschnittstelle zur Funktion dar. Weiterhin gibt es noch globale Variablen, die außerhalb von main und jeglicher anderer Funktion definiert werden. Sie sind in allen Teilen des Programms gültig.

Beispiel 10:

//Scope

#include <iostream.h>

int i = 1;                   //globale Variable, d.h. überall bekannt.

void funktion1()
{
  int c = 4;               //lokale Variable, d.h. nur in funktion1 bekannt
  cout << "Wert von i innerhalb der Funktion funktion1: "<< i << endl;
  cout << "Wert von c innerhalb der Funktion funktion1: "<< c << endl;
  i = 5;
  cout << "Wert von i innerhalb der Funktion funktion1: "<< i << endl;
}

void main()
{
  int c = 2;               //lokale Variable, d.h. nur in main bekannt

  i = 8;
  cout << "Wert von i innerhalb der Funktion main: "<< i << endl;
  cout << "Wert von c innerhalb der Funktion main: "<< c << endl;
  funktion1();
  cout << "Wert von i innerhalb der Funktion main: "<< i << endl;
  cout << "Wert von c innerhalb der Funktion main: "<< c << endl;
}

Output:
Wert von i innerhalb der Funktion main: 8
Wert von c innerhalb der Funktion main: 2
Wert von i innerhalb der Funktion funktion1: 8
Wert von c innerhalb der Funktion funktion1: 4
Wert von i innerhalb der Funktion funktion1: 5
Wert von i innerhalb der Funktion main: 5
Wert von c innerhalb der Funktion main: 2

Die Variable i ist eine globale Variable, d.h. man kann sie in beiden Funktionen benutzen. Bei komplizierteren Programmen verliert man aber beim Arbeiten mit globalen Variablen schnell den Überblick. Das Programm wird meist schlecht lesbar und unnötig kompliziert.

Beispiel 11:

//Vertauschen zweier Werte x und y durch Gebrauch von globalen Variablen

#include <iostream.h>

int x,y;

void vertausche()
{
  int h;
  h = x;
  x = y;
  y = h;
}

void main()
{
  x = 5;
  y = 7;
  cout << "x = " << x << " und " << "y = " << y << endl;
  vertausche();
  cout << "x = " << x << " und " << "y = " << y << endl;
}

Output:
x = 5 und y = 7
x = 7 und y = 5

Da globale Variablen verwendet werden, kann man mit Funktionen ohne Parameter arbeiten. Wie schon oben erwähnt, ist die Verwendung von globalen Variablen nicht zu empfehlen, da sie leicht zu Fehlern führen kann. Die Kommunikation zwischen verschiedenen Funktionen sollte daher möglichst über Parameter erfolgen (wie die nachfolgenden Beispiele zeigen werden).

Eine Möglichkeit des Datentransports ist die Übergabe per Wert. Dabei werden die Werte für x und y beim Funktionsaufruf als Kopie an die Funktion vertausche übergeben. Die Funktion vertausche arbeitet mit der Kopie, die Ausgangswerte für x und y beim Aufrufer bleiben unverändert erhalten.

Beispiel 12:

//Vertauschen zweier Werte x und y mittels Übergabe per Wert

#include <iostream.h>

void vertausche(int a, int b)
{
  int h;
  h = a;
  a = b;
  b = h;
  cout << "x = " << a << " und " << "y = " << b << endl;
}

void main()
{
  int x, y;
  x = 5;
  y = 7;
  cout << "x = " << x << " und " << "y = " << y << endl;
  vertausche (x, y);
}

Output:
x = 5 und y = 7
x = 7 und y = 5

Beispiel 13:

//Fehlgeschlagenes Vertauschen zweier Werte x und y mittels Übergabe per Wert

#include <iostream.h>

void vertausche(int a, int b)
{
  int h;
  h = a;
  a = b;
  b = h;
}

void main()
{
  int x, y;
  x = 5;
  y = 7;
  cout << "x = " << x << " und " << "y = " << y << endl;
  vertausche (x, y);
  cout << "x = " << x << " und " << "y = " << y << endl;
}

Output:
x = 5 und y = 7
x = 5 und y = 7

Die beiden Beispiel zeigen, daß die Kommunikation nur in der "Hinrichtung" funktioniert. In Beispiel 12 sieht man, daß die Werte von x und y in der Funktion vertausche tatsächlich vertauscht werden. Beispiel 13 zeigt jedoch, daß die Wertzuweisung bei a und b die Variablen x und y unverändert läßt.

Um eine Kommunikation in beide Richtungen zu erreichen, gibt es 2 Möglichkeiten:

  1. Verwendung von Adressen und Zeigern
  2. Parameterübergabe per Referenz

Zu 1:

Beispiel 14:

//Vertauschen zweier Werte x und y durch Verwendung von Adressen und Pointer

#include <iostream.h>

void vertausche(int *a, int *b)
{
  int h;
  h = *a;
  *a = *b;
  *b = h;
}

void main()
{
  int x, y;
  x = 5;
  y = 7;
  cout << "x = " << x << " und " << "y = " << y << endl;
  vertausche (&x, &y);
  cout << "x = " << x << " und " << "y = " << y << endl;
}

Output:
x = 5 und y = 7
x = 7 und y = 5

Veranschaulichung:

In main: x = 5; y = 7;


Aufruf der Funktion vertausche: vertausche (&x, &y);

In Funktion vertausche:

h = *a:

*a = *b:

*b = h:


Zu 2:

Bei der Übergabe per Referenz wird im Gegensatz zur Übergabe per Wert direkt mit dem Original gearbeitet anstatt mit einer Kopie. Das &-Zeichen gibt an, daß nicht nur der Wert, sondern zusätzlich noch die Adresse übergeben wird. Wenn man also innerhalb der Funktion vertausche den Variablen a und b Werte zuweist, werden in Wirklichkeit die Variablen x und y der Funktion main verändert.

Beispiel 15:

//Vertauschen zweier Werte x und y mittels Übergabe per Referenz

#include <iostream.h>

void vertausche(int &a, int &b)
{
  int h;
  h = a;
  a = b;
  b = h;
}

void main()
{
  int x, y;
  x = 5;
  y = 7;
  cout << "x = " << x << " und " << "y = " << y << endl;
  vertausche (x, y);
  cout << "x = " << x << " und " << "y = " << y << endl;
}

Output:
x = 5 und y = 7
x = 7 und y = 5



















Last update: 1.Januar 2001
E-Mail: rcbruens@astro.uni-bonn.de