Cast en C : guide complet et analyse approfondie #
Le cast en C est l’outil qui permet de dire au compilateur : « traite cette valeur comme si elle était d’un autre type ». Derrière une syntaxe de trois caractères se cachent des règles précises, quelques garanties solides et plusieurs pièges qui n’apparaissent qu’à l’exécution. Ce guide reprend chaque mécanisme, exemples compilés à l’appui.
(type-cible) expression. Elle demande au compilateur d’appliquer immédiatement une conversion qu’il n’aurait pas faite seul, ou qu’il aurait faite plus tard dans l’expression. Le cast ne réinterprète pas les bits au hasard : il applique les mêmes règles de conversion que l’affectation, plus quelques conversions réservées aux pointeurs.- Syntaxe :
(type-cible) expression, où la cible estvoidou un type scalaire. - Implicite ≠ explicite : le compilateur convertit déjà seul dans une affectation ou un calcul mixte ; le cast sert à forcer le moment et le type.
- Usage n°1 : forcer un calcul flottant là où deux entiers déclencheraient une division entière.
- Limite : certaines conversions ne sont pas garanties par le langage — hors plage, alignement, signe de
char. Elles se traitent, pas se subissent.
Les Fondamentaux du cast en C #
Un cast s’écrit (type-name) expression. La documentation de référence du langage précise que la cible est soit void, soit un type scalaire (entier, flottant, pointeur), et que l’expression convertie est elle aussi de type scalaire — sauf si l’on caste vers void, auquel cas la valeur est simplement évaluée puis abandonnée. On ne caste donc pas une structure entière vers une autre structure.
La distinction essentielle n’oppose pas « le cast » à « pas de cast », mais la conversion implicite à la conversion explicite. Le compilateur convertit déjà de lui-même dans une affectation, dans le passage d’un argument, ou lorsqu’une opération mélange deux types arithmétiques. Le cast ne fait qu’ajouter une conversion au moment précis où le programmeur la veut.
À lire Comprendre le cast en C : conversion explicite et implicite expliquées
| Situation | Conversion implicite | Cast explicite |
|---|---|---|
| Qui la déclenche | Le compilateur, par les règles du langage | Le programmeur, à un endroit choisi |
| Où elle apparaît | Affectation, argument, retour, opération mixte | Partout où l’on écrit (type) |
| Visible à la relecture | Non, elle est silencieuse | Oui, c’est tout son intérêt |
| Conversions de pointeurs | Seulement depuis/vers void * | Entre pointeurs d’objets, pointeur ↔ entier |
L’exemple canonique : la division entière
C’est le cas d’école, et il illustre parfaitement la notion de moment de la conversion. Deux entiers divisés donnent une division entière : le compilateur convertira bien le résultat en float au moment de l’affectation, mais la partie fractionnaire est déjà perdue. Le cast déplace la conversion avant la division.
#include <stdio.h>
int main(void)
{
int a = 8, b = 3;
float r1 = a / b; /* division entiere : la fraction est perdue */
float r2 = (float) a / b; /* a devient float, b suit, division reelle */
printf("division entiere : %.4f\n", r1);
printf("division flottante: %.4f\n", r2);
return 0;
}
division entiere : 2.0000division flottante: 2.6667gcc -Wall -Wextra -std=c11 (GCC 13.3.0, x86-64 Linux). Aucun avertissement.Notez la mécanique complète : (float) a convertit a, puis les conversions arithmétiques habituelles alignent automatiquement b sur le type flottant pour que la division ait lieu dans ce type. Un seul cast a suffi à requalifier toute l’expression.
Ce que le compilateur fait vraiment
Un cast n’est pas une réinterprétation brute de la mémoire. Sur les types arithmétiques, il applique une conversion de valeur : le nombre 9,99 devient l’entier 9, pas le motif binaire du flottant. C’est seulement sur les pointeurs que l’on s’approche d’une réinterprétation — et encore, sous conditions strictes. Deux familles de règles encadrent ces conversions numériques :
- Les promotions entières : tout type entier de rang inférieur ou égal à
int(unchar, unshort) est promu enintdès qu’il participe à un calcul — enunsigned intsiintne peut pas représenter toutes ses valeurs. - Les conversions arithmétiques habituelles : quand une opération mêle deux types arithmétiques différents, les deux opérandes sont d’abord ramenés à un type commun. C’est ce qui explique qu’un seul cast suffise dans l’exemple ci-dessus.
Applications Pratiques et Cas d’Usage #
Le cast n’est pas un ornement de style : il existe des situations où le langage ne fournit aucune autre porte de sortie. Voici les usages qui reviennent le plus souvent dans le code système, réseau et embarqué.
À lire Aide au codage CIM 10 : Comprendre la classification et ses applications
void évalue l’expression et jette le résultat. C’est la manière idiomatique de signaler « ce retour est ignoré volontairement ».intptr_t et uintptr_t, quand l’implémentation les fournit, rendent l’aller-retour bien défini pour les pointeurs d’objets.Le cast de malloc : celui dont on peut se passer
C’est la conversion que l’on voit le plus souvent écrite… et l’une des rares qui soit inutile en C. malloc renvoie un void *, et le langage convertit implicitement void * vers n’importe quel pointeur d’objet lors d’une affectation : la documentation de référence donne d’ailleurs char *p = malloc(10); comme exemple de conversion implicite. Écrire (int *) malloc(...) ne change rien au résultat et masque l’oubli éventuel de <stdlib.h>. La forme sizeof *tab évite en prime de répéter le type.
Conversions numériques : trois comportements à connaître
#include <stdio.h>
int main(void)
{
double d = 9.99;
int t = (int) d; /* troncature vers zero */
int neg = -1;
unsigned int u = (unsigned int) neg; /* arithmetique modulo */
long big = 70000L;
short s = (short) big; /* non representable */
printf("(int) 9.99 = %d\n", t);
printf("(unsigned) -1 = %u\n", u);
printf("(short) 70000 = %d\n", s);
return 0;
}
(int) 9.99 = 9(unsigned) -1 = 4294967295(short) 70000 = 4464int sur 32 bits. Seule la deuxième ligne est garantie par le langage : lisez la section suivante avant de vous appuyer sur la troisième.Ligne par ligne : la conversion d’un flottant vers un entier tronque vers zéro, elle n’arrondit pas — 9.99 donne 9, et -9.99 donnerait -9. La conversion vers un type non signé suit une arithmétique modulo parfaitement définie : on ajoute ou retranche 2b (b = nombre de bits de valeur de la cible) jusqu’à tomber dans la plage, ce qui transforme -1 en la plus grande valeur possible. La conversion vers un type signé trop étroit, elle, n’est pas garantie : voir plus bas.
Lire une trame binaire sans casser l’alignement
L’exemple classique du décodage d’une trame consiste à caster l’adresse d’un octet en pointeur d’entier 16 bits pour lire deux octets d’un coup. C’est précisément la conversion que la documentation signale comme dangereuse : si l’adresse n’est pas correctement alignée pour le type cible, le comportement est indéfini. La version portable copie les octets.
#include <stdio.h>
#include <string.h>
#include <stdint.h>
int main(void)
{
unsigned char trame[8] = { 0x00, 0x01, 0x12, 0x34, 0, 0, 0, 0 };
uint16_t reg;
memcpy(®, &trame[2], sizeof reg); /* pas de cast de pointeur */
printf("registre = 0x%04X\n", (unsigned) reg);
return 0;
}
registre = 0x34120x1234. memcpy règle l’alignement, pas le boutisme.Optimisation et Meilleures Pratiques #
Un cast bien posé documente une intention. Un cast posé pour éteindre un avertissement fait exactement l’inverse : il éteint le messager. La discipline tient en peu de règles.
À lire Logitech BCC950 Drivers : Fonctionnement et Optimisation pour la Visio Conférence
- Compiler avec
-Wall -Wextraet traiter chaque avertissement de conversion. - Vérifier les bornes avant le cast, pas après, en s’appuyant sur les macros de
<limits.h>. - Passer par
memcpypour relire une suite d’octets comme un autre type. - Utiliser
intptr_t/uintptr_tpour un aller-retour pointeur ↔ entier. - Laisser un commentaire court expliquant pourquoi le cast est là.
- Caster le retour de
malloc: c’est inutile en C et cela masque un oubli d’en-tête. - Caster pour supprimer un avertissement dont on n’a pas compris la cause.
- Déréférencer un pointeur obtenu par cast sans avoir vérifié l’alignement.
- Supposer qu’un
charest signé — ou qu’il ne l’est pas. - Caster un pointeur de fonction vers un pointeur d’objet : le langage ne le prévoit pas.
Ce que le langage ne vous garantit pas
C’est le cœur du sujet, et la raison pour laquelle un programme « qui marche » ici peut échouer ailleurs. Quatre situations reviennent constamment ; aucune ne doit être présentée comme un comportement acquis.
| Conversion | Statut | Ce que cela implique |
|---|---|---|
| Flottant → entier, valeur hors plage de la cible | Indéfini | Aucune valeur de repli : tester la plage avant de convertir. |
| Entier → type signé trop étroit | Défini par l’implémentation | Le 4464 obtenu plus haut est le choix de ce compilateur sur cette cible, pas une règle. |
char signé ou non signé | Défini par l’implémentation | Dépend de la cible et peut même être piloté par une option de compilation. Écrire signed char ou unsigned char quand le signe compte. |
| Déréférencement d’un pointeur mal aligné | Indéfini | Tolérant sur x86, fautif ailleurs. memcpy supprime le problème. |
| Pointeur → entier | Défini par l’implémentation | Même un pointeur nul ne donne pas forcément zéro ; et si le résultat n’entre pas dans le type cible, le comportement devient indéfini. |
-funsigned-char, affiche successivement « char est signé » puis « char est non signé ». Le signe de char n’est pas une propriété du langage : c’est une propriété de la compilation.Cette liste explique aussi pourquoi les analyseurs statiques s’intéressent tant aux casts : un (short) posé sur une valeur qui déborde ne produit ni erreur de compilation ni plantage, seulement un nombre faux qui voyagera loin avant d’être remarqué.
Conclusion et Perspectives #
Le cast en C n’est ni un raccourci ni une astuce : c’est une déclaration d’intention adressée au compilateur et au relecteur. Bien employé, il rend explicite une conversion que le langage aurait faite en silence, ou en ouvre une qu’il n’aurait jamais faite seul. Mal employé, il désactive le seul filet de sécurité dont dispose un langage compilé : le contrôle de types.
La ligne de partage est simple à retenir. Les conversions arithmétiques sont largement définies : troncature vers zéro depuis un flottant, arithmétique modulo vers un type non signé. Les conversions de pointeurs, elles, sont un territoire à contrat : alignement, type d’accès et retour à l’identique conditionnent la validité. C’est cette asymétrie que des langages plus récents comme Rust ou Zig ont choisi de traiter autrement, en refusant les conversions numériques silencieuses et en exigeant une écriture explicite. Le C, lui, laisse la responsabilité au programmeur — et c’est exactement pour cela que les règles ci-dessus valent d’être connues.
À lire VBA List : Fonctionnalités clés et applications pour automatiser Excel
À retenir #
- Un cast convertit une valeur, il ne réinterprète pas des bits — sauf dans le cas encadré des pointeurs.
- Le placement compte plus que le type :
(float) a / bet(float)(a / b)ne donnent pas le même résultat. - Flottant vers entier = troncature vers zéro, et comportement indéfini si la valeur sort de la plage cible.
- Vers un type non signé, la conversion est garantie (modulo) ; vers un type signé trop étroit, elle ne l’est pas.
- Pour relire des octets autrement,
memcpyplutôt qu’un cast de pointeur : l’alignement cesse d’être un pari. - Le cast de
mallocest inutile en C et masque un oubli d’en-tête.
Questions fréquentes sur le cast en C #
Quelle est la différence entre conversion implicite et cast explicite ?
Pourquoi (float) a / b et (float)(a / b) ne donnent-ils pas le même résultat ?
a devient flottant avant la division ; les conversions arithmétiques habituelles alignent alors b et la division se fait en flottant. Avec 8 et 3, on obtient respectivement 2,0000 et 2,6667.Faut-il caster le retour de malloc ?
void * se convertit implicitement vers n’importe quel pointeur d’objet lors d’une affectation ; int *tab = malloc(n * sizeof *tab); suffit. Le cast n’apporte rien et peut masquer l’oubli de <stdlib.h>. La règle diffère en C++, où le cast est obligatoire — d’où la confusion fréquente.Un cast peut-il faire perdre des données ?
Peut-on caster un pointeur vers n’importe quel autre type de pointeur ?
void * compris.Le type char est-il signé ?
char équivaut à signed char ou à unsigned char selon la cible, et ce choix peut être modifié par une option du compilateur — tout en restant un type distinct des deux. Dès que le signe intervient dans un calcul ou une comparaison, il faut écrire explicitement signed char ou unsigned char.Règles de conversion et de cast vérifiées sur la documentation de référence du langage C : Cast operators et Implicit conversions. Les trois programmes de cet article ont été compilés avec gcc -Wall -Wextra -std=c11 (GCC 13.3.0, x86-64 Linux) et exécutés : les sorties affichées sont celles obtenues.
1 avis sur « Comprendre le cast en C : conversions de type explicites et applications »