Das Array |
In der Abbildung sehen Sie wie die Lottozahlen nebeneinander stehen. Unter jedem Kasten befindet sich in eckigen Klammern die Position jeder Lottozahl. Entgegen der Gewohnheit der meisten Menschen beginnt ein Array immer an der Position 0. Wenn also sechs Zahlen im Array stehen, befindet sich die letzte an Position 5.
int Lotto[6];
lotto[2] = srand() % 49 + 1; cout << lotto[0];Die erste Zeile zeigt, wie das dritte Element -- nicht das zweite -- mit einer Zufallszahl zwischen 1 und 49 gefüllt wird. Die zweite Zeile gibt das erste Element auf dem Bildschirm aus.
int lotto[6];
srand(0);
for(i=0; i<6; i++)
{
lotto[i] = rand() % 49 + 1;
}
Da die Elemente zufällig bestückt wurden, ist es möglich, dass zwei der Elemente gleich sind. Im nächsten Schritt wird bei Gleichheit zweier Zahlen neu gezogen. Sie können in einer Schleife alle bisherigen Zahlen durchlaufen und prüfen, ob die neue Zahl bereits gezogen wurde. Das folgende Programm garantiert, dass die gezogenen Lottozahlen nicht doppelt auftreten:
#include <iostream>
#include <stdlib.h>
using namespace std;
int main()
{
int lotto[6];
int i, j;
bool neueZahl;
srand(0);
for(i=0; i<6; i++) // ziehe nacheinander sechs Zahlen
{
do // wiederhole die Ziehung, bis die neue Zahl
{ // nicht mit einer der vorigen identisch ist.
lotto[i] = rand() % 49 + 1;
neueZahl = true; // positive Grundeinstellung
for (j=0; j<i; j++)
{ // durchlaufe alle bisher gezogenen Kugeln
if (lotto[j]==lotto[i])
{ // Hier wurde ein Doppelter entdeckt
neueZahl = false;
}
}
} while (!neueZahl);
}
for (i=0; i<6; i++)
{
cout << lotto[i] << " ";
}
cout << endl;
}
Die äußere Schleife mit der Variablen i als Index durchläuft die
Anzahl der zu ziehenden Lottozahlen.
Die Ziehung selbst erfolgt innerhalb der do-while-Schleife, denn sie
soll so lange wiederholt werden, bis man eine Zahl findet, die bisher
noch nicht gezogen wurde. Die Entscheidung kann also erst nach der Aktion
im Schleifenkörper gefällt werden. Damit ist eine fußgesteuerte Schleife
die Idealbesetzung. Nach der Ziehung erfolgt die Prüfung. In einer inneren
for-Schleife werden alle bisher gezogenen Zahlen durchlaufen. Das
bedeutet, der Index j startet bei 0 und bleibt kleiner als
i, d. h. kleiner als der Index für die aktuell gezogene Zahl.
Im Falle einer Gleichheit mit den bisher gezogenen Zahlen wird die boolesche
Variable neueZahl auf false gesetzt. Das führt zu einer
Wiederholung der Ziehung. Nach jeder Ziehung muss diese Variable auf
true gesetzt werden, sonst wird die Schleife
nie verlassen, sobald einmal eine Doppelziehung entdeckt wurde.
int lotto[6]; lotto[0] = 1; // erstes Element! ... lotto[5] = 3; // sechstes Element, ok! lotto[6] = 4; // Das siebte Element: Fehler!!!
const int MAXLOTTO=6;
int lotto[MAXLOTTO];
for (int i=0; i<MAXLOTTO; i++)
{
lotto[i] = ...
}
const int MAXLOTTO=6;
int lotto[MAXLOTTO] = { 12, 7, 45, 2, 21, 9 };
Hier muss darauf hingewiesen werden, dass das nur bei einer Initialisierung funktioniert und nicht bei einer Zuweisung. Sie können an anderer Stelle im Programm einem Array nicht auf diese Art Werte zuweisen, sondern müssen dies elementweise tun.
const int MAXLOTTO=6;
int lotto[MAXLOTTO] = {0};
#include <iostream>
using namespace std;
int main()
{
double c[13];
cout << "Gesamtspeicherbedarf von double c[13]: ";
cout << sizeof(c) << endl;
cout << "Speicherbedarf eines Elements: ";
cout << sizeof(c[0]) << endl;
cout << "Anzahl der Elemente: ";
cout << sizeof(c) / sizeof(c[0]) << endl;
}

Sie sehen, dass sich die Zahlen bereits im ersten Durchlauf dahingehend geändert haben, dass nun die höchste Zahl nach rechts gewandert ist. Alle anderen Zahlen sind noch unsortiert, so dass der Vorgang wiederholt werden muss. Im nächsten Schritt müssen allerdings nur noch die ersten drei Karten bearbeitet werden, weil die höchste Karte ja bereits an der richtigen Position liegt. Auch im zweiten Durchgang wandert die höchste der verbleibenden Karten nach rechts. Zu guter Letzt müssen nur noch die ersten beiden Karten getauscht werden.
for (i=MAX-1; i>0; i--)
{
for (j=0; j<i; j++)
{
...
}
}
Anstatt die Lösung im Listing zu entwickeln, ist es vielleicht klüger,
ein Nassi-Schneidermann-Diagramm zu verwenden.
Innerhalb der inneren Schleife wird das Feld mit seinem rechten
Nachbarn verglichen. Ist das linke Feld größer als das rechte, müssen
sie getauscht werden.
Die im Buch erscheinende Grafik Struktogramm Bubblesort (bubblediagramm) fehlt hier.
Nun muss das Struktogramm nur noch in C++-Code umgesetzt werden. Das folgende Listing zeigt das Programm inklusive der Erzeugung von zufälligen Testdaten und der Bildschirmausgaben.
#include <iostream>
using namespace std;
#include <stdlib.h>
const int MAX=5;
int main()
{
int feld[MAX], hilf;
int i, j, k;
srand(56); // Zufallsgenerator vorbereiten
for (i=0; i<MAX; i++)
{ // Array besetzen und anzeigen
feld[i] = rand() % 100 + 1;
cout << feld[i] << " ";
}
cout << endl;
for(i=MAX-1; i>0; i--)
{
for (j=0; j<i; j++)
{
cout << "(" << j << "-" << j+1 << "): " ;
if (feld[j]>feld[j+1])
{ // Tauschen erforderlich
hilf = feld[j];
feld[j] = feld[j+1];
feld[j+1] = hilf;
}
cout << feld[j] << " - " << feld[j+1] << " ";
}
// Zeige das Array in diesem Durchlauf
cout << endl << MAX-i << ". Durchlauf beendet: ";
for (k=0; k<MAX; k++)
{
cout << feld[k] << " ";
}
cout << endl;
}
}
80 91 14 78 17 (0-1): 80 - 91 (1-2): 14 - 91 (2-3): 78 - 91 (3-4): 17 - 91 1. Durchlauf beendet: 80 14 78 17 91 (0-1): 14 - 80 (1-2): 78 - 80 (2-3): 17 - 80 2. Durchlauf beendet: 14 78 17 80 91 (0-1): 14 - 78 (1-2): 17 - 78 3. Durchlauf beendet: 14 17 78 80 91 (0-1): 14 - 17 4. Durchlauf beendet: 14 17 78 80 91
int quelle[MAX]; int ziel[MAX]; ziel = quelle; // kein Standard!
int quelle[MAX];
int ziel[MAX];
int i;
for (i=0; i<MAX; i++)
{
ziel[i] = quelle[i];
}
#include <string.h> int quelle[MAX]; int ziel[MAX]; memcpy(ziel, quelle, sizeof(quelle));
Da diese Variante recht fehleranfällig ist, sollten Sie wirklich nur darauf zurückgreifen, wenn Geschwindigkeit um jeden Preis gefragt ist. Um die Funktion memcpy() verwenden zu können, müssen Sie die Datei string.h einbinden.
char Vorname[6] = "Kai";In diesem Fall sind sechs Zeichen für das Array reserviert worden. In den ersten drei Elementen befinden sich die Buchstaben 'K', 'a' und 'i'. Im vierten Element mit dem Index 3 befindet sich eine 0. Der Zustand der restlichen zwei Elemente ist unbestimmt. Dort können noch alte Speicherleichen herumliegen, hier durch ein 'z' und ein 'M' angedeutet.

char Vorname[] = "Kai";
Das folgende Beispiel liest eine Zeichenkette von der Konsole ein und konvertiert den Inhalt in eine long-Variable.
#include <iostream>
using namespace std;
const int MAX=256;
int main()
{
char input[MAX];
int i = 0;
long Wert = 0;
cin.getline(input, MAX);
while (input[i]>='0' && input[i]<='9')
{
Wert *= 10;
Wert += input[i] - '0';
i++;
}
cout << Wert << endl;
}
Betrachten wir die wichtigsten Zeilen im Programm:
cin.getline(input, MAX);
while (input[i]>='0' && input[i]<='9')
Wert *= 10; Wert += input[i] - '0';
| i | Wert | input[i] | Nach dem Befehl |
|---|---|---|---|
| 0 | 0 | '7' | Wert *= 10; |
| 0 | 7 | '7' | Wert += input[i] - '0'; |
| 1 | 7 | '3' | i++; |
| 1 | 70 | '3' | Wert *= 10; |
| 1 | 73 | '3' | Wert += input[i] - '0'; |
| 2 | 73 | '5' | i++; |
| 2 | 730 | '5' | Wert *= 10; |
| 2 | 735 | '5' | Wert += input[i] - '0'; |
| 3 | 735 | '\0' | i++; |
Fließkommazahlen
Auf der Basis dieses Vorgehens ist es recht einfach, Fließkommazahlen
einzulesen, deren Nachkommastellen durch ein Komma abgetrennt werden.
Normalerweise geht C++ von der anglo-amerikanischen Schreibweise aus und
verwendet den Punkt als Dezimaltrenner.
Nach dem Durchlaufen der while-Schleife für die Vorkommastellen
wird geprüft, ob das folgende Zeichen ein Komma ist. Dann wird fast
die gleiche Schleife noch einmal durchlaufen. Allerdings wird in der
Variablen NK der Zehnerexponent ermittelt, durch den die
nächste Ziffer zu dividieren ist, bevor sie zu Wert hinzuaddiert
werden kann.
#include <iostream>
using namespace std;
const int MAX=256;
int main()
{
char input[MAX];
int i=0;
double Wert = 0;
cin.getline(input, MAX);
while (input[i]>='0' && input[i]<='9')
{
Wert *= 10;
Wert += input[i] - '0';
i++;
}
if (input[i]==',')
{
double NK = 1;
i++;
while (input[i]>='0' && input[i]<='9')
{
NK *= 10;
Wert += (input[i]-'0')/NK;
i++;
}
}
cout << input << Wert << endl;
}
Übung
Die Lösungen zu den Übungsaufgaben finden sich im Buch.
Mehrere Dimensionen
Definition
C++ ermöglicht auch mehrdimensionale Arrays.
Bei der Definition eines mehrdimensionalen Arrays wird für jede Dimension
eine weitere rechteckige Klammer angehängt.
Eine solche Konstruktion ist ein Array von Arrays.
double matrix[MAXSPALTEN][MAXZEILEN];
Hier wird ein zweidimensionales Array definiert. Es gibt MAXZEILEN viele Zeilen,
die jeweils wiederum MAXSPALTEN viele Spalten besitzen. Das Array nimmt einen
Speicherraum von sizeof(double) * MAXSPALTEN * MAXZEILEN ein.
Zugriff
Auch der Zugriff auf das zweidimensionale Array erfolgt über zwei rechteckige
Klammern. Wenn Sie das Element der vierten Spalte und der dritten Zeile
verwenden wollen, greifen Sie durch matrix[3][2] darauf zu.
Beispiel: Bermuda
Als Programmierbeispiel für zweidimensionale Arrays bieten sich viele Spiele
an, die ein zweidimensionales Spielfeld besitzen. Das hier
vorgestellte Computerspiel Bermuda ähnelt dem klassischen Schiffeversenken,
ist aber etwas anspruchsvoller. Das Spiel wird Ihnen in diesem Buch an
verschiedenen Stellen begegnen. Hier implementieren wir zunächst das Spielfeld
als Beispiel für mehrdimensionale Arrays.
Spielanleitung
In einem Spielfeld von neun mal sieben Feldern sind vier Schiffe versteckt.
Der Spieler kann jede Position anfunken. Er bekommt entweder die Mitteilung,
dass hier ein Schiff gefunden wurde, oder den Hinweis, in wie viele Richtungen
von dieser Position aus Schiffe zu sehen sind. Dabei wird nach links, rechts,
oben, unten und in alle vier diagonalen Richtungen so lange gepeilt, bis
das erste Schiff entdeckt oder der Rand des Spielfelds erreicht wurde. Zwei
Schiffe, die hintereinander in einer Richtung liegen, werden nur als ein
Schiff gezählt.
Spielfeld anzeigen
Der erste Schritt besteht darin, das Spielfeld zu definieren.
In einem zweidimensionalen Array werden die bisherigen Rateversuche des
Spielers gespeichert. Für die Darstellung der Rateversuche eignet sich der
Datentyp char am besten.
Das folgende Listing zeigt, wie das Feld zunächst mit Punkten initialisiert
wird.
Anschließend läuft das Programm in eine Schleife, in der das Spielfeld
angezeigt wird und der Anwender die Koordinaten eingeben kann.
Dazu gibt er die Zeilennummer direkt gefolgt vom Spaltenbuchstaben an,
beispielsweise 2C.
Alle durch die eingegebenen Koordinaten bezeichneten Felder werden durch ein x
im Spielfeld markiert.
Auf diese Weise haben Sie eine Rückmeldung, ob auch wirklich die richtigen
Koordinaten angesprochen wurden.
#include <iostream>
using namespace std;
const int X=9;
const int Y=7;
int main()
{
char Spielfeld[X][Y];
int x, y;
char cx, cy;
for (x=0; x<X; x++)
{
for (y=0; y<Y; y++)
{
Spielfeld[x][y] = '.';
}
}
// Anzeige und Eingabe
bool SchleifenEnde=false;
int xin, yin;
do
{
cout << " 1 2 3 4 5 6 7 8 9" << endl;
for (y=0; y<Y; y++)
{
cout << (char)('A'+y) << " ";
for (x=0; x<X; x++)
{
cout << " " << Spielfeld[x][y];
}
cout << endl;
}
cin >> cx >> cy;
xin = cx - '1';
yin = cy - 'A';
if (xin>=0 && xin<9 && yin>=0 && yin<7)
{
Spielfeld[xin][yin] = 'x';
}
else
{
SchleifenEnde = true;
}
}
while (!SchleifenEnde);
}
Wenn das Programm gestartet wird, zeigt es das Spielfeld umgeben von den
Koordinaten. Das Programm stoppt zur Eingabeaufforderung. Nach der Eingabe
von 2C -- gefolgt von Return -- erscheint an der entsprechenden Stelle ein x
auf dem Bildschirm. Die Eingabe 6F führt dazu, dass an den beiden Stellen
je ein x steht. Mit irgendeiner Eingabe, die keiner Koordinate entspricht,
wird das Programm beendet.
1 2 3 4 5 6 7 8 9
A . . . . . . . . .
B . . . . . . . . .
C . . . . . . . . .
D . . . . . . . . .
E . . . . . . . . .
F . . . . . . . . .
G . . . . . . . . .
2C
1 2 3 4 5 6 7 8 9
A . . . . . . . . .
B . . . . . . . . .
C . x . . . . . . .
D . . . . . . . . .
E . . . . . . . . .
F . . . . . . . . .
G . . . . . . . . .
6F
1 2 3 4 5 6 7 8 9
A . . . . . . . . .
B . . . . . . . . .
C . x . . . . . . .
D . . . . . . . . .
E . . . . . . . . .
F . . . . . x . . .
G . . . . . . . . .
xx
[1] Der
Begriff >>Array<< wird in
der deutschen Fachliteratur oft mit dem Wort >>Feld<< übersetzt. Leider ist
dieser Begriff nicht sehr markant, so dass viele Programmierer den
Begriff >>Array<< bevorzugen. Zu diesen gehöre ich auch, und darum
halte ich es auch hier so.
[2] Arrays sind auch nicht als Rückgabewert
von Funktionen zugelassen).
|
Diese Seite basiert auf Inhalten aus dem Buch
Arnold Willemer: Einstieg in C++ Mit freundlicher Genehmigung und Unterstützung des Verlags galileo computing |
| Informatik-Ecke Einstieg in C++ |
(C) Copyright 2003 Arnold Willemer
|