C wandelt Werte ständig zwischen Typen um. Manche dieser Umwandlungen schreibst du selbst mit einem Cast; die meisten führt der Compiler still nach Regeln aus, die du nicht gewählt hast. Zu wissen, was was ist, ist der Unterschied zwischen „warum ist mein Durchschnitt immer 3?" und Code, der tut, was er sagt.
Implizite Umwandlung
Wann immer ein Wert eines Typs auf einen Kontext trifft, der einen anderen erwartet, wandelt C ihn um:
Umwandlungen, die keine Information verlieren können (int nach double, char nach int, short nach long), sind erweiternd und immer sicher. Umwandlungen in die andere Richtung sind verengend und können Daten verlieren - aus der 3.9 oben wurde eine 3, ohne Warnung, sofern du nicht mit -Wconversion danach fragst.
Die üblichen arithmetischen Umwandlungen
Wenn ein binärer Operator Operanden verschiedener Typen hat, wandelt C sie vor der Arbeit in einen gemeinsamen Typ um. Die Leiter, von unten nach oben:
- Alles, was kleiner als
intist (char,short,_Bool), wird zuintbefördert. Das ist die Ganzzahlbeförderung, und sie passiert immer zuerst. - Ist eine Seite
long double, wird die andere zulong double. - Sonst, ist eine
double, wird die andere zudouble. - Sonst, ist eine
float, wird die andere zufloat. - Sonst gewinnt unter den Ganzzahltypen der mit dem höheren Rang, und bei Ranggleichheit gewinnt unsigned.
Diese letzte Regel verursacht echte Bugs. Der Rest ist intuitiv.
Die Ganzzahlbeförderung ist der Grund, warum char-Arithmetik nicht so überläuft, wie du es erwarten könntest - und warum das Zurückspeichern in ein char es doch tut.
Explizite Casts
Ein Cast ist der Zieltyp in Klammern:
(double)x
(int)3.9
(char)65
(unsigned int)n
Er gilt für den Ausdruck unmittelbar danach, und er bindet sehr stark - stärker als *, / oder +.
Die erste Zeile teilt als Ganzzahlen (ergibt 3) und wandelt dann 3 in 3.0 um - zu spät. Die zweite wandelt total vor der Division in eine Gleitkommazahl um, der /-Operator sieht also ein double und ein int, befördert das int und führt eine Gleitkommadivision aus.
Einen Operanden zu casten genügt. Um den anderen kümmern sich die üblichen arithmetischen Umwandlungen.
Die Ganzzahldivision reparieren
Das ist der mit Abstand häufigste Grund, in C einen Cast zu schreiben:
Die Prozentzeile ist lehrreich: passed / n ist 3 / 5, was als Ganzzahlen 0 ist, und 0 * 100 ist 0. Vor dem Teilen zu multiplizieren (100 * passed / n) behebt es auch ohne Cast, denn 300 / 5 ist exakt - aber das funktioniert nur, wenn die Zahlen mitspielen. Der Cast ist die verlässliche Lösung.
Abschneiden, nicht runden
Einen Gleitkommawert in eine Ganzzahl zu casten verwirft den Bruchteil. Es schneidet Richtung null ab - es rundet nicht:
Wenn du das auf deiner eigenen Maschine baust, denk daran, dass math.h unter Linux beim Linken -lm braucht.
Eine weitere Gefahr: Einen Gleitkommawert umzuwandeln, der für den Ganzzahltyp zu groß ist, ist undefiniertes Verhalten, kein Umschlagen. (int)1e20 kann alles Mögliche erzeugen. Prüfe den Bereich vor dem Cast, wenn der Wert nicht unter deiner Kontrolle steht.
char und int
Ein char ist in C eine kleine Ganzzahl, die einen Zeichencode hält. Zwischen beiden umzuwandeln ist alltägliche Arbeit:
digit - '0' ist das Standardidiom, um ein Ziffernzeichen in seinen Wert zu verwandeln, und es funktioniert, weil die zehn Ziffernzeichen garantiert aufeinanderfolgen. Bevorzuge bei Buchstaben toupper() und tolower() aus ctype.h gegenüber der + 32-Arithmetik - der Abstand ist eine ASCII-Tatsache, keine Garantie von C.
Eine verwandte Falle: Funktionen aus ctype.h wie isdigit und toupper nehmen ein int entgegen, das entweder EOF oder als unsigned char darstellbar sein muss. Ein schlichtes char zu übergeben, das negativ ist (möglich, da ein schlichtes char vorzeichenbehaftet sein darf), ist undefiniert. Caste es: isdigit((unsigned char)c).
Die Signed/Unsigned-Falle
Schritt 5 der Umwandlungsleiter - unsigned gewinnt bei Gleichstand - erzeugt Cs überraschendsten Vergleich:
-1 wird in unsigned int umgewandelt, was sein Bitmuster als 4.294.967.295 deutet. Das ist größer als 1, der Vergleich ist also falsch.
Dieselbe Umwandlung lässt Schleifen ewig laufen:
/* BUG: i ist unsigned, also ist i >= 0 immer wahr. Bei i gleich 0 schlägt i-- um. */
for (size_t i = n - 1; i >= 0; i--) { ... }
Und sie lässt Längenprüfungen scheitern:
/* BUG: strlen liefert size_t (unsigned). Ist die Zeichenkette kürzer
als 5, schlägt len - 5 auf eine riesige Zahl um und der Test gelingt. */
if (strlen(s) - 5 > 0) { ... }
Schreibe es als if (strlen(s) > 5) um, und die Subtraktion passiert nie.
Die Verteidigungen: Halte Anzahlen und Indizes durchgehend bei einer Vorzeichenart, kompiliere mit -Wsign-compare (in -Wextra enthalten), und wenn du mischen musst, caste ausdrücklich, nachdem du festgestellt hast, dass der Wert nicht negativ sein kann.
Zeiger casten
Casts wandeln auch zwischen Zeigertypen um, und hier bergen sie echtes Risiko, denn sie ändern, wie Speicher gedeutet wird, nicht die Bytes selbst.
Auf einer Little-Endian-Maschine gibt das 01 00 00 00 aus. Die Objektdarstellung durch ein unsigned char * zu inspizieren ist einer der wenigen Zeiger-Casts, die der Standard ausdrücklich segnet.
Die meisten anderen Zeiger-Casts sind nicht gesegnet. Ein int durch ein float * zu lesen verletzt die strikte Aliasing-Regel und ist undefiniertes Verhalten, auch wenn es kompiliert; nimm stattdessen memcpy, um Bytes umzudeuten.
Zwei wissenswerte Konventionen. void * wandelt sich in C ohne Cast in jeden Objektzeigertyp und zurück um, weshalb du das Ergebnis von malloc nicht casten solltest:
int *arr = malloc(n * sizeof *arr); /* korrektes C */
int *arr = (int *)malloc(n * sizeof *arr); /* überflüssig; verdeckt einen fehlenden Header */
In C++ ist der Cast Pflicht, weshalb so viel Code ihn hat. In C kann er den Fehler verbergen, <stdlib.h> vergessen zu haben.
Und printf("%p", ...) erwartet ein void *, Zeigerargumente brauchen dort also tatsächlich einen Cast: printf("%p", (void *)p).
Wann ein Cast die falsche Antwort ist
Ein Cast bringt den Compiler zum Schweigen. Manchmal hatte der Compiler recht.
long big = 5000000000L;
int small = (int)big; /* der Cast verbirgt echten Datenverlust */
Passt der Wert wirklich, dokumentiert der Cast, dass du geprüft hast. Passt er womöglich nicht, hat der Cast eine Warnung in eine stille falsche Antwort verwandelt. Bevor du einen schreibst, frage, ob die Lösung darin besteht, stattdessen den Typ einer Variablen zu ändern - double statt int, size_t statt int, long long statt long. Ein Cast ist vor allem dann das richtige Werkzeug, wenn zwei korrekte Typen für eine Operation zusammentreffen müssen, wie bei (double)sum / count.
Häufig gestellte Fragen
Wie castet man in C?
Setze den Zieltyp in Klammern vor den Wert: (double)x, (int)3.9, (char)65. Der Cast gilt für den Ausdruck unmittelbar danach, (double)a / b wandelt also zuerst a um und teilt dann, während (double)(a / b) als Ganzzahlen teilt und das Ergebnis umwandelt.
Wie wandle ich in C ein int in ein float um?
Die Zuweisung tut es implizit - double d = 5; speichert 5.0. Innerhalb eines Ausdrucks brauchst du oft einen expliziten Cast: (double)total / count erzwingt eine Gleitkommadivision statt einer Ganzzahldivision.
Was passiert, wenn man in C ein float in ein int castet?
Der Bruchteil wird verworfen - Richtung null abgeschnitten, nie gerundet. (int)3.9 ist 3 und (int)-3.9 ist -3. Zum Runden addiere bei positiven Werten vor dem Cast 0.5, oder nimm round(), floor() oder ceil() aus math.h.
Warum liefert der Vergleich eines vorzeichenbehafteten mit einem vorzeichenlosen int die falsche Antwort?
Cs übliche arithmetische Umwandlungen machen aus dem vorzeichenbehafteten Wert einen vorzeichenlosen, -1 < 1u ist also falsch: Aus -1 wird eine riesige positive Zahl. Halte Anzahlen und Größen bei einer Vorzeichenart, oder caste ausdrücklich, nachdem du geprüft hast, dass der Wert nicht negativ sein kann.