Programmieren in C++: Einführung
Dieses Buch ist unter einer Creative Commons-Lizenz lizensiert.
Der kleinste gemeinsame Nenner aller Computerprogramme ist: Sie verarbeiten Informationen. Ob es sich um einen einfachen Taschenrechner handelt oder um eine komplexe Steuerungssoftware für Flugzeuge – es geht immer um das Verarbeiten von Informationen.
Informationsverarbeitung bedeutet im Kleinen, dass Informationen umgewandelt oder kombiniert werden und neue Informationen entstehen. Mit einem Taschenrechner können Sie zum Beispiel zwei Zahlen addieren und erhalten eine neue Zahl als Ergebnis.
Wenn Sie sich die Eingabe von zwei Zahlen in einen Taschenrechner Schritt für Schritt vorstellen, stellen Sie fest, dass die erste Zahl, die eingegeben wird, vom Taschenrechner irgendwo gespeichert werden muss, während er auf die Eingabe der zweiten Zahl wartet. Würde die erste eingegebene Zahl nicht gespeichert werden, wüsste der Taschenrechner nach eingegebener zweiter Zahl nicht, was er eigentlich genau addieren soll.
Hier kommen nun Variablen ins Spiel. Informationen werden in Programmen in Variablen gespeichert, um sie für eine spätere Verwendung im Programm bereitzuhalten. Der Taschenrechner legt die erste eingegebene Zahl also in genau so einer Variablen ab. Wenn die zweite Zahl eingegeben wurde, kann er auf die Variable zugreifen und die zweite Zahl mit der in der Variablen gespeicherten ersten Zahl addieren.
Sie können sich Variablen recht bildlich als Töpfe vorstellen: Wenn Sie in Ihrem Programm eine Zahl speichern müssen, nehmen Sie einen Topf und legen die Zahl dort rein. Müssen Sie in Ihrem Programm eine zweite Zahl speichern, nehmen Sie einfach einen zweiten Topf. Sie können grundsätzlich so viele Variablen erstellen wie Sie brauchen und können beliebig Informationen in ihnen speichern, um sie im späteren Verlauf Ihres Programms abzurufen und zu verwenden.
Um Informationen in Programmen speichern zu können - egal, ob diese in C++ oder einer anderen Programmiersprache geschrieben sind – benötigen sie spezielle Töpfe: Variablen. In C++ gehen Sie wie folgt vor, um eine Variable anzulegen, sprich um einen Topf herbeizuzaubern.
int i;
Mit dieser Zeile zaubern Sie einen Topf namens i herbei, in dem Sie eine Zahl aufbewahren können. Variablen werden immer nach dem gleichen Schema angelegt: Zuerst geben Sie den Datentyp an gefolgt von einem Variablennamen. Im obigen Beispiel ist int
der Datentyp und i der Variablenname.
In C++ ist jeder Variable ein ganz bestimmter Datentyp zugeordnet. Der Datentyp legt fest, welche Art von Information Sie in der entsprechenden Variable speichern können. So brauchen Sie in C++, um eine Zahl speichern zu können, eine Variable, die auch tatsächlich Zahlen speichern kann. Um eine Zeichenkombination, also beispielsweise ein Wort, speichern zu können, brauchen Sie hingegen eine Variable, die Zeichenkombinationen speichern kann. Variablen besitzen also in C++ einen Datentyp, der ganz genau festlegt, welche Art von Information aufgenommen werden kann. Sie können also zum Beispiel kein Wort in der oben angelegten Variablen i speichern, weil der Datentyp int
nur Zahlen zulässt.
Am Ende einer Anweisung folgt in C++ immer ein Semikolon. Passen Sie auf, das Semikolon nicht zu vergessen, was vor allem Anfängern in C++ häufig passiert.
Wie man Variablen anlegt, wissen Sie jetzt. Sie können nun jederzeit soviele Variablen anlegen, wie Sie möchten.
int i; int j; int eine_andere_Variable; int NochEineVariable;
Variablen werden benötigt, um Informationen zu speichern. Bisher haben Sie Variablen nur erstellt, ohne in ihnen Informationen zu speichern. Der Speichervorgang selbst sieht wie folgt aus.
int i; i = 3;
Nachdem Sie eine Variable vom Typ int
angelegt haben, können Sie in dieser Variablen eine Information speichern. Die Art der Information muss natürlich zum Typ der Variablen passen. int
-Variablen können Zahlen speichern. Also sollten Sie nicht versuchen, in Variablen dieses Datentyps Zeichenketten abzulegen. Wenn Sie es dennoch versuchen, wird sich der Compiler mit einer Fehlermeldung bei Ihnen beschweren.
Um beispielsweise die Zahl 3 in der Variablen i zu speichern, geben Sie den Variablennamen i an, dann das Gleichheitszeichen, dann die Zahl 3. Vergessen Sie auch hier das Semikolon nicht am Ende der Anweisung. Achten Sie auch darauf, dass eine derartige Zuweisung immer von rechts nach links stattfindet: Rechts steht das, was Sie zuweisen bzw. speichern wollen. Links steht das, wohin gespeichert werden soll. Rechts steht die Quelle, links das Ziel.
Achten Sie auch darauf, dass der Datentyp ausschließlich bei der Definition der Variable angegeben wird. Zugriffe auf die Variable wie eben die Zuweisung eines Wertes finden nur über den Variablennamen statt.
Anstatt Zahlen einer int
-Variablen zuzuweisen, können Sie auch den Wert einer Variablen einer anderen zuweisen. Oder anders ausgedrückt: Sie kopieren die Information in einer Variablen, indem Sie sie nochmal in einer zweiten Variablen speichern.
int i; i = 3; int j; j = i;
Zuerst legen Sie wieder eine Variable i vom Typ int
an. Sie weisen dieser Variablen in einem zweiten Schritt eine Information zu, nämlich die Zahl 3. Danach legen Sie eine zweite Variable vom Typ int
an. Anstatt dieser neuen Variablen j wieder eine Zahl zuzuweisen, verwenden Sie auf der rechten Seite des Gleichheitszeichens die Variable i. Da in i die Zahl 3 gespeichert ist, ist nach dieser Zuweisung nun auch in der Variablen j die Zahl 3 gespeichert.
Wenn Sie die Werte zweier Variablen austauschen wollen, müssen Sie sich einer dritten Variable bedienen. Mal angenommen, Ihr Programm sieht wie folgt aus.
int i; i = 3; int j; j = 5;
Sie möchten nun, dass Ihr Programm den Wert der Variablen i in j speichert und dass umgekehrt der Wert von j in i gespeichert wird. Eine direkte Zuweisung von einer Variablen an die andere ist nicht möglich. Denn damit würde eine Zahl verloren gehen. Eine Lösung könnte wie folgt aussehen.
int i; i = 3; int j; j = 5; int zwischenspeicher; zwischenspeicher = i; i = j; j = zwischenspeicher;
In einer zusätzlichen Variablen namens zwischenspeicher, die natürlich auch vom Typ int
ist, wird der Wert von i gespeichert. Danach kann der Wert von j der Variablen i zugewiesen werden. j und i enthalten nun den gleichen Wert. Glücklicherweise steht in der Variablen zwischenspeicher der vorherige Wert von i noch zur Verfügung. Und der wird nun in einem letzten Schritt nach j kopiert. Der Austausch der beiden Werte ist perfekt: i enthält nun die Zahl 5 und j die Zahl 3.
Da der Austausch von Werten in zwei Variablen eine häufigere Aufgabe in Programmen darstellen kann, stellt hier die C++-Standardbibliothek eine Funktion zur Verfügung, die diese Aufgabe übernimmt.
#include <iostream> #include <algorithm> int main() { int i = 3; int j = 5; std::swap(i, j); std::cout << "i: " << i << ", j: " << j << std::endl; }
Die Funktion std::swap()
tauscht einfach die Werte der Variablen, die als Parameter an die Funktion übergeben werden. Wenn Sie sich über das zusätzliche #include
wundern, mit dem die Header-Datei algorithm
eingebunden wird: In dieser Header-Datei befindet sich die Definition von std::swap()
.
Sie haben bis jetzt lediglich den Datentyp int
kennengelernt. Dieser Datentyp eignet sich hervorragend, um Zahlen zu speichern. Bei int
handelt es sich um einen intrinsischen Datentyp. Intrinsische Datentypen sind Datentypen, die Sie ohne vorherige Bekanntmachung in Ihrem C++-Programm verwenden können. Das heißt, wenn Sie int
schreiben, weiß C++ automatisch, was Sie meinen. int
gehört also zum Grundwortschatz von C++. Eine andere Bezeichnung für intrinsische Datentypen lautet primitive Datentypen.
Neben int
bietet C++ noch ein paar weitere intrinsische Datentypen. Zu diesen zählen unter anderem short
, char
und float
. Sie werden die intrinsischen Datentypen im Folgenden noch genauer kennenlernen.
Wenn es intrinsische Datentypen gibt, gibt es natürlich auch nicht-intrinsische Datentypen. Nicht-intrinsische Datentypen sind Datentypen, die Sie nicht ohne weiteres in C++ verwenden können. Denn diese Datentypen kennt C++ nicht. Sie gehören nicht zum Grundwortschatz – genau deswegen sind sie ja nicht-intrinsisch. Um nicht-intrinsische Datentypen in C++ verwenden zu können, müssen Sie sie vor ihrer Verwendung bekannt machen.
Sie müssen wissen, wie nicht-intrinsische Datentypen bekannt gemacht werden, da Sie als C++-Programmierer später fast nur mit nicht-intrinsischen Datentypen arbeiten. Die von C++ bereitgestellten primitiven Datentypen sind zwar wichtig, aber im sprichwörtlichen Sinne primitiv.
Der vielleicht am häufigsten eingesetzte nicht-intrinsische Datentyp ist std::string
. Strings sind Variablen, die Zeichenkombinationen bzw. Zeichenketten speichern können. Denn C++ selber stellt keinen Datentypen zur Verfügung, um einfach mit Zeichenkombinationen arbeiten zu können. Um eine Variable vom Typ std::string
anzulegen, gehen Sie wie folgt vor.
#include <string> std::string s;
Der Vorgang der Variablendefinition sollte Ihnen bekannt vorkommen: Zuerst der Datentyp std::string
, dann getrennt durch ein Leerzeichen der Name der Variable - in diesem Fall also s. Da der Datentyp std::string
nicht-intrinsisch ist, muss er bekannt gemacht werden. Dies erfolgt über die Header-Datei string
, die mit #include
eingebunden wird.
Der Datentyp std::string
ist Bestandteil der C++-Standardbibliothek. Daher gehört er auch zum Namensraum std und wird über std::
angesprochen. Dieser Datentyp ist enorm wichtig, da erst über ihn ein komfortables Speichern von Zeichenketten - also mehreren Buchstaben, Wörtern oder Sätzen - möglich ist. Sie finden kaum ein Programm in der Praxis, das std::string
nicht verwendet.
char c; short s; int i; long l; float f; double d; bool b;
Oben sehen Sie alle gültigen Datentypen in C++, die Sie ohne Deklaration verwenden können. Dies sind die intrinsischen Datentypen in C++ - also die Datentypen, die C++ automatisch erkennt.
Die intrinsischen Datentypen unterscheiden sich darin, dass sie jeweils unterschiedlich viel Speicherplatz reservieren. Ein char
belegt 1 Byte, ein short
reserviert 2 Bytes, ein int
4 Bytes und ein long
ebenfalls 4 Bytes. Das trifft jedenfalls auf 32-Bit-Prozessoren zu. Auf 64-Bit-Prozessoren haben die Datentypen eventuell andere Speicherdimensionen. Ein long
belegt auf 64-Bit-Prozessoren also beispielsweise 8 Bytes.
float
und double
sind Datentypen, die Fließkommazahlen speichern können. Während die eben genannten Datentypen ausschließlich Ganzzahlen speichern können, sind die beiden Datentypen float
und double
auf Fließkommazahlen ausgelegt. Ein float
speichert hierbei eine Fließkommazahl in 4 Bytes, ein double
in 8 Bytes.
Wie Sie eventuell aus dem Buch Allgemeine Grundlagen der Programmierung wissen, besteht 1 Byte aus 8 Bits. Bits sind die kleinsten Informationseinheiten im Computer, die entweder auf den Wert 0 oder auf den Wert 1 gesetzt sind. Bits werden in der Mathematik durch das binäre Zahlensystem repräsentiert.
Sie wissen nun, dass jeder Datentyp in C++ eine bestimmte Speichergröße besitzt. Sie können nun jeweils errechnen, was jeweils in welchem Datentyp für Zahlen gespeichert werden können. Ein char
besteht aus 1 Byte - das heißt, es können bis zu 28, also 256 unterschiedliche Informationen, sprich 256 unterschiedliche Zahlen in einem char
gespeichert werden.
Nachdem ein short
aus 2 Bytes besteht, ist klar, dass in diesem Datentyp die Bandbreite der möglichen zu speichernden Zahlen größer ist. Es stehen 16 Bits zur Verfügung, daher können bis zu 65.536 Zahlen in einem short
gespeichert werden. Die Bandbreite für int
liegt dementsprechend bei 4.294.967.296 Werten - wie auch für long
, da auch dieser Datentyp auf 32-Bit-Prozessoren aus 4 Bytes besteht.
Eines wurde bisher aber noch nicht berücksichtigt: Die bisher kennengelernten Datentypen speichern sowohl positive als auch negative Zahlen. Das Vorzeichen wird hierbei jeweils durch ein Bit repräsentiert. Wenn wir also nun nochmal char
betrachten: Es stehen nicht 8 Bits zur Verfügung, sondern eigentlich nur 7 Bits. Das erste Bit steht für das Vorzeichen, wobei ein nicht gesetztes Bit positiv bedeutet, ein gesetztes Bit negativ. Wenn also ein Bit für die Speicherung einer Zahl verloren geht und dieses Bit stattdessen das Vorzeichen darstellt, verschiebt sich die Bandbreite in den negativen Bereich. Ein char
kann 256 verschiedene Informationen speichern, also zum Beispiel alle Zahlen zwischen 0 und einschließlich 255. Wenn nun ein Bit für die Speicherung einer Zahl verloren geht und stattdessen das Vorzeichen darstellt, heißt das, dass ein char
nun Zahlen von -128 bis +127 speichern kann.
Was für char
gilt, gilt natürlich auch für die anderen intrinsischen Datentypen: Ein short
kann 65.536 verschiedene Informationen speichern, nämlich Zahlen von -32.768 bis +32.767. Ein int
sowie ein long
kann Zahlen zwischen -2.147.483.648 und +2.147.483.647 speichern.
unsigned char c; unsigned short s; unsigned int i; unsigned long l;
Durch das Voranstellen des Schlüsselwortes unsigned
kann die Bedeutung des Vorzeichen-Bits aufgehoben werden. Die Bandbreite der entsprechenden Variablen verschiebt sich daraufhin in den positiven Bereich: Ein char
kann dann Zahlen von 0 bis 255 speichern, ein short
Zahlen von 0 bis 65.535 und ein int
wie auch ein long
Zahlen von 0 bis 4.294.967.295.
In der Praxis hat unsigned
eine geringe Bedeutung und wird wenig verwendet. Es ist ein Überbleibsel aus der Zeit, als Speicherplatz noch sehr kostbar war und es notwendig war, Einfluss darauf zu nehmen, ob das Vorzeichen-Bit tatsächlich das Vorzeichen speichern soll oder besser als zusätzliche Speicherposition für Zahlen zur Verfügung steht. Lediglich für Variablen, für die negative Werte partout keinen Sinn machen, kann dies explizit mit unsigned
verdeutlicht werden.
In der Praxis greift der C++-Programmierer, wenn er Zahlen speichern möchte, fast immer auf den Datentyp int
zu. Dieser Datentyp bietet eine Bandbreite, die normalerweise ausreicht und keine Probleme verursachen sollte. Dass in der Variablen eventuell nur kleine Zahlen gespeichert werden, die auch in Variablen vom Datentyp short
oder char
passen würden, und damit Speicherplatz verschwendet wird, kümmert den C++-Programmierer normalerweise nicht. Es gibt genügend andere Probleme, über die man sich den Kopf zerbrechen muss, so dass man zum Speichern von Zahlen fast standardmäßig auf den Datentyp int
zugreift.
float f; double d; f = 1.2; d = 1e10;
Die Datentypen float
und double
ermöglichen ebenfalls die Speicherung von Zahlen - und zwar von Kommazahlen. Auch wenn für Sie als Programmierer alle Zahlen einfach nur Zahlen sind, unterscheidet C++ nicht nur unterschiedliche Bandbreiten, sondern auch Ganz- und Kommazahlen. Wollen Sie in einer Variablen eine Kommazahl speichern, dann muss diese Variable vom Typ float
oder double
sein. Diese beiden Typen unterscheiden sich darin, dass float
Kommazahlen mit einfacher Genauigkeit, double
Kommazahlen mit doppelter Genauigkeit speichert. Neben der Genauigkeit besitzen die Datentypen auch unterschiedliche Bandbreiten: float
kann Zahlen von ±3.4*1038, double
Zahlen von ±1.7*10308 aufnehmen.
Beim Angeben von Kommazahlen innerhalb von Quellcode - beispielsweise bei einer Zuweisung an eine Variable - dürfen Sie kein Komma angeben, sondern müssen den Punkt als Trennzeichen verwenden.
#include <iostream> int main() { float f, g; f = 1000.43; g = 1000; std::cout << f - g << std::endl; }
Was heißt einfache und doppelte Genauigkeit? Genauigkeit bedeutet, wie genau ein Bruch bzw. der Nachkommaanteil in einer Variablen von einem bestimmten Datentyp gespeichert werden kann. Während Zahlen analog sind und es zwischen zwei Zahlen unendlich viele andere Zahlen gibt, arbeitet der Computer digital: Es können nur ganz konkrete Zustände gespeichert werden. Es können auch nur ganz konkrete Zahlen gespeichert werden. Führen Sie obiges Programm einfach aus und sehen Sie sich an, welches Ergebnis Ihr Computer ausgibt. Die Zahl 0.43, also das Ergebnis der Berechnung im Beispiel, kann im Datentyp float
nicht genau gespeichert werden - der Computer verwendet eine Annäherung.
Wenn Sie anstatt dem Datentyp float
den Datentyp double
im Beispielprogramm verwenden, wird das korrekte Ergebnis ausgegeben. Der Datentyp double
besitzt doppelte Genauigkeit. Insofern ist in diesem Fall keine Annährung notwendig, sondern das korrekte Ergebnis kann direkt gespeichert werden.
Wenn Sie jemals in einem Programm mit Kommazahlen arbeiten, denken Sie daran, dass die Speicherung von Kommazahlen zum Teil nur durch Annäherung erfolgt. Sie müssen speziell für das Arbeiten mit Kommazahlen entwickelte Bibliotheken verwenden, wenn Berechnungen durch die nicht exakte Speicherung von Kommazahlen nicht zu mathematisch falschen Ergebnissen führen dürfen.
bool b; b = true; b = false;
Der intrinsische Datentyp bool
unterscheidet sich von den bisherigen Datentypen insofern, als dass hier nicht seine physikalische Speichergröße im Vordergrund steht, sondern sein logisches Speichervermögen. Ein bool
kann den Wert true
und den Wert false
speichern. Wieviel Speicher ein bool
belegt, spielt also für den Programmierer eine untergeordnete Rolle. Wichtig ist, dass ein bool
genau zwei Informationszustände unterscheidet, nämlich true
und false
.
Im folgenden Beispiel soll Ihnen die Anwendung der verschiedenen Datentypen nähergebracht werden.
unsigned char alter; unsigned int kopfhaare; float kontostand; bool lichtschalter;
Wenn Sie in Ihrem Programm das Alter einer Person speichern wollen, würde sich der Datentyp unsigned char
empfehlen: Das Alter kann nicht negativ sein, und älter als 255 Jahre dürfte bei weitem niemand werden.
Um die Anzahl an Kopfhaaren zu speichern, müsste bereits auf den Datentypen int
zugegriffen werden. Da man nicht weniger als 0 Kopfhaare haben kann, könnte ebenfalls unsigned
verwendet werden. Der nächstkleinere Datentyp short
kann hingegen nicht verwendet werden: Die Obergrenze von short
liegt bei 65.536 – bei bis zu 150.000 Haaren, die man auf dem Kopf haben kann, ist das nicht ausreichend.
Um einen Kontostand zu speichern, bietet sich float
an. In einer float
-Variable können positive wie auch negative Kommazahlen gespeichert werden. Das einzige Problem mit float
ist, dass Berechnungen wie bereits gezeigt unter Umständen nicht die tatsächlich erwarteten Ergebnisse liefern, sondern Annäherungen.
Bei einem Lichtschalter wiederum bietet sich der Datentyp bool
an. Der Lichtschalter ist entweder an oder aus – die zwei Zustände lassen sich ideal in einer bool
-Variablen speichern.
Wie in diesem Abschnitt bereits angesprochen wird oft auch einfach nur der Datentyp int
verwendet, wenn Ganzzahlen gespeichert werden müssen. Niemand kann zwar mehrere Millionen Jahre alt werden, und ein negatives Alter gibt es auch nicht - trotzdem werden Sie in der Praxis oft einfach nur die Verwendung von int
sehen, auch wenn ein anderer Datentyp scheinbar geeigneter wäre.
char
Der Datentyp char
kann wie Sie bereits wissen bis zu 256 unterschiedliche Zahlen speichern. char
wird von C++-Programmierern jedoch normalerweise nicht verwendet, um Zahlen zu speichern, sondern Buchstaben. Der Datentyp std::string
, der Zeichenketten speichert (also mehrere Buchstaben), basiert intern auf dem Datentyp char
. Da stellt sich die Frage, wie in char
Buchstaben gespeichert werden können.
Buchstaben werden intern im Computer durch Zahlen repräsentiert. Das heißt, jedem Buchstaben oder genauer jedem Zeichen steht eine ganz bestimmte Zahl gegenüber. Das Auflösen von Buchstaben in die entsprechende Zahl geschieht mit Hilfe von Tabellen. Es gibt eine ganze Reihe an Tabellen, die angeben, wie Buchstaben in Zahlen aufgelöst werden und umgekehrt. Die wichtigste Tabelle ist der ASCII-Code. Denn jedes Betriebssystem kennt diese Tabelle, um Buchstaben in Zahlen umzuwandeln und zurück. Im Folgenden sehen Sie einen Auszug aus der ASCII-Tabelle.
ASCII-Code | Zeichen |
---|---|
... | |
58 | : |
59 | ; |
60 | < |
61 | = |
62 | > |
63 | ? |
64 | @ |
65 | A |
66 | B |
67 | C |
... |
Der ASCII-Code definiert 128 Zeichen bzw. in seiner erweiterten Form 256 Zeichen. Jedem dieser Zeichen ist eine Zahl zugeordnet. Wenn Sie sich erinnern: Ein char
kann genau 256 unterschiedliche Zahlen speichern. Ein char
stellt also von seiner Speichergröße her den idealen Datentyp dar, um Zeichen aus dem ASCII-Code zu repräsentieren. Wenn Sie in C++ den Datentyp char
verwenden, geht C++ automatisch davon aus, dass Sie keine Zahl speichern möchten, sondern einen Buchstaben. Führen Sie einfach folgendes Beispielprogramm aus.
#include <iostream> int main() { char c; c = 65; std::cout << "c: " << c << std::endl; }
Ihnen wird nicht die Zahl 65 auf den Bildschirm ausgegeben, sondern der Buchstabe A. Was ist passiert? C++ geht davon aus, dass Sie in Variablen des Datentyps char
Buchstaben speichern. Das heißt, C++ hat den Wert in der Variablen c als ASCII-Code interpretiert. Gemäß der ASCII-Tabelle entspricht die Zahl 65 dem Großbuchstaben A. Deswegen hat das Programm Ihnen auch A auf den Bildschirm ausgegeben.
char c; c = 'A';
Anstatt Buchstaben umständlich über den ASCII-Code zu speichern, können Sie den Buchstaben natürlich auch direkt angeben. Dazu müssen Sie das Zeichen jedoch in einfache Anführungszeichen setzen.
Möchten Sie Ihr C++-Programm dazu zwingen, den Wert in der Variablen c nicht als Buchstabe, sondern als Zahl zu interpretieren, schreiben Sie folgendes.
#include <iostream> int main() { char c; c = 65; std::cout << "c: " << static_cast<int>(c) << std::endl; }
Mit Hilfe von static_cast
wird die Variable c temporär in den Datentyp int
umgewandelt. Wie Sie bereits erfahren haben ist der Datentyp int
der Datentyp, der oft standardmäßig für die Speicherung von Zahlen verwendet wird. Ein int
wird in C++ daher auch niemals als Buchstabe interpretiert, sondern immer als Zahl.
Wenn Sie sich fragen, wo static_cast
herkommt: Es handelt sich hierbei um einen sogenannten Cast-Operator, der zum Grundwortschatz von C++ gehört. Deswegen müssen Sie ihn auch nicht zuerst über eine Header-Datei bekannt machen, sondern können ihn direkt verwenden - der Compiler weiß, was Sie meinen.
Ein Array ist ein Datentopf, der genaugenommen aus mehreren Variablen vom gleichen Typ besteht. Die einzelnen Variablen in diesem Datentopf werden über Indizes angesprochen - der Variablenname bezieht sich auf das gesamte Array. Eine andere Bezeichnung für Array ist Datenfeld.
Arrays werden typischerweise dann verwendet, wenn viele Informationen vom gleichen Typ gespeichert werden müssen. Stellen Sie sich vor, Sie schreiben eine Adressverwaltung und müssen die Namen von 100 Personen speichern. Bevor Sie nun anfangen, 100 Variablen zu definieren, können Sie ein Array definieren, das aus 100 Stellen besteht.
int i[3];
Um ein Array anzulegen wird einfach hinter dem Variablennamen in eckigen Klammern die Größe des Datenfeldes angegeben. Obiges Datenfeld besteht also genaugenommen aus drei Variablen vom Typ int
. Die drei Elemente im Array i werden jedoch nicht über einen Namen angesprochen - der Variablenname i bezieht sich ja auf das gesamte Array. Um auf einzelne Elemente in diesem Array zuzugreifen, muss mit einem Index gearbeitet werden.
int i[3]; i[0] = 10; i[1] = 15; i[2] = 20;
Die erste Variable sprich die erste Stelle in einem Array besitzt immer den Index 0 - der Index startet in C++ immer bei 0. Um auf das letzte Element im Array zuzugreifen, wird der Index Feldgröße minus 1 verwendet. Wenn das Array wie oben also 3 Stellen umfasst, besitzt die erste Stelle den Index 0, die zweite Stelle den Index 1 und die dritte und letzte Stelle den Index 2.
Das Arbeiten mit Arrays kann brandgefährlich sein. Es ist Aufgabe des Programmierers, dafür zu sorgen, dass verwendete Indizes tatsächlich gültig sind. Wenn Ihr Programm fehlerhaft arbeitet und einen Index verwendet, der über die Feldgröße hinausgeht, kann dies zu unvorhersehbaren Folgen wie beispielsweise einem Programmabsturz führen. Das Arbeiten mit Arrays erfordert also besondere Aufmerksamkeit.
Im Zusammenhang mit dem Datentyp char
kann man ganz besondere Arrays erstellen. Sie erinnern sich sicher noch daran, dass der Datentyp char
vorrangig verwendet wird, um Buchstaben zu speichern. Wenn Sie nun ein Array vom Typ char
erstellen, sollten sich mehrere Buchstaben hintereinander speichern lassen.
char c[5]; c[0] = 'H'; c[1] = 'a'; c[2] = 'l'; c[3] = 'l'; c[4] = 'o';
Im obigen Beispiel greifen Sie über den Index auf die einzelnen Stellen im Array c zu und weisen verschiedene Buchstaben zu. Wie Sie unschwer erkennen können bilden die Buchstaben im Array das Wort Hallo
.
#include <iostream> int main() { char c[6]; c[0] = 'H'; c[1] = 'a'; c[2] = 'l'; c[3] = 'l'; c[4] = 'o'; c[5] = '\0'; std::cout << c << std::endl; }
Obiges Programm gibt das Wort Hallo
auf den Bildschirm aus. Dazu wird das Array c über <<
an std::cout weitergegeben. Damit std::cout weiß, wann die Zeichenkette eigentlich aufhört, wird ein merkwürdiges Zeichen benötigt, das Null-Zeichen heißt. Null-Zeichen schließen für gewöhnlich Zeichenketten ab, die in Arrays vom Typ char
gespeichert sind. Wenn Sie das Null-Zeichen nicht angeben, greift std::cout auf die nächste Stelle zu, die sich hinter dem Array befindet. std::cout überprüft also nicht, ob es über das Array hinaus auf Speicher zugreift, der gar nicht mehr zum Array gehört, und somit einen ungültigen Index verwendet. std::cout gibt alles auf den Bildschirm aus, bis es auf das Null-Zeichen trifft.
Das Null-Zeichen befindet sich in der ASCII-Tabelle an allererster Stelle. Sie können also statt der Angabe des Zeichens in einfachen Anführungszeichen auch einfach die entsprechende Zahl aus der ASCII-Tabelle angeben - in diesem Fall also 0. Genau daher hat das Null-Zeichen seinen Namen.
#include <iostream> int main() { char c[6]; c[0] = 'H'; c[1] = 'a'; c[2] = 'l'; c[3] = 'l'; c[4] = 'o'; c[5] = 0; std::cout << c << std::endl; }
Die Arrays, die Sie bis jetzt kennen, sind eindimensionale Arrays. Das heisst, das Array besteht aus genau einer Aneinanderreihung mehrerer Variablen.
char c[2][6]; c[0][0] = 'H'; c[0][1] = 'a'; c[0][2] = 'l'; c[0][3] = 'l'; c[0][4] = 'o'; c[0][5] = '\0'; c[1][0] = 'W'; c[1][1] = 'e'; c[1][2] = 'l'; c[1][3] = 't'; c[1][4] = '\0';
Dieses Array c ist nun zweidimensional. Es besteht aus genau zwei Aneinanderreihungen von jeweils sechs Variablen. Während Sie sich eindimensionale Arrays auch einfach als Zeile vorstellen können, besitzt ein zweidimensionales Array die Form einer Tabelle.
Um auf einzelne Stellen in diesem zweidimensionalen Array zuzugreifen, müssen nun auch jeweils zwei Indizes angegeben werden. Im obigen Beispiel werden die Wörter Hallo
und Welt
zugewiesen, jeweils wieder abgeschlossen mit dem Null-Zeichen. Achten Sie auch darauf, dass das Wort Welt
aus einem Buchstaben weniger besteht als das Wort Hallo
, so dass das Null-Zeichen eine Stelle früher gesetzt ist. Dies ist kein Problem - Hauptsache, das Null-Zeichen ist überhaupt gesetzt.
#include <iostream> int main() { char c[2][6]; c[0][0] = 'H'; c[0][1] = 'a'; c[0][2] = 'l'; c[0][3] = 'l'; c[0][4] = 'o'; c[0][5] = '\0'; c[1][0] = 'W'; c[1][1] = 'e'; c[1][2] = 'l'; c[1][3] = 't'; c[1][4] = '\0'; std::cout << c[0] << " " << c[1] << std::endl; }
Um die beiden Wörter auf den Bildschirm auszugeben, müssen jeweils eindimensionale Arrays an std::cout übergeben werden. Dies erfolgt wie im Beispiel angegeben.
Wenn es zweidimensionale Arrays gibt, gibt es auch dreidimensionale Arrays. Genaugenommen kann ein Array beliebig viele Dimensionen besitzen - wobei sich natürlich irgendwann die Frage stellt, wie übersichtlich das Array noch ist. Auch hört die bildliche Vorstellungskraft spätestens beim vierdimensionalen Array auf. Mehrdimensionale Arrays werden aber analog zu zweidimensionalen Arrays erstellt: Es wird für jede neue Dimension eine Zahl zwischen eckigen Klammern angegeben. Auch der Zugriff auf ein Element im Array erfolgt über Angabe von Indizes für jede Dimension.
#include <iostream> int main() { char c[10]; std::cout << "Groesse des Arrays: " << sizeof(c) << std::endl; }
Um die Größe eines Arrays zu ermitteln, steht der Operator sizeof
zur Verfügung. Dieser Operator errechnet die Speichergröße eines Arrays. Es kommt hierbei auf die Anzahl der Elemente im Array und den Datentypen des Arrays an. Das Array c besteht aus 10 Elementen vom Datentyp char
. char
belegt 1 Byte im Speicher - somit liefert sizeof
im obigen Beispiel 1 * 10, also 10 zurück.
Es spielt für den Operator sizeof
keine Rolle, ob und wenn ja welche Werte im Array stehen. Auch das Null-Zeichen ist völlig egal. sizeof
ermittelt die Speichergröße eines Arrays, und die ist unabhängig von den gespeicherten Werten.
sizeof
wird häufig dann verwendet, wenn eine Funktion auf ein Array zugreift und in einem Parameter die Größe des Arrays erwartet. Anstatt die Größe als Zahl anzugeben lässt man sie vom Compiler mit Hilfe von sizeof
automatisch errechnen. Das hat den Vorteil, dass im Falle einer Größenänderung des Arrays automatisch alle Funktionen die neue berichtigte Größe bekommen. Man muss also nicht den gesamten Code aktualisieren und all die Stellen suchen, an denen die Größe des Arrays als Zahl angegeben wurde.
Schauen Sie sich dazu folgendes Beispielprogramm an.
#include <iostream> int main() { std::cout << "Geben Sie Ihren Vor- und Nachnamen ein: " << std::flush; char name[40]; std::cin.getline(name, sizeof(name)); std::cout << "Sie heissen " << name << "." << std::endl; }
Im Einführungskapitel haben Sie gesehen, wie mit std::cin Eingaben vorgenommen werden können. In diesem Fall wird zwar auch auf std::cin zugegriffen. Dies geschieht aber auf eine Art und Weise, die Sie noch nicht kennen.
std::cin bietet eine Methode namens getline()
an. Diese Methode, die im obigen Beispielprogramm aufgerufen wird, erwartet zwei Parameter: Ein Array vom Typ char
und die Größe dieses Arrays. Indem die Größe des Arrays nicht als Zahl, sondern mit sizeof(name)
angegeben wird, errechnet der Compiler die Größe automatisch. Sollte das Programm später angepasst werden und die Größe des Arrays geändert werden, muss nicht zusätzlich der Parameter für getline()
aktualisiert werden.
getline()
speichert die gesamte Eingabe bis zu einem abschließenden Enter in dem Array, das als Parameter angegeben ist. Sollte die Eingabe größer sein als das Array Speicherplatz bietet, werden die restlichen Zeichen verworfen und nicht gespeichert. Da getline()
die Eingabe im Array mit einem Null-Zeichen automatisch abschließt, können im obigen Beispielprogramm bis zu 39 Zeichen von der Eingabe gespeichert werden.
Wenn Sie sich fragen, was der Unterschied zwischen dem Lesen einer Eingabe mit getline()
ist und der Methode, die in den Beispielprogrammen im ersten Kapitel vorgestellt wurde: Ein Leerzeichen ist für getline()
ein ganz normales Zeichen wie jedes andere auch. getline()
bricht also eine Eingabe nicht ab, wenn es auf ein Leerzeichen trifft. Daher kann im obigen Beispielprogramm sowohl der Vor- als auch der Nachname gleichzeitig eingelesen werden, auch wenn diese wie üblich durch ein Leerzeichen getrennt werden.
Während C-Programmierer übrigens tatsächlich mit Arrays vom Typ char
arbeiten müssen, um Zeichenketten wie Wörter oder Sätze in ihren Programmen verwenden zu können, brauchen Sie dies als C++-Programmierer glücklicherweise nicht. Als C++-Programmierer verwenden Sie stattdessen den bereits erwähnten nicht-intrinsischen Datentyp std::string
und brauchen sich normalerweise nicht mit Arrays vom Typ char
herumzuschlagen, nur um ein paar Buchstaben zu speichern. Da es aber wie eben gesehen durchaus Code gibt, der Arrays erwartet, kommen Sie um die Anwendung von Arrays vom Typ char
nicht unbedingt herum.
Auch wenn in diesem Abschnitt besonders häufig Arrays vom Typ char
Verwendung fanden: Sie können selbstverständlich auch Arrays mit einem anderen Datentyp definieren. Der Umgang mit Arrays eines anderen Datentypen ist aber insofern einfach als dass Sie sich dort nicht um dieses ominöse Null-Zeichen kümmern müssen - das gibt es nur bei Arrays vom Typ char
.
Copyright © 2001-2010 Boris Schäling