C++-Programmierung/ Einführung in C++/ Rechnen mit unterschiedlichen Datentypen

Aus Wikibooks

Wechseln zu: Navigation, Suche


Sie kennen nun die Datentypen in C++ und haben auch schon mit int-Variablen gerechnet. In diesem Kapitel erfahren Sie, wie man mit Variablen unterschiedlichen Typs rechnet. Es geht also weniger um das Ergebnis selbst, als viel mehr darum wie der Ergebnisdatentyp lautet.

Inhaltsverzeichnis

[Bearbeiten] Ganzzahlen unter sich

Das Rechnen mit Ganzzahlen ist leicht zu begreifen. Die „kleinen“ Datentypen werden als int behandelt. Bei den größeren entscheidet der größte Datentyp über den Ergebnistyp. Die folgende Liste zeigt die Zusammenhänge:

char           + char           => int           | wchar_t        + char           => int
char           + wchar_t        => int           | wchar_t        + wchar_t        => int
char           + signed char    => int           | wchar_t        + signed char    => int
char           + unsigned char  => int           | wchar_t        + unsigned char  => int
char           + short          => int           | wchar_t        + short          => int
char           + unsigned short => int           | wchar_t        + unsigned short => int
char           + int            => int           | wchar_t        + int            => int
char           + unsigned int   => unsigned int  | wchar_t        + unsigned int   => unsigned int
char           + long           => long          | wchar_t        + long           => long
char           + unsigned long  => unsigned long | wchar_t        + unsigned long  => unsigned long

signed char    + char           => int           | unsigned char  + char           => int
signed char    + wchar_t        => int           | unsigned char  + wchar_t        => int
signed char    + signed char    => int           | unsigned char  + signed char    => int
signed char    + unsigned char  => int           | unsigned char  + unsigned char  => int
signed char    + short          => int           | unsigned char  + short          => int
signed char    + unsigned short => int           | unsigned char  + unsigned short => int
signed char    + int            => int           | unsigned char  + int            => int
signed char    + unsigned int   => unsigned int  | unsigned char  + unsigned int   => unsigned int
signed char    + long           => long          | unsigned char  + long           => long
signed char    + unsigned long  => unsigned long | unsigned char  + unsigned long  => unsigned long

short          + char           => int           | unsigned short + char           => int
short          + wchar_t        => int           | unsigned short + wchar_t        => int
short          + signed char    => int           | unsigned short + signed char    => int
short          + unsigned char  => int           | unsigned short + unsigned char  => int
short          + short          => int           | unsigned short + short          => int
short          + unsigned short => int           | unsigned short + unsigned short => int
short          + int            => int           | unsigned short + int            => int
short          + unsigned int   => unsigned int  | unsigned short + unsigned int   => unsigned int
short          + long           => long          | unsigned short + long           => long
short          + unsigned long  => unsigned long | unsigned short + unsigned long  => unsigned long

int            + char           => int           | unsigned int   + char           => unsigned int
int            + wchar_t        => int           | unsigned int   + wchar_t        => unsigned int
int            + signed char    => int           | unsigned int   + signed char    => unsigned int
int            + unsigned char  => int           | unsigned int   + unsigned char  => unsigned int
int            + short          => int           | unsigned int   + short          => unsigned int
int            + unsigned short => int           | unsigned int   + unsigned short => unsigned int
int            + int            => int           | unsigned int   + int            => unsigned int
int            + unsigned int   => unsigned int  | unsigned int   + unsigned int   => unsigned int
int            + long           => long          | unsigned int   + long           => long oder unsigned long
int            + unsigned long  => unsigned long | unsigned int   + unsigned long  => unsigned long

long           + char           => long          | unsigned long  + char           => unsigned long
long           + wchar_t        => long          | unsigned long  + wchar_t        => unsigned long
long           + signed char    => long          | unsigned long  + signed char    => unsigned long
long           + unsigned char  => long          | unsigned long  + unsigned char  => unsigned long
long           + short          => long          | unsigned long  + short          => unsigned long
long           + unsigned short => long          | unsigned long  + unsigned short => unsigned long
long           + int            => long          | unsigned long  + int            => unsigned long
long           + unsigned int   => unsigned long | unsigned long  + unsigned int   => unsigned long
long           + long           => long          | unsigned long  + long           => unsigned long
long           + unsigned long  => unsigned long | unsigned long  + unsigned long  => unsigned long

Zugegebenermaßen wirkt dies erst einmal erschlagend, aber es ist eigentlich nicht schwer zu begreifen. Bei jeder Rechenoperation hat jeder der 2 Operanden, sowie das Ergebnis der Rechnung einen Datentyp:

Crystal Clear app terminal.png
#include <iostream>

int main(){
    char  Zahl1=22;
    short Zahl2=40;

    std::cout << Zahl1 * Zahl2 << std::endl; // 22   * 40    =  880
                                             // char + short => int
}

[Bearbeiten] Gleitkommarechnen

Beim Rechnen mit Gleitkommazahlen gelten im Grunde die gleichen Regeln wie bei Ganzzahlen. Der Ergbnistyp entspricht auch hier dem des Operanden mit dem „größeren“ Typ. Die Reihenfolge lautet: float, double, long double. Es gilt also:

float       + float       => float
float       + double      => double
float       + long double => long double

double      + float       => double
double      + double      => double
double      + long double => long double

long double + float       => long double
long double + double      => long double
long double + long double => long double

[Bearbeiten] Casting

Casting meint in diesem Zusammenhang, die Umwandlung eines Datentyps in einen anderen. Diese Typumwandlung kann sowohl automatisch (implizit) stattfinden, als auch vom Programmierer angegeben (explizit) werden.

[Bearbeiten] Implizite Typumwandlung

Mit impliziter Typumwandlung hatten Sie bereits reichlich zu tun, denn es kann ausschließlich mit Zahlen gerechnet werden die den gleichen Typ besitzen.

Beispiele:

char  + int          => int          | int          + int          => int
short + unsigned int => unsigned int | unsigned int + unsigned int => unsigned int
float + double       => double       | double       + double       => double

[Bearbeiten] Umformungsregeln

Viele binäre Operatoren, die arithmetische oder Aufzählungsoperanden erwarten, verursachen Umwandlungen und ergeben Ergebnistypen auf ähnliche Weise. Der Zweck ist, einen gemeinsamen Typ zu finden, der auch der Ergebnistyp ist. Dieses Muster wird "die üblichen arithmetischen Umwandlungen" genannt, die folgendermaßen definiert sind:

  • Wenn ein Operand vom Typ long double ist, dann wird der andere zu long double konvertiert.
  • Andernfalls, wenn ein Operand vom Typ double ist, dann wird der andere zu double konvertiert.
  • Andernfalls, wenn ein Operand vom Typ float ist, dann wird der andere zu float konvertiert.
  • Ansonsten werden die integralen Umwandlungen auf beide Operanden angewendet:
  • Wenn ein Operand vom Typ unsigned long ist, dann wird der andere zu unsigned long konvertiert.
  • Andernfalls, wenn ein Operand vom Typ long und der andere vom Typ unsigned int, dann wird, falls ein long alle Werte eines unsigned int darstellen kann, der unsigned int-Operand zu long konvertiert; andernfalls werden beide Operanden zu unsigned long konvertiert.
  • Andernfalls, wenn ein Operand vom Typ long ist, dann wird der andere zu long konvertiert.
  • Andernfalls, wenn ein Operand vom Typ unsigned int ist, dann wird der andere zu unsigned int konvertiert.

Hinweis: Der einzig verbleibende Fall ist, dass beide Operanden vom Typ int sind.

Diese Regeln wurden so aufgestellt, dass dabei stets ein Datentyp in einen anderen Datentyp mit "größerem" Wertebereich umgewandelt wird. Das stellt sicher, dass bei der Typumwandlung keine Wertverluste durch Überläufe entstehen. Es können allerdings bei der Umwandlung von Ganzzahlen in float-Werte Rundungsfehler auftreten:

Crystal Clear app terminal.png
std::printf( "17000000 + 1.0f = %f.\n", 17000000 + 1.0f );

Diese Programmzeile gibt auf einem Computer, wo float eine 32-Bit-Gleitkommazahl ist, folgendes aus:

17000000 + 1.0f = 17000000.000000.

Warum? Für die Berechnung werden beide Operanden in den Datentyp float konvertiert und anschließend addiert. Eine 32-Bit-Gleitkommazahl ist aber nicht in der Lage, Zahlen in der Größenordnung von 17 Mio. mit der nötigen Genauigkeit zu speichern, um zwischen 17000000 und 17000001 zu unterscheiden. Das Ergebnis der Addition wird daher wieder auf 17000000 gerundet. Dies geschieht in diesem Falle sogar, obwohl der Wert anschließend auf double "aufgeweitet" wird, da die printf-Methode Gleitkommawerte standardmäßig als double erhält (und durch die Formatangabe "%f" auch als double erwartet).

[Bearbeiten] Explizite Typumwandlung

In C++ gibt es dafür zwei Möglichkeiten. Zum einen den aus C übernommenen Cast (Typ)Wert und zum anderen die vier (neuen) C++ Casts.

Crystal Clear app terminal.png
static_cast< Zieltyp >(Variable)
const_cast< Zieltyp >(Variable)
dynamic_cast< Zieltyp >(Variable)
reinterpret_cast< Zieltyp >(Variable)
Symbol kept vote.svg
Tipp
Die Leerzeichen zwischen dem Zieltyp und den spitzen Klammern sind nicht zwingend erforderlich, Sie sollten sich diese Notation jedoch angewöhnen. Speziell wenn Sie später mit Templates oder Namensräumen arbeiten, ist es nützlich Datentypen ein wenig von ihrer Umgebung zu isolieren. Sie werden an den entsprechenden Stellen noch auf die ansonsten möglichen Doppeldeutigkeiten hingewiesen.
Symbol move vote.svg
Thema wird später näher erläutert…
Im Moment benötigen Sie nur den static_cast. Was genau die Unterschiede zwischen diesen Casts sind und wann man welchen einsetzt erfahren Sie in einem späteren Kapitel. Die C-Casts sollten Sie aber auf keinen Fall einsetzen, weshalb erfahren Sie ebenfalls später.

[Bearbeiten] Ganzzahlen und Gleitkommazahlen

Wird mit einer Ganzzahl und einer Gleitkommazahl gerechnet, so ist das Ergebnis vom gleichen Typ wie die Gleitkommazahl.

[Bearbeiten] Rechnen mit Zeichen

Mit Zeichen zu rechnen ist besonders praktisch. Um beispielsweise das gesamte Alphabet auszugeben zählen Sie einfach vom Buchstaben 'A' bis einschließlich 'Z':

Crystal Clear app terminal.png
#include <iostream>

int main(){
    for(char i = 'A'; i <= 'Z'; ++i){
        std::cout << i;
    }
}
Crystal Clear app kscreensaver.png
Ausgabe:
ABCDEFGHIJKLMNOPQRSTUVWXYZ

Für eine Erklärung des obigen Quellcodes, lesen Sie bitte das Kapitel Schleifen.

Wenn Sie binäre Operatoren auf Zeichen anwenden ist das Ergebnis (mindestens) vom Typ int. Im folgenden Beispiel wird statt eines Buchstabens, der dazugehörige ASCII-Wert ausgeben. Um also wieder ein Zeichen auszugeben, müssen Sie das Ergebnis wieder in den Zeichentyp casten. (Beachten Sie im folgenden Beispiel, dass die Variable i - im Gegensatz zum vorherigen Beispiel - nicht vom Typ char ist):

Crystal Clear app terminal.png
#include <iostream>

int main(){
    char zeichen = 'A';

    for(int i = 0; i < 26; ++i){
        std::cout << zeichen + i << ' ';               // Ergebnis int
    }

    std::cout << std::endl;

    for(int i = 0; i < 26; ++i){
        std::cout << static_cast< char >(zeichen + i); // Ergebnis char
    }
}
Crystal Clear app kscreensaver.png
Ausgabe:
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
ABCDEFGHIJKLMNOPQRSTUVWXYZ


Persönliche Werkzeuge