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

Aus Wikibooks

Wechseln zu: Navigation, Suche


Variablen sind Behälter für Werte, sie stellen gewissermaßen das Gedächtnis eines Programms bereit. Konstanten sind Variablen, die ihren Wert nie verändern. Der Datentyp einer Variable oder Konstante beschreibt, wie der Inhalt zu verstehen ist. Ein Rechner kennt nur zwei Zustände: 0 und 1. Durch eine Aneinanderreihung solcher Zustände lassen sich verschiedene Werte darstellen. Mit 8 aufgereihten Zuständen (= 8 Bit = 1 Byte) lassen sich bereits 256 verschiedene Werte darstellen. Diese Werte kann man als Ganzzahl, Zeichen, Wahrheitswert oder Gleitkommazahl interpretieren. Der Datentyp gibt Auskunft darüber, um was es sich handelt.

Inhaltsverzeichnis

[Bearbeiten] Datentypen

Zunächst sollen die Datentypen von C++ beschrieben werden, denn sie sind grundlegend für eine Variable oder Konstante. Es gibt 4 Gruppen von Datentypen: Wahrheitswerte, Zeichen, Ganzzahlen und Gleitkommazahlen.

[Bearbeiten] Wahrheitswerte

Der Datentyp für Wahrheitswerte heißt in C++ bool, was eine Abkürzung für Boolean ist. Er kann nur 2 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.

[Bearbeiten] Zeichen

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 zugeordnet ist, 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 verschieden Zeichen darstellen. Diese genügen für einen erweiterten ASCII-Code, welcher zum Beispiel auch deutsche Umlaute definiert. wchar_t ist ein Datentyp für Unicodezeichen und hat gewöhnlich eine Größe von 2 Byte, ist aber zunehmend auch mit 4 Byte zu finden. Die folgende Liste enthält einige nützliche Links zu Artikeln der Wikipedia:

[Bearbeiten] Ganzzahlen

Für ganze Zahlen sind die Datentypen short, int und long definiert. Weiterhin sind, wie schon gesagt, auch char und wchar_t ganzzahlige Datentypen. Mit Ausnahme von wchar_t kann jedem dieser Datentypen ein signed oder unsigned vorangestellt werden. signed bedeutet mit, unsigned ohne Vorzeichen, entsprechend hat der Datentyp dann einen negativen und einen positiven ( signed) oder nur einen positiven ( unsigned) Wertebereich, welcher dann aber doppelt so groß ist. wchar_t entspricht meistens dem Datentyp unsigned short, dies ist in ISO-C++ aber nicht vorgeschrieben.

ISO-C++ schreibt auch die genaue Größe der Datentypen nicht vor, es gibt lediglich die Reihenfolge bezüglich der Größe vor:

char <= short <= int <= long

Außerdem ist festgelegt, dass short mindestens 2 Byte und long mindestens 4 Byte lang sein müssen. int hat (üblicherweise) auf 16-Bit-Rechnern eine Größe von 2 Byte und auf 32-Bit-Rechnern eine Größe von 4 Byte. Die nachfolgende Tabelle zeigt die ganzzahligen Datentypen mit ihrer üblichen Größe und dem entsprechendem Wertebereich:

Typ Speicherplatz Wertebereich (dezimal)
char 1 Byte -128 bis +127 bzw. 0 bis 255
signed char 1 Byte -128 bis +127
unsigned char 1 Byte 0 bis 255
short 2 Byte -32768 bis +32767
unsigned short 2 Byte 0 bis 65535
int 4 Byte -2147483648 bis +2147483647
unsigned int 4 Byte 0 bis 4294967295
long 4 Byte -2147483648 bis +2147483647
unsigned long 4 Byte 0 bis 4294967295

Auffällig ist, dass in der Tabelle nur für char eine signed Variante vorkommt. Das liegt daran, dass die übrigen Datentypen ohne den Zusatz signed immer vorzeichenbehaftet sind, für char ist dies hingegen nicht festgelegt. Dennoch können Sie das Schlüsselwort signed natürlich auch in Verbindung mit den anderen Datentypen ( signed short, signed int und signed long) benutzen. In bestimmten Situationen kann dies zum Beispiel helfen die Übersicht zu erhöhen. Wenn Sie char für Zeichen benutzen, ist eine Angabe von signed oder unsigned nicht nötig, möchten Sie eine Variable dieses Datentyps zum Rechnen benutzen, sollten Sie die Schlüsselworte hingegen angeben.

Das waren jetzt viele Informationen auf wenig Raum. Merken Sie sich einfach in etwa die Größe der 4 Datentypen, den Wertebereich können Sie dann entsprechend ableiten (2Anzahl der Bits, 1 Byte = 8 Bit). Merken Sie sich weiterhin das signed vorzeichenbehaftet, unsigned vorzeichenlos bedeutet und dass im Falle einer fehlenden Angabe signed angenommen wird.

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 in Ihrer Variable gespeichert wird, 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 solchem 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:

Crystal Clear app terminal.png
Davon ausgehend, dass short eine Größe von 2 Byte hat, finden je 2 Über- bzw. Unterläufe statt.
#include <iostream>

int main(){
    short Variable1=15000;
    unsigned short Variable2=15000;

    std::cout << "short Variable:          " << Variable1 << std::endl
              << "unsigned short Variable: " << Variable2 << std::endl
              << "+30000\n\n";

    Variable1 += 30000;
    Variable2 += 30000;

    std::cout << "short Variable:          " << Variable1 << " (Überlauf)" << std::endl
              << "unsigned short Variable: " << Variable2 << std::endl
              << "+30000\n\n";

    Variable1 += 30000;
    Variable2 += 30000;

    std::cout << "short Variable:          " << Variable1 << std::endl
              << "unsigned short Variable: " << Variable2 << " (Überlauf)" << std::endl
              << "-30000\n\n";

    Variable1 -= 30000;
    Variable2 -= 30000;

    std::cout << "short Variable:          " << Variable1 << std::endl
              << "unsigned short Variable: " << Variable2 << " (Unterlauf)" << std::endl
              << "-30000\n\n";

    Variable1 -= 30000;
    Variable2 -= 30000;

    std::cout << "short Variable:          " << Variable1 << " (Unterlauf)" << std::endl
              << "unsigned short Variable: " << Variable2 << std::endl;
}
Crystal Clear app kscreensaver.png
Ausgabe:
short Variable:          15000
unsigned short Variable: 15000
+30000

short Variable:          -20536 (Überlauf)
unsigned short Variable: 45000
+30000

short Variable:          9464
unsigned short Variable: 9464 (Überlauf)
-30000

short Variable:          -20536
unsigned short Variable: 45000 (Unterlauf)
-30000

short Variable:          15000 (Unterlauf)
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 Variable:

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 Horizontalstrich, wodurch das Ergebnis (in dezimaler Schreibweise) 9464 und nicht 75000 lautet.

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 angezogen, was zu einem negativen Ergebnis führt oder besser führen würden, denn die Untergrenze ist in diesem Fall ja 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

Die duale Darstellung der 4 Rechnungen mit der short-Variable 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 die erste Ziffer (welche durch einen weiteren Horizontalstrich abgegrenzt wurde) nun als Vorzeichenbit interpretiert wird (   – Positiv, 1 – Negativ). Dies führt zu einer veränderten Darstellung im Dezimalsystem.

[Bearbeiten] Gleitkommazahlen

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 wahrscheinlicher eher selten bis nie brauchen, daher sei an dieser Stelle auf den Wikipediaartikel über Gleitkommazahlen verwiesen. In C++ werden Sie Gleitkommazahlen/-variable für das Rechnen mit Kommazahlen verwenden. Es gibt 3 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 \pm3,4 \cdot 10^{38} 1,2 \cdot 10^{-38} 6 Stellen
double 8 Byte \pm1,7 \cdot 10^{308} 2,3 \cdot 10^{-308} 15 Stellen
long double 10 Byte \pm1,1 \cdot 10^{4932} 3,4 \cdot 10^{-4932} 19 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 sind.

[Bearbeiten] Variablen

Bevor eine Variable verwendet werden kann, muss sie dem Compiler bekannt gegeben werden. Dies bezeichnet man als Deklaration der Variable.

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:

Datentyp Name;

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

Datentyp Variable1, Variable2, Variable3;

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

Crystal Clear app terminal.png
int Zahl=100;  // Möglichkeit 1
int Zahl(100); // Möglichkeit 2

Die erste Variante ist weit verbreitet, aber nicht 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 kennen lernen, sobald es um Klassen geht. Für den Moment sollten Sie sich für eine Variante entscheiden.

Für Möglichkeit 1 spricht die große Verbreitung und die damit verbundene „gewohnte Benutzung“. Für Möglichkeit 2 spricht hingegen die bessere Lesbarkeit sobald man sich daran gewöhnt hat.

Variablen mit Anfangswerten können natürlich auch hintereinander angelegt werden, sofern Sie den gleichen Datentyp besitzen:

Crystal Clear app terminal.png
int Zahl1(77), Zahl2, Zahl3=58; // 3 int-Variablen von denen 2 Anfangswerte haben

Wenn Sie einer Variable 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 Variable keinen Wert zu und benutzen sie, so ist der Inhalt zufällig. Genaugenommen handelt es sich dann um die Bitfolge, die an der Stelle im Speicher stand, an der Ihre Variable angelegt wurde.

Das nachfolgende kleine Programm zeigt dies:

Crystal Clear app terminal.png
#include <iostream>                                         // Ein-/Ausgabe

int main(){
    int Zahl;                                               // Ganzzahlige Variable
    double Kommazahl1, Kommazahl2;                          // Gleitkommavariablen
    char Zeichen;                                           // Zeichenvariable

    std::cout << "Zahl: "       << Zahl       << std::endl  // Ausgabe der Werte
              << "Kommazahl1: " << Kommazahl1 << std::endl  // welche jedoch
              << "Kommazahl2: " << Kommazahl2 << std::endl  // nicht festgelegt
              << "Zeichen: "    << Zeichen    << std::endl; // wurden
}
Crystal Clear app kscreensaver.png
Ausgabe:
Zahl: -1211024315
Kommazahl1: 4.85875e-270
Kommazahl2: -3.32394e-39
Zeichen: f

Die Ausgabe lautet bei jedem Ausführen des Programms anders. 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. Variablen keinen Anfangswert zu geben ist beispielsweise sinnvoll, wenn Sie vorhaben über cin einen Wert in die Variable einzulesen.

Crystal Clear app terminal.png
#include <iostream>                                         // Ein-/Ausgabe
 
int main(){
    int Zahl;                                               // Ganzzahlige Variable
    double Kommazahl1, Kommazahl2;                          // Gleitkommavariablen
    char Zeichen;                                           // Zeichenvariable

    std::cout << "Geben Sie bitte durch Leerzeichen getrennt eine Ganzzahl, 2 Kommazahlen "
            "und ein Zeichen ein:\n";

    std::cin >> Zahl                                        // Eingabe von Werten
             >> Kommazahl1                                  // mit denen die 4
             >> Kommazahl2                                  // Variablen gefüllt
             >> Zeichen;                                    // werden

    std::cout << "Zahl: "       << Zahl       << std::endl  // Ausgabe der Werte
              << "Kommazahl1: " << Kommazahl1 << std::endl  // welche zuvor
              << "Kommazahl2: " << Kommazahl2 << std::endl  // eingegeben
              << "Zeichen: "    << Zeichen    << std::endl; // wurden
}
Crystal Clear app kscreensaver.png
Ausgabe:
Geben Sie bitte durch Leerzeichen getrennt eine Ganzzahl, 2 Kommazahlen und ein Zeichen ein:
Benutzereingabe: 6 8.4 6.0 g
Zahl: 6
Kommazahl1: 8.4
Kommazahl2: 6
Zeichen: g

[Bearbeiten] Konstanten

Konstanten sind, wie schon oben beschrieben, Variablen, welche ihren Wert nicht verändern. Daraus folgt logisch, dass eine Konstante immer mit einem Anfangswert initialisiert werden muss, andernfalls hätten Sie eine Konstante mit einem zufälligen Wert und das ergibt keinen Sinn. Das Schlüsselwort, um eine Variable zu einer Konstante zu machen, ist const. Es gehört immer zu dem, was links davon steht, es sei denn, links steht nichts mehr, dann gehört es zu dem Teil auf der rechten Seite. Dies klingt zwar kompliziert, ist es aber eigentlich gar nicht. Für uns bedeutet es im Moment nur, dass Sie 2 Möglichkeiten haben, eine Variable zu einer Konstante zu machen:

Crystal Clear app terminal.png
const int Zahl(400); // Alternativ: const int Zahl=400;
// oder
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 schwerer zu lesen, als alles andere.

[Bearbeiten] Literale und ihre Datentypen

Ein Literal ist eine Zeichenkette, die zu Darstellung des Wertes einer der oben beschriebenen Datentypen dient.

Crystal Clear app terminal.png
#include <iostream>                 // Ein-/Ausgabe

int main() {
    std::cout << 100  << std::endl  // 100 ist ein int-Literal
              << 4.7  << std::endl  // 4.7 ist ein double-Literal
              << 'h'  << std::endl  // 'h' ist ein char-Literal
              << true << std::endl; // true ist ein bool-Literal
}
Crystal Clear app kscreensaver.png
Ausgabe:
100
4.7
h
1

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

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.

Crystal Clear app terminal.png
5.0  // double
6.7f // float
2.6F // float
9.4l // long double
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.

Crystal Clear app terminal.png
5.7e10
3.3e-3
8.7e876L
4.2e-4F

Wie die letzten beiden Beispiele zeigen, können auch hierbei die Suffixe für den Dateityp genutzt werden. Um ein Zeichen beziehungsweise eine Zeichenketten als wchar_t zu kennzeichnen, stellt man ihr ein L voran:

Crystal Clear app terminal.png
'a'                 // char
L'b'                // wchar_t
"Ich bin ein Text"  // char*
L"Ich bin ein Text" // wchar_t*

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

Nun zu den Ganzzahlen. Neben der dezimalen Darstellung von Zahlen gibt es in C++ auch die Möglichkeit der Oktalen und Hexadezimalen Darstellung. Um eine Zahl als Oktal zu kennzeichnen, wird ihr eine   (Null) vorangestellt, für eine Hexadezimalzahl wird 0x (zu empfehlen, da deutlich besser Lesbar) oder 0X vorangestellt. Die Groß-/Kleinschreibung der hexadezimalen Ziffern spielt keine Rolle.

Crystal Clear app terminal.png
756        // Dezimal,     Dezimal: 756
046        // Oktal,       Dezimal: 38
0757       // Oktal,       Dezimal: 495
0xffff     // Hexadezimal, Dezimal: 65535
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. Weiterhin kann jeder Ganzzahl das Suffix u oder U für unsigned und l oder L für long angehängt werden. Auch beide Suffixe gleichzeitig sind möglich. Die Reihenfolge ändert sich entsprechend den durch die Suffixe festgelegten Kriterien.

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.


Persönliche Werkzeuge