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_t

zu 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.