4.6 Implizite Typumwandlung
Wenn die Operanden eines Ausdrucks nicht von demselben Typ sind, kann der Compiler selbstständig eine Typumwandlung durchführen. Eine Typumwandlung wird auch als Typecasting bezeichnet. Diese automatische Typumwandlung läuft nach bestimmten festgefügten Regeln ab; deshalb heißt sie auch implizites Typecasting. Auf diese implizite Umwandlung soll in diesem Abschnitt kurz eingegangen werden.
Implizite Umwandlungen bei Funktionen
Gleiches gilt beim Aufruf einer Funktion, wenn das Argument nicht mit dem Typ des Parameters übereinstimmt. Hier führt der Compiler ebenfalls eine implizite Umwandlung des Typs durch. Dasselbe gilt für Rückgabewerte von Funktionen, wenn der rückzugebende Ausdruck vom Rückgabetyp unterschiedlich ist. In dem entsprechenden Kapitel über Funktionen gehen wir auf diese Dinge noch sehr ausführlich ein.
4.6.1 Arithmetische Umwandlung
Von einer arithmetischen Typumwandlung ist die Rede, wenn zwei unterschiedliche Typen so umgewandelt werden, dass sie im Idealfall den ursprünglichen Wert behalten. In der Praxis wird eine solche Umwandlung bei Operatoren verwendet, die zwei arithmetische Operanden erwarten. Ein einfaches Beispiel dazu:
01 int iVal = 1;
02 double dVal = 1.5;
03 printf("%f\n", iVal + dVal);
In diesem Beispiel wird in Zeile (03) der Wert iVal vom Typ int in eine double-Zahl umgewandelt, weil dieser Typ »mächtiger« als der int-Typ ist. Sie können das gerne testen, indem Sie das Umwandlungszeichen der Zeile (03) auf %d (für int-Wert) ändern. Bei der Übersetzung sollte sich der Compiler mit einer Warnung bei Ihnen melden, dass der Format-String von printf() nicht mit dem Typ übereinstimmt, und dass dies unter Umständen »komische« Ausgaben erzeugen kann.
Der Mächtigere bekommt den Zuschlag
Bei einer arithmetischen Umwandlung können Sie sich somit darauf verlassen, dass der schwächere (least signifficant) in den mächtigeren (most signifficant) Typ umgewandelt wird. Der mächtigste Typ von allen hat auch die meisten Bits (nämlich 80 bzw. 128) und ist long double. Zwar gibt es hierbei, abhängig vom Compiler, noch spezielle Ausnahmefälle, auf die hier allerdings nicht näher eingegangen wird. Es sollte vielleicht noch angemerkt werden, dass nichts umgewandelt wird, wenn beide Operanden denselben Typ haben.
Das Ziel dieser Regeln bei einer arithmetischen Umwandlung ist es somit, dass im folgenden theoretischen Beispiel sowohl Operand1, als auch Operand2 auf denselben Typ gebracht werden:
Operand1 Operator Operand2
Die Gleitkommatypen long double, double und float sind mächtiger als die Integer-Typen. Daher gilt der Reihe nach:
-
Ist ein Operand ein long double, wird auch der andere Operand in ein long double umgewandelt.
-
Ist ein Operand ein double, wird auch der andere Operand in ein double umgewandelt.
-
Ist ein Operand ein float, wird auch der andere Operand in ein float umgewandelt.
-
Seit C99 hat der Typ long long int die höchste Mächtigkeit und _Bool die niedrigste Mächtigkeit, und signed und unsigned werden seitdem als gleich mächtig notiert.
Wenn kein Operand ein Gleitkommatyp ist, werden diese Regeln auch in einer bestimmten Reihenfolge bei den Integer-Typen angewendet, obgleich es hier etwas schwieriger ist, eine einheitliche Regel wie bei den Gleitkommazahlen aufzulisten: Daher können Sie sich hierbei zumindest an der folgenden grundlegenden Reihenfolge orientieren, die bei den meisten modernen Compilern (seit C11) funktionieren sollte:
-
Wenn beide Operanden signed-Integer-Typen oder beide Operanden unsigned-Integer-Typen sind, wird der Operand mit einer niedrigeren Mächtigkeit in den Typ mit dem Operanden der höheren Mächtigkeit umgewandelt.
-
Wenn der eine Operand ein unsigned-Integer-Typ ist, der andere ein signed-Integer-Typ mit gleicher oder niedrigere Mächtigkeit, wird der signed-Integer-Typ in den Operanden des unsigned-Integer-Typs umgewandelt, und es gehen eventuell Vorzeichen verloren.
-
Wenn ein Operand ein signed-Integer-Typ ist, der die Werte des anderen Operanden vom unsigned-Integer-Typ abbilden kann, wird der unsigned-Integer-Typ in den Operanden des signed-Integer-Typs konvertiert.
-
Ansonsten werden beide Operanden in einen unsigned-Integer-Typ umgewandelt, der dieselbe Mächtigkeit wie der entsprechende signed-Integer-Typ hat.
Es gibt geringfügige Unterschiede zwischen dem Regelwerk von C90 (ANSI-C) und C11, aber diese sind für die hier geschilderten Beispiele nicht relevant. Compiler, die nicht mindestens C11 unterstützen, gibt es einfach nicht mehr, außer in speziellen Bereichen. Die hier aufgelisteten Punkte dürften also in 99 Prozent der Fälle passen.
4.6.2 Typpromotionen
Zu diesem Regelwerk der arithmetischen Umwandlung kommen noch die Typpromotionen. Die Rede ist von einer Promotion (oder auch Typerweiterung), wenn der Umwandlungstyp den kompletten Umfang des umzuwandelnden Typs komplett abbilden kann, ohne dass es zu Bitverlusten kommt. Eine solche Promotion wird vom Compiler automatisch durchgeführt. So werden Integer-Typen bevorzugt in ein int und Gleitkommatypen in double umgewandelt, weil diese häufig als die effizientesten Typen gelten.
Trifft keine Regel der arithmetischen Umwandlung bei Integer-Typen zu, und ist ein Operand beispielsweise vom Typ signed char, signed short oder wchar_t, werden beide Operanden in ein signed int umgewandelt. Gleiches gilt, wenn ein Operand ein unsigned char oder unsigned short ist: Dann werden beide Operanden in ein unsigned int umgewandelt. Eine ähnliche Promotion gibt es auch bei den Gleitkommazahlen bei der Umwandlung vom Typ float nach double.
Möglicher Verlust von Bits bei der Typumwandlung
Bei den arithmetischen Umwandlungen und Promotionen gibt es auch Umwandlungen, die nicht verlustfrei durchgeführt werden können. So ist es z. B. nicht möglich, die Nachkommastelle einer Gleitkommazahl in einem Integer-Typ zu speichern. Ebenso ist es nicht möglich, einen negativen signed-Typen in einem vorzeichenlosen unsigned-Typen zu speichern, denn dies würde der Operation *(-1)+1 entsprechen – und auch wenn es logisch erscheint, können Sie niemals den Wert eines breiteren Typs in einem kleineren Typ unterbringen, wenn der breitere Typ den Wertebereich des kleineren Typs sprengt. So würde z. B. die Typumwandlung uint_32 a=1234567 (0x12d687 Hex); uint_16 b=a; zu dem Ergebnis b=54919 (0xd687 Hex) führen.