Les bases du C : types, opérateurs et contrôle du déroulement

Écris tes premiers programmes en C et comprends exactement ce qu’ils font. Compile et exécute du code avec gcc ou clang et corrige les problèmes signalés par le compilateur, stocke des données dans des variables entières, caractères et à virgule flottante, puis affiche et lis des valeurs avec 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

  1. 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 C
      Lire 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 erreurs
      Distinguer 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.
  2. 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 variables
      Cré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 valeurs
      Choisir 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 limites
      Stocker 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.
  3. 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 printf
      Utiliser 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 scanf
      Lire les nombres et les caractères saisis par l’utilisateur, vérifier que la lecture a réussi et traiter les saisies invalides.
  4. 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’affectation
      Appliquer +, -, *, / 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 types
      Expliquer 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’évaluation
      Pré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.
  5. 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 switch
      Utiliser 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.
  6. 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 type
      Dé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ébordements
      Choisir 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.

Types et opérateurs en C : cours

6 chapitres

1

Compiler 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 :

OptionRésultat
-ETexte après prétraitement
-SLangage assembleur
-cUn 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 -Wextra pour 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
TypeCe que tu observesOù chercher
Erreur de compilationAucun nouvel exécutableSyntaxe, déclarations et types
AvertissementUn problème probable ; la construction se poursuit généralementL’expression indiquée dans le message
Erreur d’édition de liensLe 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écutionUn problème apparaît pendant l’exécutionOpérations et valeurs rencontrées avec cette entrée
Erreur de logiqueLe programme s’exécute mais donne un résultat non vouluConditions, 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.
2

Stocker 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.
  • const fournit une variable typée accessible en lecture seule ; #define fournit 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.

TypeNombre d’octets habituel sous Linux/macOS 64 bitsDifférence sous Windows 64 bits
char1Aucune
short2Aucune
int4Aucune
long84 octets
long long8Aucune

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 int signé 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 char seul 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.
3

Afficher 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 affichageConversion
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 %f compte les décimales.
  • La précision de %g compte les chiffres significatifs.
  • Écris %% pour obtenir un signe pour cent littéral dans un format.
  • printf n’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 %lf pour lire un double.
  • 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.
4

Calculer 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épartInstructionValeurs finales
a = 5y = a++;y = 5, a = 6
a = 5y = ++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ésx++, 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.
5

Contrô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 if cré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 break d’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 :

Passagesumn
16409
21540
3154
4190

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

  • while teste d’abord la condition ; do-while la 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 for exé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 break interne 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é.
6

Reconnaî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

6 Leçons Interactives

1

Construire un message de bienvenue et diagnostiquer une modification qui échoue

9 étapes
Suivre un premier programme du code source à son exécution, puis utiliser les diagnostics et une trace d’exécution pour corriger les problèmes.
  1. 1
    IntroductionSuivre 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.

  2. 2
    ExplicationLire 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, void signifie que main n’a pas de paramètres ; int indique le type de sa valeur de retour. Les accolades délimitent le corps et les instructions simples se terminent par des points-virgules.

  3. 3
    ExempleConstruire 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
    ./ready
    

    La construction sélectionne C17 et des avertissements utiles, puis nomme l’exécutable ready. L’exécution affiche Ready suivi 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.

  4. 4
    LienSituer 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, -S et -c arrêtent la construction à des étapes de plus en plus avancées. En particulier, -c s’arrête à un fichier objet : l’étape d’édition de liens nécessaire à l’exécutable n’est pas encore achevée.

  5. 5
    Dé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 afficher Begin. Enregistrer le code source seul ne met pas le code machine à jour.

  6. 6
    ExempleCorriger 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.

  7. 7
    ConseilUtiliser 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 -Wextra aident à révéler ces erreurs ; -Werror leur 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.

  8. 8
    Dé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 ./ready affiche 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é.

  9. 9
    Ré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.

2

Choisir le stockage des dénombrements et des mesures

13 étapes
Suivre les affectations et la portée, puis relier la plage des entiers et la précision des valeurs à virgule flottante au choix des types.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionDéterminer les besoins d’une valeur
  2. 2
    ExempleUne valeur copiée reste inchangée
  3. 3
    ExplicationGarder les noms dans leurs blocs
  4. 4
    DéfiSuivre un nom masqué
  5. 5
    ExplicationComparer les plages avant de choisir
  6. 6
    ExempleLire une configuration de bits et un littéral
  7. 7
    DéfiChoisir une plage portable
  8. 8
    ExplicationComprendre les écarts des fractions décimales
  9. 9
    ExempleComparer avec une erreur autorisée
  10. 10
    DéfiDistinguer conversion et arrondi
  11. 11
    Point cléLa précision peut manquer avant la plage de valeurs
  12. 12
    DéfiAdapter le type à la tâche
  13. 13
    RécapitulatifVérifier le stockage de deux façons
3

Lire des mesures et afficher des résultats clairs

11 étapes
Faire correspondre les formats aux types, examiner les saisies partielles et suivre un calcul vérifié sur des mesures.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionRelier les valeurs typées au texte
  2. 2
    ExplicationLire le format de sortie
  3. 3
    ExempleConstruire un affichage aligné
  4. 4
    DéfiPrévoir le remplissage d’un entier
  5. 5
    ExplicationLire dans les bonnes destinations
  6. 6
    ExempleRepérer une conversion partielle
  7. 7
    ConseilÉviter de lire un saut de ligne restant par accident
  8. 8
    DéfiSuivre ce qu’une lecture échouée préserve
  9. 9
    ExempleVérifier le domaine du calcul
  10. 10
    DéfiCombiner la lecture et l’affichage
  11. 11
    RécapitulatifEffectuer deux vérifications à la saisie
4

Lire une expression, des types aux effets

17 étapes
Étudier dans un ordre défini les divisions, conversions, conditions logiques de protection, masques, regroupements et mises à jour.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionIndiquer les valeurs et les types
  2. 2
    ExempleDécomposer une quantité en parties entières
  3. 3
    ExplicationDistinguer les deux incrémentations
  4. 4
    DéfiSuivre une mise à jour composée
  5. 5
    ExemplePlacer l’opérande à virgule flottante assez tôt
  6. 6
    Point cléDe petites valeurs non signées peuvent être calculées en int signé
  7. 7
    DéfiSuivre une valeur signée dans un calcul non signé
  8. 8
    ExplicationRelier des comparaisons complètes
  9. 9
    ExempleExécuter d’abord une condition de protection
  10. 10
    ExempleModifier un indicateur sans perturber les autres
  11. 11
    DéfiComparer la vérité et les bits communs
  12. 12
    ExplicationRegrouper avant de suivre le calcul
  13. 13
    ConseilPlacer le masque à l’intérieur de la comparaison
  14. 14
    LienChoisir une valeur sans évaluer les deux
  15. 15
    DéfiDes parenthèses peuvent-elles corriger deux mises à jour ?
  16. 16
    DéfiCombiner un regroupement et un test de plage
  17. 17
    RécapitulatifExaminer une expression en trois passages
5

Suivre des choix et construire un total cumulé

18 étapes
Étudier les branches, un menu switch, une saisie à sentinelle et des comptages imbriqués avec des sorties volontaires.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionSuivre le chemin réellement emprunté
  2. 2
    ExplicationUtiliser la première branche vraie
  3. 3
    ConseilLes accolades et l’égalité comptent toutes deux
  4. 4
    DéfiTrouver la première tranche correspondante
  5. 5
    ExplicationSélectionner un point d’entrée de commande
  6. 6
    ExemplePartager volontairement une action
  7. 7
    DéfiTrouver la cible de break
  8. 8
    ExplicationLire la saisie avant de l’utiliser
  9. 9
    ExempleSuivre le calcul avec une sentinelle
  10. 10
    DéfiTraiter un arrêt immédiat
  11. 11
    LienDemander une fois avec do-while
  12. 12
    ExplicationCompter exactement les valeurs voulues
  13. 13
    ExempleConserver un total entre les passages
  14. 14
    ConseilÉviter les compteurs qui manquent la sortie
  15. 15
    ExempleCompter les lignes séparément
  16. 16
    DéfiSuivre une sortie anticipée dans des boucles imbriquées
  17. 17
    DéfiCombiner un indicateur et une boucle de menu
  18. 18
    RécapitulatifVérifier chaque chemin vers le passage suivant
6

Sécuriser un produit avant de le calculer

12 étapes
Distinguer les règles de débordement, élargir le type au bon moment et vérifier les cas limites sans provoquer la défaillance recherchée.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionExaminer le calcul avant le stockage
  2. 2
    ExempleTrouver l’opération dans le type trop étroit
  3. 3
    ExplicationUn résultat non signé défini mais potentiellement incorrect
  4. 4
    DéfiUne conversion explicite tardive corrige-t-elle le produit ?
  5. 5
    ExempleÉlargir le type de l’opération elle-même
  6. 6
    ExplicationVérifier une addition sans additionner
  7. 7
    ExempleVérifier un produit positif par division
  8. 8
    DéfiVérifier correctement la borne
  9. 9
    Point cléUn résultat représentable peut masquer une somme qui ne l’est pas
  10. 10
    ConseilUtiliser les outils et tester les limites
  11. 11
    DéfiVérifier une conception complète des plages
  12. 12
    RécapitulatifDémontrer la sécurité avant l’opération

Types et opérateurs en C : QCM

50 Questions Pratiques en 5 séries

Série 1 : Construire des programmes et suivre les valeurs stockées10 Questions Pratiques

Question 1 sur 10

Un programme a été construit avec succès, puis son message de bienvenue a été modifié et enregistré. L’exécution de l’exécutable existant affiche toujours l’ancien message. Quelle action permet de prendre en compte la modification ?
  1. Exécuter à nouveau le même exécutable.
  2. Changer le nom du fichier exécutable.
  3. Compiler à nouveau le code source enregistré.
  4. Ajouter un saut de ligne au message.
Afficher la réponseMasquer la réponse
Réponse : C
L’exécutable contient le résultat de la construction précédente. Enregistrer le texte source ne reconstruit pas ce fichier : compile donc le code source modifié, puis exécute le nouvel exécutable. Exécuter à nouveau l’ancien utilise le même code machine qu’auparavant et ne peut pas prendre en compte le message modifié.

Question 2 sur 10

Un compilateur signale un point-virgule manquant, suivi de plusieurs erreurs sur des lignes ultérieures. Que dois-tu corriger en premier ?
  1. Le premier problème de syntaxe signalé.
  2. Le dernier problème de syntaxe signalé.
  3. Chaque ligne suivante avant de reconstruire.
  4. Les données d’entrée de l’exécutable.
Afficher la réponseMasquer la réponse
Réponse : A
Commence par la première erreur et examine la ligne indiquée ainsi que la précédente. Un point-virgule manquant peut perturber l’interprétation des instructions suivantes par le compilateur. Corriger d’abord toutes les lignes ultérieures peut modifier du code correct qui n’a été signalé qu’à cause de l’erreur précédente.

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);
  1. 9 36
  2. 6 24
  3. 6 36
  4. 9 24
Afficher la réponseMasquer la réponse
Réponse : D
La déclaration de total calcule six fois quatre une seule fois et stocke 24. L’affectation suivante donne neuf à units mais ne recalcule pas total : la sortie est donc 9 et 24. Attendre 36 revient à considérer l’affectation précédente comme une formule active reliant les deux variables.

Question 4 sur 10

Une valeur doit pouvoir contenir des résultats négatifs et des dénombrements positifs allant jusqu’à 6 000 000 000 sur les systèmes Linux et Windows 64 bits courants. Quel type entier standard fournit la plage nécessaire ?
  1. int
  2. long long
  3. unsigned int
  4. long
Afficher la réponseMasquer la réponse
Réponse : B
Un long long standard possède au moins 64 bits et peut représenter ces valeurs positives et négatives. Un long Windows courant ne possède que 32 bits, même lorsque Windows lui-même est sur 64 bits. Unsigned int offre aussi une plage insuffisante sur ces systèmes et ne peut pas représenter les valeurs négatives.

Question 5 sur 10

Quelle valeur décimale le littéral entier C17 014 représente-t-il ?
  1. 14
  2. 10
  3. 12
  4. 16
Afficher la réponseMasquer la réponse
Réponse : C
Le zéro initial fait de 014 un littéral octal. Ses chiffres représentent un groupe de huit plus quatre, soit douze. Le lire comme quatorze en décimal revient à ignorer le préfixe de base ; un littéral décimal pour quatorze s’écrit 14 sans zéro initial.

Question 6 sur 10

Un double calculé diffère très légèrement d’un double cible après plusieurs opérations décimales. Quelle comparaison convient à une mesure qui autorise une petite erreur absolue ?
  1. Comparer la différence absolue à l’erreur autorisée.
  2. Comparer leurs affichages à la précision maximale.
  3. Convertir les deux en int avant de tester l’égalité.
  4. Exiger l’égalité exacte après affectation à des double.
Afficher la réponseMasquer la réponse
Réponse : A
Un test de différence absolue comme fabs(actual - target) < tolerance vérifie l’erreur autorisée pour la mesure. L’égalité exacte peut échouer car les calculs décimaux introduisent des différences d’arrondi en binaire. La conversion en int supprime les fractions et peut masquer une différence bien plus grande que l’erreur autorisée.

Question 7 sur 10

Qu’affiche printf("|%04d|", 23); entre les barres verticales ?
  1. | 23|
  2. |2300|
  3. |23 |
  4. |0023|
Afficher la réponseMasquer la réponse
Réponse : D
La largeur demande au moins quatre caractères et l’indicateur zéro fournit des zéros initiaux : 23 devient donc 0023. Un alignement ordinaire à droite utiliserait deux espaces initiaux, mais c’est le comportement de %4d sans l’indicateur zéro. La largeur n’ajoute pas de zéros à la valeur.

Question 8 sur 10

Avec 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 ?
  1. Retour 2 ; a = 17, b = 42.
  2. Retour 1 ; a = 17, b = 9.
  3. Retour 0 ; a = 2, b = 9.
  4. Retour 1 ; a = 17, b = 0.
Afficher la réponseMasquer la réponse
Réponse : B
Le premier %d stocke 17. La saisie contient ensuite un espace là où le format exige des deux-points littéraux : la correspondance s’arrête donc et b conserve son ancienne valeur, neuf. Le nombre renvoyé est un ; un espace entre les nombres ne peut pas remplacer la ponctuation exigée par le format.

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);
  1. 14 14
  2. 270 270
  3. 14 270
  4. 270 14
Afficher la réponseMasquer la réponse
Réponse : D
Les deux valeurs caractères sont promues en int avant l’addition : sum reçoit donc 270 sans rebouclage à 255. La conversion dans small réduit ensuite 270 modulo 256, ce qui donne 14. Prévoir 14 pour les deux valeurs revient à appliquer la petite plage de stockage à l’addition après promotion, où elle ne s’applique pas.

Question 10 sur 10

La commande 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 ?
  1. Un exécutable ; le prétraitement reste nécessaire.
  2. Du code assembleur ; l’assemblage reste nécessaire.
  3. Un fichier objet ; l’édition de liens reste nécessaire.
  4. Du C prétraité ; la compilation reste nécessaire.
Afficher la réponseMasquer la réponse
Réponse : C
L’option -c compile et assemble le code source en code objet, normalement sensor.o, puis s’arrête avant l’édition de liens. Celle-ci reste nécessaire pour créer l’exécutable et résoudre les références externes. Le code assembleur est la sortie antérieure sélectionnée par -S, et non par -c.
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

Une variable est déclarée double distance_m;. Quelle conversion scanf et quel argument correspondent à son type ?
  1. scanf("%f", &distance_m)
  2. scanf("%lf", &distance_m)
  3. scanf("%lf", distance_m)
  4. scanf("%d", &distance_m)
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 2 sur 10

Que valent -23 / 6 et -23 % 6 en C17 ?
  1. −4 et 1
  2. −3 et 5
  3. −4 et −5
  4. −3 et −5
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 3 sur 10

Pour int distance = 19, time = 4;, quelle expression calcule une vitesse de type double en conservant la partie fractionnaire ?
  1. (double)distance / time
  2. (double)(distance / time)
  3. distance / time
  4. (int)(distance / (double)time)
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

Une lecture d’un int avec %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 ?
  1. 46 ; il ne reste rien.
  2. 0 ; il reste « 46kg ».
  3. 46 ; il reste « kg ».
  4. Aucune valeur ; il reste « kg ».
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

Pour des valeurs int positives ou nulles total et count, quelle condition vérifie que la moyenne entière est supérieure à 5 sans diviser par zéro ?
  1. total / count > 5 && count != 0
  2. count != 0 && total / count > 5
  3. count != 0 & total / count > 5
  4. count == 0 || total / count > 5
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 6 sur 10

Quelle valeur produit 2 + 5 * 3 % 7 ?
  1. 1
  2. 0
  3. 7
  4. 3
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

Quel texte printf("|%6.1f|", 8.375); affiche-t-il avec l’arrondi ordinaire au dixième le plus proche ?
  1. | 8.375|
  2. |8.4 |
  3. | 8.4|
  4. |0008.4|
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);
  1. 16 10 10
  2. 18 10 10
  3. 16 9 10
  4. 24 11 11
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);
  1. 1 4294967295
  2. 1 -1
  3. 0 1
  4. 0 4294967295
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 10 sur 10

Une valeur non signée est flags = 10u. Quelle valeur obtient-on en activant le bit 2, le bit de poids le plus faible étant le bit 0 ?
  1. 12
  2. 14
  3. 8
  4. 2
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';
  1. C
  2. B
  3. F
  4. Aucun caractère n’est affecté.
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");
}
  1. B
  2. BCD
  3. BC
  4. ABC
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++;
}
  1. n = 1, passes = 5
  2. n = 2, passes = 5
  3. n = 3, passes = 4
  4. n = 2, passes = 4
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

Combien de passages sont exécutés dans for (int i = 4; i < 19; i += 4) ?
  1. 3
  2. 4
  3. 5
  4. 15
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");
}
  1. Low
  2. LowOther
  3. Rien ne s’affiche.
  4. 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);
  1. 10
  2. 7
  3. 13
  4. 20
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

Le corps d’une boucle doit afficher une invite une fois avant de pouvoir tester la première réponse. Quelle forme de boucle exprime directement cet ordre ?
  1. while avec un test avant le corps
  2. do-while
  3. for avec un test avant le corps
  4. switch avec une étiquette default
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;
}
  1. 35
  2. 9
  3. 26
  4. 8
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++;
}
  1. 1
  2. 3
  3. 0
  4. 2
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);
  1. 2 6.5
  2. 3 4.3
  3. 3 6.7
  4. 4 5.0
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

Quelle valeur 0u - 1u produit-il lorsque unsigned int possède 32 bits de valeur ?
  1. −1
  2. 2 147 483 647
  3. 4 294 967 295
  4. 0
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 2 sur 10

Avec les valeurs int sur 32 bits a = 60000 et b = 40000, quelle expression calcule leur produit exact en toute sécurité dans un long long courant sur 64 bits ?
  1. (long long)(a * b)
  2. (long long)a * b
  3. a * b
  4. (double)(a * b)
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 3 sur 10

Pour flags = 6u non signé, quel résultat produit flags & 2u == 0u ?
  1. 1
  2. 2
  3. 6
  4. 0
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

Pour des valeurs int quelconques a et b, quel test détecte un débordement d’addition signée avant d’évaluer a + b ?
  1. (b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b)
  2. (b > 0 && a > INT_MAX - b) || (b < 0 && a > INT_MIN - b)
  3. (b > 0 && a >= INT_MAX - b) || (b < 0 && a <= INT_MIN - b)
  4. (b > 0 && a < INT_MAX - b) || (b < 0 && a < INT_MIN - b)
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

Sur une plateforme où unsigned short est sur 16 bits et int sur 32 bits, deux variables unsigned short contiennent chacune 48000. Que se passe-t-il lorsque leur produit est évalué sans conversion explicite ?
  1. Elles sont multipliées en non signé et rebouclent modulo 65536.
  2. Elles sont promues en unsigned int et rebouclent à 2³².
  3. Elles sont promues en int, dont le produit déborde.
  4. Elles sont promues en long long et donnent le produit exact.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 6 sur 10

Un programme C17 ajoute un à un int déjà égal à INT_MAX. Sur quel résultat un code C portable peut-il compter ?
  1. Le résultat doit être INT_MIN sur tout système.
  2. Le résultat doit rester égal à INT_MAX.
  3. Le résultat doit être converti en unsigned int.
  4. Aucun résultat n’est garanti par le langage.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

Deux extrémités int positives ou nulles satisfont low <= high. Sur un système où int est sur 32 bits, elles valent 1 800 000 000 et 2 000 000 000. Quelle expression calcule leur milieu sans débordement ?
  1. (low + high) / 2
  2. low + (high - low) / 2
  3. low + high / 2
  4. low / 2 + high
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 8 sur 10

Un programme C17 convertit la valeur double représentable −6,875 en int. Quelle valeur est stockée ?
  1. −6
  2. −7
  3. 6
  4. −6,875
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 9 sur 10

Un accumulateur de factorielle int positif vaut f, et le multiplicateur suivant est l’int positif i. Quelle condition indique que f * i dépasserait INT_MAX avant d’effectuer la multiplication ?
  1. f >= INT_MAX / i
  2. f / i > INT_MAX
  3. f > INT_MAX / i
  4. f * i > INT_MAX
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 10 sur 10

Un calcul int donne la sortie attendue dans une construction, mais déclenche un signalement de débordement signé avec -fsanitize=undefined. Quelle conclusion doit guider la correction ?
  1. La construction réussie prouve que le résultat est portable.
  2. Une conversion printf plus large suffit à corriger le signalement.
  3. Le signalement garantit que l’int a rebouclé vers une valeur précise.
  4. Le débordement atteint doit être empêché dans le calcul.
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

Un programme demande deux mesures double dans un seul appel scanf. Quelle valeur de retour confirme que les deux affectations ont réussi ?
  1. 0
  2. 2
  3. 1
  4. EOF
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);
  1. 14
  2. 18
  3. 40
  4. 23
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 3 sur 10

Un int days sur 32 bits vaut 30000. Quelle expression calcule correctement sa durée en millisecondes dans un long long sur 64 bits ?
  1. 86400000LL * days
  2. (long long)(86400000 * days)
  3. 86400000 * days
  4. (long long)(86400 * days) * 1000
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++);
  1. Il affiche 4 et 5 car les arguments sont évalués de gauche à droite.
  2. Il affiche 5 et 4 car les arguments sont évalués de droite à gauche.
  3. Il a un comportement indéfini dû à des modifications non séquencées.
  4. Il affiche 4 et 5 ou 5 et 4 comme possibilités définies.
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");
  1. A only
  2. Both
  3. BothA only
  4. Rien ne s’affiche.
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++;
}
  1. À un, continue quitte la boucle avant le test suivant.
  2. À un, continue saute la mise à jour, donc i reste à un.
  3. À zéro, la condition empêche la première mise à jour.
  4. À quatre, le corps de la boucle remet i à un.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

Une valeur de stock non signée vaut 7u et une quantité demandée vaut 11u. Quelle règle évite un rebouclage du stock tout en permettant une soustraction valide lorsque le stock est suffisant ?
  1. Soustraire uniquement si stock >= requested.
  2. Soustraire, puis rejeter si le nouveau stock est négatif.
  3. Soustraire uniquement si stock <= requested.
  4. Soustraire, puis exiger un nouveau stock non nul.
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;
  1. 15
  2. 9
  3. 11
  4. 7
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;
    }
}
  1. 0
  2. 1
  3. 2
  4. 3
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;
  1. 70,0
  2. 0,7
  3. 14,0
  4. 0,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

91 Mots-Clés, chacun avec sa définition

A

accumulateur
Un accumulateur conserve un résultat cumulé au fil des passages d’une boucle, en partant d’une valeur initiale adaptée hors des opérations répétées.
L’accumulateur de somme des chiffres commence à zéro et ajoute un chiffre extrait à chaque passage de la boucle.
affectation
Une affectation calcule une valeur et la stocke dans une destination existante, en la convertissant dans le type de destination.
L’affectation count = count + 1; lit l’ancienne valeur et la remplace par une valeur supérieure de une unité.
affectation composée
Une affectation composée combine un opérateur et le stockage de son résultat dans la destination de gauche.
L’affectation composée total += item; met à jour le total cumulé sans modifier l’élément.
arithmétique vérifiée
L’arithmétique vérifiée effectue un calcul tout en indiquant si le résultat mathématique exact est représentable dans le type de destination choisi.
L’opération d’arithmétique vérifiée ckd_mul de C23 peut signaler un produit qui n’est pas représentable dans sa destination.
associativité
L’associativité détermine comment les opérateurs de même niveau de priorité sont regroupés, sans fixer généralement l’ordre d’évaluation.
L’associativité de gauche à droite de la division regroupe 24 / 4 / 2 sous la forme (24 / 4) / 2, ce qui donne trois.
avertissement
Un avertissement est un diagnostic qui signale un problème probable tout en permettant généralement à la construction de se poursuivre.
Un avertissement de format peut révéler que %d est associé à un argument double, une combinaison invalide.

B

bloc d’instructions
Un bloc d’instructions regroupe des déclarations et des instructions entre accolades afin qu’elles puissent former un seul corps contrôlé.
Un bloc d’instructions maintient un message et une mise à jour de compteur ensemble dans la même branche conditionnelle.
boucle do-while
Une boucle do-while exécute son corps avant de vérifier sa condition de répétition, ce qui garantit au moins une exécution du corps.
Une boucle do-while lit dans son corps la première réponse à vérifier avant de décider s’il faut la redemander.
boucle for
Une boucle for place l’initialisation, une condition de répétition et une mise à jour dans un même en-tête associé à un corps répété.
Une boucle for commençant à zéro avec i < n et i++ effectue n passages pour n positif ou nul lorsque les mises à jour sont représentables.
boucle imbriquée
Une boucle imbriquée est une boucle située dans une autre boucle, dont la séquence interne est exécutée à nouveau pour chaque passage externe atteint.
Une boucle imbriquée qui effectue quatre passages internes pour chacune de trois lignes exécute son corps interne douze fois.
boucle infinie
Une boucle infinie se répète sans atteindre de sortie, souvent parce que l’état testé ne change jamais comme prévu.
Un continue placé avant l’unique mise à jour peut créer une boucle infinie qui teste sans cesse une valeur inchangée.
boucle while
Une boucle while répète son corps tant qu’une condition est vraie, en vérifiant cette condition avant chaque exécution du corps.
Une boucle while qui traite des chiffres s’arrête lorsque la division entière a réduit la valeur restante à zéro.
branche
Une branche est un chemin d’instructions sélectionné par un choix, comme la partie if ou la partie else d’une instruction conditionnelle.
Un programme de notation comportant une chaîne else-if n’atteint qu’une branche, celle qui commence par la première condition vraie.

C

caractères blancs
Les caractères blancs comprennent notamment les espaces, les tabulations et les sauts de ligne, que les formats peuvent ignorer ou lire selon la conversion.
Les conversions numériques de scanf ignorent les caractères blancs initiaux, mais %c lit un saut de ligne restant sauf si on lui demande de l’ignorer.
chaîne de format
Une chaîne de format combine du texte littéral et des spécifications de conversion qui contrôlent la manière dont un appel d’entrée ou de sortie traite les valeurs.
La chaîne de format dans printf("Mass: %.1f kg\n", mass) fournit un intitulé, une précision décimale et une unité.
code de retour
Un code de retour est la valeur qu’un programme transmet à son environnement appelant lorsqu’il se termine, pour indiquer une réussite ou un échec.
Un code de retour de zéro provenant de main permet à un script appelant de reconnaître que l’exécution a réussi.
code source
Le code source est le texte C lisible par un humain qu’un compilateur traduit en instructions utilisées pour construire un programme.
Après avoir modifié dans le code source le message de bienvenue affiché par un programme, reconstruis le programme avant d’exécuter la nouvelle version.
compilateur
Un compilateur traduit un programme d’un langage source vers une autre forme, comme du code assembleur ou du code machine.
Le compilateur vérifie une déclaration C et signale un point-virgule manquant avant de créer un nouvel exécutable.
complément à deux
Le complément à deux est une représentation binaire signée dans laquelle le bit de poids fort a un poids négatif.
En complément à deux sur huit bits, la configuration 10000000 représente −128.
comportement défini par l’implémentation
Un comportement défini par l’implémentation est un choix que l’implémentation C doit documenter lorsque la norme autorise plusieurs comportements.
En C17, la conversion d’un entier hors plage en type signé a un comportement défini par l’implémentation ou déclenche un signal.
comportement indéfini
Un comportement indéfini se produit lorsqu’un programme C effectue une opération pour laquelle le langage n’impose aucune exigence.
Un produit signé qui déborde a un comportement indéfini même si une exécution sans optimisation semble reboucler.
condition
Une condition est une expression dont la valeur, nulle ou non nulle, détermine le chemin suivi par le programme.
La condition dans if (items) est fausse pour zéro, tandis qu’un dénombrement négatif non nul fait exécuter la branche.
constante caractère
Une constante caractère est un caractère écrit entre apostrophes dont la valeur est un code entier ; les constantes caractères ordinaires du C sont de type int.
La constante caractère ASCII 'A' peut s’afficher comme la lettre A avec %c ou comme le nombre 65 avec %d.
conversion élargissante
Une conversion élargissante convertit une valeur dans un type dont la plage représentable est suffisante avant sa participation à un calcul risqué.
La conversion élargissante de a en long long avant a * b change le type du produit ; convertir après le produit intervient trop tard.
conversion explicite (cast)
Une conversion explicite demande la conversion vers un type nommé, en plaçant ce type entre parenthèses avant une expression.
La conversion explicite dans (double)sum / count prend effet avant la division et conserve un quotient fractionnaire.
conversion hors plage
Une conversion hors plage tente de représenter une valeur source qui se situe hors des valeurs autorisées par le type de destination.
Une conversion hors plage d’un très grand double en int est indéfinie lorsque la partie entière n’est pas représentable.
conversion implicite
Une conversion implicite change automatiquement le type d’une valeur lorsqu’une opération ou une affectation exige un type différent.
Ajouter un int à un double applique une conversion implicite à l’opérande entier avant l’addition.
conversions arithmétiques usuelles
Les conversions arithmétiques usuelles choisissent des types communs pour les opérandes de nombreuses opérations arithmétiques et comparaisons.
Les conversions arithmétiques usuelles transforment un int négatif en valeur non signée lorsqu’il est comparé à un unsigned int.

D

débordement d’entier
Un débordement d’entier se produit lorsque le résultat mathématique d’un calcul entier se situe hors de la plage du type de résultat.
Un produit int peut provoquer un débordement d’entier avant que son résultat n’atteigne une destination long long.
décalage de bits
Un décalage de bits déplace la configuration de bits d’un entier vers la gauche ou la droite d’un nombre de positions soumis aux règles de largeur et de signe du type promu.
Décaler une valeur non signée de deux positions vers la droite supprime les deux bits de poids le plus faible de sa représentation.
déclaration
Une déclaration introduit un nom et son type afin que le C puisse vérifier comment l’objet ou la fonction nommé est utilisé.
La déclaration int count = 0; donne au nom un type entier et fournit aussi sa valeur initiale.
division entière
La division entière divise des opérandes entiers et supprime la partie fractionnaire du quotient par troncature vers zéro.
La division entière dans 22 / 5 donne quatre, même lorsque le résultat calculé est affecté à un double.

E

effet de bord
Un effet de bord est un changement, comme la modification de données stockées ou une opération d’entrée ou de sortie, qui se produit pendant l’évaluation d’une expression.
Une incrémentation modifie une variable comme effet de bord ; deux incrémentations conflictuelles nécessitent donc un ordre défini.
enchaînement des cas (fall-through)
L’enchaînement des cas désigne la poursuite de l’exécution d’un cas switch dans le code suivant sans transfert de contrôle intermédiaire.
L’enchaînement des cas après un break manquant peut exécuter l’affichage du cas suivant sans tester à nouveau son étiquette.
entier non signé
Un entier non signé représente uniquement des nombres entiers positifs ou nuls et utilise une réduction modulo la plage lorsque le calcul dépasse celle-ci.
Un entier non signé convient bien comme masque de bits, mais soustraire une quantité de stock excessive peut provoquer un rebouclage vers une valeur énorme.
entier signé
Un entier signé est un type de nombre entier qui représente des valeurs négatives ainsi que positives ou nulles dans une plage fixe.
Un entier signé peut stocker une température négative exprimée en nombre entier, mais les additions ultérieures doivent toujours rester dans sa plage.
entrée standard
L’entrée standard est le flux de texte entrant habituel du programme, souvent relié au clavier ou à une entrée redirigée.
Un appel scanf lit l’entrée standard et convertit les caractères correspondants suivants en valeurs stockées.
erreur d’arrondi
Une erreur d’arrondi est la différence introduite lorsqu’une valeur ou le résultat d’un calcul est remplacé par une valeur représentable proche.
Plusieurs additions décimales peuvent produire une petite erreur d’arrondi qui fait échouer un test d’égalité exacte.
erreur de compilation
Une erreur de compilation est un problème détecté pendant la traduction du code source qui empêche une nouvelle construction de réussir.
Une parenthèse fermante sans correspondance provoque une erreur de compilation avant qu’un nouvel exécutable puisse être construit.
erreur de décalage d’une unité
Une erreur de décalage d’une unité fait qu’une boucle ou un calcul de borne inclut une valeur de trop ou de moins.
Remplacer 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
Une erreur de logique amène un programme à calculer ou à choisir autre chose que le résultat voulu, même s’il s’exécute.
Tester les tranches de scores dans le mauvais ordre crée une erreur de logique qui affecte la mauvaise note.
exécutable
Un exécutable est un programme construit sous une forme que son système d’exploitation cible peut charger et exécuter sur le processeur prévu.
L’exécution de ./hello lance l’exécutable dans le dossier courant sans appeler à nouveau le compilateur C.

É

échec de correspondance
Un échec de correspondance se produit lorsque l’entrée disponible ne correspond pas à la conversion ou au caractère littéral attendu par scanf.
Des lettres au début d’une conversion %d provoquent un échec de correspondance et restent non lues pour une nouvelle tentative.
éditeur de liens
L’éditeur de liens combine le code objet et résout les références à des éléments de code et de données nommés pour produire un programme complet.
L’éditeur de liens résout la référence à printf ; son code de bibliothèque peut être fourni par une bibliothèque partagée.
étiquette case
Une étiquette case indique une expression constante entière distincte qui peut servir de point d’entrée dans une instruction switch.
L’étiquette case case 4: est valide en C17 ; une variable const int ordinaire ne peut pas fournir sa valeur.
étiquette default
Une étiquette default fournit un point d’entrée dans une instruction switch pour une valeur de contrôle qui ne correspond à aucune étiquette case.
Une étiquette default peut envoyer un numéro de menu inattendu vers un message d’erreur au lieu de sauter l’instruction switch.
évaluation en court-circuit
L’évaluation en court-circuit saute l’opérande de droite d’un opérateur logique lorsque son opérande de gauche détermine déjà le résultat.
L’évaluation en court-circuit dans count != 0 && sum / count > 4 saute la division lorsque count vaut zéro.

F

fin de l’entrée
La fin de l’entrée signifie que le flux entrant ne contient plus de caractères disponibles à lire ; elle est distincte d’un texte ordinaire sans correspondance.
Une boucle de saisie numérique s’arrête à la fin de l’entrée, mais peut aussi s’arrêter plus tôt sur un texte non numérique.

I

identificateur
Un identificateur est un nom utilisé dans le code C, comme un nom de variable, dont l’écriture suit des règles et distingue les majuscules des minuscules.
L’identificateur distance_m est valide ; 2distance ne peut pas être un nom car il commence par un chiffre.
incrémentation postfixée
Une incrémentation postfixée ajoute un à une variable tout en fournissant son ancienne valeur à l’expression qui la contient.
Avec n = 3, l’incrémentation postfixée dans old = n++; donne trois à old et laisse n à quatre.
initialisation
L’initialisation donne à un objet sa valeur initiale dans sa déclaration, avant que des affectations ultérieures ne la modifient.
L’initialisation de total à zéro avant une boucle fournit à la première addition une valeur de départ définie.
instruction break
Une instruction break quitte immédiatement l’instruction switch ou la boucle englobante la plus proche et poursuit l’exécution après cette construction.
Une instruction break dans un switch de menu quitte ce switch tandis que la boucle while englobante continue.
instruction continue
Une instruction continue saute le reste du corps de la boucle et passe à son prochain point de reprise.
Une instruction continue dans une boucle for atteint la mise à jour de l’en-tête ; dans une boucle while, elle revient au test.
instruction switch
Une instruction switch choisit une étiquette d’entrée en comparant une valeur de contrôle entière à des valeurs entières fixes de cas.
Une instruction switch peut associer la même action aux caractères de commande r minuscule et R majuscule.
itération
Une itération est une exécution du corps d’une boucle, après que son test de contrôle a autorisé le début de ce passage.
Une boucle for parcourant 3, 6, 9 et 12 comporte quatre itérations, même si la mise à jour finale du compteur atteint 15.

L

largeur de champ
Une largeur de champ est le nombre minimal de caractères réservé à une valeur de sortie formatée, signe et point décimal compris.
Une largeur de champ de six dans %6d ajoute quatre espaces avant 42, tandis qu’une valeur de sept chiffres s’affiche toujours en entier.
littéral entier
Un littéral entier est une constante entière écrite dans le code source avec une notation de base et, éventuellement, un suffixe de type.
Le littéral entier 0x30 utilise l’hexadécimal, tandis que 48u écrit cette valeur en décimal non signé.

M

macro
Une macro définit un remplacement par le préprocesseur, substitué dans le code source ultérieur avant la compilation normale.
Avec #define LIMIT 8, le lexème LIMIT est remplacé par 8 partout où cette macro s’applique.
masque de bits
Un masque de bits est une configuration entière utilisée pour sélectionner, activer, désactiver ou examiner certains bits d’une autre valeur.
Le 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
La mise en tampon des sorties conserve temporairement le texte produit avant de l’envoyer, ce qui peut retarder une invite sans saut de ligne.
Une invite peut rester dans le tampon de sortie jusqu’à ce que fflush(stdout) l’envoie avant un appel de lecture.

N

NaN
Un NaN est une valeur à virgule flottante IEEE signifiant « pas un nombre », utilisée pour des résultats comme zéro divisé par zéro.
Un NaN est considéré comme différent de lui-même ; l’égalité ordinaire ne peut donc pas le traiter comme une valeur mesurée ordinaire.
nombre d’affectations réussies
Le nombre d’affectations réussies renvoyé par scanf est le nombre de destinations remplies avec succès avant l’arrêt de l’appel.
Un nombre d’affectations réussies égal à deux lors de la lecture de trois mesures ne confirme pas la troisième destination.

O

opérande
Un opérande est une valeur ou une expression à laquelle un opérateur est appliqué ; son type contribue à déterminer le comportement de l’opération.
Dans sum / count, les types des opérandes déterminent si la division conserve un résultat fractionnaire.
opérateur bit à bit
Un opérateur bit à bit agit sur les bits individuels d’opérandes entiers pour combiner, inverser ou décaler leurs configurations de bits.
L’opérateur bit à bit & ne conserve que les bits actifs communs à douze et dix, ce qui donne huit.
opérateur conditionnel
L’opérateur conditionnel utilise condition ? a : b pour choisir une expression de valeur après avoir testé la condition.
Avec une condition nulle, l’opérateur conditionnel dans condition ? a : b évalue b et saute a.
opérateur logique
Un opérateur logique combine ou inverse des valeurs de vérité et produit zéro ou un au moyen de &&, || ou !.
L’opérateur logique dans ready && valid teste si les deux valeurs stockées sont non nulles.
opérateur relationnel
Un opérateur relationnel compare des valeurs numériques à l’aide de <, <=, > ou >= et produit l’int zéro ou un.
L’opérateur relationnel dans height >= 1 vérifie une borne ; une autre comparaison vérifie l’autre.
opérateur virgule
L’opérateur virgule évalue son opérande de gauche avant celui de droite et fournit la valeur de l’opérande de droite.
L’opérateur virgule ordonne les mises à jour dans i++, j-- ; les virgules entre les arguments de printf ne le font pas.
ordre d’évaluation
L’ordre d’évaluation décrit quand les opérandes sont évalués et les effets de bord se produisent ; c’est une question distincte du regroupement de l’expression.
En C17, les arguments d’une fonction n’ont pas d’ordre d’évaluation imposé de gauche à droite malgré leur position dans le code.

P

portée de bloc
La portée de bloc rend un nom local disponible de sa déclaration jusqu’à la fin du bloc qui le contient.
La portée de bloc empêche d’utiliser une variable temporaire déclarée dans un bloc if après la fin de ce bloc.
précision
La précision est la quantité d’informations significatives qu’un format numérique peut conserver ; elle est distincte de sa plus grande valeur en grandeur.
La précision limitée de float fait perdre les pas d’un entier à des grandeurs encore bien inférieures au maximum du type.
préprocesseur
Le préprocesseur traite les directives comme #include et le remplacement des macros avant les principales étapes de traduction du C.
Le préprocesseur insère les déclarations d’un en-tête standard avant que le compilateur vérifie leur utilisation.
priorité des opérateurs
La priorité des opérateurs détermine quels opérateurs sont liés le plus étroitement à leurs opérandes lorsque les parenthèses ne précisent pas le regroupement.
La priorité supérieure de la multiplication regroupe 4 * 2 avant l’addition dans 3 + 4 * 2.
promotion entière
Une promotion entière convertit un petit type entier en int, ou en unsigned int si int ne peut pas représenter toute sa plage.
Sur un système où short est sur 16 bits et int sur 32 bits, la promotion entière permet aux opérandes unsigned short d’être multipliés en int.

R

rebouclage des entiers non signés
Le rebouclage des entiers non signés est la réduction modulo d’un résultat arithmétique non signé, qui le ramène dans la plage positive ou nulle du type.
Soustraire un à un zéro non signé illustre le rebouclage des entiers non signés en produisant la valeur maximale.
reste
Un reste est la valeur produite par l’opération entière % du C ; lorsqu’il est non nul, il a le signe du dividende.
Le reste -22 % 5 vaut −2 car le quotient tronqué vaut −4 et l’identité de la division doit être respectée.
résultat intermédiaire
Un résultat intermédiaire est une valeur calculée dans une expression plus grande avant une opération ou une affectation ultérieure.
La somme dans (low + high) / 2 est un résultat intermédiaire qui peut déborder même lorsque le milieu est représentable.

S

sanitizer
Un sanitizer est un outil de vérification à l’exécution ajouté pendant la compilation qui signale certaines opérations invalides sur les chemins exécutés.
Un sanitizer de comportement indéfini peut repérer la multiplication où un calcul de compteur signé déborde.
sentinelle
Une sentinelle est une valeur désignée qui signale la fin des données et est reconnue avant le traitement ordinaire.
Une sentinelle zéro peut terminer un calcul de moyenne sans augmenter sa somme ni son nombre de données.
séquence d’échappement
Une séquence d’échappement utilise une barre oblique inverse dans un littéral caractère ou de chaîne C pour représenter un caractère spécial.
La 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)
Un sinon pendant est un else dans des instructions conditionnelles imbriquées dont le rattachement peut être ambigu pour un lecteur en l’absence d’accolades.
La règle du sinon pendant rattache un else au if sans else le plus proche, quelle que soit l’indentation.
spécification de conversion
Une spécification de conversion est une partie d’un format commençant par un signe pour cent qui indique le type et la présentation requis d’une valeur.
La spécification de conversion %lld correspond à un argument long long ; %d ne peut pas le convertir automatiquement.

T

test aux limites
Un test aux limites utilise des valeurs situées à une borne pertinente ou à proximité afin de vérifier le comportement là où une règle ou une plage change.
Un test aux limites pour une addition comprend une somme égale à INT_MAX et une paire qui le dépasserait de un.
tolérance
Une tolérance est une différence numérique autorisée, choisie selon l’ordre de grandeur et l’exactitude exigée pour une comparaison.
Le programme compare fabs(measured - target) à la tolérance au lieu d’exiger une égalité exacte.
trace d’exécution
Une trace d’exécution consigne les changements de valeurs et les étapes exécutées d’un programme afin de vérifier son comportement dans l’ordre.
Dans une boucle de somme des chiffres, une trace des chiffres restants et de la somme cumulée après chaque passage aide à trouver une mise à jour incorrecte.
troncature vers zéro
La troncature vers zéro supprime la partie fractionnaire d’un nombre, rapprochant ainsi un nombre négatif de zéro.
La troncature vers zéro fait que la conversion de la valeur représentable −7,9 en int donne −7 au lieu de −8.
type entier de largeur fixe
Un type entier de largeur fixe est un typedef entier, lorsqu’il est disponible, dont le nom fixe la largeur exacte en bits et indique s’il est signé.
Le type entier de largeur fixe uint8_t, lorsqu’il est fourni, contient les valeurs de zéro à 255.

V

valeur à virgule flottante
Une valeur à virgule flottante représente une grandeur réelle à l’aide d’une précision fixe et d’une échelle, souvent avec un arrondi.
Une longueur stockée sous forme de valeur à virgule flottante peut approcher la valeur décimale saisie sans lui correspondre exactement.
valeur booléenne
Une valeur booléenne représente faux ou vrai ; la conversion en bool du C transforme zéro en faux et toute valeur non nulle en vrai.
Après l’inclusion de <stdbool.h> en C17, bool ready = 2; stocke la valeur booléenne vrai.
variable
Une variable est un emplacement de stockage nommé dont le type déclaré détermine comment sa valeur stockée est interprétée.
La variable mass_kg stocke une mesure tandis que count enregistre le nombre d’échantillons saisis.
vérification des bornes
Une vérification des bornes teste si les entrées ou un résultat envisagé respectent les bornes avant qu’une opération risquée soit effectuée.
Une vérification des bornes du stock compare les quantités disponible et demandée avant qu’une soustraction non signée puisse reboucler.

À propos de cette unité

Par StudeliSignaler une erreur

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.

Commencer