C++-Programmierung/ Einführung in C++/ Variablen, Konstanten und ihre Datentypen

Aus Wikibooks
Zur Navigation springen Zur Suche springen


Variablen sind Behälter für Werte, sie stellen gewissermaßen das Gedächtnis eines Programms bereit. Konstanten sind spezielle Variablen, sie ändern ihren Wert nie. Der Datentyp einer Variablen oder Konstanten beschreibt, wie der Inhalt zu verstehen ist.

Ein Rechner kennt nur zwei Grundzustände: 0 und 1. Durch eine Aneinanderreihung solcher Zustände lassen sich mehr verschiedene Werte darstellen. Mit acht aufgereihten Zuständen (= 8 Bit = 1 Byte) lassen sich bereits 256 verschiedene Werte darstellen. Diese Werte kann man beispielsweise als Ganzzahl, (Schrift-)Zeichen, Wahrheitswert oder Gleitkommazahl interpretieren. Der Datentyp gibt daher Auskunft darüber, um was es sich handelt.

Der C++-Standard schreibt nicht vor, dass ein Byte aus genau 8 Bit bestehen muss – diese Anzahl ist jedoch weitverbreitet. Es ist also möglich, dass auf einer speziellen Prozessorarchitektur z. B. eine Anzahl von 10 Bit als „ein Byte“ festgelegt ist. Dies ist jedoch äußerst selten der Fall, daher werden wir im Folgenden annehmen, dass ein Byte aus 8 Bit besteht.

Datentypen[Bearbeiten]

Zunächst sollen die Datentypen von C++ beschrieben werden, denn sie sind grundlegend für eine Variable oder Konstante. Die vier wichtigsten (Gruppen von) Datentypen sind: Wahrheitswerte, Zeichen, Ganzzahlen und Gleitkommazahlen.

Wahrheitswerte[Bearbeiten]

Der Datentyp für Wahrheitswerte heißt in C++ bool, was eine Abkürzung für boolean ist. Er kann nur zwei Zustände annehmen: true (wahr) oder false (falsch). Obwohl eigentlich 1 Bit ausreichen würde, hat bool mindestens eine Größe von einem Byte (also 8 Bit), denn 1 Byte ist die kleinste adressierbare Einheit und somit die Minimalgröße für jeden Datentyp. Es ist auch durchaus möglich, dass ein bool beispielsweise 4 Byte belegt, da dies auf einigen Prozessorarchitekturen die Zugriffsgeschwindigkeit erhöht.

Zeichen[Bearbeiten]

Zeichen sind eigentlich Ganzzahlen. Sie unterscheiden sich von diesen nur bezüglich der Ein- und Ausgabe. Jeder Zahl ist ein Zeichen zugeordnet. Mit den Zahlen lässt sich ganz normal rechnen aber bei der Ausgabe erscheint das zugeordnete Zeichen auf dem Bildschirm. Welches Zeichen welcher Zahl entspricht, wird durch den verwendeten Zeichensatz festgelegt.

Die meisten Zeichensätze beinhalten den sogenannten ASCII-Code (American Standard Code for Information Interchange), welcher die Zeichen 0 – 127 belegt. Er enthält 32 Steuerzeichen (0 – 31) und 96 druckbare Zeichen (32 – 127).

char ist der Standard-Datentyp für Zeichen. Er ist in der Regel 1 Byte groß und kann somit 256 verschiedene Zeichen darstellen. Diese genügen für einen erweiterten ASCII-Code, welcher zum Beispiel auch deutsche Umlaute definiert. Für Unicode-Zeichen gibt es die Datentypen char16_t mit einer Größe von 2 Byte und char32_t mit einer Größe von 4 Byte. Früher nutzte man für Unicode auch den Datentyp wchar_t, welcher je nach System 2 oder 4 Byte groß war. Dieser Datentyp sollte jedoch nicht mehr eingesetzt werden. Die folgende Liste enthält einige nützliche Links zu Artikeln der Wikipedia:

Es gibt in C++ 4 eingebaute Datentypen für Zeichen:

  • char
  • char16_t
  • char32_t
  • wchar_t (veraltet)

Ganzzahlen[Bearbeiten]

C++ definiert folgende eingebaute Datentypen für Ganzzahlen:

Schreibweise Typ Anzahl Bits nach data model
C++ standard LP32 ILP32 LLP64 LP64
signed char signed char mindestens
8
8 8 8 8
unsigned char unsigned char
short short mindestens
16
16 16 16 16
short int
signed short
signed short int
unsigned short unsigned short
unsigned short int
int int mindestens
16
16 32 32 32
signed
signed int
unsigned unsigned
unsigned int
long long mindestens
32
32 32 32 64
long int
signed long
signed long int
unsigned long unsigned long
unsigned long int
long long long long mindestens
64
64 64 64 64
long long int
signed long long
signed long long int
unsigned long long unsigned long long
unsigned long long int

Alle Schreibweisen sind identisch zu dem jeweils zugeordneten Typ. Die Schreibweise, die in der Typ-Spalte verwendet wird, ist die in diesem Buch genutzte Schreibweise. In der Praxis finden sie aber, je nachdem wer den Code geschrieben hat, auch die äquivalenten Schreibweisen der ersten Spalte.

Die genaue Anzahl der Bits hängt von der Implementierung ab und wird gemeinhin als data model bezeichnet. Vier data models sind weit verbreitet:

  • 32 Bit Systeme
    • LP32 oder 2/4/4 (int hat 16 Bits, long und Zeiger haben 32 Bits)
      • Win16 API
    • ILP32 oder 4/4/4 (int, long und Zeiger haben 32 Bits)
      • Win32 API
      • Unix und Unixoide Systeme (Linux, Mac OS X)
  • 64 Bit Systeme
    • LLP64 oder 4/4/8 (int und long haben 32 Bits, Zeiger haben 64 Bits)
      • Win64 API
    • LP64 oder 4/8/8 (int hat 32 Bits, long und Zeiger haben 64 Bits)
      • Unix und Unixoide Systeme (Linux, Mac OS X)

Der Wertebereich von vorzeichenbehafteten Typen (»signed«) berechnet sich durch:

Für vorzeichenlose Typen (»unsigned«) berechnet er sich durch:

Auffällig sind in der Tabelle die beiden char-Datentypen. Im Gegensatz zu den anderen Datentypen gibt es hier keine Schreibweise, in der am Ende ein int steht. Und was noch wichtiger ist, signed char darf nicht mit char abgekürzt werden! Das liegt daran, dass char ein Datentyp für Zeichen ist, während signed char und unsigned char üblicherweise Zahlen repräsentieren. Historisch bedingt ist die Trennung zwischen Zeichen und Zahlen in C++ leider sehr unsauber, was sich vor allem bei den char-Datentypen zeigt.

Sowohl signed char, als auch unsigned char werden bei der Ein- und Ausgabe als Zeichen behandelt, weshalb hier immer zuvor nach int gecasted (umgewandelt, Näheres im Kapitel Casts) werden muss. Der Datentyp char kann vom Wertebereich her je nach Compiler entweder zu signed char oder zu unsigned char identisch sein. Zu beachten ist jedoch, dass char dennoch ein eigenständiger Typ ist! Für den Augenblick ist diese Unterscheidung nicht so wichtig, wir werden jedoch noch einmal auf dieses (im Kontext von Templates wichtige) Detail zu sprechen kommen.

Für den Augenblick sollten Sie sich merken: Wenn Sie eine Zahl mit einer 1-Byte-Variablen repräsentieren wollen, dann nutzen Sie signed char oder unsigned char, wenn Sie ein Zeichen repräsentieren wollen, verwenden Sie char.

Wählen Sie ein int, wenn dieser Typ alle Zahlen des nötigen Wertebereichs aufnehmen kann, bei vorzeichenlosen Zahlen verwenden Sie unsigned. Reicht dieser Wertebereich nicht aus und ist long größer, dann nehmen Sie long (bzw. unsigned long). short und unsigned short sollte nur Verwendung finden, wenn Speicherplatz knapp ist, etwa bei Verwendung großer Arrays, oder wenn Low-Level-Datenstrukturen festgelegter Größe benutzt werden müssen. Achten Sie darauf, dass der theoretisch größte Wert, welcher für Ihre Variable auftreten könnte, den größten möglichen Wert nicht überschreitet. Selbiges gilt natürlich auch für die Unterschreitung des kleinstmöglichen Wertes.

Ein Unter- oder Überlauf ist übrigens durchaus möglich. Die meisten Compiler bieten zwar eine Option an, um in einem solchen Fall einen Fehler zu erzeugen, aber diese Option ist standardmäßig nicht aktiv. Im folgenden kleinen Beispiel werden die Datentypen short (min: -32768, max: 32767) und unsigned short benutzt und bei beiden wird je ein Unter- und ein Überlauf ausgeführt:

Nuvola-inspired-terminal.svg

Davon ausgehend, dass short eine Größe von 2 Byte hat, finden je zwei Über- bzw. Unterläufe statt.


 1 #include <iostream>
 2 
 3 int main(){
 4     short variable1 = 15000;
 5     unsigned short variable2 = 15000;
 6 
 7     std::cout << "short Variable:          " << variable1 << std::endl
 8               << "unsigned short Variable: " << variable2 << std::endl
 9               << "+30000\n\n";
10 
11     variable1 += 30000;
12     variable2 += 30000;
13 
14     std::cout << "short Variable:          " << variable1 << " (Überlauf)" << std::endl
15               << "unsigned short Variable: " << variable2 << std::endl
16               << "+30000\n\n";
17 
18     variable1 += 30000;
19     variable2 += 30000;
20 
21     std::cout << "short Variable:          " << variable1 << std::endl
22               << "unsigned short Variable: " << variable2 << " (Überlauf)" << std::endl
23               << "-30000\n\n";
24 
25     variable1 -= 30000;
26     variable2 -= 30000;
27 
28     std::cout << "short Variable:          " << variable1 << std::endl
29               << "unsigned short Variable: " << variable2 << " (Unterlauf)" << std::endl
30               << "-30000\n\n";
31 
32     variable1 -= 30000;
33     variable2 -= 30000;
34 
35     std::cout << "short Variable:          " << variable1 << " (Unterlauf)" << std::endl
36               << "unsigned short Variable: " << variable2 << std::endl;
37 }
Crystal Clear app kscreensaver.svg
Ausgabe:
 1 short Variable:          15000
 2 unsigned short Variable: 15000
 3 +30000
 4 
 5 short Variable:          -20536 (Überlauf)
 6 unsigned short Variable: 45000
 7 +30000
 8 
 9 short Variable:          9464
10 unsigned short Variable: 9464 (Überlauf)
11 -30000
12 
13 short Variable:          -20536
14 unsigned short Variable: 45000 (Unterlauf)
15 -30000
16 
17 short Variable:          15000 (Unterlauf)
18 unsigned short Variable: 15000

Verständlicher wird dieses Phänomen, wenn man die Zahlen binär (Duales Zahlensystem) darstellt. Der Einfachheit halber beginnen wir mit der Darstellung der unsigned short Variablen, die ersten beiden Additionen von jeweils 30000:

Addition im Dualsystem mit unsigned short als Datentyp

Rechnung 1
        |0011101010011000| 15000
     +  |0111010100110000| 30000
--------------------------------
Merker  |111      11     |
--------------------------------
     =  |1010111111001000| 45000

Rechnung 2
        |1010111111001000| 45000
     +  |0111010100110000| 30000
--------------------------------
Merker 1|1111111         |
--------------------------------
     = 1|0010010011111000| 9464

Die beiden Rechnungen weisen keinerlei Besonderheiten auf. Da nur die letzten 16 Ziffern beachtet werden (2 Byte = 16 Bit), entfällt in der zweiten Rechnung die 1 vor dem Vertikalstrich, wodurch das Ergebnis (in dezimaler Schreibweise) 9464 und nicht 75000 lautet. Anschließend werden von der unsigned short -Ganzzahl zwei Mal jeweils 30000 subtrahiert:

Subtraktion im Dualsystem mit unsigned short als Datentyp

Rechnung 3
          |0010010011111000| 9464
     -    |0111010100110000| 30000
----------------------------------
Merker...1|1111111         |
----------------------------------
     =...1|1010111111001000| 45000

Rechnung 4
          |1010111111001000| 45000
     -    |0111010100110000| 30000
----------------------------------
Merker    |111      11     |
----------------------------------
     =    |0011101010011000| 15000

In diesem Fall ist die zweite Rechnung unauffällig. In der ersten Rechnung wird hingegen eine große Zahl von einer kleineren abgezogen, was zu einem negativen Ergebnis führt – oder besser – führen würde, denn die Untergrenze ist in diesem Fall 0. Dort wo die 3 Punkte stehen, folgt eine unendliche Anzahl von Einsen. Dies ist keineswegs nur bei Dualzahlen der Fall. Wenn Sie im dezimalen System eine große Zahl von einer kleineren nach den üblichen Regeln der schriftlichen Subtraktion abziehen, so erhalten Sie ein ähnliches Ergebnis:

          24
     -    31
------------
Merker...1
------------
     =...993

Im Programm werden mit der Vorzeichen-behafteten Ganzzahl, der short-Variablen, ebenfalls vier Rechnungen durchgeführt. Deren duale Darstellung wird Ihnen sehr bekannt vorkommen:

Addition im Dualsystem mit short als Datentyp

Rechnung 1
        |0|011101010011000| 15000
     +  |0|111010100110000| 30000
--------------------------------
Merker  |1|11      11     |
--------------------------------
     =  |1|010111111001000| -20536

Rechnung 2
        |1|010111111001000| -20536
     +  |0|111010100110000| 30000
--------------------------------
Merker 1|1|111111         |
--------------------------------
     = 1|0|010010011111000| 9464

Subtraktion im Dualsystem mit short als Datentyp

Rechnung 3
          |0|010010011111000| 9464
     -    |0|111010100110000| 30000
----------------------------------
Merker...1|1|111111         |
----------------------------------
     =...1|1|010111111001000| -20536

Rechnung 4
          |1|010111111001000| -20536
     -    |0|111010100110000| 30000
----------------------------------
Merker    |1|11      11     |
----------------------------------
     =    |0|011101010011000| 15000

Die dualen Ziffern sind die gesamte Zeit über exakt die gleichen, der einzige Unterschied besteht darin, dass negative Zahlen in Zweierkomplement-Darstellung repräsentiert werden. Dies führt zu einer veränderten Darstellung im Dezimalsystem.

Meist ist es besser, wenn man die genaue Größe der Datentypen festlegt. Hierfür muss der Header cstdint eingebunden werden. Darin sind Alias-Namen für die eingebauten Datentypen definiert:

  • std::int8_t optional
  • std::int16_t optional
  • std::int32_t optional
  • std::int64_t optional
  • std::int_fast8_t
  • std::int_fast16_t
  • std::int_fast32_t
  • std::int_fast64_t
  • std::int_least8_t
  • std::int_least16_t
  • std::int_least32_t
  • std::int_least64_t
  • std::intmax_t
  • std::intptr_t optional
  • std::uint8_t optional
  • std::uint16_t optional
  • std::uint32_t optional
  • std::uint64_t optional
  • std::uint_fast8_t
  • std::uint_fast16_t
  • std::uint_fast32_t
  • std::uint_fast64_t
  • std::uint_least8_t
  • std::uint_least16_t
  • std::uint_least32_t
  • std::uint_least64_t
  • std::uintmax_t
  • std::uintptr_t optional

Alle Datentypen, die mit int beginnen, stehen für signed Datentypen, alle, die mit uint beginnen, für unsigned Datentypen. Die mit »optional« gekennzeichneten Datentypen sind nur definiert, wenn sie durch die Plattform auch nativ unterstützt werden. Dies ist jedoch auf den schon oben referenzierten Systemen immer der Fall. Lediglich auf Mikrocontrollern oder ähnlichem kann es hier zu Problemen kommen.

Die least-Datentypen entsprechen dem kleinstem Typ, der mindestens so viele Bits hat, die fast-Datentypen dem schnellsten Datentyp, der mindestens so viele Bits hat. Die max-Datentypen entsprechen dem jeweils größten verfügbaren Datentyp. Die ptr-Datentypen haben exakt so viele Bit wie ein Zeiger (siehe Kapitel Zeiger) und sind daher ebenfalls nur verfügbar, wenn die Hardware einen entsprechenden Integer-Datentyp nativ unterstützt.

In der Regel ist es am sinnvollsten die exakten Datentypen zu verwenden, also etwa std::uint16_t, wenn man einen Datentyp möchte, der 16 Bit hat und vorzeichenlos ist. Die fast-Datentypen können nötigenfalls zur Geschwindigkeitsoptimierung innerhalb von Funktionen eingesetzt werden.

Gleitkommazahlen[Bearbeiten]

Eine Gleitkommavariable kann sich eine bestimmte Anzahl Ziffern merken und dazu die Position des Kommas. Das Wissen über den internen Aufbau einer solchen Zahl werden Sie wahrscheinlich eher selten bis nie brauchen, daher sei an dieser Stelle auf den Wikipediaartikel über Gleitkommazahlen verwiesen. In C++ werden Sie Gleitkommazahlen/-variablen für das Rechnen mit Kommazahlen verwenden. Es gibt drei Datentypen für Gleitkommazahlen, die in der folgenden Tabelle mit ihren üblichen Werten aufgelistet sind:

Typ Speicherplatz Wertebereich kleinste positive Zahl Genauigkeit
float 4 Byte 6 Stellen
double 8 Byte 12 Stellen
long double 10 Byte 18 Stellen

Die Auswahl eines Gleitkommadatentyps ist weniger einfach als die einer Ganzzahl. Wenn Sie nicht genau wissen, was Sie nehmen sollen, ist double in der Regel eine gute Wahl. Sobald Sie erst einmal ausreichend Erfahrung haben, wird es Ihnen leichter fallen abzuschätzen, ob float oder long double für Ihr Problem vielleicht eine bessere Wahl ist.

Variablen[Bearbeiten]

Bevor eine Variable verwendet werden kann, muss sie dem Compiler bekannt gegeben werden. Dies bezeichnet man als Deklaration der Variablen. Das eigentliche Anlegen einer Variablen, so dass der Compiler Speicherplatz für sie reserviert, wird Definition genannt. Eine Definition ist immer auch eine Deklaration und bei Variablen ist der Unterschied zwischen Deklaration und Definition etwas zu kompliziert, um ihn an dieser Stelle bereits zu erklären. Sie werden die Begriffe zunächst in Zusammenhang mit Funktionen kennenlernen und später auch für Variablen. Sie sollten sich jedoch jetzt bereits merken, dass es sich beim Anlegen der Variablen, die wir verwenden, immer um Definitionen handelt. Dies ist insofern wichtig, als dass eine Definition immer nur einmal geschrieben werden darf, während eine Deklaration beliebig oft vorgenommen werden kann. Um einen Vergleich zur realen Welt zu ziehen, wollen wir das Entstehen neuen Lebens betrachten. Sie können beliebig oft erzählen, dass ein bestimmtes Kind geboren wird. Tatsächlich geschehen kann dies aber nur einmal. Im vorherigen Kapitel haben wir bereits mit Variablen vom Typ int gerechnet. Nun sollen Sie lernen, wie Variablen in C++ angelegt werden. Die allgemeine Syntax lautet:

Crystal Project Tutorials.png
Syntax:
«Datentyp» «Name»;
«Nicht-C++-Code», »optional«

Außerdem ist es möglich, mehrere Variablen des gleichen Typs hintereinander anzulegen:

Crystal Project Tutorials.png
Syntax:
«Datentyp» «Variable1», «Variable2», «Variable3»;
«Nicht-C++-Code», »optional«

Auch kann man einer Variablen einen Anfangswert geben, dies bezeichnet man als Initialisierung. Es gibt zwei syntaktische Möglichkeiten (Schreibweisen) für Initialisierungen, welche anhand einer int Variable gezeigt werden soll:

Nuvola-inspired-terminal.svg
1 int zahl=100;  // Möglichkeit 1
2 int zahl(100); // Möglichkeit 2

Die erste Variante ist weit verbreitet aber nicht zwingend besser. Bei den fundamentalen Datentypen von C++ spielt es keine Rolle, welche Variante Sie verwenden, aber bei komplexeren Datentypen (Klassen) kann es zu Verwechslungen mit dem Zuweisungsoperator kommen, wenn Sie Möglichkeit 1 benutzen. Den genauen Unterschied zwischen einer Initialisierung und einer Zuweisung werden Sie kennenlernen, sobald es um Klassen geht. Für den Moment sollten Sie sich für eine der beiden Varianten entscheiden. Für Möglichkeit 1 spricht die große Verbreitung und die damit verbundene intuitive Nutzung. In diesem Buch werden wir diese erste Methode verwenden und in Zusammenhang mit Klassen auch eine Empfehlung geben, wann diese Methode zur besseren Übersicht im Quellcode beitragen kann. Für Möglichkeit 2 spricht hingegen, dass diese Syntax für Initialisierungen immer gültig ist. Nebenbei wird bei dieser Methode deutlich, dass es sich um eine Initialisierung handelt.

Variablen mit Anfangswerten können natürlich auch hintereinander angelegt werden, sofern sie den gleichen Datentyp besitzen, allerdings ist davon aus Gründen der Übersichtlichkeit abzuraten.

Nuvola-inspired-terminal.svg
1 int zahl1(77), zahl2, zahl3=58; // Drei int-Variablen, von denen zwei Anfangswerte haben

Wenn Sie einer Variablen keinen Anfangswert geben, müssen Sie ihr später im Programm noch einen Wert zuweisen, bevor Sie mit ihr arbeiten (also damit rechnen oder den Inhalt ausgeben lassen). Weisen Sie einer solchen Variablen keinen Wert zu und benutzen sie, so kann der Inhalt zufällig sein. Genaugenommen handelt es sich dann um die Bitfolge, die an der Stelle im Speicher stand, an der Ihre Variable angelegt wurde. Es gibt in C++ Regeln, in welchen Fällen der Compiler eine Variable ohne explizite Initialisierung implizit mit 0 initialisiert und wann stattdessen einfach der aktuelle Speicherinhalt stehen bleibt. Allerdings sind diese Regeln so kompliziert, dass es sich nicht lohnt, sie sich zu merken. Denn sollte je ein anderer Programmierer Ihren Code lesen, so muss auch dieser die Regeln kennen, um den Code sofort verstehen zu können. Das nachfolgende kleine Programm zeigt einen Fall, in dem C++ besagt, dass keine implizite Initialisierung mit 0 stattfindet.

Nuvola-inspired-terminal.svg
 1 #include <iostream>                                         // Ein-/Ausgabe
 2 
 3 int main(){
 4     int zahl;                                               // Ganzzahlige Variable
 5     double kommazahl1, kommazahl2;                          // Gleitkommavariablen
 6     char zeichen;                                           // Zeichenvariable
 7 
 8     std::cout << "zahl: "       << zahl       << std::endl  // Ausgabe der Werte
 9               << "kommazahl1: " << kommazahl1 << std::endl  // welche jedoch
10               << "kommazahl2: " << kommazahl2 << std::endl  // nicht festgelegt
11               << "zeichen: "    << zeichen    << std::endl; // wurden
12 }
Crystal Clear app kscreensaver.svg
Ausgabe:
1 zahl: -1211024315
2 kommazahl1: 4.85875e-270
3 kommazahl2: -3.32394e-39
4 zeichen: f

Die Ausgabe kann bei jedem Ausführen des Programms anders lauten. Sollte dies bei Ihnen nicht der Fall sein, so stehen nur zufällig die gleichen Werte an der Stelle im Speicher, welchen die jeweilige Variable belegt. Spätestens nach einem Neustart Ihres Rechners haben Sie höchstwahrscheinlich eine andere Ausgabe. Variablen keinen Anfangswert zu geben, ist beispielsweise sinnvoll, wenn Sie vorhaben, über std::cin einen Wert in die Variable einzulesen. Dennoch würde es auch in diesem Fall keinen Schaden anrichten, wenn Sie die Variablen explizit mit 0 initialisieren.

Nuvola-inspired-terminal.svg
 1 #include <iostream>                                         // Ein-/Ausgabe
 2  
 3 int main(){
 4     int zahl;                                               // Ganzzahlige Variable
 5     double kommazahl1, kommazahl2;                          // Gleitkommavariablen
 6     char zeichen;                                           // Zeichenvariable
 7 
 8     std::cout << "Geben Sie bitte durch Leerzeichen getrennt eine Ganzzahl, zwei Kommazahlen "
 9             "und ein Zeichen ein:\n";
10 
11     std::cin >> zahl                                        // Eingabe von Werten
12              >> kommazahl1                                  // mit denen die vier
13              >> kommazahl2                                  // Variablen gefüllt
14              >> zeichen;                                    // werden
15 
16     std::cout << "Zahl: "       << zahl       << std::endl  // Ausgabe der Werte
17               << "Kommazahl1: " << kommazahl1 << std::endl  // welche zuvor
18               << "Kommazahl2: " << kommazahl2 << std::endl  // eingegeben
19               << "Zeichen: "    << zeichen    << std::endl; // wurden
20 }
Crystal Clear app kscreensaver.svg
Ausgabe:
1 Geben Sie bitte durch Leerzeichen getrennt eine Ganzzahl, zwei Kommazahlen und ein Zeichen ein:
2 Benutzereingabe: 6 8.4 6.0 g
3 Zahl: 6
4 Kommazahl1: 8.4
5 Kommazahl2: 6
6 Zeichen: g

Konstanten[Bearbeiten]

Konstanten sind, wie schon oben beschrieben, Variablen, welche ihren Wert nicht verändern. Daraus folgt, dass einer Konstanten nur genau ein Mal ein Wert zugewiesen werden kann; in C++ muss dies das Initialisieren mit einem Anfangswert sein, andernfalls hätten Sie eine Konstante mit einem zufälligen Wert und das ergibt kaum einen Sinn. Das Schlüsselwort, um eine Variable zu einer Konstanten zu machen, ist const. Es gehört immer zu dem, was links davon steht, es sei denn, links von ihm steht nichts mehr, dann gehört 'const' zu dem Begriff auf dessen rechter Seite. Dies klingt zwar kompliziert, ist es aber eigentlich gar nicht. Für uns bedeutet es im Moment nur, dass Sie zwei Möglichkeiten haben, eine Variable zu einer Konstanten zu machen:

Nuvola-inspired-terminal.svg
1 const int zahl(400); // Alternativ: const int zahl=400;
2 // oder
3 int const zahl(400); // Alternativ: int const zahl=400;

Beides hat die gleiche Wirkung, wieder ist die erste Variante weit verbreitet und wieder ist die zweite Variante der besseren Lesbarkeit bei komplexeren Datentypen (Arrays von Zeigern Konstante auf Memberfunktionen…) vorzuziehen. Entscheiden Sie sich für die Variante, die Ihnen besser gefällt und verwenden Sie diese. Wichtig ist, dass Sie der Variante, für die Sie sich entscheiden, treu bleiben, wenigstens für die Dauer eines Projekts. Denn Code, in dem sich der Schreibstil ständig ändert, ist schwieriger zu lesen.

Literale und ihre Datentypen[Bearbeiten]

Ein Literal ist eine Angabe im Quellcode, die einen konkreten Wert angibt und einen der oben beschriebenen Datentypen besitzt.

Nuvola-inspired-terminal.svg
1 #include <iostream>                 // Ein-/Ausgabe
2 
3 int main() {
4     std::cout << 100  << std::endl  // 100 ist ein int-Literal
5               << 4.7  << std::endl  // 4.7 ist ein double-Literal
6               << 'h'  << std::endl  // 'h' ist ein char-Literal
7               << true << std::endl; // true ist ein bool-Literal
8 }
Crystal Clear app kscreensaver.svg
Ausgabe:
1 100
2 4.7
3 h
4 1

Bei der Ausgabe ist zu beachten, dass Boolean-Werte als 0 (false) bzw. 1 (true) ausgegeben werden.

Automatische Bestimmung des Datentyps[Bearbeiten]

Da die Bestimmung des Typs für einen ganzzahligen Wert etwas schwieriger ist als bei den übrigen, werden wir diese zuletzt behandeln. Bei Gleitkommaliteralen ist festgelegt, dass es sich um double-Werte handelt. Um einen Gleitkommaliteral mit einem anderen Typ zu erhalten, ist ein so genanntes Suffix nötig.

Nuvola-inspired-terminal.svg
1 5.0  // double
2 6.7f // float
3 2.6F // float
4 9.4l // long double
5 4.0L // long double

Eine Zahl mit Komma (.) ist also ein double-Wert. Folgt der Zahl ein f oder ein F wird sie zu einem float-Wert und folgt ihr ein l oder ein L wird sie zu einem long double-Wert. Gleitpunktzahlen können auch in der wissenschaftlichen Schreibweise dargestellt werden.

Nuvola-inspired-terminal.svg
1 5.7e10
2 3.3e-3
3 8.7e876L
4 4.2e-4F

Wie die letzten beiden Beispiele zeigen, können auch hierbei die Suffixe für den Datentyp genutzt werden.

Um ein Zeichen beziehungsweise eine Zeichenkette als char16_t bzw. char32_tzu kennzeichnen, stellt man ein kleines u bzw. großes U voran. Für wchar_t nutzt man ein großes L.

Nuvola-inspired-terminal.svg
1 'a'                 // char
2 u'b'                // char16_t
3 U'b'                // char32_t
4 L'b'                // wchar_t
5 "Ich bin ein Text"  // char const*
6 u"Ich bin ein Text" // char16_t const*
7 U"Ich bin ein Text" // char32_t const*
8 L"Ich bin ein Text" // wchar_t const*

Was das Sternchen (*) hinter dem Datentyp im Kommentar bedeutet, werden Sie in einem späteren Kapitel erfahren. bool kann nur zwei Zustände annehmen, entsprechend gibt es auch nur zwei bool-Literale: true und false.

Nun zu den Ganzzahlen. Neben der dezimalen Darstellung von Zahlen gibt es in C++ auch die Möglichkeit der Binären, Oktalen und Hexadezimalen Darstellung. Um eine Zahl als Binär zu kennzeichnen, wird ein 0b oder 0B vorangestellt, für Oktal wird nur eine 0 (Null) vorangestellt und für eine Hexadezimalzahl wird 0x oder 0X vorangestellt. Die Groß-/Kleinschreibung der hexadezimalen Ziffern a bis f spielt keine Rolle.

Nuvola-inspired-terminal.svg
1 756        // Dezimal,     Dezimal: 756
2 0b10       // Binär,       Dezimal: 2
3 0B111      // Binär,       Dezimal: 7
4 046        // Oktal,       Dezimal: 38
5 0757       // Oktal,       Dezimal: 495
6 0xffff     // Hexadezimal, Dezimal: 65535
7 0X1234ABcd // Hexadezimal, Dezimal: 305441741

Der Datentyp wird durch die Größe des Wertes bestimmt, wobei die folgende Reihenfolge gilt: int, unsigned int, long, unsigned long, long long, unsigned long long. Weiterhin kann jeder Ganzzahl auch explizit das Suffix u oder U für unsigned und ein l oder L für long bzw. ein ll oder LL für long long angehängt werden. Die Reihenfolge ändert sich entsprechend den durch die Suffixe festgelegten Kriterien.

übersichtlichere int-Schreibweise[Bearbeiten]

Es ist außerdem möglich, Integer-Literale an beliebigen Stellen durch das Zeichen ' zu trennen, um die Übersicht zu verbessern. Dies kann insbesondere bei binären Literalen nützlich sein, da diese oft sehr lang sind.

Nuvola-inspired-terminal.svg
1 9'756'432'108         // ' als Tausender-Trennzeichen
2 978'3'446'43981'8     // ' als Trennzeichen für eine ISBN-Nummer
3 0B1111'0000'1010'0101 // ' als Trennzeichen für einen 16 Bit Wert mit 4 Gruppen

Das Wissen über die Datentypen von Literalen werden Sie wahrscheinlich eher selten benötigen, daher reicht es „mal etwas davon gehört zu haben“ und es, wenn nötig, nachzuschlagen.