C++-Programmierung/ Einführung in C++/ Rechnen mit unterschiedlichen Datentypen
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.
Ganzzahlen
[Bearbeiten]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 schwierig zu begreifen. Bei jeder Rechenoperation hat jeder der 2 Operanden, sowie das Ergebnis der Rechnung, einen Datentyp:
#include <iostream>
int main(){
char zahl1=22;
short zahl2=40;
std::cout << zahl1 * zahl2 << std::endl; // 22 * 40 = 880
// char + short => int
}
Gleitkommarechnen
[Bearbeiten]Beim Rechnen mit Gleitkommazahlen gelten im Grunde die gleichen Regeln wie bei Ganzzahlen. Der Ergebnistyp entspricht auch hier dem des Operanden mit dem „größeren“ Typ. Die aufsteigende 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
Casting
[Bearbeiten]Casting bedeutet in diesem Zusammenhang die Umwandlung eines Datentyps in einen anderen. Diese Typumwandlung kann sowohl automatisch (implizit) stattfinden, als auch vom Programmierer angegeben (explizit) werden.
Implizite Typumwandlung
[Bearbeiten]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
Umformungsregeln
[Bearbeiten]Viele binäre Operatoren, die arithmetische oder Aufzählungsoperanden erwarten, verursachen Umwandlungen und ergeben Ergebnistypen auf ähnliche Weise. Der Zweck ist, einen gemeinsamen Ergebnistyp zu finden. Dieses Muster wird "die üblichen arithmetischen Umwandlungen" genannt, die folgendermaßen definiert sind:
„Gleitkomma geht vor“:
- Wenn ein Operand vom Typ
long double
ist, dann wird der andere zulong double
konvertiert. - Andernfalls, wenn ein Operand vom Typ
double
ist, dann wird der andere zudouble
konvertiert. - Andernfalls, wenn ein Operand vom Typ
float
ist, dann wird der andere zufloat
konvertiert.
Ist kein Gleitkommatyp beteiligt, dann werden folgende Ganzzahl-Umwandlungen auf beide Operanden angewendet:
- Wenn ein Operand vom Typ
unsigned long
ist, dann wird der andere zuunsigned long
konvertiert. - Andernfalls, wenn ein Operand vom Typ
long
und der andere vom Typunsigned int
, dann wird, falls einlong
alle Werte einesunsigned int
darstellen kann, derunsigned int
-Operand zulong
konvertiert; andernfalls werden beide Operanden zuunsigned long
konvertiert. - Andernfalls, wenn ein Operand vom Typ
long
ist, dann wird der andere zulong
konvertiert. - Andernfalls, wenn ein Operand vom Typ
unsigned int
ist, dann wird der andere zuunsigned 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:
Für die Berechnung werden zunächst beide Operanden in den Datentyp float
konvertiert und anschließend addiert. Das Ergebnis ist wiederum ein float
und somit aber nicht in der Lage, Zahlen in der Größenordnung von 17 Millionen mit der nötigen Genauigkeit zu speichern, um zwischen 17000000 und 17000001 zu unterscheiden. Das Ergebnis der Addition ist daher wieder 17000000.
Explizite Typumwandlung
[Bearbeiten]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.
static_cast< Zieltyp >(Variable)
const_cast< Zieltyp >(Variable)
dynamic_cast< Zieltyp >(Variable)
reinterpret_cast< Zieltyp >(Variable)
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.
Im Moment benötigen Sie nur den static_cast
. Was genau die Unterschiede zwischen diesen Casts sind und wann man welchen einsetzt, erfahren Sie im Kapitel Casts. Auf C-Casts wird in diesem Kapitel ebenfalls eingegangen, merken Sie sich jedoch schon jetzt, dass Sie diese nicht einsetzen sollten. Natürlich müssen Sie sie als C++-Programmierer dennoch kennen, falls Sie einmal auf einen solchen stoßen sollten.
Ganzzahlen und Gleitkommazahlen
[Bearbeiten]Wird mit einer Ganzzahl und einer Gleitkommazahl gerechnet, so ist das Ergebnis vom gleichen Typ wie die Gleitkommazahl.
Rechnen mit Zeichen
[Bearbeiten]Mit Zeichen zu rechnen, ist besonders praktisch. Um beispielsweise das gesamte Alphabet auszugeben, zählen Sie einfach vom Buchstaben 'A'
bis einschließlich 'Z'
:
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 ausgegeben. 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):
#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
}
}
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