- Apprendre
- Informatique et numérique
- Programmation
- Langage C : pointeurs, mémoire et structures
- Types et opérateurs en C
Les bases du C : types, opérateurs et contrôle du déroulement
printf et scanf. Construis des expressions avec les opérateurs arithmétiques, de comparaison, logiques et bit à bit du C en prévoyant les conversions et les priorités, puis contrôle le déroulement d’un programme avec if, switch, while, do-while et for. Termine en découvrant pourquoi les entiers débordent, ce qui se produit alors et comment écrire et tester du code qui évite ces débordements.DébutantUnité 1 sur 8 de la formation Formation : Langage C : pointeurs, mémoire et structures
Ce que tu vas apprendre
- Transformer du code source C en programme exécutable, savoir ce que fait le compilateur à chaque étape et utiliser ses messages pour repérer et corriger les erreurs.
- Écrire, compiler et exécuter un premier programme CLire les différentes parties d’un programme C minimal, le compiler avec gcc ou clang et exécuter le fichier obtenu depuis un terminal.
- Lire les messages du compilateur et corriger les erreursDistinguer les erreurs de compilation, les avertissements, les erreurs d’édition de liens, d’exécution et de logique, et utiliser les messages du compilateur et des exécutions de test pour les corriger.
- Écrire, compiler et exécuter un premier programme C
- Déclarer et initialiser des variables, choisir entre les types entiers et à virgule flottante du C, et prévoir la plage de valeurs et la précision de chaque type.
- Déclarer, initialiser et nommer des variablesCréer des variables avec un type et un nom qui a un sens, leur donner des valeurs initiales et utiliser des constantes pour les valeurs fixes.
- Choisir un type entier et connaître sa plage de valeursChoisir parmi les types entiers signés et non signés du C, les valeurs caractères et booléennes, en tenant compte de leurs tailles, de leurs plages de valeurs et de la forme de leurs littéraux.
- Utiliser les nombres à virgule flottante et connaître leurs limitesStocker des nombres réels dans float et double, prévoir leur précision et leurs erreurs d’arrondi, et convertir en toute sécurité les valeurs à virgule flottante en entiers et inversement.
- Déclarer, initialiser et nommer des variables
- Mettre en forme précisément les nombres et le texte avec printf, puis lire et vérifier les valeurs saisies par l’utilisateur avec scanf.
- Mettre en forme les sorties avec printfUtiliser les spécifications de conversion, les largeurs de champ, la précision, les indicateurs et les séquences d’échappement pour obtenir exactement la sortie voulue.
- Lire et vérifier les données saisies avec scanfLire les nombres et les caractères saisis par l’utilisateur, vérifier que la lecture a réussi et traiter les saisies invalides.
- Mettre en forme les sorties avec printf
- Utiliser les opérateurs arithmétiques, d’affectation, de comparaison, logiques et bit à bit du C, et prévoir la valeur et le type de toute expression à partir des conversions, de la priorité et de l’associativité.
- Calculer avec les opérateurs arithmétiques et d’affectationAppliquer +, -, *, / et %, la division entière, l’affectation composée, l’incrémentation et la décrémentation, et éviter leurs pièges habituels.
- Prévoir les conversions de types dans les expressions mêlant plusieurs typesExpliquer comment le C convertit les opérandes de types différents, utiliser les conversions explicites à bon escient et éviter le piège des mélanges entre types signés et non signés.
- Combiner des conditions avec les opérateurs relationnels, logiques et bit à bitÉcrire des comparaisons et des conditions composées correctes, utiliser l’évaluation en court-circuit et manipuler les bits individuellement.
- Appliquer les règles de priorité, d’associativité et d’ordre d’évaluationPrévoir comment le C regroupe les opérateurs dans une expression, évaluer les expressions étape par étape et utiliser des parenthèses lorsque le regroupement n’est pas clair.
- Calculer avec les opérateurs arithmétiques et d’affectation
- Choisir entre plusieurs possibilités avec if, else et switch, et répéter des opérations avec des boucles while, do-while et for qui démarrent, s’exécutent et s’arrêtent exactement comme prévu.
- Effectuer des choix avec if et elseÉcrire des instructions if, else if et else avec des conditions et des accolades correctes, et éviter les pièges courants de la syntaxe C.
- Choisir parmi plusieurs cas avec switchUtiliser switch avec des étiquettes case, break et default pour choisir entre des valeurs entières fixes, et n’utiliser l’enchaînement des cas que volontairement.
- Répéter des instructions tant qu’une condition est vraie avec while et do-whileÉcrire des boucles contrôlées par une condition qui démarrent correctement, progressent et s’arrêtent, notamment des boucles de saisie et des boucles qui doivent s’exécuter au moins une fois.
- Compter avec des boucles for, imbriquer des boucles et les interrompre avant leur termeÉcrire des boucles for qui s’exécutent exactement le nombre de fois voulu, imbriquer des boucles et utiliser break et continue à bon escient.
- Effectuer des choix avec if et else
- Expliquer ce qui se passe lorsqu’un résultat n’est pas représentable dans son type entier, et écrire et tester du code qui évite ou détecte les débordements.
- Expliquer ce qui se passe lorsqu’une valeur n’est pas représentable dans un typeDécrire le rebouclage des entiers non signés, le débordement indéfini des entiers signés et les conversions hors plage, et les relier à des défaillances réelles.
- Écrire du code qui évite ou détecte les débordementsChoisir des types dont la plage est suffisante, vérifier les opérations avant qu’elles ne débordent et utiliser les outils du compilateur et des tests aux limites pour détecter rapidement les débordements.
- Expliquer ce qui se passe lorsqu’une valeur n’est pas représentable dans un type
Types et opérateurs en C : cours
1Compiler et exécuter des programmes C
Le code C devient un programme exécutable grâce à une construction. Découvre les parties d’un premier programme complet, compile-le selon une norme du langage choisie et exécute le fichier obtenu. Lorsqu’une construction ou un test échoue, utilise l’étape de l’échec et le premier message du compilateur pour trouver la cause. Suivre attentivement le cycle de modification, de construction et de test évite qu’un ancien exécutable masque une modification qui a échoué et facilite la vérification de chaque correction.
1.1 Du code source au programme en cours d’exécution
Un programme C commence sous forme de texte brut et s’exécute sous forme de code machine. Le compilateur relie ces deux formes. Enregistrer une modification change le code source ; cela ne change pas l’exécutable construit auparavant.
Lire un programme complet
Enregistre ce programme sous le nom hello.c :
#include <stdio.h>
int main(void)
{
printf("Hello, world!\n");
return 0;
}
#include <stdio.h> rend disponibles les déclarations des entrées et sorties standard, dont celle de printf. L’exécution commence dans main. Dans int main(void), int indique le type de retour et void signifie qu’il n’y a pas de paramètres. Les accolades délimitent le corps. L’appel à printf écrit le texte entre guillemets ; \n fait passer à une nouvelle ligne. return 0; termine le programme et signale sa réussite au système d’exploitation. Un code de retour non nul peut signaler un échec. Depuis C99, atteindre la fin de main renvoie également zéro.
Les instructions simples se terminent par un point-virgule. La ligne d’inclusion n’en prend pas. Le C distingue les majuscules des minuscules : Printf et printf sont des noms différents. Les espaces entre les lexèmes ne modifient généralement pas le programme, mais une indentation cohérente rend la structure des blocs visible.
Compiler, puis exécuter
Dans le terminal, ouvre le dossier contenant le code source et saisis :
gcc -std=c17 -Wall -Wextra hello.c -o hello
./hello
La première commande construit un exécutable nommé hello ; la seconde l’exécute depuis le dossier courant. Sa sortie est Hello, world! suivi d’un saut de ligne. Tu peux utiliser clang à la place de gcc. Sans -o hello, le nom habituel du fichier produit sous Linux ou macOS est a.out. Les exécutables Windows utilisent .exe. La commande make hello de CS50 est un raccourci qui demande à l’outil de construction d’appeler le compilateur.
Après avoir modifié le message de bienvenue, enregistre, compile à nouveau et exécute à nouveau. Si la compilation échoue, un ancien exécutable peut encore être présent. Son exécution montre l’ancien comportement : vérifie donc que la construction a réussi avant de juger ta modification.
Ce que fait la construction
Le préprocesseur traite les directives comme #include en insérant le texte des fichiers d’en-tête. Le compilateur proprement dit traduit le C en langage assembleur. L’assembleur produit du code objet qui peut encore contenir des noms non résolus. L’éditeur de liens combine le code objet et résout les références comme printf. Le code de bibliothèque peut être copié dans un exécutable par liaison statique ou fourni par une bibliothèque partagée chargée à l’exécution du programme.
Tu peux arrêter la construction à des étapes intermédiaires :
| Option | Résultat |
|---|---|
-E | Texte après prétraitement |
-S | Langage assembleur |
-c | Un fichier objet, comme hello.o |
Un fichier objet n’est pas encore l’exécutable final. Un exécutable peut fonctionner sans le compilateur, mais il est construit pour un système d’exploitation et un processeur particuliers. Pour porter le programme sur une autre plateforme, compiles-y son code source.
Commentaires et versions du langage
// commence un commentaire qui se termine au saut de ligne. /* ... */ peut couvrir plusieurs lignes, mais ces commentaires ne peuvent pas être imbriqués : le premier */ termine le commentaire. Utilise les commentaires pour expliquer une décision que le code seul ne rend pas claire.
Choisis explicitement la version du langage. C17 prend en charge les exemples ci-dessus, notamment les commentaires //, long long et les déclarations dans les en-têtes de boucles for. C23 ajoute des fonctionnalités comme bool en tant que mot-clé ; cette version a été publiée sous le nom ISO/IEC 9899:2024. -Wall -Wextra active des avertissements utiles. Les valeurs par défaut varient selon le compilateur : un -std=c17 explicite indique donc clairement les règles du langage voulues.
Points clés
- Le code source C doit être compilé à nouveau après chaque modification.
- L’exécution commence dans
main; renvoyer zéro signale une réussite. - Les étapes de construction sont le prétraitement, la compilation, l’assemblage et l’édition de liens.
- Utilise
-std=c17 -Wall -Wextrapour choisir la norme et activer les avertissements.
1.2 Trouver la cause initiale d’une erreur
Le moment où un problème apparaît t’aide à en trouver la cause. Un programme peut ne pas être construit, s’arrêter pendant son exécution ou se terminer avec un résultat incorrect. Ces situations demandent des vérifications différentes.
Distinguer les types de problèmes
| Type | Ce que tu observes | Où chercher |
|---|---|---|
| Erreur de compilation | Aucun nouvel exécutable | Syntaxe, déclarations et types |
| Avertissement | Un problème probable ; la construction se poursuit généralement | L’expression indiquée dans le message |
| Erreur d’édition de liens | Le code objet existe, mais l’exécutable ne peut pas être achevé | Noms de fonctions non résolus ou bibliothèques nécessaires |
| Erreur d’exécution | Un problème apparaît pendant l’exécution | Opérations et valeurs rencontrées avec cette entrée |
| Erreur de logique | Le programme s’exécute mais donne un résultat non voulu | Conditions, formules et mises à jour |
Un avertissement peut signaler du code invalide ou un comportement indéfini aussi bien qu’une erreur ordinaire. Une compilation réussie ne garantit pas que le programme est correct.
Lire un diagnostic
Un message du compilateur indique normalement un emplacement dans le code source, un niveau de gravité et une description. Un message comme hello.c:5:16: error: expected ';' after expression signale un point-virgule manquant près de la ligne 5. Le compilateur remarque parfois le problème à la ligne suivante. Examine la ligne indiquée et celle qui la précède.
Corrige d’abord la première erreur, puis compile à nouveau. Un guillemet non fermé peut faire apparaître les lignes suivantes comme faisant partie d’une chaîne ; une accolade non appariée peut faire apparaître des déclarations ultérieures dans le mauvais bloc. Plusieurs messages peuvent disparaître après une seule correction. Ne commence pas par modifier toutes les lignes mentionnées dans la sortie.
Pour cet extrait :
int total = 6
printf("%d\n", total);
La déclaration doit comporter un point-virgule après 6. La ligne printf peut être l’endroit où le compilateur détecte pour la première fois que la déclaration précédente n’est pas terminée. Vérifie aussi l’orthographe exacte, l’appariement des parenthèses et des accolades, ainsi que l’en-tête qui déclare une fonction de bibliothèque. Un appel à printf nécessite <stdio.h> ; les appels de fonctions non déclarées ne sont pas autorisés en C99 et dans les versions ultérieures.
Prendre les avertissements au sérieux
Compile avec -Wall -Wextra. Ces options aident à détecter une affectation dans une condition, les variables inutilisées, les comparaisons entre valeurs signées et non signées et les formats de sortie incompatibles. Par exemple, printf("%d\n", 3.5); demande un entier mais reçoit un double ; le format ne convertit pas l’argument pour toi. Corrige le format en utilisant %f ou convertis volontairement la valeur. -Werror fait en sorte que les avertissements arrêtent la construction.
Comprendre le comportement indéfini
Un comportement indéfini signifie que le C n’impose aucune exigence sur le comportement du programme. Le débordement d’un entier signé, la division entière par zéro et certaines lectures de variables non initialisées peuvent le provoquer. Un programme peut s’arrêter, afficher une valeur inattendue ou sembler fonctionner. Les paramètres du compilateur peuvent modifier ce qui se produit.
Un message comme « Floating point exception » peut même résulter d’une division entière par zéro sur certains systèmes. Recherche la cause dans l’opération et la saisie, en ne te limitant pas à la formulation d’un message du système d’exploitation. Vérifie un diviseur avant d’effectuer la division.
Suivre l’origine d’un résultat incorrect
Pour une erreur de logique, prévois à la main le résultat d’un petit exemple et compare-le à la sortie réelle. Ajoute des appels temporaires à printf pour afficher un total cumulé ou la valeur testée par une condition. Modifie une cause à la fois et reconstruis le programme après chaque modification. Teste des valeurs ordinaires et des cas limites comme zéro, un, une entrée négative et la plus grande entrée attendue. Une trace d’exécution montre à quel endroit le programme s’écarte pour la première fois de ta prévision.
Points clés
- Corrige le premier diagnostic et reconstruis le programme avant d’examiner les messages suivants.
- Un avertissement peut signaler un comportement indéfini ; la compilation seule ne prouve pas que le programme est correct.
- Fais correspondre les conversions de format aux types des arguments.
- Pour un résultat incorrect, suis l’exécution avec une petite entrée et compare les valeurs intermédiaires.
- Teste les cas limites ainsi que les entrées ordinaires.
2Stocker des données dans des variables de types de base
Un type détermine comment une valeur stockée est interprétée et quelles valeurs il peut contenir. Commence par les déclarations, les valeurs initiales et les noms disponibles dans un bloc. Choisis ensuite les types entiers selon leur plage de valeurs et les types à virgule flottante selon leur précision et leur plage. Les codes de caractères et les valeurs booléennes trouvent aussi leur place dans cet ensemble. Lire les littéraux et vérifier les bornes te prépare à prévoir ce qu’un calcul peut représenter avant de l’écrire.
2.1 Variables, noms et valeurs fixes
Une variable donne un nom à une valeur stockée. Son type indique au C comment interpréter cet espace de stockage. Tu déclares le type avant d’utiliser la variable, et elle conserve ce type pendant toute sa durée de vie.
Donner une valeur initiale à chaque variable
int count = 0;
double length_m = 1.8;
count = count + 1;
Les deux premières lignes déclarent et initialisent des variables. La troisième affecte une nouvelle valeur à une variable existante : elle lit la valeur actuelle de count, ajoute un et stocke le résultat dans cette variable. Une affectation est une action : count = count + 1 a donc un sens, même si ce ne serait pas une équation mathématique.
L’initialisation a lieu dans une déclaration ; une affectation peut avoir lieu plus tard. Dans int a, b = 5;, seule b est initialisée. Une variable locale a a une valeur indéterminée jusqu’à ce que tu y stockes une valeur. La lire peut provoquer un comportement imprévisible, notamment un comportement indéfini. Initialise les compteurs et les totaux dès leur déclaration, comme dans int total = 0;.
Une affectation copie la valeur à cet instant :
int width = 4;
int area = width * 3;
width = 7;
printf("%d %d\n", width, area);
Ce code affiche 7 12. L’affectation à area a calculé 12 une seule fois. Elle n’a pas créé de formule qui se recalcule à chaque modification de width. Pour mettre area à jour, affecte-lui un nouveau calcul.
Choisir des noms lisibles
Un identificateur peut contenir des lettres, des chiffres et des tirets bas, mais il ne peut pas commencer par un chiffre ni être un mot-clé. height_cm et second_place conviennent ; 2nd_place ne convient pas et my-score est lu comme une soustraction. Les noms distinguent les majuscules des minuscules : total et Total sont donc différents. Des noms qui indiquent une signification ou une unité facilitent l’examen des formules.
Suivre la portée de bloc
Un nom local est disponible de sa déclaration jusqu’à la fin de son bloc. Depuis C99, les déclarations peuvent apparaître parmi d’autres instructions.
int count = 2;
{
int count = 9;
printf("%d\n", count);
}
printf("%d\n", count);
La déclaration interne masque le nom externe pendant l’exécution de ce bloc. La sortie est 9, puis 2, sur deux lignes distinctes. Il s’agit de deux variables, et non de deux affectations à une même variable. Déclarer deux fois le même nom dans un bloc est une erreur. Réutiliser des noms dans des blocs imbriqués est autorisé, mais rend le code plus difficile à suivre : préfère donc des noms qui rendent chaque rôle clair.
Nommer les valeurs fixes
const int SECONDS_PER_MINUTE = 60;
#define MAX_HEIGHT 8
const crée une variable typée à laquelle il est impossible d’affecter une nouvelle valeur par ce nom. Une instruction ultérieure SECONDS_PER_MINUTE = 90; doit donner lieu à un diagnostic. L’initialiseur donne la valeur fixe lors de la déclaration.
#define crée un remplacement par le préprocesseur : les occurrences ultérieures de MAX_HEIGHT sont remplacées par le lexème 8 avant la compilation. Cette directive ne contient ni = ni point-virgule. Un point-virgule ferait partie du remplacement et pourrait rendre invalide une expression comme height > MAX_HEIGHT.
Un nom comme SECONDS_PER_MINUTE indique au lecteur pourquoi 60 apparaît dans un calcul et te donne un seul endroit où gérer cette valeur. Les majuscules sont une convention de nommage, et non une règle qui rend une valeur constante. C’est la déclaration ou la directive qui produit ce comportement.
Points clés
- Initialise les variables locales avant de les lire.
- Une affectation copie une valeur une fois ; les valeurs qui en dépendent ne se mettent pas à jour toutes seules.
- Un bloc limite les endroits où un nom local peut être utilisé.
- Les noms distinguent les majuscules des minuscules et ne peuvent pas commencer par un chiffre.
constfournit une variable typée accessible en lecture seule ;#definefournit un remplacement de lexème.
2.2 Choisir les types entiers, caractères et booléens
Les types entiers stockent les nombres entiers exactement dans une plage fixe. Choisis un type selon les valeurs que le programme doit traiter, y compris les résultats intermédiaires, et selon que les valeurs négatives ont ou non un sens.
Tailles et plages de valeurs
La norme garantit des largeurs minimales : au moins 8 bits pour char, 16 pour short et int, 32 pour long et 64 pour long long. Chaque type de cette séquence est au moins aussi large que le précédent. Les tailles réelles dépendent de la plateforme.
| Type | Nombre d’octets habituel sous Linux/macOS 64 bits | Différence sous Windows 64 bits |
|---|---|---|
char | 1 | Aucune |
short | 2 | Aucune |
int | 4 | Aucune |
long | 8 | 4 octets |
long long | 8 | Aucune |
sizeof(int) donne la taille en octets sous forme d’un size_t ; affiche-la avec %zu. L’en-tête <limits.h> fournit des noms tels que INT_MIN, INT_MAX, UINT_MAX, LLONG_MAX et CHAR_BIT, le nombre de bits par octet.
Avec N bits de valeur, un entier non signé prend les valeurs de 0 à 2ᴺ − 1. Un entier signé sur N bits en complément à deux couvre les valeurs de −2ᴺ⁻¹ à 2ᴺ⁻¹ − 1. Ainsi, un int sur 32 bits va de −2 147 483 648 à 2 147 483 647, tandis qu’un unsigned int sur 32 bits atteint 4 294 967 295. C23 impose le complément à deux ; C17 autorisait aussi d’autres représentations.
Dans une configuration sur 8 bits en complément à deux, le bit de poids fort a le poids −128. 11111111 représente −1 et 10000000 représente −128. Pour former −5, pars de 00000101, inverse les bits pour obtenir 11111010, puis ajoute un pour obtenir 11111011.
Les caractères sont aussi des entiers
Un char stocke un code de caractère dans un octet. Utilise des apostrophes pour une constante caractère, comme dans 'A'. En ASCII, 'A' a le code 65 et '0' le code 48. printf("%c %d\n", 'A', 'A'); affiche A 65 en ASCII. Une constante caractère en C est de type int : sizeof('A') vaut donc sizeof(int).
Le type char seul peut être signé ou non signé selon l’implémentation. Utilise signed char ou unsigned char lorsque la plage numérique compte. <stdint.h> propose des types entiers de largeur fixe tels que int32_t et uint8_t sur les plateformes qui prennent en charge ces largeurs exactes ; les plateformes courantes le font.
Lire les littéraux entiers
Une constante décimale comme 42 est de type int si sa valeur est représentable dans ce type. Des suffixes permettent de demander d’autres types : 42u est de type unsigned, 42L de type long, 42LL de type long long et 42ULL de type unsigned long long. L’hexadécimal utilise 0x, comme dans 0x2A. Un zéro initial indique l’octal : 052 signifie aussi 42 et 010 signifie 8. 09 est un nombre octal invalide. C23 ajoute la notation binaire, comme 0b101010.
Valeurs de vérité et choix du type
En C17, inclus <stdbool.h> pour utiliser bool, true et false ; C23 en fait des mots-clés. Une conversion en bool donne faux pour zéro et vrai pour toute valeur non nulle, y compris 0.5. Affiche une valeur booléenne avec %d pour voir 0 ou 1.
Utilise int pour les dénombrements et les compteurs de boucle ordinaires. Utilise long long ou un type de largeur fixe adapté lorsque la plage peut dépasser environ 2,1 milliards. Les types non signés conviennent aux configurations de bits, mais une soustraction passant sous zéro reboucle vers une grande valeur : ils nécessitent donc eux aussi des vérifications des bornes.
Points clés
- Utilise les bornes des types au lieu de supposer une taille pour
long. - Un
intsigné sur 32 bits atteint 2 147 483 647. - Les valeurs non signées ne peuvent pas être négatives et rebouclent lorsqu’un calcul passe sous zéro.
- Un zéro initial marque le début d’un littéral entier octal.
- Le caractère signé ou non du type
charseul dépend de l’implémentation. - En C17, les noms des valeurs booléennes proviennent de
<stdbool.h>.
2.3 Valeurs décimales et précision limitée
Un type à virgule flottante stocke une approximation d’un nombre réel. Il possède une grande plage de valeurs, mais seulement un nombre fixe de chiffres significatifs. La plage et la précision répondent à des questions différentes : quelle peut être la grandeur d’une valeur et avec quelle finesse peut-on distinguer des valeurs proches ?
Choisir un type à virgule flottante
Sur les systèmes IEEE 754 courants, float occupe 4 octets et offre environ 6 à 7 chiffres décimaux significatifs. double occupe 8 octets et en offre environ 15 à 16. Leurs plus grandes valeurs finies sont d’environ 3,4 × 10³⁸ et 1,8 × 10³⁰⁸. long double est au moins aussi précis que double, avec un format qui dépend de la plateforme. double est un choix par défaut utile pour les grandeurs mesurées.
Les constantes comme 2.0, 3.14 et 1e-3 sont de type double ; 1e-3 signifie 0,001. Ajoute f pour obtenir un float, comme dans 2.5f, ou L pour un long double. La constante 2 est un entier : elle peut donc conduire à une division entière là où 2.0 produirait une division à virgule flottante.
Observer l’approximation stockée
De nombreuses fractions décimales n’ont pas de représentation binaire finie exacte. Sur un système courant en double précision :
printf("%.20f\n", 0.1);
printf("%.17f\n", 0.1 + 0.2);
Les sorties sont 0.10000000000000000555 et 0.30000000000000004. Les chiffres supplémentaires révèlent l’approximation stockée. Ils expliquent pourquoi 0.1 + 0.2 == 0.3 peut être faux.
Utilise une tolérance pour comparer des mesures calculées :
#include <math.h>
/* inside main */
if (fabs(a - b) < 1e-9) {
printf("Close enough\n");
}
fabs renvoie une différence absolue. Choisis la tolérance en fonction de l’ordre de grandeur et de la précision des valeurs ; 1e-9 est un exemple, pas un réglage universel. Certaines compilations sous Linux nécessitent -lm à la fin de la commande du compilateur pour effectuer l’édition de liens avec les fonctions mathématiques.
La précision influe sur les grands nombres entiers
Un double binaire représente exactement tous les entiers jusqu’à 2⁵³. Un float binaire cesse de représenter tous les entiers au-delà de 2²⁴ = 16 777 216 : affecter 16 777 217 à un tel float donne 16 777 216. Une plus grande plage ne signifie pas que chaque pas entier reste représentable.
L’affichage arrondit la valeur binaire stockée. printf("%.2f\n", 2.675); affiche généralement 2.67, car la valeur stockée est légèrement inférieure à 2,675. Pour des montants exacts en centimes entiers, utilise un entier comme long long cents = 1999;, puis mets l’affichage en forme à la fin.
Convertir et arrondir à bon escient
La conversion en entier supprime la partie fractionnaire vers zéro : (int)3.99 vaut 3 et (int)-3.99 vaut −3. round de <math.h> renvoie une valeur à virgule flottante arrondie à l’entier le plus proche, les valeurs à mi-chemin étant arrondies en s’éloignant de zéro : round(-2.5) donne −3,0. Ne convertis ce résultat en entier que s’il est représentable. Une conversion d’une valeur à virgule flottante en entier dont la partie entière est hors plage constitue un comportement indéfini.
En arithmétique IEEE, les résultats hors de la plage finie peuvent devenir infinis. La division d’une valeur à virgule flottante non nulle par zéro peut produire l’infini ; zéro divisé par zéro produit NaN, qui signifie « pas un nombre ». Une valeur NaN est considérée comme différente même d’elle-même. Il s’agit de comportements des nombres à virgule flottante IEEE ; la division entière par zéro a un comportement indéfini.
Points clés
- Les types à virgule flottante ont une précision et une plage de valeurs limitées.
- Compare les mesures calculées avec une tolérance adaptée.
- L’affichage arrondit l’approximation stockée.
- La conversion d’une valeur à virgule flottante en entier tronque vers zéro et exige un résultat représentable.
3Afficher des résultats et lire des données avec printf et scanf
Un programme doit échanger clairement du texte et des valeurs. Mets les sorties en forme pour qu’un lecteur puisse distinguer les intitulés, les champs et les mesures arrondies, puis fais correspondre chaque conversion d’entrée à la variable qui reçoit la valeur. La lecture de texte peut avoir plusieurs résultats : toutes les valeurs demandées, certaines seulement ou aucune conversion. Vérifier ces résultats et le texte non lu permet de fonder les calculs ultérieurs sur les valeurs que le programme a réellement obtenues.
3.1 Afficher des valeurs dans le format voulu
printf transforme des valeurs en texte à l’aide d’une chaîne de format. Les caractères ordinaires s’affichent tels qu’ils sont écrits. Une conversion qui commence par % indique comment afficher l’argument suivant.
Faire correspondre chaque conversion à son argument
printf("%d samples, %.2f kg\n", 4, 1.5);
Ce code affiche 4 samples, 1.50 kg suivi d’un saut de ligne. Les conversions correspondent aux arguments dans l’ordre. Un format ne convertit pas le type de l’argument fourni dans le type que tu avais en tête.
| Type de valeur ou affichage | Conversion |
|---|---|
int | %d ou %i |
unsigned int | %u |
long, long long | %ld, %lld |
size_t | %zu |
double, ou float promu | %f, %e, %g |
long double | %Lf |
| Caractère, littéral de chaîne de caractères | %c, %s |
| Hexadécimal non signé, octal | %x ou %X, %o |
Un argument float est promu en double dans printf, donc les deux utilisent %f ; %lf est équivalent ici. Passer 3 à %f est une incompatibilité de type, et non une demande d’afficher 3.0. Utilise 3.0 ou (double)3. Un type incompatible ou un argument manquant peut provoquer un comportement indéfini. Compile avec les avertissements activés pour que les littéraux de chaînes de format soient vérifiés.
Régler la largeur et la précision
Une largeur est un nombre minimal de caractères, signe et point décimal compris. Une précision à virgule flottante après le point détermine le nombre de décimales pour %f. Dans %8.2f, huit est la largeur minimale et deux est le nombre de chiffres après le point décimal. Un nombre plus large est affiché en entier ; la largeur ne le tronque pas.
Avec l’entier 42, %5d donne 42, %-5d donne 42 , %05d donne 00042 et %+d donne +42. Sans précision indiquée, %f affiche six décimales.
printf("%-6s%8s\n", "Item", "Price");
printf("%-6s%8.2f\n", "Tea", 2.5);
printf("%-6s%8.2f\n", "Cake", 12.75);
Chaque intitulé occupe un champ de six caractères aligné à gauche, suivi d’un champ de prix de huit caractères aligné à droite. Les deux prix ont deux décimales, de sorte que leurs points décimaux sont alignés. Les espaces proviennent des largeurs de champ, et non d’une séparation automatique entre les arguments.
Affichages scientifique et général
%e utilise la notation scientifique : 12 345,678 s’affiche sous la forme 1.234568e+04 avec la précision par défaut. Pour %g, la précision compte les chiffres significatifs, avec six par défaut. Il utilise la notation scientifique lorsque l’exposant décimal est inférieur à −4 ou supérieur ou égal à la précision, et supprime les zéros finaux. Ainsi, %g affiche 0,0001 sous la forme 0.0001, 0,00001 sous la forme 1e-05 et 1 000 000,0 sous la forme 1e+06. Il ne choisit pas simplement l’écriture la plus courte possible.
Afficher des caractères spéciaux
Dans une chaîne C, \n signifie un saut de ligne, \t une tabulation, \\ une barre oblique inverse et \" un guillemet. Un signe pour cent dans un format s’écrit %% : printf("100%%\n"); affiche 100% et un saut de ligne.
printf n’ajoute pas lui-même d’espaces ni de sauts de ligne. printf("Count:"); printf("%d", 4); affiche Count:4. Un terminal peut retarder l’affichage d’un texte sans saut de ligne ; fflush(stdout); envoie une invite en attente avant que la saisie soit demandée.
Points clés
- Fais correspondre les conversions aux types réels des arguments.
- La largeur de champ est un minimum ; la précision de
%fcompte les décimales. - La précision de
%gcompte les chiffres significatifs. - Écris
%%pour obtenir un signe pour cent littéral dans un format. printfn’ajoute aucun espace ni saut de ligne si le format ne le prévoit pas.
3.2 Lire les données saisies et vérifier le résultat
scanf lit du texte, le convertit et stocke des valeurs dans des variables. Vérifie son résultat avant d’utiliser ces variables. Un texte qui commence par des chiffres peut n’être accepté qu’en partie : une conversion réussie et une ligne de saisie entièrement valide sont donc deux choses différentes.
Fournir une destination du bon type
int count;
double mass_kg;
if (scanf("%d %lf", &count, &mass_kg) != 2) {
return 1;
}
Le & transmet l’adresse de chaque variable afin que scanf puisse y stocker une valeur. %d demande un int, %u un entier non signé, %ld un long et %lld un long long. Pour une saisie à virgule flottante, %f demande un float et %lf un double. Contrairement à ce qui se passe avec printf, ces deux conversions ne sont pas interchangeables. Un & manquant ou un type de destination incorrect peut provoquer un comportement indéfini.
%d lit en décimal. %i reconnaît aussi les saisies hexadécimales et octales à zéro initial ; avec %i, le texte « 010 » signifie 8. Utilise %d pour les dénombrements décimaux ordinaires.
Vérifier la valeur de retour
scanf renvoie le nombre d’affectations réussies. Pour deux valeurs demandées, une lecture réussie donne 2. Une conversion échouée laisse sa destination inchangée. Celle-ci peut encore être non initialisée : ne l’utilise donc que si le nombre renvoyé confirme la réussite. EOF signifie que l’entrée s’est terminée ou qu’une erreur d’entrée s’est produite avant la fin de la première conversion.
Pour scanf("%d:%d", &h, &m), les deux-points doivent apparaître tels quels. La saisie « 14:30 » remplit les deux variables. La saisie « 14 30 » remplit h, puis ne trouve pas les deux-points attendus et laisse m inchangé. La valeur de retour est 1.
Comprendre les caractères blancs et les saisies partielles
Les conversions numériques ignorent les espaces, tabulations et sauts de ligne initiaux. %c lit le caractère immédiatement suivant, y compris un saut de ligne restant. Utilise scanf(" %c", &choice) pour ignorer les caractères blancs avant de lire un caractère visible. Évite un saut de ligne final dans le format : il peut faire attendre l’appel jusqu’à l’arrivée d’un caractère qui n’est pas un caractère blanc.
Avec %d, « 12abc » stocke 12 et laisse « abc » non lu. « 3.7 » stocke 3 et laisse « .7 ». « abc » ne stocke rien et renvoie 0. Répéter la même conversion sans consommer les données invalides peut échouer indéfiniment sur le même caractère.
Reprendre après une conversion échouée
int n, result;
while ((result = scanf("%d", &n)) != 1) {
if (result == EOF) {
return 1;
}
int ch;
while ((ch = getchar()) != '\n' && ch != EOF) {
}
printf("Try again: ");
fflush(stdout);
}
getchar renvoie un int, ce qui lui permet de représenter à la fois un caractère et EOF. La boucle interne élimine la ligne dont la conversion a échoué. La boucle externe réessaie, mais accepte toujours un préfixe numérique tel que 12 dans « 12abc ». Exiger que le caractère suivant soit '\n' permet de vérifier la règle plus stricte d’un entier immédiatement suivi d’un saut de ligne ; cette règle rejette aussi les espaces finaux. Une règle qui autorise les espaces ou une dernière ligne se terminant à la fin de fichier doit traiter ces cas explicitement.
Après une conversion réussie, vérifie la plage autorisée, par exemple de 1 à 8 pour un réglage de hauteur. scanf ne rejette pas de manière sûre une valeur numérique hors plage : une valeur trop grande pour sa destination provoque un comportement indéfini. Utilise des entrées de test représentables dans le type choisi. Pour calculer un résultat tel que la masse divisée par la hauteur au carré, valide des mesures positives et garde le carré groupé sous la forme height_m * height_m.
Points clés
- Transmets les adresses des variables et utilise
%lfpour lire undouble. - Vérifie que le nombre renvoyé correspond au nombre de valeurs nécessaires.
- Une conversion réussie peut laisser des données supplémentaires non lues.
- Consomme les données invalides avant de réessayer une conversion numérique échouée.
- Vérifie les valeurs autorisées après la lecture ; la conversion d’entrée a ses propres limites liées à la plage du type.
- Utilise
" %c"lorsqu’une lecture de caractère doit ignorer les caractères blancs.
4Calculer avec des opérateurs et des expressions
Une expression combine des valeurs, des types et des opérateurs. Suis d’abord la division entière et les mises à jour, puis les conversions qui se produisent à chaque opération. Les comparaisons et les conditions logiques de protection déterminent si un calcul est atteint, tandis que les opérations bit à bit agissent sur les bits individuellement. Enfin, distingue le regroupement fixé par la priorité du séquencement nécessaire lorsqu’une expression modifie une variable. La valeur et le type d’un résultat intermédiaire comptent tous deux.
4.1 Calculs, affectations et mises à jour d’une unité
Toute expression C a une valeur et un type. Le type détermine le fonctionnement d’une opération. La division entière, par exemple, ne peut pas conserver un résultat fractionnaire, même si tu le stockes ensuite dans une variable à virgule flottante.
Quotients et restes
+, - et * additionnent, soustraient et multiplient. / divise et % donne un reste entier. La multiplication, la division et le reste sont prioritaires sur l’addition et la soustraction.
Lorsque les deux opérandes d’une division sont des entiers, le C tronque le quotient vers zéro. 17 / 5 vaut 3, -7 / 2 vaut −3 et 7 / -3 vaut −2. Le reste satisfait l’identité de la division : multiplier le quotient par le diviseur et ajouter le reste donne le dividende. Ainsi, -7 % 2 vaut −1 et 7 % -3 vaut 1. Un reste non nul a le signe du dividende.
Pour vérifier si un nombre est pair, utilise n % 2 == 0. Pour vérifier s’il est impair, qu’il soit positif ou négatif, utilise n % 2 != 0 ; tester si le reste vaut exactement 1 ne repère pas les entiers impairs négatifs. % exige des opérandes entiers ; <math.h> fournit fmod pour les restes à virgule flottante. Un diviseur entier nul rend / et % indéfinis : vérifie-le donc avant l’opération.
Pour 135 minutes, 135 / 60 donne deux heures entières et 135 % 60 donne les 15 minutes restantes. Pour un entier décimal positif, % 10 extrait son dernier chiffre et / 10 le supprime : 4096 donne 6 et 409.
Conserver les fractions lorsque c’est nécessaire
int correct = 45, total = 60;
double percent = 100.0 * correct / total;
La constante à virgule flottante rend la multiplication à virgule flottante, et la division donne 75,0. Dans correct / total * 100, la première opération est une division entière et donne zéro. Multiplier ce zéro ne peut pas récupérer la fraction perdue. Effectuer d’abord une multiplication entière, avec correct * 100 / total, donne ici 75, à condition que le produit soit représentable.
Stocker une nouvelle valeur
Une affectation évalue sa partie droite et stocke la valeur convertie à gauche. L’expression d’affectation a cette valeur stockée : a = b = 0; affecte donc zéro aux deux variables, avec un regroupement à partir de la droite.
Les affectations composées combinent un calcul et un stockage : x += 5 ajoute cinq à x, et x /= 2 divise x par deux en utilisant son type actuel. Tout l’opérande de droite fait partie du calcul. Si x vaut 4, x *= 2 + 1 lui donne la valeur 12, car cette expression multiplie par trois.
Mises à jour préfixées et postfixées
++ ajoute un et -- soustrait un. Seules sur une ligne, i++; et ++i; ont le même effet final. Dans une expression plus grande, la forme postfixée fournit l’ancienne valeur ; la forme préfixée fournit la nouvelle.
| Départ | Instruction | Valeurs finales |
|---|---|---|
a = 5 | y = a++; | y = 5, a = 6 |
a = 5 | y = ++a; | y = 6, a = 6 |
Veille à ce que l’ordre des modifications soit facile à déterminer. i = i++ + 1; et printf("%d %d\n", n, n++); comportent des utilisations conflictuelles non séquencées de la même variable et ont un comportement indéfini. Sépare la lecture, la mise à jour et l’affichage dans des instructions distinctes lorsque leur ordre compte.
Points clés
- La division entière tronque vers zéro.
- Un reste entier non nul a le signe du dividende.
- Utilise un opérande à virgule flottante avant la division si tu as besoin d’une fraction.
- La forme postfixée fournit l’ancienne valeur ; la forme préfixée fournit la valeur mise à jour.
- Place les lectures et les mises à jour conflictuelles dans des instructions distinctes.
4.2 Les types sont déterminés avant le stockage d’un résultat
Le C convertit les valeurs à mesure que chaque opérateur est appliqué. La variable de destination reçoit le résultat final ; elle ne modifie pas rétroactivement les opérations précédentes de l’expression.
Convertir avant de diviser
int sum = 7, count = 2;
double a = sum / count;
double b = (double)sum / count;
double c = (double)(sum / count);
a devient 3,0 : les deux opérandes entiers produisent l’entier 3, qui est ensuite converti. b devient 3,5 : la conversion explicite convertit sum avant la division, donc count est également converti en double. c devient 3,0 : sa conversion explicite se trouve en dehors de la division et intervient après la suppression de la fraction.
La même règle s’applique dans une expression plus longue. 7 / 2 * 2.0 donne 6,0, car le premier résultat est l’entier 3. 7 / 2.0 * 2 donne 7,0. Parcours les opérateurs dans l’ordre de leur regroupement en indiquant le type de chaque valeur intermédiaire.
Trouver un type arithmétique commun
Si l’un des opérandes est à virgule flottante, le calcul utilise généralement le type à virgule flottante le plus large présent : long double, puis double, puis float. Pour les entiers, les petits types subissent d’abord une promotion entière. Sur les plateformes où char occupe 8 bits, short 16 bits et int 32 bits, toute valeur des types plus petits est représentable dans int ; ces types sont donc promus en int.
Avec unsigned char a = 200, b = 100;, a + b est l’entier 300 sur un système où les octets comptent 8 bits et les int 32 bits. Il ne reboucle pas à 255 pendant l’addition. Stocker 300 dans un caractère non signé sur 8 bits le réduit à 44. La promotion peut aussi produire un débordement signé : deux valeurs unsigned short de 50 000 sont multipliées comme des int, mais 2 500 000 000 dépasse INT_MAX sur 32 bits.
Pour les types signés et non signés, le rang et la plage de valeurs représentables déterminent le type commun. int avec unsigned int donne une conversion en type non signé. Un type signé de rang supérieur peut conserver une opération signée s’il représente toutes les valeurs du type non signé ; sinon, le calcul peut tout de même être non signé.
Le piège du mélange entre types signés et non signés
Avec un entier non signé sur 32 bits, -1 < 0u est faux. La valeur signée −1 est convertie en valeur non signée 4 294 967 295 avant la comparaison. De même, 2u - 10 donne 4 294 967 288. Un résultat de sizeof est lui aussi non signé, ce qui compte lorsqu’on le compare à un dénombrement potentiellement négatif. Active les avertissements et garde les grandeurs liées dans des types compatibles.
Les conversions lors du stockage peuvent faire perdre des informations
Une conversion d’une valeur à virgule flottante en entier tronque vers zéro. Sa partie entière doit être représentable, sinon le comportement est indéfini. Un entier converti en type non signé sur N bits est réduit modulo 2ᴺ ; −1 devient la valeur non signée maximale. En C17, la conversion d’un entier hors plage en type signé donne un résultat défini par l’implémentation ou déclenche un signal défini par l’implémentation. Cette conversion est différente du débordement indéfini d’une opération arithmétique signée.
Les constantes caractères participent aussi aux calculs. '7' - '0' donne 7 car le C garantit des codes consécutifs pour les chiffres décimaux. En ASCII, les lettres sont également consécutives, mais ne suppose pas que cette propriété vaut pour tous les jeux de caractères. Dans les formules entières, multiplier avant de diviser ne peut conserver des informations utiles que si ce produit est représentable dans son type de calcul.
Points clés
- Le type de destination ne change pas les calculs précédents.
- Convertis explicitement un opérande avant l’opération qui nécessite un type plus large ou à virgule flottante.
- Les petits entiers sont promus avant les calculs.
- Le mélange d’opérandes signés et non signés peut modifier les comparaisons et les soustractions.
- La conversion hors plage et le débordement d’une opération arithmétique signée suivent des règles différentes.
- Suis les types intermédiaires ainsi que les valeurs finales.
4.3 Conditions et configurations de bits
Une condition demande si une affirmation est vraie. Une opération bit à bit agit sur les bits individuels d’un entier. Le C utilise des opérateurs différents pour ces tâches, même lorsque leurs symboles se ressemblent.
Comparer des valeurs et combiner des tests
Les comparaisons <, >, <=, >=, == et != produisent un int : 1 pour vrai et 0 pour faux. Dans une condition, zéro est faux et toute valeur non nulle est vraie. Ainsi, if (count) teste si count est non nul. == compare deux valeurs ; = en affecte une nouvelle.
&& signifie que les deux conditions doivent être vraies, || qu’au moins l’une doit être vraie, et ! inverse la valeur de vérité. Ils renvoient 0 ou 1. Un intervalle de 1 à 8 demande deux comparaisons :
height >= 1 && height <= 8
1 <= height <= 8 n’exprime pas cet intervalle en C. L’expression est regroupée sous la forme (1 <= height) <= 8 : la seconde comparaison reçoit donc 0 ou 1 et est toujours vraie. Pour tester si la valeur est hors de l’intervalle, écris height < 1 || height > 8.
La négation d’une condition composée modifie les deux parties : !(a && b) équivaut à !a || !b, et !(a || b) à !a && !b. Ce sont les lois de De Morgan. Elles expliquent pourquoi un test à l’extérieur d’un intervalle demande un « ou », alors qu’un test à l’intérieur demande un « et ».
Placer une condition de protection avant l’opération risquée
if (d != 0 && n / d > 2) {
printf("Above two\n");
}
&& évalue d’abord sa partie gauche. Si elle est fausse, il ignore la partie droite. || ignore sa partie droite lorsque la partie gauche est vraie. Cette évaluation en court-circuit permet à une condition de protection de protéger une opération ultérieure. Inverser les deux parties ci-dessus effectuerait la division avant de vérifier le diviseur. Pour les entiers signés, un problème de plage distinct existe lorsque INT_MIN est divisé par −1 sur un système en complément à deux ; protège aussi ce cas lorsque des valeurs quelconques sont possibles.
Un test d’année bissextile combine divisibilité et exceptions : (y % 4 == 0 && y % 100 != 0) || y % 400 == 0. L’année 2000 satisfait le test de divisibilité par 400 ; 1900 échoue car l’exception concernant les siècles s’applique.
Manipuler un bit à la fois
L’opérateur bit à bit & conserve les bits activés dans les deux opérandes ; | conserve les bits activés dans l’un ou l’autre ; ^ conserve les bits qui diffèrent ; ~ inverse les bits. Avec 12 (1100) et 10 (1010), les résultats sont 8, 14 et 6 pour &, | et ^.
Les décalages déplacent les bits. Pour des valeurs positives ou nulles dont le résultat est représentable, x << k multiplie par 2ᵏ, et x >> k supprime k bits de poids faible, ce qui divise par 2ᵏ en supprimant le reste. Utilise des types non signés pour manipuler des bits. En C17, un décalage d’un nombre négatif de bits ou d’un nombre supérieur ou égal à la largeur du type promu est indéfini ; décaler une valeur signée négative vers la gauche est indéfini et la décaler vers la droite donne un résultat défini par l’implémentation.
Les rangs des bits commencent à zéro. flags | (1u << 3) active le bit 3 ; flags & ~(1u << 3) le désactive ; (flags >> 3) & 1u le lit. 1 & 2 vaut 0 car aucun bit actif n’est commun, tandis que 1 && 2 vaut 1 car les deux valeurs sont non nulles. Les opérateurs bit à bit évaluent les deux opérandes et ne peuvent pas remplacer les conditions logiques de protection.
Points clés
- Les comparaisons et les opérateurs logiques produisent 0 ou 1.
- Un test d’appartenance à un intervalle relie deux comparaisons avec
&&. - Les conditions de protection en court-circuit doivent précéder l’opération protégée.
- Utilise les opérateurs logiques pour les valeurs de vérité et les opérateurs bit à bit avec des opérandes non signés pour les masques.
- Les rangs des bits commencent à zéro ; les nombres de bits à décaler doivent être inférieurs à la largeur du type promu.
4.4 Regrouper les expressions et ordonner les effets
Deux questions t’aident à lire une expression : quels opérandes appartiennent à chaque opérateur, et quand les opérations ayant des effets de bord ont-elles lieu ? La priorité et l’associativité répondent à la question du regroupement. Elles ne fixent généralement pas l’ordre d’évaluation des opérandes.
Suivre la priorité
Les opérateurs situés plus haut dans ce tableau sont plus prioritaires. Chaque ligne est moins prioritaire que la précédente ; les opérateurs séparés par « puis » ont des niveaux distincts.
| Par ordre décroissant de priorité | Opérateurs |
|---|---|
| Postfixés | x++, x--, appels |
| Unaires | ++x, --x, +, -, !, ~, conversions explicites, sizeof, adressage & |
| Multiplicatifs | *, /, % |
| Additifs | +, - |
| Décalages | <<, >> |
| Relationnels | <, <=, >, >= |
| Égalité | ==, != |
| Bit à bit | &, puis ^, puis ` |
| Logiques | &&, puis ` |
| Conditionnel | ?: |
| Affectation | =, += et autres affectations composées |
| Virgule | , |
Les parenthèses modifient le regroupement. 2 + 3 * 4 vaut 14 ; (2 + 3) * 4 vaut 20.
Utiliser l’associativité à priorité égale
La plupart des opérateurs arithmétiques binaires sont regroupés de gauche à droite. 10 - 4 - 3 signifie (10 - 4) - 3, ce qui vaut 3. 100 / 10 / 2 donne 5. Les affectations sont regroupées de droite à gauche : a = b = 0 stocke donc zéro dans les deux variables. Le regroupement décrit la structure de l’expression ; il n’établit pas de règle générale d’exécution de gauche à droite.
Pour int a = 3, b = 4, c = 5;, suis l’évaluation de a + b * c > 20 && c % 2 == 1. La multiplication donne 20, l’addition donne 23 et 23 > 20 donne 1. Le reste vaut 1, et son test d’égalité donne 1. Enfin, 1 && 1 vaut 1.
Protéger les tests de bits et les affectations avec des parenthèses
x & 1 == 0 signifie x & (1 == 0) et produit donc toujours zéro. Écris (x & 1) == 0 pour tester le bit de poids faible. L’addition est aussi prioritaire sur le décalage : 1 << 2 + 1 vaut 1 << 3, soit 8.
L’affectation est moins prioritaire que la comparaison. a = b == c stocke le résultat de la comparaison, 0 ou 1, dans a. Dans while ((ch = getchar()) != '\n'), les parenthèses internes font que l’affectation reçoit d’abord le caractère. Sans elles, ch recevrait un résultat de comparaison.
Choisir une valeur ou séquencer deux actions
condition ? first : second évalue la condition, puis uniquement l’expression de valeur sélectionnée. max = (a > b) ? a : b; choisit une valeur ; un if peut choisir un bloc d’actions. L’opérateur virgule évalue d’abord son expression de gauche et fournit la valeur de son expression de droite. Il peut coordonner deux mises à jour dans un en-tête de boucle for. Les virgules entre les arguments de fonctions sont des séparateurs et ne fixent pas cet ordre.
Séparer les effets de bord
Le C laisse non spécifié l’ordre de nombreux opérandes et arguments. n++ * n++ comporte des modifications non séquencées de la même variable et présente donc un comportement indéfini. Des parenthèses supplémentaires ne corrigent pas l’absence de séquencement. Utilise des instructions distinctes lorsqu’une action doit en précéder une autre. &&, ||, ?: et l’opérateur virgule fournissent des règles de séquencement précises ; la priorité des opérateurs arithmétiques ordinaires n’offre aucune garantie de ce type.
Points clés
- La priorité détermine le regroupement ; elle ne détermine généralement pas l’ordre d’évaluation.
- À un même niveau arithmétique, le regroupement se fait généralement de gauche à droite.
- Mets entre parenthèses le résultat d’une opération bit à bit avant de le comparer.
- L’opérateur conditionnel n’évalue que l’expression de valeur sélectionnée.
- L’opérateur virgule séquence les opérandes ; les virgules qui séparent les arguments ne le font pas.
- Sépare les mises à jour conflictuelles pour éviter un comportement indéfini.
5Contrôler le déroulement d’un programme avec des conditions et des boucles
Le contrôle du déroulement détermine quelles instructions s’exécutent et combien de fois. Utilise des conditions pour sélectionner une branche, des cas entiers exacts pour sélectionner une entrée dans une instruction switch et des boucles pour répéter une tâche jusqu’à ce que sa condition d’arrêt soit satisfaite. Les traces d’exécution rendent l’ordre visible : valeurs initiales, tests, instructions du corps et mises à jour. Prête une attention particulière aux entrées limites et à la destination de break ou de continue dans les constructions imbriquées, où une petite modification de syntaxe peut changer tout le chemin suivi.
5.1 Choisir la bonne branche
Une instruction if choisit si une action doit être exécutée. Sa condition doit être entre parenthèses. Zéro signifie faux ; toute valeur non nulle signifie vrai.
Utiliser des accolades pour montrer la branche
if (temperature < 0) {
printf("Freezing\n");
} else {
printf("At or above freezing\n");
}
Le premier bloc s’exécute pour une température négative. À zéro exactement, la comparaison est fausse et le bloc else s’exécute. Lis la borne directement à partir de <, <=, > ou >= ; le message doit correspondre à cette borne.
Sans accolades, seule l’instruction suivante est contrôlée :
if (temperature > 30)
printf("Hot\n");
printf("Drink water\n");
Le second message s’affiche quelle que soit la température. L’indentation aide les lecteurs, mais ne change pas la grammaire. Les accolades permettent d’ajouter une seconde instruction à une branche sans la laisser accidentellement à l’extérieur.
Ordonner une chaîne de conditions avec else if
Une chaîne if-else if-else exécute la première branche dont la condition est vraie, puis ignore les autres. Pour des tranches de scores, commence par la borne inférieure la plus élevée :
if (score >= 90) {
grade = 'A';
} else if (score >= 80) {
grade = 'B';
} else if (score >= 70) {
grade = 'C';
} else {
grade = 'F';
}
Un score de 85 échoue au premier test et satisfait le deuxième. Un score de 90 satisfait le premier. Si tu testais score >= 70 en premier, 85 entrerait dans cette branche et n’atteindrait jamais la branche B voulue. Des instructions if distinctes se comportent différemment : chaque condition est testée indépendamment, si bien que plusieurs blocs peuvent s’exécuter.
Repérer deux petites erreurs de syntaxe
if (x = 5) affecte 5, puis considère cette valeur non nulle comme vraie. Utilise if (x == 5) pour comparer. Avec if (x = 0), l’affectation a toujours lieu mais la condition est fausse. Les avertissements aident à repérer les affectations accidentelles dans les conditions.
Dans if (x > 0);, le point-virgule est une instruction contrôlée vide. Un bloc placé après s’exécute indépendamment, même lorsque x est négatif. N’ajoute pas de point-virgule entre une condition if et le bloc qu’elle doit contrôler.
Associer explicitement chaque else
Un else se rattache au if précédent le plus proche qui n’a pas déjà de else. Dans des choix imbriqués, utilise des accolades pour rendre l’association voulue claire. L’imbrication est utile lorsque les tests échoués nécessitent des messages différents : vérifie d’abord si une mesure est valide, puis si sa valeur se situe dans la tranche voulue. Combiner les deux avec && fonctionne lorsqu’une seule action commune est nécessaire.
Placer les exceptions avant les règles générales
Les années bissextiles fournissent un exemple utile de choix ordonné. Teste d’abord la divisibilité par 400, qui indique une année bissextile. Teste ensuite la divisibilité par 100, qui indique une année non bissextile. Teste ensuite la divisibilité par 4 ; sinon, l’année n’est pas bissextile. Pour 2000, la première branche s’applique. Pour 1900, la deuxième s’applique. Vérifier uniquement la divisibilité par quatre ne tiendrait pas compte de l’exception concernant les siècles. L’ordre compte, car les règles générales peuvent inclure des cas soumis à une exception plus précise.
Points clés
- Une chaîne de conditions avec else if n’exécute que sa première branche vraie.
- Les accolades déterminent quelles instructions appartiennent à une branche.
==compare ;=affecte une valeur.- Un point-virgule après une condition
ifcrée une branche vide.
5.2 Sélectionner des cas fixes avec switch
Une instruction switch choisit un point de départ à l’aide d’une valeur entière. Elle est utile pour un caractère de commande ou un numéro de menu offrant plusieurs choix fixes. L’exécution peut ensuite se poursuivre dans les cas suivants si tu ne l’arrêtes pas.
Sélectionner un cas et le quitter
switch (choice) {
case 1:
printf("Start\n");
break;
case 2:
printf("Pause\n");
break;
default:
printf("Unknown choice\n");
break;
}
Lorsque choice vaut 2, le programme commence à case 2, affiche Pause et quitte l’instruction switch à break. Si aucun cas ne correspond, l’exécution commence à default. Sans étiquette default, une valeur sans correspondance fait sauter l’instruction switch.
L’expression de contrôle doit être de type entier, notamment char ou une énumération. Chaque cas doit être une expression constante entière distincte, comme 3, 'q' ou un nom approprié défini avec #define. En C17, une variable const int n’est pas une expression constante entière utilisable comme étiquette de cas. Les valeurs à virgule flottante et les chaînes ne peuvent pas servir de valeurs de contrôle pour switch.
Comprendre l’enchaînement des cas
Les étiquettes de cas marquent des points d’entrée, et non des blocs distincts qui se terminent automatiquement. Supprime le premier break ci-dessus et le choix 1 affiche à la fois Start et Pause. Le programme ne teste pas si choice vaut 2 avant de continuer dans ce cas. L’exécution ne s’arrête qu’à un transfert de contrôle ou à la fin de l’instruction switch.
Plusieurs étiquettes peuvent partager une même action :
case 'y':
case 'Y':
printf("Yes\n");
break;
Les deux caractères déclenchent la même action. Lorsqu’un cas effectue volontairement une opération puis se poursuit dans un autre, explique l’intention par un commentaire. C23 propose aussi [[fallthrough]];. Les avertissements peuvent aider à trouver un break manquant, mais regrouper volontairement des étiquettes ne demande pas de répéter l’action.
Placer les variables locales d’un cas dans un bloc
En C17, une déclaration ne peut pas suivre directement une étiquette de cas. Entoure les instructions de ce cas d’accolades :
case 3: {
int delay = 5;
printf("%d\n", delay);
break;
}
Le bloc limite aussi la portée de delay à ce cas. C23 assouplit la restriction sur les déclarations après une étiquette, mais les accolades rendent toujours la portée claire.
Choisir entre switch et if
Utilise switch pour des choix entiers exacts. Utilise if-else if pour les inégalités, les conditions portant sur plusieurs variables ou les mesures à virgule flottante. Des tranches de scores de même largeur peuvent parfois être converties en cas exacts par division entière. Pour des scores de 0 à 100, score / 10 donne 8 pour 85 ; les cas 9 et 10 peuvent partager la tranche supérieure. Vérifie que le score appartient à ce domaine afin que les scores négatifs ou trop élevés ne soient pas silencieusement affectés à une tranche.
Une instruction switch dans une boucle
Un break quitte l’instruction switch ou la boucle englobante la plus proche. Dans une instruction switch elle-même située dans une boucle, break ne termine que l’instruction switch. Pour terminer la boucle du menu, modifie un indicateur vérifié par sa condition ou quitte main lorsque le programme doit se terminer. Un continue dans cette instruction switch s’applique à la boucle englobante et la fait passer à son itération suivante. Suis la construction visée par chaque transfert avant de déterminer si le menu s’arrête.
Points clés
- Une instruction switch sélectionne une étiquette d’entrée à l’aide d’une valeur entière.
- L’exécution se poursuit d’un cas au suivant jusqu’à
break, un autre transfert de contrôle ou la fin. - Les valeurs des cas doivent être des expressions constantes entières distinctes.
- En C17, utilise un bloc pour une déclaration locale à un cas.
- Le
breakd’une instruction switch ne termine pas aussi une boucle englobante.
5.3 Répéter jusqu’à ce que les données indiquent l’arrêt
Une boucle while teste une condition avant chaque passage. Une boucle do-while teste la condition après le corps : elle s’exécute donc au moins une fois. Choisis la forme qui place le test là où le programme en a besoin.
Suivre un calcul contrôlé par une condition
int n = 4096, sum = 0;
while (n > 0) {
sum += n % 10;
n /= 10;
}
n % 10 extrait un chiffre et n /= 10 le supprime. Les valeurs après chaque passage sont les suivantes :
| Passage | sum | n |
|---|---|---|
| 1 | 6 | 409 |
| 2 | 15 | 40 |
| 3 | 15 | 4 |
| 4 | 19 | 0 |
La condition suivante est fausse : la boucle se termine donc avec une somme des chiffres égale à 19. Si n vaut zéro au départ, le corps ne s’exécute aucune fois et la somme reste nulle. Les entrées négatives sautent également cette boucle : un programme qui les accepte doit donc définir comment les traiter.
Une boucle de comptage initialise un compteur, le teste et le met à jour. D’autres boucles progressent en lisant des données ou en atteignant break. En divisant 100 plusieurs fois par deux par division entière, les valeurs sont 50, 25, 12, 6, 3 et 1 : six passages sont nécessaires pour atteindre un.
Laisser la saisie contrôler la répétition
int x, sum = 0, count = 0;
while (scanf("%d", &x) == 1 && x != 0) {
sum += x;
count++;
}
if (count != 0) {
printf("%.2f\n", (double)sum / count);
}
Pour la saisie « 5 8 3 0 », la sentinelle zéro termine la boucle et n’est pas ajoutée. La somme vaut 16, le nombre de valeurs est 3 et la moyenne affichée est 5.33. Le test du nombre renvoyé protège l’utilisation de x ; le test du nombre de valeurs protège la division si la première valeur est zéro. Ce calcul exige aussi des entrées et des totaux représentables dans int.
Sans le test de la sentinelle, while (scanf("%d", &x) == 1) s’arrête en cas d’échec de conversion numérique ou de fin de l’entrée. Dans un terminal, Ctrl+D signale normalement la fin de l’entrée sous Linux et macOS ; sous Windows, utilise Ctrl+Z suivi d’Entrée.
Exécuter une fois avant de tester
int height;
do {
printf("Height (1-8): ");
fflush(stdout);
if (scanf("%d", &height) != 1) {
return 1;
}
} while (height < 1 || height > 8);
L’invite et la lecture ont lieu avant le test de plage. Les valeurs zéro et neuf font recommencer la boucle ; les valeurs un et huit sont acceptées. Le point-virgule après while (...) est obligatoire dans cette forme. Dans cet exemple, le contrôle de conversion met fin au programme si l’entrée n’est pas numérique ; le seul test de plage ne peut pas traiter une conversion échouée.
Progresser sur chaque chemin qui poursuit la boucle
break quitte immédiatement la boucle la plus interne. continue saute le reste du corps et revient à la condition dans une boucle while. Si une mise à jour se trouve après un continue, elle est également sautée. Une boucle peut alors continuer indéfiniment à tester la même valeur.
Avant d’exécuter une boucle, détermine ce qui change et pourquoi la condition d’arrêt finira par être atteinte. Une boucle while (1) voulue peut s’arrêter grâce à break ; une mise à jour de compteur oubliée est une autre situation. Ctrl+C arrête un programme bloqué dans un terminal. Teste la plus petite entrée et la valeur à la borne d’arrêt pour détecter les erreurs où la boucle ne s’exécute pas ou effectue un passage de trop.
Points clés
whileteste d’abord la condition ;do-whilela teste après au moins un passage.- Une sentinelle arrête la saisie sans faire partie des données.
- Initialise les accumulateurs en dehors de la boucle.
- Tout chemin qui poursuit la boucle doit progresser vers la condition d’arrêt.
- Dans une boucle
while, continue peut sauter une mise à jour située plus loin. - Vérifie que le nombre de données n’est pas nul avant de calculer une moyenne.
5.4 Compter les passages et combiner des boucles
Une boucle for rassemble les éléments d’un comptage dans un seul en-tête. Les boucles imbriquées répètent une tâche interne à chaque passage de la boucle externe. Elles sont plus faciles à vérifier lorsque tu listes les valeurs réelles du compteur et repères chaque sortie.
Suivre l’en-tête dans l’ordre
for (int i = 0; i < 3; i++) {
printf("%d ", i);
}
L’initialisation s’exécute une fois. La condition est ensuite testée, le corps s’exécute si elle est vraie, puis la mise à jour s’exécute avant le test suivant. La sortie est 0 1 2 . La variable déclarée dans l’en-tête est locale à la boucle ; déclare-la avant la boucle si tu as besoin de sa valeur finale ensuite.
Pour n positif ou nul, i = 0; i < n; i++ s’exécute n fois et parcourt les valeurs de zéro à n − 1. Avec i <= n, elle s’exécute une fois de plus. Pour un pas positif s allant de a à b inclus, le nombre de passages est (b - a) / s + 1 en division entière, à condition que les calculs et les mises à jour du compteur soient représentables. De 2 à 20 par pas de 3, les valeurs sont 2, 5, 8, 11, 14, 17, 20 : sept passages.
Garder l’accumulateur à l’extérieur
int total = 0;
for (int i = 1; i <= 100; i++) {
total += i;
}
Le total final vaut 5050. L’initialisation doit avoir lieu une fois avant la boucle. Déplacer int total = 0; dans le corps crée un nouveau total à chaque passage et fait perdre les résultats précédents.
Choisir des compteurs qui peuvent atteindre la fin
Un point-virgule directement après l’en-tête for rend le corps vide. Un bloc placé ensuite s’exécute alors une fois après la boucle. Utilise des accolades directement rattachées à l’en-tête pour rendre le corps visible.
Un compte à rebours non signé avec i >= 0 ne peut pas s’arrêter de cette manière : les valeurs non signées sont toujours positives ou nulles, et décrémenter zéro provoque un rebouclage vers la valeur maximale. Un compteur à virgule flottante qui ajoute 0.1 à répétition peut dépasser la valeur exacte 1.0 sans l’atteindre : x != 1.0 n’est donc pas une condition d’arrêt fiable. Compte avec un entier et calcule une mesure comme x = i / 10.0 dans le corps.
Construire des lignes avec des boucles imbriquées
À chaque passage de la boucle externe, la boucle interne démarre et parcourt sa propre séquence. Trois passages externes et quatre passages internes fixes exécutent le corps interne douze fois. Lorsque la borne interne dépend de la ligne, compte chaque ligne séparément.
int height = 3;
for (int row = 1; row <= height; row++) {
for (int s = 0; s < height - row; s++) printf(" ");
for (int h = 0; h < row; h++) printf("#");
printf("\n");
}
Les lignes contiennent deux espaces et un dièse, un espace et deux dièses, puis trois dièses. Le saut de ligne se place après les boucles internes, de sorte que chaque passage externe produit une ligne.
Sauter une opération ou s’arrêter avant la fin
continue dans une boucle for passe à la mise à jour, puis au test suivant. Une boucle qui additionne uniquement les valeurs impaires de 1 à 9 peut sauter les valeurs paires et se terminer avec 25. break quitte immédiatement la boucle la plus interne. Dans des boucles imbriquées, un break interne n’arrête pas la boucle externe.
Une boucle for est souvent le choix le plus clair pour un intervalle de compteur connu ; while convient à une répétition contrôlée par les données, et do-while à une opération qui doit avoir lieu avant le test. Réécrire une boucle for sous forme de boucle while demande une attention particulière à continue, car la mise à jour doit aussi avoir lieu sur ce chemin.
Points clés
- Une boucle
forexécute l’initialisation une fois et la mise à jour après chaque passage qui se poursuit. - Vérifie les valeurs parcourues pour détecter les erreurs de décalage d’une unité.
- Utilise des compteurs entiers dont la plage est suffisante.
- Les boucles imbriquées redémarrent la séquence interne à chaque passage externe.
- Un
breakinterne ne quitte que la boucle interne. - Dans une boucle
for, continue exécute tout de même la mise à jour de l’en-tête. - Initialise un total cumulé en dehors du corps répété.
6Reconnaître et prévenir les débordements d’entiers
Les plages finies des types entiers influent sur les calculs, même lorsque le résultat final semble assez petit. Distingue le rebouclage des entiers non signés, le débordement des entiers signés et les valeurs perdues lors d’une conversion, puis examine les types des opérations intermédiaires. La prévention commence avant un calcul risqué : convertis un opérande dans un type plus large, démontre que les bornes sont respectées ou utilise une opération vérifiée. La plage des compteurs et les tests aux limites aident à trouver des défaillances que de petites entrées ordinaires et de courtes exécutions de test peuvent manquer.
6.1 Ce qui se passe lorsqu’un entier n’est pas représentable
La plage de valeurs d’un entier est finie. Un résultat mathématique peut être valide tout en étant trop grand pour le type C utilisé pour le calculer. Les conséquences dépendent du fait qu’il s’agit d’une opération non signée, signée ou d’une conversion.
L’arithmétique non signée reboucle
Un résultat non signé sur N bits est réduit modulo 2ᴺ. Avec un unsigned int sur 32 bits, UINT_MAX + 1u donne zéro, 0u - 1u donne 4 294 967 295 et 3u - 5u donne 4 294 967 294. Le résultat est défini, mais il peut être incorrect pour la tâche : soustraire cinq articles d’un stock de trois ne devrait pas produire des milliards d’articles.
Pour uint8_t b = 255;, b = b + 1; stocke zéro. Sur un système courant où int est sur 32 bits, l’addition est d’abord effectuée en int et produit 256 ; la conversion de cette valeur en type non signé sur 8 bits produit zéro. Stocker une valeur dans un petit type non signé ne signifie pas que chaque opération intermédiaire utilise ce petit type.
Le débordement d’une opération arithmétique signée est indéfini
Avec un int sur 32 bits, ajouter un à INT_MAX nécessite 2 147 483 648. Le C ne définit pas le résultat de cette opération signée. Le matériel peut sembler reboucler vers INT_MIN, mais le compilateur peut optimiser en supposant qu’un débordement signé ne se produit jamais. Une vérification comme if (x + 1 < x) peut être supprimée pour les entiers signés, car toute addition signée définie de un donne une valeur plus grande.
Ne calcule pas d’abord pour examiner ensuite les dégâts. Une opération indéfinie s’est déjà produite avant qu’un test ultérieur s’exécute.
Une destination plus large peut intervenir trop tard
int a = 50000, b = 50000;
long long product = a * b;
La multiplication est effectuée en int et déborde donc sur un système où int est sur 32 bits. La largeur de la destination ne peut pas y remédier. (long long)a * b change le type de l’opération avant la multiplication et produit 2 500 000 000.
Les factorielles croissent rapidement : 12! vaut 479 001 600 et est représentable dans un entier signé sur 32 bits ; 13! vaut 6 227 020 800 et ne l’est pas. Avec l’arithmétique non signée sur 32 bits, 13! reboucle vers 1 932 053 504. Un long long signé sur 64 bits peut contenir 20! = 2 432 902 008 176 640 000, mais pas 21!.
La conversion suit des règles distinctes
La conversion d’un entier en type non signé utilise une réduction modulo la plage. En C17, la conversion d’un entier hors plage en type signé donne un résultat ou un signal défini par l’implémentation. La conversion d’une valeur à virgule flottante dont la partie entière tronquée n’est pas représentable dans le type entier de destination constitue un comportement indéfini. La lecture d’un nombre hors plage par une conversion numérique de scanf est également indéfinie.
Les dépassements de plage peuvent avoir de graves conséquences
Le premier vol d’Ariane 5, le 4 juin 1996, a été perdu après qu’une valeur à virgule flottante n’a pas pu être représentée par un entier signé sur 16 bits dans son logiciel de référence inertielle. Ce programme était écrit en Ada ; la leçon sur la vérification des plages de conversion s’applique aussi au C.
Un compteur de secondes signé sur 32 bits commençant à l’origine du temps Unix atteint 2 147 483 647 le 19 janvier 2038 à 03:14:07 UTC. Les systèmes qui utilisent cette représentation du temps ont besoin d’une représentation plus large. En 2015, la FAA s’est également penchée sur le logiciel de commande des générateurs du Boeing 787, qui pouvait déborder après 248 jours d’alimentation continue. Ces défaillances montrent pourquoi une estimation des plages doit tenir compte à la fois d’une longue durée de fonctionnement et des valeurs intermédiaires.
Les limites des types à virgule flottante sont différentes : les systèmes IEEE courants peuvent produire l’infini, et un float peut perdre la précision sur les nombres entiers avant d’atteindre son maximum. Le rebouclage d’un entier non signé produit une valeur numérique ordinaire ; le débordement signé ne donne aucun résultat fiable.
Points clés
- L’arithmétique non signée utilise une réduction modulo la plage.
- Le débordement d’une opération arithmétique signée a un comportement indéfini.
- Une destination plus large ne peut pas corriger une opération intermédiaire effectuée dans un type trop étroit.
- La conversion d’entier, la conversion d’une valeur à virgule flottante et le débordement arithmétique suivent des règles différentes.
- Estime les valeurs sur toute la durée de fonctionnement.
6.2 Prévenir les débordements avant qu’ils ne se produisent
Commence par déterminer la plus grande valeur que chaque calcul peut produire. Inclus les produits intermédiaires et les mises à jour des compteurs, et pas seulement le résultat final. Choisis ensuite un type ou une vérification préalable qui maintient chaque opération dans la plage autorisée.
Élargir le type du calcul dès le départ
Un entier signé sur 32 bits peut contenir environ 2,1 milliards, tandis qu’un long long signé sur 64 bits peut contenir jusqu’à 9 223 372 036 854 775 807. Un compteur de millisecondes atteint la limite d’un entier signé sur 32 bits en environ 24,9 jours. Utilise un type sur 64 bits lorsqu’une durée peut être plus longue.
Écris (long long)a * b pour élargir le type d’un produit entier avant son évaluation. Les constantes comptent aussi : 86400LL * days commence la multiplication en long long. (long long)(a * b) ne convertit le résultat qu’après le calcul du produit dans le type plus étroit et ne peut donc pas empêcher son débordement. Les types plus larges ont eux aussi des limites ; compare-les aux plus grandes valeurs possibles.
Vérifier une addition à l’aide d’une soustraction
Pour int a et int b, inclus <limits.h> et effectue ce test :
if ((b > 0 && a > INT_MAX - b) ||
(b < 0 && a < INT_MIN - b)) {
printf("Sum out of range\n");
} else {
int sum = a + b;
printf("%d\n", sum);
}
Chaque soustraction est protégée par le signe de b, ce qui permet au test lui-même de rester représentable. Pour 2 000 000 000 plus 200 000 000, INT_MAX - b vaut 1 947 483 647, une valeur inférieure à a : l’addition est donc refusée. Le cas d’égalité est représentable : lorsque a est égal à INT_MAX - b, la somme vaut exactement INT_MAX.
Vérifier une multiplication positive par division
Pour des opérandes int positifs, a > INT_MAX / b signifie que le produit dépasserait le maximum. La condition évite de calculer le produit et évite un diviseur nul puisque b est positif. Dans un calcul de factorielle, le test avant de multiplier 12! par 13 détecte que 479 001 600 est supérieur à 165 191 049. Arrête-toi avant cette multiplication.
Pour un stock non signé, teste stock >= requested avant de soustraire. Passer simplement à un type non signé n’empêche pas un résultat absurde dû au rebouclage.
Réécrire les expressions intermédiaires risquées
Le milieu (low + high) / 2 peut déborder même si son résultat est représentable. Lorsque 0 <= low <= high et que les deux valeurs sont de type int, utilise low + (high - low) / 2. La différence est positive ou nulle et reste dans la plage, et le résultat ne dépasse jamais high. Ces conditions comptent ; la formule ne constitue pas une preuve générale pour des extrémités signées quelconques.
Les compteurs de boucle ont eux aussi besoin d’une plage suffisante. Un short sur 16 bits ne peut pas atteindre 40 000 : une boucle qui l’utilise pour compter jusque-là ne peut donc pas se terminer comme prévu. Utilise un type de compteur capable de représenter la borne et toute mise à jour finale.
Utiliser l’arithmétique vérifiée et les tests
C23 fournit <stdckdint.h> avec ckd_add, ckd_sub et ckd_mul. Par exemple, ckd_add(&r, a, b) stocke un résultat et renvoie vrai lorsque le résultat mathématique n’est pas représentable dans r. GCC et clang fournissent aussi __builtin_add_overflow, __builtin_sub_overflow et __builtin_mul_overflow comme extensions du compilateur.
Les avertissements détectent certains débordements de constantes et certaines erreurs de mélange entre valeurs signées et non signées. Dans GCC ou clang, -fsanitize=undefined -g peut signaler un débordement signé atteint pendant un test ; -ftrapv demande l’arrêt du programme en cas de débordement d’une addition, d’une soustraction ou d’une multiplication signée. Teste zéro, un, moins un, la plus grande entrée attendue et les valeurs aux bornes et juste en dessous. Un outil de test vérifie les chemins que tu exécutes : conserve donc dans le code le raisonnement sur les plages de valeurs.
Points clés
- Convertis un opérande dans un type plus large avant une opération risquée.
- Pour une addition signée, utilise des vérifications préalables qui tiennent compte des signes.
- La vérification préalable d’une multiplication par division exige des opérandes positifs.
- Une soustraction non signée nécessite aussi une vérification des bornes.
- La formule plus sûre du milieu exige des extrémités positives ou nulles et ordonnées.
- L’arithmétique vérifiée indique si le résultat est représentable dans sa destination.
- Les tests aux limites et les sanitizers complètent le raisonnement sur les plages de valeurs.
Types et opérateurs en C : exemples
1Construire un message de bienvenue et diagnostiquer une modification qui échoue
9 étapes
- 1IntroductionSuivre la construction
Un programme C enregistré est du texte. Pour l’exécuter, il faut un exécutable produit à partir de ce texte. Garde clairement à l’esprit le rôle des deux fichiers : une modification change le code source, tandis qu’une construction réussie remplace l’exécutable.
Tu vas construire un court message de bienvenue, examiner le rôle de chaque étape et diagnostiquer une modification qui échoue. L’habitude essentielle consiste à vérifier le résultat de la construction avant de tirer des conclusions sur le nouveau code source à partir d’une exécution.
- 2ExplicationLire la structure du programme
Le programme commence dans
main:#include <stdio.h> int main(void) { printf("Ready\n"); return 0; }L’en-tête fournit la déclaration de
printf. Le corps affiche une ligne, puis renvoie un code de réussite. Ici,voidsignifie que main n’a pas de paramètres ;intindique le type de sa valeur de retour. Les accolades délimitent le corps et les instructions simples se terminent par des points-virgules. - 3ExempleConstruire et exécuter le programme
Enregistre le code source sous le nom
ready.c. Dans son dossier, saisis :gcc -std=c17 -Wall -Wextra ready.c -o ready ./readyLa construction sélectionne C17 et des avertissements utiles, puis nomme l’exécutable
ready. L’exécution afficheReadysuivi d’un saut de ligne. Sous Linux ou macOS,./désigne le dossier courant. Tu peux utiliser clang à la même place dans la commande ; les exécutables Windows utilisent le suffixe.exe. - 4LienSituer le code objet
Le prétraitement traite la directive d’inclusion. La compilation produit ensuite du code assembleur, puis l’assemblage produit du code objet. L’édition de liens résout les références et achève l’exécutable ; le code de bibliothèque peut être fourni par une bibliothèque partagée à l’exécution.
Les options
-E,-Set-carrêtent la construction à des étapes de plus en plus avancées. En particulier,-cs’arrête à un fichier objet : l’étape d’édition de liens nécessaire à l’exécutable n’est pas encore achevée. - 5DéfiPrévoir la prochaine exécution
Tu construis le programme de bienvenue, puis tu enregistres une modification qui remplace « Ready » par « Begin ». Tu exécutes l’exécutable existant sans compiler. Quel texte apparaît et comment le modifier ?
---reveal---
Il affiche toujours
Ready. L’exécutable correspond à la construction réussie précédente. Compile la modification enregistrée et exécute le nouvel exécutable pour afficherBegin. Enregistrer le code source seul ne met pas le code machine à jour. - 6ExempleCorriger la première erreur
Après une modification, il manque un point-virgule à cette ligne :
printf("Begin\n") return 0;Le compilateur peut pointer la première ligne ou
return, où il remarque que l’instruction d’expression n’est pas terminée. Ajoute le point-virgule après l’appel et reconstruis le programme. Commence par cette première cause même si les messages suivants mentionnent d’autres lignes ; une seule erreur de syntaxe peut perturber la lecture du code suivant par le compilateur. - 7ConseilUtiliser les avertissements comme indices
Un avertissement permet généralement à la construction de se poursuivre, mais il peut signaler une erreur grave.
printf("%d\n", 2.5);transmet un double là où le format exige un int, ce qui constitue un comportement indéfini.Corrige l’incompatibilité de type au lieu d’accepter ce qu’affiche une exécution de test. Les avertissements de
-Wall -Wextraaident à révéler ces erreurs ;-Werrorleur fait arrêter la construction. Pour un calcul incorrect, affiche les valeurs intermédiaires et compare-les à une courte trace réalisée à la main. - 8DéfiDistinguer une construction échouée d’une exécution incorrecte
Après une construction réussie du programme de bienvenue, une nouvelle modification ne se compile pas. L’exécution de
./readyaffiche toujours un message de bienvenue. Cela montre-t-il que la modification est valide et que dois-tu examiner ensuite ?---reveal---
Non. L’ancien exécutable peut subsister après une construction échouée. Examine la première erreur du compilateur, y compris la ligne précédente du code source, corrige-la et reconstruis le programme avec succès. Exécute ensuite le nouvel exécutable et compare sa sortie à ta prévision. Le message de bienvenue existant ne dit rien sur la modification qui a échoué.
- 9RécapitulatifSuivre un cycle de correction clair
Relie chaque test au code source qui a réellement été construit. Enregistre la modification, compile, lis le premier diagnostic, corrige sa cause et reconstruis le programme avant de l’exécuter.
Si le programme se construit mais calcule une valeur incorrecte, utilise une courte trace et des entrées limites. Si une opération a un comportement indéfini, modifie le code pour qu’elle ne puisse pas se produire ; une sortie d’apparence normale n’est pas un résultat fiable pour cette opération.
2Choisir le stockage des dénombrements et des mesures
13 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionDéterminer les besoins d’une valeur
- 2ExempleUne valeur copiée reste inchangée
- 3ExplicationGarder les noms dans leurs blocs
- 4DéfiSuivre un nom masqué
- 5ExplicationComparer les plages avant de choisir
- 6ExempleLire une configuration de bits et un littéral
- 7DéfiChoisir une plage portable
- 8ExplicationComprendre les écarts des fractions décimales
- 9ExempleComparer avec une erreur autorisée
- 10DéfiDistinguer conversion et arrondi
- 11Point cléLa précision peut manquer avant la plage de valeurs
- 12DéfiAdapter le type à la tâche
- 13RécapitulatifVérifier le stockage de deux façons
3Lire des mesures et afficher des résultats clairs
11 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionRelier les valeurs typées au texte
- 2ExplicationLire le format de sortie
- 3ExempleConstruire un affichage aligné
- 4DéfiPrévoir le remplissage d’un entier
- 5ExplicationLire dans les bonnes destinations
- 6ExempleRepérer une conversion partielle
- 7ConseilÉviter de lire un saut de ligne restant par accident
- 8DéfiSuivre ce qu’une lecture échouée préserve
- 9ExempleVérifier le domaine du calcul
- 10DéfiCombiner la lecture et l’affichage
- 11RécapitulatifEffectuer deux vérifications à la saisie
4Lire une expression, des types aux effets
17 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionIndiquer les valeurs et les types
- 2ExempleDécomposer une quantité en parties entières
- 3ExplicationDistinguer les deux incrémentations
- 4DéfiSuivre une mise à jour composée
- 5ExemplePlacer l’opérande à virgule flottante assez tôt
- 6Point cléDe petites valeurs non signées peuvent être calculées en int signé
- 7DéfiSuivre une valeur signée dans un calcul non signé
- 8ExplicationRelier des comparaisons complètes
- 9ExempleExécuter d’abord une condition de protection
- 10ExempleModifier un indicateur sans perturber les autres
- 11DéfiComparer la vérité et les bits communs
- 12ExplicationRegrouper avant de suivre le calcul
- 13ConseilPlacer le masque à l’intérieur de la comparaison
- 14LienChoisir une valeur sans évaluer les deux
- 15DéfiDes parenthèses peuvent-elles corriger deux mises à jour ?
- 16DéfiCombiner un regroupement et un test de plage
- 17RécapitulatifExaminer une expression en trois passages
5Suivre des choix et construire un total cumulé
18 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionSuivre le chemin réellement emprunté
- 2ExplicationUtiliser la première branche vraie
- 3ConseilLes accolades et l’égalité comptent toutes deux
- 4DéfiTrouver la première tranche correspondante
- 5ExplicationSélectionner un point d’entrée de commande
- 6ExemplePartager volontairement une action
- 7DéfiTrouver la cible de break
- 8ExplicationLire la saisie avant de l’utiliser
- 9ExempleSuivre le calcul avec une sentinelle
- 10DéfiTraiter un arrêt immédiat
- 11LienDemander une fois avec do-while
- 12ExplicationCompter exactement les valeurs voulues
- 13ExempleConserver un total entre les passages
- 14ConseilÉviter les compteurs qui manquent la sortie
- 15ExempleCompter les lignes séparément
- 16DéfiSuivre une sortie anticipée dans des boucles imbriquées
- 17DéfiCombiner un indicateur et une boucle de menu
- 18RécapitulatifVérifier chaque chemin vers le passage suivant
6Sécuriser un produit avant de le calculer
12 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionExaminer le calcul avant le stockage
- 2ExempleTrouver l’opération dans le type trop étroit
- 3ExplicationUn résultat non signé défini mais potentiellement incorrect
- 4DéfiUne conversion explicite tardive corrige-t-elle le produit ?
- 5ExempleÉlargir le type de l’opération elle-même
- 6ExplicationVérifier une addition sans additionner
- 7ExempleVérifier un produit positif par division
- 8DéfiVérifier correctement la borne
- 9Point cléUn résultat représentable peut masquer une somme qui ne l’est pas
- 10ConseilUtiliser les outils et tester les limites
- 11DéfiVérifier une conception complète des plages
- 12RécapitulatifDémontrer la sécurité avant l’opération
Types et opérateurs en C : QCM
Série 1 : Construire des programmes et suivre les valeurs stockées10 Questions Pratiques
Question 1 sur 10
Afficher la réponseMasquer la réponse
Question 2 sur 10
Afficher la réponseMasquer la réponse
Question 3 sur 10
Quelles valeurs ce code affiche-t-il ?
int units = 6;
int total = units * 4;
units = 9;
printf("%d %d\n", units, total);
Afficher la réponseMasquer la réponse
Question 4 sur 10
Afficher la réponseMasquer la réponse
Question 5 sur 10
014 représente-t-il ?Afficher la réponseMasquer la réponse
Question 6 sur 10
Afficher la réponseMasquer la réponse
Question 7 sur 10
printf("|%04d|", 23); entre les barres verticales ?Afficher la réponseMasquer la réponse
Question 8 sur 10
int a = 2, b = 9;, un appel à scanf("%d:%d", &a, &b) reçoit « 17 42 ». Quelle valeur est renvoyée et quelles valeurs sont stockées ?Afficher la réponseMasquer la réponse
Question 9 sur 10
Sur un système où unsigned char est sur 8 bits et int sur 32 bits, qu’affiche ce code ?
unsigned char a = 230, b = 40;
int sum = a + b;
unsigned char small = sum;
printf("%d %u\n", sum, (unsigned)small);
Afficher la réponseMasquer la réponse
Question 10 sur 10
gcc -std=c17 -c sensor.c réussit. Qu’a-t-elle produit et quelle étape de construction reste nécessaire avant une exécution ordinaire ?Afficher la réponseMasquer la réponse
Série 2 : Lire des données et calculer avec les types C10 Questions Pratiques
Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.
Question 1 sur 10
double distance_m;. Quelle conversion scanf et quel argument correspondent à son type ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
-23 / 6 et -23 % 6 en C17 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
int distance = 19, time = 4;, quelle expression calcule une vitesse de type double en conservant la partie fractionnaire ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
%d accepte la saisie « 46kg ». Quelle valeur est stockée et quelle partie de la saisie reste non lue immédiatement après la conversion ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
total et count, quelle condition vérifie que la moyenne entière est supérieure à 5 sans diviser par zéro ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
2 + 5 * 3 % 7 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
printf("|%6.1f|", 8.375); affiche-t-il avec l’arrondi ordinaire au dixième le plus proche ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
Quelles valeurs sont affichées ?
int n = 8;
int a = n++;
int b = ++n;
a *= b % 4;
printf("%d %d %d\n", a, b, n);
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
Avec int et unsigned int sur 32 bits, qu’affiche ce code ?
int delta = -3;
unsigned int limit = 2;
printf("%d %u\n", delta < limit, limit + delta);
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
flags = 10u. Quelle valeur obtient-on en activant le bit 2, le bit de poids le plus faible étant le bit 0 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Série 3 : Suivre les branches, les instructions switch et les boucles10 Questions Pratiques
Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.
Question 1 sur 10
Un score vaut 76. Quel caractère est affecté ?
if (score >= 70) grade = 'C';
else if (score >= 85) grade = 'B';
else grade = 'F';
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
Qu’affiche ce code lorsque mode vaut 2 ?
switch (mode) {
case 1: printf("A"); break;
case 2: printf("B");
case 3: printf("C"); break;
default: printf("D");
}
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
Quelles sont les valeurs finales de n et passes ?
int n = 45, passes = 0;
while (n > 2) {
n /= 2;
passes++;
}
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
for (int i = 4; i < 19; i += 4) ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
Qu’affiche ce code ?
int level = 2;
if (level = 0) {
printf("Low");
} else {
printf("Other");
}
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
Quel total est affiché ?
int total = 0;
for (int r = 1; r <= 4; r++) {
for (int c = 1; c <= r; c++) {
if (c == 3) break;
total += c;
}
}
printf("%d\n", total);
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
Quelle somme obtient-on ?
int sum = 0;
for (int i = 2; i <= 8; i++) {
if (i % 3 == 0) continue;
sum += i;
}
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
Combien de fois « tick » est-il affiché ?
int running = 1, passes = 0;
while (running && passes < 3) {
switch (passes) {
case 1: break;
default: printf("tick\n");
}
passes++;
}
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
La saisie est « 9 4 0 7 ». Quels nombre et moyenne sont affichés ?
int x, sum = 0, count = 0;
while (scanf("%d", &x) == 1 && x != 0) {
sum += x;
count++;
}
if (count) printf("%d %.1f\n", count, (double)sum / count);
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Série 4 : Diagnostiquer les conversions et les dépassements de plage10 Questions Pratiques
Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.
Question 1 sur 10
0u - 1u produit-il lorsque unsigned int possède 32 bits de valeur ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
a = 60000 et b = 40000, quelle expression calcule leur produit exact en toute sécurité dans un long long courant sur 64 bits ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
flags = 6u non signé, quel résultat produit flags & 2u == 0u ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
a + b ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
f * i dépasserait INT_MAX avant d’effectuer la multiplication ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
-fsanitize=undefined. Quelle conclusion doit guider la correction ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Série 5 : Combiner saisie, contrôle du déroulement et calculs sûrs10 Questions Pratiques
Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.
Question 1 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
Quelle valeur est affichée ?
int total = 0;
for (int r = 2; r <= 5; r++) {
for (int c = 1; c < r; c++) {
if (c % 2 == 0) continue;
total += r;
}
}
printf("%d\n", total);
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
days sur 32 bits vaut 30000. Quelle expression calcule correctement sa durée en millisecondes dans un long long sur 64 bits ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
Que garantit cet extrait C17 au sujet de sa sortie ?
int n = 4;
printf("%d %d\n", n++, n++);
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
Qu’affiche ce code lorsque a = -2 et b = 7 ?
if (a > 0)
if (b > 0) printf("Both");
else printf("A only");
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
Pourquoi cette boucle cesse-t-elle de progresser après avoir affiché zéro une fois ?
int i = 0;
while (i < 4) {
if (i == 1) continue;
printf("%d\n", i);
i++;
}
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
Avec unsigned int sur 32 bits, quelle valeur ce code laisse-t-il dans flags ?
unsigned int flags = 13u;
flags &= ~(1u << 2);
flags |= 1u << 1;
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
Un menu commence avec running = 1 et actions = 0. La saisie est « s q s ». Que vaut actions après cette boucle ?
char cmd;
while (running && scanf(" %c", &cmd) == 1) {
switch (cmd) {
case 'q': running = 0; break;
case 's': actions++; break;
}
}
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
Quelle valeur est affectée à rate ?
int completed = 14, planned = 20;
double rate = completed / planned * 100.0;
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Tu bloques sur ton propre exercice ? Aide aux Devoirs
Mots-Clés
A
accumulateur
affectation
count = count + 1; lit l’ancienne valeur et la remplace par une valeur supérieure de une unité.affectation composée
total += item; met à jour le total cumulé sans modifier l’élément.arithmétique vérifiée
ckd_mul de C23 peut signaler un produit qui n’est pas représentable dans sa destination.associativité
24 / 4 / 2 sous la forme (24 / 4) / 2, ce qui donne trois.avertissement
%d est associé à un argument double, une combinaison invalide.B
bloc d’instructions
boucle do-while
boucle for
i < n et i++ effectue n passages pour n positif ou nul lorsque les mises à jour sont représentables.boucle imbriquée
boucle infinie
boucle while
branche
C
caractères blancs
%c lit un saut de ligne restant sauf si on lui demande de l’ignorer.chaîne de format
printf("Mass: %.1f kg\n", mass) fournit un intitulé, une précision décimale et une unité.code de retour
code source
compilateur
complément à deux
10000000 représente −128.comportement défini par l’implémentation
comportement indéfini
condition
if (items) est fausse pour zéro, tandis qu’un dénombrement négatif non nul fait exécuter la branche.constante caractère
'A' peut s’afficher comme la lettre A avec %c ou comme le nombre 65 avec %d.conversion élargissante
a * b change le type du produit ; convertir après le produit intervient trop tard.conversion explicite (cast)
(double)sum / count prend effet avant la division et conserve un quotient fractionnaire.conversion hors plage
conversion implicite
conversions arithmétiques usuelles
D
débordement d’entier
décalage de bits
déclaration
int count = 0; donne au nom un type entier et fournit aussi sa valeur initiale.division entière
22 / 5 donne quatre, même lorsque le résultat calculé est affecté à un double.E
effet de bord
enchaînement des cas (fall-through)
entier non signé
entier signé
entrée standard
erreur d’arrondi
erreur de compilation
erreur de décalage d’une unité
i < n par i <= n dans une boucle de comptage commençant à zéro provoque une erreur de décalage d’une unité.erreur de logique
exécutable
./hello lance l’exécutable dans le dossier courant sans appeler à nouveau le compilateur C.É
échec de correspondance
%d provoquent un échec de correspondance et restent non lues pour une nouvelle tentative.éditeur de liens
printf ; son code de bibliothèque peut être fourni par une bibliothèque partagée.étiquette case
case 4: est valide en C17 ; une variable const int ordinaire ne peut pas fournir sa valeur.étiquette default
évaluation en court-circuit
count != 0 && sum / count > 4 saute la division lorsque count vaut zéro.F
fin de l’entrée
I
identificateur
distance_m est valide ; 2distance ne peut pas être un nom car il commence par un chiffre.incrémentation postfixée
n = 3, l’incrémentation postfixée dans old = n++; donne trois à old et laisse n à quatre.initialisation
instruction break
instruction continue
instruction switch
itération
L
largeur de champ
%6d ajoute quatre espaces avant 42, tandis qu’une valeur de sept chiffres s’affiche toujours en entier.littéral entier
0x30 utilise l’hexadécimal, tandis que 48u écrit cette valeur en décimal non signé.M
macro
#define LIMIT 8, le lexème LIMIT est remplacé par 8 partout où cette macro s’applique.masque de bits
1u << 5 sélectionne le bit cinq, en comptant le bit de poids le plus faible au rang zéro.mise en tampon des sorties
fflush(stdout) l’envoie avant un appel de lecture.N
NaN
nombre d’affectations réussies
O
opérande
sum / count, les types des opérandes déterminent si la division conserve un résultat fractionnaire.opérateur bit à bit
& ne conserve que les bits actifs communs à douze et dix, ce qui donne huit.opérateur conditionnel
condition ? a : b pour choisir une expression de valeur après avoir testé la condition.condition ? a : b évalue b et saute a.opérateur logique
&&, || ou !.ready && valid teste si les deux valeurs stockées sont non nulles.opérateur relationnel
<, <=, > ou >= et produit l’int zéro ou un.height >= 1 vérifie une borne ; une autre comparaison vérifie l’autre.opérateur virgule
i++, j-- ; les virgules entre les arguments de printf ne le font pas.ordre d’évaluation
P
portée de bloc
précision
préprocesseur
#include et le remplacement des macros avant les principales étapes de traduction du C.priorité des opérateurs
4 * 2 avant l’addition dans 3 + 4 * 2.promotion entière
R
rebouclage des entiers non signés
reste
% du C ; lorsqu’il est non nul, il a le signe du dividende.-22 % 5 vaut −2 car le quotient tronqué vaut −4 et l’identité de la division doit être respectée.résultat intermédiaire
(low + high) / 2 est un résultat intermédiaire qui peut déborder même lorsque le milieu est représentable.S
sanitizer
sentinelle
séquence d’échappement
\n ajoute un saut de ligne dans une chaîne C ; printf utilise %% pour un signe pour cent.sinon pendant (dangling else)
spécification de conversion
%lld correspond à un argument long long ; %d ne peut pas le convertir automatiquement.T
test aux limites
tolérance
fabs(measured - target) à la tolérance au lieu d’exiger une égalité exacte.trace d’exécution
troncature vers zéro
type entier de largeur fixe
uint8_t, lorsqu’il est fourni, contient les valeurs de zéro à 255.V
valeur à virgule flottante
valeur booléenne
<stdbool.h> en C17, bool ready = 2; stocke la valeur booléenne vrai.variable
mass_kg stocke une mesure tandis que count enregistre le nombre d’échantillons saisis.vérification des bornes
À propos de cette unité
Crée ton propre Matériel d'Étude
Transforme tes notes, diapos et documents en Guides d'Étude, Questions Pratiques, Flashcards et 6 autres outils.