C++ für einsteiger - ein Basis-Tutorial www.mbergmann-sh.de

C++ Tutorial – Variablen, Konstanten und Aufzählungstypen

Was ist eine Variable?

Programme müssen auf irgendeine Weise die verwendeten Daten speichern. Variablen und Konstanten bieten verschiedene Möglichkeiten, diese Daten darzustellen und zu manipulieren. In C++ dient eine Variable dazu, Informationen zu speichern. Eine Variable ist eine Stelle im Arbeitsspeicher (Hauptspeicher, RAM) des Computers, in der man einen Wert ablegen und später wieder abrufen kann.

Man kann sich den Arbeitsspeicher als eine Reihe von Fächern vorstellen, die in einer langen Reihe angeordnet sind. Jedes Fach – oder Speicherstelle – ist fortlaufend nummeriert. Diese Nummern bezeichnet man als Speicheradressen oder einfach als Adressen. Eine Variable reserviert ein oder mehrere Fächer, in denen dann ein Wert abgelegt werden kann.

Der Name deiner Variablen (zum Beispiel meineVariable) ist ein Bezeichner für eines dieser Fächer, damit man es leicht finden kann, ohne dessen Speicheradresse zu kennen. Wie die folgende Abbildung zeigt, beginnt unsere Variable meineVariable an der Speicheradresse 103. Je nach Größe (= dem Datentyp) von meineVariable
kann die Variable eine oder mehrere Speicheradressen belegen:

Variable im Hauptspeicher - C++-Kurs für einsteiger www.mbergmann-sh.de

Speicher reservieren

Wenn man in C++ eine Variable definiert, muss man dem Compiler nicht nur deren Namen, sondern auch den Datentyp der Variablen mitteilen – also, ob es sich zum Beispiel um eine Ganzzahl (Integer) oder ein Zeichen (Buchstaben, Ziffern etc.) handelt. Anhand dieser Information weiß der Compiler, um welche Art Variable es sich handelt und wieviel Platz im Speicher für die Aufnahme des Wertes der Variablen zu reservieren ist.

 Jedes »Fach« im Speicher ist ein Byte groß. Wenn die erzeugte Variable vier Bytes benötigt, muss man vier Bytes im Speicher – oder vier Fächer – reservieren. Der Variablentyp (zum Beispiel int für Integer) teilt dem Compiler mit, wie viele Speicherplätze (oder Fächer) für diese Variable benötigt werden.

 Da Computer Werte in Bits und Bytes darstellen und Speicher in Bytes gemessen wird, ist es wichtig, dass du diese Begriffe verstehst und verinnerlichst.

Größe von Integer-Werten

Jeder Variablentyp belegt im Speicher einen bestimmten Bereich, dessen Größe immer gleichbleibend ist, auf verschiedenen Computern aber unterschiedlich groß sein kann. Das heißt, ein Integer-Wert (Datentyp int) nimmt auf der einen Maschine zwei Bytes, auf einer anderen vielleicht vier ein – aber auf ein und demselben Computer ist dieser Platz immer gleich groß.

 Eine Variable vom Typ char (zur Aufnahme von Zeichen) ist gewöhnlich ein Byte lang. Eine Ganzzahl vom Typ short belegt auf den meisten Computern zwei Bytes, eine Ganzzahl vom Typ long ist normalerweise vier Bytes lang, und eine Ganzzahl (ohne das Schlüsselwort short oder long) kann zwei oder vier Bytes einnehmen. Die Größe einer Ganzzahl wird vom Computer (16Bit oder 32Bit oder 64Bit) oder vom Compiler bestimmt. Auf einem 64-Bit-PC mit aktuellem C++ Compiler belegen die Ganzzahlen vier Bytes. Dieses Tutorial geht davon aus, dass Ganzzahlen vier Bytes groß sind. Das muss bei dir jedoch nicht so sein. Mit dem Listing „sizes.cpp“ lässt sich die genaue Größe der Typen auf Ihrem Computer bestimmen.

// Listing: sizes.cpp
#include <iostream>

using namespace std;

int main(void)
{
    cout << "Groesse eines int:\t\t"       << sizeof(int)        << " Bytes.\n";
    cout << "Groesse eines short int:\t"   << sizeof(short int)  << " Bytes.\n";
    cout << "Groesse eines long int:\t"    << sizeof(long int)   << " Bytes.\n";
    cout << "Groesse eines char:\t\t"      << sizeof(char)       << " Bytes.\n";
    cout << "Groesse eines float:\t\t"     << sizeof(float)      << " Bytes.\n";
    cout << "Groesse eines double:\t"      << sizeof(double)     << " Bytes.\n";
    cout << "Groesse eines bool:\t\t"      << sizeof(bool)       << " Bytes.\n";
    
    return 0;
}

Hinweis: Der sizeof()-Operator gibt den Speicherbedarf einer Variable oder eines Datentyps zurück.

Ausgabe:

Groesse eines int:       4 Bytes.
Groesse eines short int: 2 Bytes.
Groesse eines long int:  8 Bytes.
Groesse eines char:      1 Bytes.
Groesse eines float:     4 Bytes.
Groesse eines double:    8 Bytes.
Groesse eines bool:      1 Bytes.

Vorzeichenbehaftete Variablen: signed und unsigned

Alle genannten Typen kommen außerdem in zwei Versionen vor: mit Vorzeichen (signed
) und ohne Vorzeichen (unsigned). Dem liegt der Gedanke zugrunde, dass man manchmal zwar negative Zahlen benötigt, manchmal aber nicht. Ganze Zahlen (short und long) ohne das Wort unsigned werden als signed (das heißt: vorzeichenbehaftet) angenommen. Vorzeichenbehaftete Ganzzahlen sind entweder negativ oder positiv, während ganze Zahlen ohne Vorzeichen (unsigned int) immer positiv sind.

 Da sowohl für vorzeichenbehaftete als auch vorzeichenlose Ganzzahlen dieselbe Anzahl von Bytes zur Verfügung steht, ist die größte Zahl, die man in einem unsigned
int
speichern kann, doppelt so groß wie die größte positive Zahl, die man in einem signed int unterbringt. Ein unsigned short int kann Zahlen von 0 bis 65535 speichern. Bei einem signed short int ist die Hälfte der Zahlen negativ. Daher kann ein signed short int Zahlen im Bereich von -32768 bis 32767 darstellen. Sollte dich dieses etwas verwirren, so findest du unter Tabelle: Datentypen unter C/C++ eine ausführliche Beschreibung.

Grundlegende Datentypen

In C/C++ gibt es weitere Variablentypen, die man zweckentsprechend in ganzzahlige Variablen (die bisher behandelten Typen), Fließkommavariablen und Zeichenvariablen einteilt.

Die Werte von Fließkommavariablen lassen sich als Bruchzahlen ausdrücken – das heißt, es handelt sich um reelle Zahlen.

Zeichenvariablen (char) nehmen ein einzelnes Byte auf und dienen der Speicherung der 256 möglichen Zeichen und Symbole der ASCII- und erweiterten ASCII-Zeichensätze.

 Der ASCIIZeichensatz ist ein Standard, der die im Computer verwendeten Zeichen definiert. ASCII steht als Akronym für American Standard Code for Information Interchange (amerikanischer Standard-Code für den Informationsaustausch). Nahezu jedes Computer-Betriebssystem unterstützt ASCII. Daneben sind meistens weitere internationale Zeichensätze möglich.

 Die in C++-Programmen verwendeten gängigsten Variablentypen sind in der Tabelle „Variablentypen“ aufgeführt. Diese Tabelle zeigt den Variablentyp, den belegten Platz im Speicher (Grundlage ist der Computer des Autors) und den möglichen Wertebereich, der sich aus der Größe des Variablentyps ergibt. Vergleichen Sie dazu die Ausgabe des Programms aus dem Listing „sizes.cpp“.

 Tabelle: Variablentypen

Typ Größe Wert
bool1 Bytetrue oder false
unsigned short int2 Byte0 bis 65,535
short int2 Byte-32,768 bis 32,767
unsigned long int4 Byte0 bis 4,294,967,295
long int4 Byte-2,147,483,648 bis

2,147,483,647

int (16 Bit)2 Byte-32,768 bis 32,767
int (32/64 Bit)4 Byte-2,147,483,648 bis

2,147,483,647

unsigned int (16 Bit)2 Byte0 bis 65,535
unsigned int (32/64 Bit)4 Byte0 bis 4,294,967,295
char1 Byte256 Zeichenwerte
float4 Byte1.2e-38 bis 3.4e38
double8 Byte2.2e-308 bis 1.8e308

 Variablen definieren

Wenn du eine Variable deklarierst, wird dafür Speicherplatz allokiert (bereitgestellt). Was auch immer zu diesem Zeitpunkt sich in dem Speicherplatz befindet, stellt den Wert dieser Variablen dar. Wie du dieser Speicherposition einen neuen Wert zuweisen kannst, wirst du gleich erfahren.

Eine Variable erzeugt oder definiert man, indem man den Typ, mindestens ein Leerzeichen, den Variablennamen und ein Semikolon eintippt. Als Variablenname eignet sich nahezu jede Buchstaben-/Ziffernkombination, die allerdings keine Leerzeichen enthalten darf. Gültige Variablennamen sind zum Beispiel x, J23qrsnf und meinAlter. Gute Variablennamen sagen bereits etwas über den Verwendungszweck der Variablen aus („sprechende“ Variablennamen) und erleichtern damit das Verständnis für den Programmablauf. Die folgende Anweisung definiert eine Integer-Variable namens meinAlter:

  int meinAlter;

Für die Programmierpraxis möchte ich dir nahelegen, wenig aussagekräftige Namen wie J23qrsnf zu vermeiden und kurze, aus einem Buchstaben bestehende Variablennamen (wie x oder i) auf Variablen zu beschränken, die nur kurz, für wenige Zeilen Code benötigt werden (z. B. als Zählvariable). Verwende ansonsten lieber „sprechende“ Namen wie meinAlter oder wie_viele_Katzen. Solche Namen sind leichter zu verstehen, wenn du dich drei Wochen später kopfkratzend nach dem Sinn und Zweck deines Codes fragst.

Groß-/Kleinschreibung, Umlaute und Sonderzeichen

C/C++ beachtet die Groß-/Kleinschreibung und behandelt demnach Großbuchstaben und Kleinbuchstaben als verschiedene Zeichen. Eine Variable namens alter unterscheidet sich von Alter und diese wiederum von ALTER.

Für die Schreibweise von Variablennamen gibt es mehrere Konventionen. Unabhängig davon, für welche du dich entscheidest ist es ratsam, innerhalb eines Programms bei der einmal gewählten Methode zu bleiben.

 Viele Programmierer bevorzugen für Variablennamen Kleinbuchstaben. Wenn der Name aus zwei Wörtern besteht (zum Beispiel mein Auto), gibt es zwei übliche Konventionen: mein_auto oder meinAuto. Letztere Form wird auch als Kamel-Notation („CamelCase“) bezeichnet, da die Großschreibung im Wort selbst an einen Kamelhöcker erinnert.

Umlaute und bestimmte Sonderzeichen (äÄ oO üÜ ß \ /) dürfen in Variablennamen nicht vorkommen.

Ungarische Notation

Viele fortgeschrittene Programmierer schreiben Ihren Code in der sogenannten Ungarischen Notation. Dieser Notation liegt der Gedanke zugrunde, dass jede Variable mit einem oder mehreren Buchstaben beginnt, die auf den Typ der Variablen verweisen. So wird ganzzahligen Variablen (Integer) ein kleines ivorangestellt oder Variablen vom Typ long ein kleines l. Andere Notationen verweisen auf Konstanten, globale Variablen, Zeiger und so weiter. Dies ist jedoch für die C-Programmierung von wesentlich größerer Bedeutung als für C++, da C++ die Erzeugung benutzerdefinierter Datentypen unterstützt (siehe »Klassen«), und von sich aus typenstrenger ist.

Schlüsselwörter

In C++ sind bestimmte Wörter reserviert, die man nicht als Variablennamen verwenden darf. Es handelt sich dabei um die Schlüsselwörter, mit denen der Compiler das Programm steuert. Zu den Schlüsselwörtern gehören zum Beispiel if, while, for und main. Im allgemeinen fallen aussagekräftige Name für Variablen nicht mit Schlüsselwörtern zusammen. Eine Liste der C++-Schlüsselwörter findest du im Anhang: Schlüsselwörter in C/C++.

Mehrere Variablen gleichzeitig erzeugen

In einer Anweisung lassen sich mehrere Variablen desselben Typs gleichzeitig erzeugen, indem man den Typ schreibt und dahinter die Variablennamen durch Kommata getrennt aufführt. Dazu ein Beispiel:

unsigned int meinAlter, meinGewicht;   //Zwei Variablen vom Typ unsigned int
long Flaeche, Breite, Laenge;          //Drei Variablen vom Typ long

 Wie man sieht, werden meinAlter und meinGewicht gemeinsam als Variablen vom Typ unsigned int deklariert. Die zweite Zeile deklariert drei eigenständige Variablen vom Typ long mit den Namen Flaeche, Breite und Laenge. Der Typ (long) wird allen Variablen zugewiesen, so dass man in ein- und derselben Definitionsanweisung keine unterschiedlichen Typen festlegen kann.

Werte an Variablen zuweisen

Einen Wert weist man einer Variablen mit Hilfe des Zuweisungsoperators (=) zu. Zum Beispiel formuliert man die Zuweisung des Wertes 5 an die Variable Breite wie folgt:

unsigned short Breite;
Breite = 5;

Diese Schritte kann man zusammenfassen und die Variable Breite bei ihrer Definition initialisieren:

unsigned short Breite = 5;

 Die Initialisierung sieht nahezu wie eine Zuweisung aus, und bei Integer-Variablen gibt es auch kaum einen Unterschied. Bei der späteren Behandlung von Konstanten werden Sie sehen, das man bestimmte Werte initialisieren muss, da Zuweisungen nicht möglich sind. Der wesentliche Unterschied besteht darin, dass die Initialisierung bei der Erzeugung der Variablen stattfindet.

Ebenso wie Sie mehrere Variable gleichzeitig definieren können, ist es auch möglich, mehr als eine Variable auf einmal zu erzeugen. Betrachten wir folgendes Beispiel:

//Erzeugung von zwei long-Variablen und ihre Initialisierung
long Breite = 5, Laenge = 7;

In diesem Beispiel wird die Variable Breite vom Typ long mit 5 und die Variable Laenge
vom Typ long mit dem Wert 7 initialisiert. Sie können aber auch Definitionen und Initialisierungen mischen:

int meinAlter = 39, ihrAlter, seinAlter = 40;

Das Listing „flaeche.cpp“ zeigt ein vollständiges Programm, das du sofort kompilieren kannst. Es berechnet die Fläche eines Rechtecks und schreibt das Ergebnis auf den Bildschirm:

// Listing: flaeche.cpp
// - demonstriert den Einsatz von Variablen
#include <iostream>

using namespace std;

int main(void)
{
    unsigned short int Width = 5, Length;
    Length = 10;
    
    // eine Variable vom Typ 'unsigned short int' erzeugen und
    // mit dem Ergebnis der Multiplikation von Width und Length
    // initialisieren:
    unsigned short int Area = (Width * Length);
    
    // Werte ausgeben:
    cout << "Breite:  " << Width << endl;
    cout << "Laenge:  " << Length << endl;
    cout << "Flaeche: " << Area << endl;
    
    return 0;
}

Ausgabe:

Breite: 5
Laenge: 10
Flaeche: 50

Programmanalyse:

  • Zeile 3 enthält die include-Anweisung für die iostream-Bibliothek, die wir benötigen, um cout verwenden zu können. In Zeile 4 beginnt das Programm.

  • In Zeile 9 wird die Variable Width als vorzeichenloser short int definiert und mit dem Wert 5 initialisiert. Eine weitere Variable vom gleich Typ, Length, wird ebenfalls hier definiert, aber nicht initialisiert. In Zeile 10 erfolgt die Zuweisung des Wertes 10 an die Variable Length.

  • Zeile 15 definiert die Variable Area vom Typ unsigned short int und initialisiert sie mit dem Wert, der sich aus der Multiplikation von Width und Length ergibt. In den Zeilen 18 bis 20 erfolgt die Ausgabe der Variablenwerte auf dem Bildschirm. Beachte, dass das spezielle Wort endl eine neue Zeile erzeugt.

Pseudo-Datentypen mit typedef

Es ist lästig, zeitraubend und vor allem fehleranfällig, wenn man häufig unsigned short
int
schreiben muss. In C++ kann man einen Alias für diese Wortfolge mit Hilfe des Schlüsselwortes typedef (für Typendefinition) erzeugen.

 Mit diesem Schlüsselwort erzeugt man lediglich ein Synonym und keinen neuen Typ (letzteres heben wir uns für später auf). Auf das Schlüsselwort typedef folgt ein vorhandener Typ und danach gibt man den neuen Namen an. Den Abschluss bildet ein Semikolon. Beispielsweise erzeugt

typedef unsigned short int USHORT;

den neuen Namen USHORT, den man an jeder Stelle verwenden kann, wo man sonst unsigned short int schreiben würde. Listing „typedef.cpp“ ist eine Neuauflage von Listing „flaeche.cpp“ und verwendet die Typendefinition USHORT anstelle von unsigned short int. Schauen wir uns das am praktischen Beispiel an:

// Listing: typedef.cpp
// Zeigt die Verwendung des Schlüsselworts typedef
#include <iostream>

using namespace std;

typedef unsigned short int USHORT;        // mit typedef definiert

int main()
{
  USHORT  Width = 5;
  USHORT Length;
  Length = 10;
    
  USHORT Area  = Width * Length;
    
  cout << "Breite:  " << Width << "\n";
  cout << "Laenge:  "  << Length << endl;
  cout << "Flaeche: " << Area <<endl;
  return 0;
}

Das Programm macht dasselbe wie sein Vorgänger flaeche.cpp – allerdings haben wir uns per typedef ein wenig Tipparbeit gespart.

Ausgabe:

Breite: 5
Laenge: 10
Flaeche: 50

Wann verwendet man short und wann long?

 Neueinsteiger in die C++-Programmierung wissen oft nicht, wann man eine Variable als long und wann als short deklarieren sollte. Die Regel ist einfach: Wenn der in der Variablen zu speichernde Wert zu groß für seinen Typ werden kann, nimmt man einen größeren Typ.

 Wie die Tabelle Datentypen unter C/C++ zeigt, können ganzzahlige Variablen vom Typ unsigned short (vorausgesetzt, dass sie aus 2 Bytes bestehen) nur Werte bis zu 65535 aufnehmen. Variablen vom Typ signed short verteilen ihren Wertebereich auf negative und positive Zahlen. Deshalb ist das Maximum eines solchen Typs nur halb so groß.

 Obwohl Integer-Zahlen vom Typ unsigned long sehr große Ganzzahlen aufnehmen können (bis 4.294.967.295), hat auch dieser Typ einen begrenzten Wertebereich. Benötigt man größere Zahlen, kann man auf float oder double ausweichen und einen gewissen Genauigkeitsverlust in Kauf nehmen. Variablen vom Typ float oder double können zwar extrem große Zahlen speichern, allerdings sind auf den meisten Computern nur die ersten 7 bzw. 19 Ziffern signifikant. Das bedeutet, dass die Zahl nach dieser Stellenzahl mit Verlust gerundet wird. Alternativ könnte man aber auch den Datentyp unsigned long long verwenden – dieser speichert Werte zwischen 0 und 18446744073709551615.

Schlechter Tipp für Fulderer: Kürzere Variablen belegen weniger Speicher. Heute jedoch ist Speicher billig und das Leben kurz. Deshalb lassen Sie sich nicht davon abhalten, int zu verwenden, auch wenn damit 4 Byte auf Ihrem PC belegt werden.

Ein guter Tipp hingegen ist es, vor der Vergabe eines Datentyps für eine Variable genau zu prüfen, wie groß eine zu speichernde Zahl tatsächlich werden könnte.

Bereichsüberschreitung bei Integer-Werten vom Typ unsigned

 Die Tatsache, dass Ganzzahlen vom Typ unsigned long nur einen begrenzten Wertebereich aufnehmen können, ist nur selten ein Problem. Aber was passiert, wenn der Platz im Verlauf des Programms zu klein wird?

Wenn eine Ganzzahl vom Typ unsigned ihren Maximalwert erreicht, schlägt der Zahlenwert um und beginnt von vorn. Vergleichbar ist das mit einem Kilometerzähler. Das Listing „ueberlauf.cpp“ demonstriert den Versuch, einen zu großen Wert in einer Variablen vom Typ short int abzulegen:

// Listing: ueberlauf.cpp
// demonstriert den Überlauf einer Variablen
#include <iostream>
 
using namespace std;

int main(void)
{
  unsigned short int smallNumber;

  smallNumber = 65535;
  cout << "Kleine Zahl: " << smallNumber << endl;
  smallNumber++;
  cout << "Kleine Zahl: " << smallNumber << endl;
  smallNumber++;
  cout << "Kleine Zahl: " << smallNumber << endl;
    
  return 0;
}

Ausgabe:

Kleine Zahl: 65535
Kleine Zahl: 0
Kleine Zahl: 1

Programmanalyse:

  • Zeile 9 deklariert smallNumber vom Typ unsigned short int (auf dem Computer des Autors 2 Bytes für einen Wertebereich zwischen 0 und 65535). Zeile 11 weist den Maximalwert smallNumber zu und gibt ihn in Zeile 12 aus.
  • Die Anweisung in Zeile 13 inkrementiert smallNumber, das heißt, addiert den Wert 1. Das Symbol für das Inkrementieren ist ++ (genau wie der Name C++ eine Inkrementierung von C symbolisieren soll). Der Wert in smallNumber sollte nun 65536 lauten. Da aber Ganzzahlen vom Typ unsigned short keine Zahlen größer als 65535 speichern können, schlägt der Wert zu 0 um. Die Ausgabe dieses Wertes findet in Zeile 14 statt.
  • Die Anweisung in Zeile 15 inkrementiert smallNumber erneut. Es erscheint nun der neue Wert 1.

Bereichsüberschreitung bei Integer-Werten vom Typ signed

Im Gegensatz zu unsigned Integer-Zahlen besteht bei einer Ganzzahl vom Typ signed die Hälfte des Wertebereichs aus negativen Werten. Den Vergleich mit einem Kilometerzähler stellen wir nun so an, dass er bei einem positiven Überlauf vorwärts und bei negativen Zahlen rückwärts läuft. Vom Zählerstand 0 ausgehend erscheint demnach die Entfernung ein Kilometer entweder als 1 oder -1. Wenn man den Bereich der positiven Zahlen verlässt, gelangt man zur größten negativen Zahl und zählt dann weiter herunter bis Null. das Listing „ueberlauf2.cpp“ zeigt die Ergebnisse, wenn man auf die maximale positive Zahl in einem signed short int eine 1 addiert:

 // Listing: ueberlauf2.cpp
 // demonstriert den Überlauf einer Variablen
 #include <iostream>
 
 using namespace std;
 
 int main()
{
  short int smallNumber;
  smallNumber = 32767;
    
  cout << "Kleine Zahl: " << smallNumber << endl;
  smallNumber++;
  cout << "Kleine Zahl: " << smallNumber << endl;
  smallNumber++;
  cout << "Kleine Zahl: " << smallNumber << endl; 
    
  return 0;
}

Ausgabe:

Kleine Zahl: 32767
Kleine Zahl: -32768
Kleine Zahl: -32767

Programmanalyse:

  • Zeile 9 deklariert smallNumber dieses Mal als signed short int (wenn man nicht explizit unsigned festlegt, gilt per Vorgabe signed). Das Programm läuft fast genau wie das vorherige, liefert aber eine andere Ausgabe. Um diese Ausgabe zu verstehen, muss man die Bit-Darstellung vorzeichenbehafteter (signed) Zahlen in einer Integer-Zahl von 2 Bytes Länge kennen.

     Analog zu vorzeichenlosen Ganzzahlen findet bei vorzeichenbehafteten Ganzzahlen ein Umschlagen vom größten positiven Wert in den höchsten negativen Wert statt.

Zeichen

Zeichen-Variablen (vom Typ char) sind in der Regel 1 Byte groß und können damit 256 Werte aufnehmen. Eine Variable vom Typ char kann als kleine Zahl (0-255) oder als Teil des ASCII-Zeichensatzes interpretiert werden. ASCII steht für American Standard Code for Information Interchange (amerikanischer Standard-Code für den Informationsaustausch). Mit dem ASCII-Zeichensatz und seinem ISO-Gegenstück (International Standards Organization) können alle Buchstaben, Zahlen und Satzzeichen codiert werden.

Computer haben keine Ahnung von Buchstaben, Satzzeichen oder Sätzen. Alles was sie verstehen, sind Zahlen. Im Grunde genommen kann ein Computer nur feststellen, ob genügend Strom an einem bestimmten Leitungspunkt vorhanden ist. Wenn ja, wird dies intern mit einer 1 dargestellt, wenn nicht mit einer 0. Durch die Kombination von Einsen und Nullen erzeugt der Computer Muster, die als Zahlen interpretiert werden können. Und diese Zahlen können wiederum Buchstaben und Satzzeichen zugewiesen werden

Im ASCII-Code wird dem kleinen »a« der Wert 97 zugewiesen. Allen Klein- und Großbuchstaben sowie den Zahlen und Satzzeichen werden Werte zwischen 1 und 128 zugewiesen. Weitere 128 Zeichen und Symbole sind für den Computer-Hersteller reserviert. In der Realität hat sich aber der erweiterte IBM-Zeichensatz und auf modernen Rechnern der UTF-8 Zeichensatz als Quasi-Standard durchgesetzt.

Zeichen und Zahlen

 Wenn du ein Zeichen, zum Beispiel »a«, in einer Variablen vom Typ char ablegst, steht dort eigentlich eine Zahl zwischen 0 und 255. Der Compiler kann Zeichen (dargestellt durch ein einfaches Anführungszeichen gefolgt von einem Buchstaben, einer Zahl oder einem Satzzeichen und einem abschließenden einfachen Anführungszeichen) problemlos in ihren zugeordneten ASCII-Wert und wieder zurück verwandeln.

 Die Wert/Buchstaben-Beziehung ist zufällig. Dass dem kleinen »a« der Wert 97 zugewiesen wurde, ist reine Willkür. So lange jedoch, wie jeder (Tastatur, Compiler und Bildschirm) sich daran hält, gibt es keine Probleme. Du solltest jedoch beachten, dass zwischen dem Wert 5 und dem Zeichen »5« ein großer Unterschied besteht. Letzteres hat einen Wert von 53, so wie das »a« einen Wert von 97 hat.

Schreiben wir ein Programm, welches Zeichen anhand ihres ASCII-Codes ausdruckt (Listing „asciicode.cpp“):

// Listing: asciicode.cpp
// druckt Zeichen anhand von Zahlen
#include <iostream>

using namespace std;

int main(void)
{
  for (int i = 32; i<128; i++)
    cout << (char) i;
    
  return 0;
}

Ausgabe:

!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

Dieses einfache Programm druckt die Zeichenwerte für die Integer 32 bis 127.

Besondere Zeichen

 Der C++-Compiler kennt einige spezielle Formatierungszeichen. Die Tabelle Escape-Codes listet die geläufigsten auf. In deinem Code gibst du diese Zeichen mit einem vorangestellten Backslash (auch Escape-Zeichen genannt) ein. Um zum Beispiel einen Tabulator in deinen Code mit aufzunehmen, würdest du ein einfaches Anführungszeichen, den Backslash, den Buchstaben t und ein abschließendes einfaches Anführungszeichen eingeben.

char tabZeichen = '\t';

 Dies Beispiel deklariert eine Variable vom Typ char und initialisiert sie mit dem Zeichenwert \t, der als Tabulator erkannt wird. Diese speziellen Druckzeichen werden benötigt, wenn die Ausgabe entweder auf dem Bildschirm, in eine Datei oder einem anderen Ausgabegerät erfolgen soll.

 Ein Escape-Zeichen ändert die Bedeutung des darauf folgenden Zeichens. So ist das Zeichen n zum Beispiel nur der Buchstabe n. Wird davor jedoch ein Escape-Zeichen gesetzt (\), steht das Ganze für eine neue Zeile.

Tabelle: Escape-Codes

Zeichen Bedeutung
 \n Neue Zeile
 \t Tabulator
\b Backspace
 \“ Anführungszeichen
 \‘ Einfaches Anführungszeichen
 \? Fragezeichen
 \\ Backslash

Was ist eine Konstante?

Konstanten sind ebenso wie Variablen benannte Speicherstellen. Während sich Variablen aber ändern können, behalten Konstanten – wie der Name bereits sagt – immer ihren Wert. Du musst eine Konstante bei der Erzeugung initialisieren und kannst ihr dann später keinen neuen Wert zuweisen.

Literale Konstanten

 C++ kennt zwei Arten von Konstanten: literale und symbolische.

 Eine literale Konstante ist ein Wert, den man direkt in das Programm an der Stelle des Vorkommens eintippt. In der Anweisung

int meinAlter = 39;

 ist meinAlter eine Variable vom Typ int, während die Zahl 39 eine literale Konstante bezeichnet. Man kann 39 keinen Wert zuweisen oder diesen Wert ändern.

Symbolische Konstanten

 Eine symbolische Konstante wird genau wie eine Variable durch einen Namen repräsentiert. Allerdings lässt sich im Gegensatz zu einer Variablen der Wert einer Konstanten nicht nach deren Initialisierung ändern.

 Wenn dein Programm eine Integer-Variable namens Studenten und eine weitere namens Klassen enthält, kann man die Anzahl der Studenten berechnen, wenn die Anzahl der Klassen bekannt ist und man weiß, dass 15 Studenten zu einer Klasse gehören:

Studenten = Klassen * 15;

Wenn man später die Anzahl der Stunden pro Klasse ändern möchte, braucht man das nur in der Definition der Konstanten StudentenProKlasse vorzunehmen, ohne dass man alle Stellen ändern muss, wo man diesen Wert verwendet hat.

 Es gibt zwei Möglichkeiten, eine symbolische Konstante in C++ zu deklarieren. Die herkömmliche und inzwischen veraltete Methode aus dem Sprachumfang von C erfolgt mit der Präprozessor-Direktiven #define.

Konstanten mit #define definieren

Um eine Konstante auf die herkömmliche Weise zu definieren, gibt man ein:

#define StudentenProKlasse 15

 Beachte, dass StudentenProKlasse keinen besonderen Typ (etwa int oder char) aufweist. #define nimmt eine einfache Textersetzung vor. Der Präprozessor schreibt an alle Stellen, wo StudentenProKlasse vorkommt, die Zeichenfolge 15 in den Quelltext.

 Da der Präprozessor vor dem Compiler ausgeführt wird, kommt Ihr Compiler niemals mit der symbolischen Konstanten in Berührung, sondern bekommt immer die Zahl 15 zugeordnet.

Konstanten mit const definieren

 Obwohl #define nach wie vor funktioniert, gibt es in C++ eine neue, bessere und elegantere Lösung zur Definition von Konstanten:

const unsigned short int StudentenProKlasse = 15;

Dieses Beispiel deklariert ebenfalls eine symbolische Konstante namens StudentenProKlasse
, dieses Mal ist aber StudentenProKlasse als Typ unsigned short int definiert. Diese Version bietet verschiedene Vorteile. Zum einen lässt sich der Code leichter warten und zum anderen werden unnötige Fehler vermieden. Der größte Unterschied ist der, dass diese Konstante einen Typ hat und der Compiler die zweckmäßige – sprich typgerechte – Verwendung der Konstanten frühzeitig prüfen kann. Es liegt auf der Hand, dass du dieser Lösung den Vorzug geben solltest.

Erinnerung: Konstanten können nicht während der Ausführung des Programms geändert werden. Wenn du gezwungen bist, die Konstante studentsPerClass zu ändern, dann musst du den Code ändern und neu kompilieren.

Aufzählungstypen (Enumeration)

 Mit Hilfe von Aufzählungskonstanten (enum) können Sie neue Typen erzeugen und dann Variablen dieser Typen definieren, deren Werte auf einen bestimmten Bereich beschränkt sind. Beispielsweise kann man FARBE als Aufzählung deklarieren und dafür fünf Werte definieren: ROT, BLAU, GRUEN, WEISS und SCHWARZ.

 Die Syntax für Aufzählungstypen besteht aus dem Schlüsselwort enum, gefolgt vom Typennamen, einer öffnenden geschweiften Klammer, einer durch Kommata getrennte Liste der möglichen Werte, einer schließenden geschweiften Klammern und einem Semikolon. Dazu ein Beispiel:

enum FARBE { ROT, BLAU, GRUEN, WEISS, SCHWARZ };

 Diese Anweisung realisiert zwei Aufgaben:

  1. FARBE ist der Name der Aufzählung, das heißt, ein neuer Typ.
  2. ROT wird zu einer symbolischen Konstanten mit dem Wert 0, BLAU zu einer symbolischen Konstanten mit dem Wert 1, GRUEN zu einer symbolischen Konstanten mit dem Wert 2 usw.

Jeder Aufzählungskonstanten ist ein Integer-Wert zugeordnet. Wenn man nichts anderes festlegt, weist der Compiler der ersten Konstanten den Wert 0 zu und nummeriert die restlichen Konstanten fortlaufend durch. Jede einzelne Konstante lässt sich aber auch mit einem bestimmten Wert initialisieren, wobei die Werte der nicht initialisierten Konstanten immer um 1 höher sind als die Werte ihres Vorgängers. Schreibt man daher

enum FARBE { ROT=100, BLAU, GRUEN=500, WEISS, SCHWARZ=700 };

erhält ROT den Wert 100, BLAU den Wert 101, GRUEN den Wert 500, WEISS den Wert 501 und SCHWARZ den Wert 700.

Damit kannst du Variablen vom Typ FARBE definieren, denen dann allerdings nur einer der Aufzählungswerte (in diesem Falle ROT, BLAU, GRUEN, WEISS oder SCHWARZ oder die Werte 100, 101, 500, 501 oder 700) zugewiesen werden kann. Du kannst Ihrer Variablen FARBE beliebige Farbwerte zuweisen, ja sogar beliebige Integer-Werte, auch wenn es keine gültige Farbe ist. Ein guter Compiler wird in einem solchen Fall jedoch eine Fehlermeldung ausgeben. Merke dir unbedingt, dass Aufzählungsvariablen vom Typ unsigned int sind und dass es sich bei Aufzählungskonstanten um Integer-Variablen handelt. Es ist jedoch von Vorteil, diesen Werten einen Namen zu geben, wenn Sie mit Farben, Wochentagen oder ähnlichen Wertesätzen arbeiten. Im Listing „enum.cpp“ findest du ein Programm, das eine Aufzählungskonstante verwendet:

// Listing: enum.cpp
// demonstriert die Verwendung von
// Aufzählungskonstanten
#include <iostream>

using namespace std;

int main(void)
{
    // Variable für die Auswahl eines Wochentags deklarieren:
    int auswahl;
    // Aufzählung definieren:
    enum Wochentage
    {
        Sonntag, Montag, Dienstag,
        Mittwoch, Donnerstag, Freitag,
        Samstag
    };
    
    // Programmtitel ausgeben:
    cout << "\nWochentage" << endl;
    cout << "----------" << endl;
    
    // Benutzereingabe holen:
    cout << "\nGib die Kennziffer eines Wochentags an (0 - 6): ";
    cin >> auswahl;
    
    // -- Benutzereingabe auswerten --
    switch(auswahl)
    {
        case Sonntag:
            cout << "\nDu hast den Sonntag im Sinn...";
            break;
        case Montag:
            cout << "\nDu hast den Montag im Sinn...";
            break;
        case Dienstag:
            cout << "\nDu hast den Dienstag im Sinn...";
            break;
        case Mittwoch:
            cout << "\nDu hast den Mittwoch im Sinn...";
            break;
        case Donnerstag:
            cout << "\nDu hast den Donnerstag im Sinn...";
            break;
        case Freitag:
            cout << "\nDu hast den Freitag im Sinn...";
            break;
        case Samstag:
            cout << "\nDu hast den Samstag im Sinn...";
            break;
        default:
            cout << "\nFehler: Nur Ziffern 0 bis 6 erlaubt!";
            break;
    }
    
    if ((auswahl == Sonntag) || (auswahl == Samstag))
        cout << "\nDu bist ja schon im Wochenende!\n";
    else
        cout << "\nOK - nimm dir mal 'nen Tag frei!\n";

  return 0;
}

Ausgabe:

bergmann@MB-Workstation:~/Projekte/Cpp-Kurs$ ./enum

Wochentage
----------

Gib die Kennziffer eines Wochentags an (0 - 6): 3

Du hast den Mittwoch im Sinn...
OK - nimm dir mal 'nen Tag frei!

Programmanalyse:

Der Anwender wird gebeten, einen Wert zwischen 0 und 6 einzugeben. Die Eingabe von z. B. »Sonntag« als Tag ist nicht möglich. Das Programm hat keine Ahnung, wie es die Buchstaben in Sonntag in einen der Aufzählungswerte übersetzen soll. Es kann jedoch die numerischen Werte, die der Anwender eingibt, mit einer oder mehreren Aufzählungskonstanten (wie in den Zeilen 15 bis 17 notiert) abgleichen. Die Verwendung von Aufzählungskonstanten verdeutlicht die Absicht des Vergleichs besser. Du hättest das Ganze natürlich auch mit Integer-Konstanten erreichen können – mehr dazu im nächsten Beispiel.

  • Zeile 11 deklariert eine Integer-Variable, in der die Benutzereingabe gespeichert werden soll
  • In den Zeilen 13 bis 18 definieren wir eine Aufzählung mit dem Namen Wochentage. Sie beinhaltet die Wochentage als symbolische Konstanten. Da keiner Konstanten explizit ein Wert zugewiesen wurde, beginnt die Aufzählung mit Sonntag = 0 und endet mit Samstag = 6.
  • Die Zeilen 25 und 26 dienen der Benutzereingabe: Zeile 25 fordert per cout zur Eingabe auf und Zeile 26 weist die Eingabe per cin der Variablen auswahl zu.
  • Ab Zeile 29 werten wir die Benutzereingabe aus und reagieren darauf. Dazu bedienen wir uns zunächst der Verzweigung mittels switch – case – break. Der switch-Anweisung folgt, in geschweifte Klammern gepackt, ein Anweisungsblock. Er besteht aus mehreren case-Anweisungen, die mittels einer break-Anweisung abgeschlossen werden. Zwischen case und break notiert man weitere auszuführende Anweisungen – in unserem Fall die Ausgabe des gewählten Wochentags.
  • In Zeile 52 wird eine default-Aktion für switch angegeben. Sie wird ausgeführt, wenn der Benutzer eine Zahl (oder gar ein Zeichen) eingibt, die nicht durch die Fallunterscheidungen der einzelnen case-Anweisungen abgedeckt wird. In unserem vorliegenden Fall gibt default eine Fehlermeldung aus. Die switch-Anweisung eignet sich besonders gut, um auf bestimmte, einzelne Werte zu reagieren.Die switch-Anweisung wertet die symbolischen Konstanten Sonntag (0) bis Samstag (6) aus, indem sie deren hinterlegte numerische Werte für Vergleiche heranzieht.
  • Ab Zeile 57 benutzen wir eine if – else Verzweigung, um dem Benutzer, je nach seiner getroffenen Auswahl, noch eine kleine Meldung zukommen zu lassen. Das Schema dieses Instruments zu Fallunterscheidung sieht wie folgt aus:
    if (1. Bedingung || 2. Bedingung)  // WENN Bedingung 1 ODER Bedingung 2 zutrifft, dann
      gib Meldung 1 aus
    else // ANDERNFALLS...
      gib Meldung 2 aus
  • Zeile 57 prüft die erste Bedingung: Hat die Variable auswahl den Wert 0 (Sonntag) ODER 6 (Samstag)? Falls Bedingung 1 zutrifft, dann wird in Zeile 58 die Meldung „Du bist ja schon im Wochenende!“ ausgegeben und die Verzweigung wird verlassen. Der else-Zweig wird dabei übersprungen, da die Bedingung ja bereits erfüllt ist. Andernfalls geht es weiter in Zeile 59.
  • Zeile 59: Der else-Zweig prüft, ob Bedingung 1 nicht erfüllt wurde (weder Sonntag, noch Samstag gewählt). Trifft das zu, so wird in Zeile 60 die Meldung „OK – nimm dir mal ’nen Tag frei!“ ausgegeben.Die if-Anweisung eignet sich gut, wenn mehrere Bedingungen miteinander kombiniert werden müssen. Die Bedingungen können dabei mittels bool’scher Operatoren miteinander verknüpft werden. Die Zeichenfolge „||“ steht in C/C++ für das logische ODER.
  • Das Programm prüft zuerst per switch-Anweisung, ob ein gültiger Wert eingegeben wurde und arbeitet danach die verknüpften Bedingungen der if-Anweisung ab. Beide Programmsegmente liefern auf jeden Fall eine Meldung.
  • Auch bei Eingabe einer nicht erlaubten Ziffer ändert sich daran nichts. Das Programm gibt dann zwar eine (gewollte) Fehlermeldung aus, bietet dir aber trotzdem einen Urlaubstag an. 😉

Ich hatte ja eingangs schon erwähnt, dass man das Programm, anstatt eine Aufzählung zu verwenden, auch mit einfachen Integer-Konstanten umsetzen könnte. Das Listing „intchoice.cpp“ zeigt dir in einer abgespeckten Version, wie das funktioniert:

// Listing: intchoice.cpp
// demonstriert die Verwendung von
// Integer-Konstanten
#include <iostream>

using namespace std;

int main(void)
{
  // Variable für die Auswahl eines Wochentags deklarieren:
  int auswahl;
  // Integer-Konstanten für Wochentage definieren:
  const int Sonntag    = 0;
  const int Montag     = 1;
  const int Dienstag   = 2;
  const int Mittwoch   = 3;
  const int Donnerstag = 4;
  const int Freitag    = 5;
  const int Samstag    = 6;
  
  // Benutzereingabe holen:
  cout << "\nGib die Kennziffer eines Wochentags an (0 - 6): ";
  cin >> auswahl;
  
  // äußere Schleife: Fehleingaben abfangen
  if ((auswahl > -1) && (auswahl < 7))	
    // innere Schleife: Eingabe auswerten
    if ((auswahl == Sonntag) || (auswahl == Samstag))
      cout << "\nDu bist ja schon im Wochenende!\n";
    else
      cout << "\nOK - nimm dir mal 'nen Tag frei!\n";
  else
    cout << "Fehler: nur 0 bis 6 erlaubt!" << endl;
  
  return 0;
}

Ausgabe:

# 1. Programmlauf: Ungültige Eingabe
bergmann@MB-Workstation:~/Projekte/Cpp-Kurs$ ./intchoice

Gib die Kennziffer eines Wochentags an (0 - 6): 8
Fehler: nur 0 bis 6 erlaubt!

# 2. Programmlauf: Tag 3 gewählt
bergmann@MB-Workstation:~/Projekte/Cpp-Kurs$ ./intchoice

Gib die Kennziffer eines Wochentags an (0 - 6): 3

OK - nimm dir mal 'nen Tag frei!

Programmanalyse:

In diesem Programm wurde  jede Konstante (Sonntag, Montag etc.) einzeln definiert, ein Aufzählungstyp Tage existiert hier nicht. Aufzählungskonstanten haben allerdings den Vorteil, dass sie selbsterklärend sind – die Absicht des Aufzählungstyps Tage ist jedem sofort klar.

  • Anstatt einen Aufzählungstyp zu verwenden, definieren wir hier in den Zeilen 13 bis 19 einzelne Integer-Konstanten und weisen ihnen einen entsprechenden Wert zu.
  • Die Zeilen 22 und 23 dienen der Benutzereingabe mittels cout und cin.
  • Zeile 23: Der Variablen auswahl wird per cin der eingelesene Wert übergeben.
  • In Zeile 25 beginnt die äußere if-else Schleife – sie dient zum Abfangen unzulässiger Zifferneingaben und endet in Zeile 33. Die Bedingung lauten dieses Mal:
    WENN auswahl GRÖSSER als -1 UND auswahl KLEINER als 7... (die Zeichenfolge „&&“ steht für das logischen UND in C/C++)
  • Falls die Bedingung nicht zutrifft (Eingabe einer nicht erlaubten Ziffer), dann wird das Programm beim else in Zeile 32 fortgesetzt und gibt in Zeile 33 eine Fehlermeldung aus. Die äußere if-Schleife ist damit abgeschlossen und das Programm endet in Zeile 35.
  • Trifft die Bedingung zu, so wird die innere if-Schleife ab Zeile 28 abgearbeitet. Sie prüft, ob einer der Werte für Sonntag oder Samstag vorliegt, verzweigt entsprechend (zu Zeile 29 oder Zeile 31) und endet dann. Somit kann dann auch die äußere if-Schleife geschlossen werden, da alle Bedingungen abgearbeitet sind. Auch in diesem Fall endet das Programm anschließend mit Zeile 35.

Das Beispiel demonstriert auch den Umstand, dass if-else Schleifen ineinander verschachtelt werden können. In diesem Fall wird zunächst die äußere Schleife verarbeitet/ausgewertet und erst danach die Innere.

Zusammenfassung

In diesem Kapitel hast du Variablen und Konstanten für numerische Werte und Zeichen kennengelernt, in denen du in C/C++ während der Ausführung deines Programms Daten speichern kannst. Numerische Variablen sind entweder Ganzzahlen (char, short und long int) oder Fließkommazahlen (float und double). Die Zahlen können darüber hinaus vorzeichenlos oder vorzeichenbehaftet (unsigned und signed) sein. Wenn auch alle Typen auf unterschiedlichen Computersystemen unterschiedlich groß sein können, so wird jedoch mit dem Typ für einen bestimmte Computertyp immer eine genaue Größe angegeben.

 Bevor man eine Variable verwenden kann, muss man sie deklarieren. Damit legt man gleichzeitig auch den Datentyp fest, der sich in der Variablen speichern lässt. Wenn man eine zu große Zahl in einer Integer-Variablen ablegt, erhält man ein falsches Ergebnis.

 Dieses Kapitel hat auch literale und symbolische Konstanten, sowie Aufzählungskonstanten behandelt und die beiden Möglichkeiten zur Deklaration symbolischer Konstanten aufgezeigt: Die Verwendung von #define und des Schlüsselwortes const.