TD/TP 1 en C : 20 exercices et 3 questions de cours corrigés

TD/TP n° 1 : affectations, variables, expressions arithmétiques et logiques, entrées/sorties et structures conditionnelles.

20 exercices + 3 questions de coursTous les cours C

Avant de commencer : lire et exécuter le C

Une variable est une zone de stockage typée. Une déclaration telle que double x; réserve cette zone mais ne lui donne pas une valeur utilisable. Une affectation x = 2.5; écrit une valeur. Le signe = affecte ; == compare. Les instructions s’exécutent dans l’ordre, sauf lorsqu’un choix modifie le chemin suivi.

Le socle commun à tous les programmes
Instruction communeRôle et rappel
#include <stdio.h>Rend visibles les déclarations des fonctions d’entrées/sorties ; ce n’est pas une instruction exécutée à chaque calcul.
#include <math.h>Déclare les fonctions mathématiques et les outils isfinite et fabs utilisés pour vérifier les réels.
int main(void)Point d’entrée : la fonction ne reçoit aucun paramètre et renvoie un état de terminaison entier.
scanf(...) != nombre_attenduContrôle le nombre de conversions réussies. Le retour 1 signifie une variable lue, pas la valeur saisie.
&x dans scanfFournit l’adresse de x : scanf doit écrire dans la variable. printf utilise la valeur, sans &.
return 1;Arrête immédiatement le programme en signalant une erreur. Les calculs placés après ne sont pas exécutés.
return 0;Termine normalement le programme. Cette valeur n’est pas affichée à l’écran.
{ ... } et ;Les accolades délimitent un bloc ; le point-virgule termine une instruction. L’indentation rend le parcours lisible.
Les formats de lecture et d’affichage ne sont pas interchangeables
Type de variableLecture scanfAffichage printfAttention
int%d et int *%dEntier signé ; la saisie doit être représentable dans int.
unsigned int%u et unsigned int *%uUtilisé pour une valeur absolue pouvant dépasser INT_MAX.
long long%lld et long long *%lldEntier large utilisé pour le calcul du salaire.
float%f et float *%fÀ l’appel de printf, float est promu en double.
double%lf et double *%f ou %.6f%.6f fixe six chiffres après le point ; %lf n’est pas nécessaire dans printf.
char %c et char *%cL’espace avant %c ignore les espaces et retours à la ligne en attente.

Domaine des exemples. Pour rester sur les notions du TD, les lectures entières supposent un token entier représentable dans le type demandé. Vérifier le retour de scanf détecte un échec de conversion, mais ne constitue pas un analyseur complet de ligne ni un contrôle portable des dépassements lors de la conversion. Les réels des nouvelles solutions sont finis et bornés à ±1 000 000 ; les codes contrôlent cette borne, puis les contraintes propres à l’exercice. Utiliser un point décimal, sans séparateur de milliers. Les sorties utilisent volontairement des caractères ASCII pour rester faciles à compiler sur plusieurs systèmes.

Compilation conseillée avec GCC :

gcc -std=c11 -Wall -Wextra -Wpedantic exercice.c -o exercice -lm
# Puis, sous Linux/macOS :
./exercice
# Sous Windows PowerShell :
.\exercice.exe

Le drapeau -lm permet de lier la bibliothèque mathématique sur les systèmes qui le demandent, notamment pour sqrt. Les sorties de tests ci-dessous ne répètent pas l’invite de saisie : les tableaux montrent l’entrée et le résultat qui suit. Chaque ligne d’entrée peut être tapée sur une ou plusieurs lignes, sauf le caractère opérateur qui vient après les deux nombres.

Cours pour démarrer : Structure d’un programme C · Variables et affectation · Types de données · Entrées et sorties : printf et scanf.

Deux niveaux de correction. La version simple (naïve) suppose une saisie correcte et les conditions indiquées au-dessus du programme. La version plus générale conserve les contrôles et les cas particuliers déjà expliques. Simplifier ne signifie pas introduire un mauvais format printf, une division entiere involontaire ou une expression au comportement indéfini.

Les blocs Sortie écran reproduisent une session : les valeurs tapées au clavier figurent apres les invites et ne sont pas des valeurs imprimées par le programme. Chaque essai correspond a un nouveau lancement.

Partie 1 : Affichage et calculs

Exercice 1 : Analyser six programmes, affichages et calculs

Que produit chacun des six programmes suivants ? Pour ceux qui lisent des nombres, donner le comportement selon les entrées. Voici les programmes du support, avec leur logique et leurs formats conservés :

Programme a du sujet

#include <stdio.h>
int main() {
    printf("*****\n");
    printf("****\n");
    printf("***\n");
    printf("**\n");
    printf("*\n");
    return 0;
}

Programme b du sujet

#include <stdio.h>
int main() {
    printf("*****\n");
    printf("*      *\n");
    printf("*      *\n");
    printf("*      *\n");
    printf("*****\n");
    return 0;
}

Programme c du sujet

#include <stdio.h>
int main() {
    float a, b, max;
    printf("Enter two numbers");
    scanf("%f", &a);
    scanf("%f", &b);
    if (a < b) { max = b; }
    else { max = a; }
    printf("maximum =%d", max);
    return 0;
}

Programme d du sujet

#include <stdio.h>
int main() {
    float a, b, c, sum;
    printf("Enter thre numbers");
    scanf("%f", &a);
    scanf("%f", &b);
    scanf("%f", &c);
    sum = a + b + c;
    printf("sum=%f", sum);
    return 0;
}

Programme e du sujet

#include <stdio.h>
#define TEN 10
int main() {
    float moyenne;
    printf("Enter the average");
    scanf("%f", &moyenne);
    if (moyenne < TEN) { printf("Echec"); }
    else { printf("Succès"); }
    return 0;
}

Programme f du sujet

#include <stdio.h>
#define PI 3.14
int main() {
    float r, s;
    printf("Enter the radius");
    scanf("%f", &r);
    s = r * r * PI;
    printf("result=%f\n", s);
    return 0;
}
Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Une chaîne entre guillemets est affichée littéralement, sauf les séquences comme \n. Une entrée au clavier n’est pas connue avant l’exécution : les programmes c à f n’ont donc pas une sortie numérique unique. Un if choisit un seul de ses deux blocs.

À revoir dans les cours : Structure d’un programme C · Entrées et sorties : printf et scanf · Types de données · Structures de contrôle : if et else · Constantes et littéraux.

Erreur importante du programme c original : printf("maximum =%d", max) attend un int alors que l’argument est un double après promotion du float. Le comportement est indéfini : on ne peut pas prédire une valeur correcte, ni conclure à une simple troncature. La correction utilise %f. Les autres programmes sont analysés pour des saisies valides ; les versions complètes ci-dessous ajoutent des contrôles de lecture et des retours à la ligne.

Pour a et b, l’affichage est entièrement déterminé. Pour d, la sortie originale commence par Enter thre numbers puis sum=... sans nouvelle ligne imposée entre les deux. Pour e, elle commence par Enter the average puis Echec ou Succès. Pour f, elle commence par Enter the radius puis result=.... Les versions corrigées séparent proprement les invites et les résultats.

Ce programme ne lit aucune valeur et ne calcule aucun choix : la version simple et la version générale sont identiques. Les deux affichages restent exactement ceux expliqués ci-dessous.

Version plus générale : 1a, Triangle d’étoiles

1a : Triangle d’étoiles : programme complet

#include <stdio.h>
int main(void) {
    printf("*****\n");
    printf("****\n");
    printf("***\n");
    printf("**\n");
    printf("*\n");
    return 0;
}
Comprendre 1a : Triangle d’étoiles
Instruction ou étapeExplication et état du programme
#include et mainstdio.h déclare printf ; main est exécutée au lancement. Ici aucune saisie n’est nécessaire.
printf("*****\n");Écrit cinq étoiles puis passe à la ligne. Le est un caractère de contrôle ; les deux signes barre oblique et n ne s’impriment pas.
Les quatre printf suivantsImpriment successivement 4, 3, 2 et 1 étoile. Après chaque appel, le curseur commence une nouvelle ligne.
return 0Le dessin est terminé ; le programme se termine normalement.
Exécutions commentées : 1a, Triangle d’étoiles
SaisieRésultat après l’invitePourquoi ce résultat ?
(aucune)***** **** *** ** *L’affichage a cinq lignes de longueurs décroissantes ; aucune variable ne change.

Sorties écran : 1a, Triangle d’étoiles

Exécution 1. L’affichage a cinq lignes de longueurs décroissantes ; aucune variable ne change.

*****
****
***
**
*

Ce programme ne lit aucune valeur et ne calcule aucun choix : la version simple et la version générale sont identiques. Les deux affichages restent exactement ceux expliqués ci-dessous.

Version plus générale : 1b, Cadre tel qu’écrit

1b : Cadre tel qu’écrit : programme complet

#include <stdio.h>
int main(void) {
    printf("*****\n");
    printf("*      *\n");
    printf("*      *\n");
    printf("*      *\n");
    printf("*****\n");
    return 0;
}
Comprendre 1b : Cadre tel qu’écrit
Instruction ou étapeExplication et état du programme
printf("*****\n")La première et la dernière ligne contiennent cinq étoiles.
printf("* *\n")Chaque ligne intérieure contient une étoile, six espaces, puis une étoile : elle mesure huit colonnes.
Les trois appels identiquesLa même ligne intérieure est répétée trois fois, sans boucle. Les espaces d’une chaîne sont conservés.
Différence entre intention et codeLe sujet semble vouloir un cadre, mais les largeurs 5 et 8 ne coïncident pas. On décrit le code réellement écrit.
Exécutions commentées : 1b, Cadre tel qu’écrit
SaisieRésultat après l’invitePourquoi ce résultat ?
(aucune)***** * * * * * * *****Les deux bordures ne couvrent pas la largeur intérieure. Pour un carré de largeur 5, il faudrait trois espaces entre les étoiles.

Sorties écran : 1b, Cadre tel qu’écrit

Exécution 1. Les deux bordures ne couvrent pas la largeur intérieure. Pour un carré de largeur 5, il faudrait trois espaces entre les étoiles.

*****
*      *
*      *
*      *
*****

Version simple (naïve) : 1c, Maximum, version corrigée

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme.

#include <stdio.h>
#include <math.h>

int main(void) {
    float a, b, max;
    puts("Entrer a b :");
    scanf("%f %f", &a, &b);
    if (a < b) {
        max = b;
    } else {
        max = a;
    }
    printf("maximum =%f\n", max);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : 1c, Maximum, version corrigée - version simple

Exécution 1. Le deuxième nombre est le plus grand.

Entrer a b :
2.5 7.25
maximum =7.250000

Exécution 2. Le bloc else affecte a à max.

Entrer a b :
8 3
maximum =8.000000

Exécution 3. L’égalité suit else sans changer la réponse.

Entrer a b :
4 4
maximum =4.000000

Version plus générale : 1c, Maximum, version corrigée

1c : Maximum, version corrigée : programme complet

#include <stdio.h>
#include <math.h>

int main(void) {
    float a, b, max;
    puts("Entrer a b :");
    if (scanf("%f %f", &a, &b) != 2) {
        puts("Saisie invalide.");
        return 1;
    }
    if (!isfinite(a) || fabs(a) > 1000000.0 || !isfinite(b) || fabs(b) > 1000000.0) {
        puts("Valeur hors domaine.");
        return 1;
    }
    if (a < b) {
        max = b;
    } else {
        max = a;
    }
    printf("maximum =%f\n", max);
    return 0;
}
Comprendre 1c : Maximum, version corrigée
Instruction ou étapeExplication et état du programme
float a, b, maxa et b reçoivent les deux réels. max n’est affecté qu’après la comparaison.
scanf et contrôleDeux conversions %f sont attendues. Si une lecture échoue, le calcul est abandonné avant d’utiliser une valeur non initialisée.
if (a < b)Avec a = 2.5 et b = 7.25, le test est vrai : max devient b. Le bloc else est ignoré.
elseAvec a = 8 et b = 3, le test est faux : max devient a. Si a et b sont égaux, prendre a donne aussi le maximum.
printf("maximum =%f\n", max)Le format attend un double ; max, de type float, est automatiquement promu en double à cet appel. Les six décimales sont le format par défaut.
Exécutions commentées : 1c, Maximum, version corrigée
SaisieRésultat après l’invitePourquoi ce résultat ?
2.5 7.25maximum =7.250000Le deuxième nombre est le plus grand.
8 3maximum =8.000000Le bloc else affecte a à max.
4 4maximum =4.000000L’égalité suit else sans changer la réponse.
abcSaisie invalide.Aucune conversion numérique n’a réussi.

Sorties écran : 1c, Maximum, version corrigée

Exécution 1. Le deuxième nombre est le plus grand.

Entrer a b :
2.5 7.25
maximum =7.250000

Exécution 2. Le bloc else affecte a à max.

Entrer a b :
8 3
maximum =8.000000

Exécution 3. L’égalité suit else sans changer la réponse.

Entrer a b :
4 4
maximum =4.000000

Exécution 4. Aucune conversion numérique n’a réussi.

Entrer a b :
abc
Saisie invalide.

Version simple (naïve) : 1d, Somme de trois réels

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme.

#include <stdio.h>
#include <math.h>

int main(void) {
    float a, b, c, sum;
    puts("Entrer a b c :");
    scanf("%f %f %f", &a, &b, &c);
    sum = a + b + c;
    printf("sum=%f\n", sum);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : 1d, Somme de trois réels - version simple

Exécution 1. 1.5 + 2.25 + 3 = 6.75.

Entrer a b c :
1.5 2.25 3
sum=6.750000

Exécution 2. Les termes se compensent.

Entrer a b c :
-2 1 1
sum=0.000000

Version plus générale : 1d, Somme de trois réels

1d : Somme de trois réels : programme complet

#include <stdio.h>
#include <math.h>

int main(void) {
    float a, b, c, sum;
    puts("Entrer a b c :");
    if (scanf("%f %f %f", &a, &b, &c) != 3) {
        puts("Saisie invalide.");
        return 1;
    }
    if (!isfinite(a) || fabs(a) > 1000000.0 || !isfinite(b) || fabs(b) > 1000000.0 || !isfinite(c) || fabs(c) > 1000000.0) {
        puts("Valeur hors domaine.");
        return 1;
    }
    sum = a + b + c;
    printf("sum=%f\n", sum);
    return 0;
}
Comprendre 1d : Somme de trois réels
Instruction ou étapeExplication et état du programme
Les quatre variables floatLes trois entrées et la somme sont des flottants, donc certains décimaux ne sont représentés qu’approximativement.
sum = a + b + cAvec 1.5, 2.25 et 3.0, la somme vaut 6.75 ; l’affectation écrit ce résultat dans sum.
printf("sum=%f\n", sum)Affiche six décimales. Dans le code original il n’y a pas de retour à la ligne après le résultat ; la version proposée en ajoute un.
Exécutions commentées : 1d, Somme de trois réels
SaisieRésultat après l’invitePourquoi ce résultat ?
1.5 2.25 3sum=6.7500001.5 + 2.25 + 3 = 6.75.
-2 1 1sum=0.000000Les termes se compensent.

Sorties écran : 1d, Somme de trois réels

Exécution 1. 1.5 + 2.25 + 3 = 6.75.

Entrer a b c :
1.5 2.25 3
sum=6.750000

Exécution 2. Les termes se compensent.

Entrer a b c :
-2 1 1
sum=0.000000

Version simple (naïve) : 1e, Réussite et seuil

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme.

#include <stdio.h>
#include <math.h>
#define TEN 10

int main(void) {
    float moyenne;
    puts("Entrer moyenne :");
    scanf("%f", &moyenne);
    if (moyenne < TEN) {
        puts("Echec");
    } else {
        puts("Succes");
    }
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : 1e, Réussite et seuil - version simple

Exécution 1. 9.5 < 10 est vrai.

Entrer moyenne :
9.5
Echec

Exécution 2. 10 < 10 est faux : le seuil est inclus dans le succès.

Entrer moyenne :
10
Succes

Exécution 3. Le bloc else est exécuté.

Entrer moyenne :
15
Succes

Version plus générale : 1e, Réussite et seuil

1e : Réussite et seuil : programme complet

#include <stdio.h>
#include <math.h>
#define TEN 10

int main(void) {
    float moyenne;
    puts("Entrer moyenne :");
    if (scanf("%f", &moyenne) != 1) {
        puts("Saisie invalide.");
        return 1;
    }
    if (!isfinite(moyenne) || fabs(moyenne) > 1000000.0) {
        puts("Valeur hors domaine.");
        return 1;
    }
    if (moyenne < TEN) {
        puts("Echec");
    } else {
        puts("Succes");
    }
    return 0;
}
Comprendre 1e : Réussite et seuil
Instruction ou étapeExplication et état du programme
#define TEN 10Le préprocesseur remplace TEN par 10. Une macro ne réserve pas une variable et ne se termine pas par un point-virgule.
moyenne < TENUne moyenne strictement inférieure à 10 suit le premier bloc. Le sujet n’impose ici aucun intervalle de notes supplémentaire.
elseContient tous les autres réels du domaine de l’exemple : 10 est donc un succès. À la différence de <, <= aurait classé 10 en échec.
putsAffiche sa chaîne et ajoute un saut de ligne. Le sujet affiche Succès ; la solution emploie Succes pour la portabilité de l’encodage.
Exécutions commentées : 1e, Réussite et seuil
SaisieRésultat après l’invitePourquoi ce résultat ?
9.5Echec9.5 < 10 est vrai.
10Succes10 < 10 est faux : le seuil est inclus dans le succès.
15SuccesLe bloc else est exécuté.

Sorties écran : 1e, Réussite et seuil

Exécution 1. 9.5 < 10 est vrai.

Entrer moyenne :
9.5
Echec

Exécution 2. 10 < 10 est faux : le seuil est inclus dans le succès.

Entrer moyenne :
10
Succes

Exécution 3. Le bloc else est exécuté.

Entrer moyenne :
15
Succes

Version simple (naïve) : 1f, Aire d’un disque

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme. Le rayon est supposé positif ou nul.

#include <stdio.h>
#include <math.h>
#define PI 3.14

int main(void) {
    float r, s;
    puts("Entrer r :");
    scanf("%f", &r);
    s = r * r * PI;
    printf("result=%f\n", s);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : 1f, Aire d’un disque - version simple

Exécution 1. Aire = 3.14 × 2 × 2, arrondie à six décimales.

Entrer r :
2
result=12.560000

Exécution 2. Un rayon nul donne une aire nulle.

Entrer r :
0
result=0.000000

Version plus générale : 1f, Aire d’un disque

1f : Aire d’un disque : programme complet

#include <stdio.h>
#include <math.h>
#define PI 3.14

int main(void) {
    float r, s;
    puts("Entrer r :");
    if (scanf("%f", &r) != 1) {
        puts("Saisie invalide.");
        return 1;
    }
    if (!isfinite(r) || fabs(r) > 1000000.0) {
        puts("Valeur hors domaine.");
        return 1;
    }
    if (r < 0.0f) {
        puts("Rayon invalide.");
        return 1;
    }
    s = r * r * PI;
    printf("result=%f\n", s);
    return 0;
}
Comprendre 1f : Aire d’un disque
Instruction ou étapeExplication et état du programme
PI = 3.14On conserve l’approximation imposée par le sujet. La constante 3.14 est un double ; le résultat de l’expression est ensuite converti en float lors de l’affectation à s.
r < 0Un rayon géométrique ne peut pas être négatif. Le code original ne le contrôlait pas ; la solution refuse cette saisie.
s = r * r * PIPour r = 2 : r*r = 4 ; 4*3.14 ≈ 12.56. La formule calcule une aire, pas le volume d’une sphère.
Affichage %fUn arrondi binaire peut être visible au sixième chiffre. Sur l’environnement de test, r = 2 affiche 12.560000.
Exécutions commentées : 1f, Aire d’un disque
SaisieRésultat après l’invitePourquoi ce résultat ?
2result=12.560000Aire = 3.14 × 2 × 2, arrondie à six décimales.
0result=0.000000Un rayon nul donne une aire nulle.
-1Rayon invalide.La garde arrête le programme.

Sorties écran : 1f, Aire d’un disque

Exécution 1. Aire = 3.14 × 2 × 2, arrondie à six décimales.

Entrer r :
2
result=12.560000

Exécution 2. Un rayon nul donne une aire nulle.

Entrer r :
0
result=0.000000

Exécution 3. La garde arrête le programme.

Entrer r :
-1
Rayon invalide.

Exercice 2 : Valeur absolue d’un entier

Lire un entier et afficher sa valeur absolue.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

La valeur absolue mesure la distance à zéro : |n| = n si n ≥ 0 et |n| = −n si n < 0. Toutefois, sur les machines usuelles, INT_MIN n’a pas d’opposé représentable dans int. Une correction complète doit traiter ce cas sans faire déborder le type signé.

À revoir dans les cours : Types de données · Structures de contrôle : if et else · Variables et affectation.

Le schéma naïf if (n < 0) n = -n; suffit sur de petites valeurs, mais ne couvre pas tout le domaine de int. Ne pas supposer que abs(INT_MIN) résout le problème : l’opposé reste non représentable. La valeur numérique d’INT_MIN dépend de l’implémentation ; le programme ci-dessous ne la code pas en dur.

Version simple (naïve) : Valeur absolue sans débordement signé

On suppose un entier valide dont la valeur absolue est representable dans int : on exclut INT_MIN sur les machines usuelles. L'instruction n=-n suffit alors pour les valeurs négatives ordinaires. La version générale conserve le traitement sans débordement de ce cas limite.

#include <stdio.h>

int main(void) {
    int n;
    puts("Entrer n :");
    scanf("%d", &n);
    if (n < 0) { n = -n; }
    printf("Valeur absolue = %d\n", n);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Valeur absolue sans débordement signé - version simple

Exécution 1. La branche négative construit 7.

Entrer n :
-7
Valeur absolue = 7

Exécution 2. Zéro est déjà positif ou nul.

Entrer n :
0
Valeur absolue = 0

Exécution 3. La branche else recopie 12.

Entrer n :
12
Valeur absolue = 12

Version plus générale : Valeur absolue sans débordement signé

Valeur absolue sans débordement signé : programme complet

#include <stdio.h>

int main(void) {
    int n;
    unsigned int magnitude;
    puts("Entrer n :");
    if (scanf("%d", &n) != 1) {
        puts("Saisie invalide.");
        return 1;
    }
    if (n < 0) {
        magnitude = (unsigned int)(-(n + 1)) + 1U;
    } else {
        magnitude = (unsigned int)n;
    }
    printf("Valeur absolue = %u\n", magnitude);
    return 0;
}
Comprendre Valeur absolue sans débordement signé
Instruction ou étapeExplication et état du programme
int n ; unsigned int magnitudeLa saisie est signée ; le résultat est non signé afin de pouvoir représenter |INT_MIN|.
n < 0Avec −7, le test est vrai : on doit construire 7. Avec 0 ou 5, on recopie simplement la valeur.
-(n + 1)Pour −7 : n+1 = −6, puis son opposé = 6. Même pour le plus petit int, n+1 et son opposé sont représentables.
(unsigned int)(...) + 1UConvertit le résultat en non signé avant d’ajouter 1. Avec −7 : 6U+1U = 7U. On n’évalue jamais -INT_MIN en arithmétique signée.
printf avec %uCorrespond à unsigned int. Utiliser %d pour magnitude serait une mauvaise association de types.
Exécutions commentées : Valeur absolue sans débordement signé
SaisieRésultat après l’invitePourquoi ce résultat ?
-7Valeur absolue = 7La branche négative construit 7.
0Valeur absolue = 0Zéro est déjà positif ou nul.
12Valeur absolue = 12La branche else recopie 12.
-2147483648Valeur absolue = 2147483648Cas INT_MIN sur l’environnement testé à int 32 bits ; le résultat dépasse INT_MAX.

Sorties écran : Valeur absolue sans débordement signé

Exécution 1. La branche négative construit 7.

Entrer n :
-7
Valeur absolue = 7

Exécution 2. Zéro est déjà positif ou nul.

Entrer n :
0
Valeur absolue = 0

Exécution 3. La branche else recopie 12.

Entrer n :
12
Valeur absolue = 12

Exécution 4. Cas INT_MIN sur l’environnement testé à int 32 bits ; le résultat dépasse INT_MAX.

Entrer n :
-2147483648
Valeur absolue = 2147483648

Exercice 3 : Volume d’une sphère

Lire le rayon R et calculer V = (4/3) × π × R × R × R.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Une formule mathématique doit être traduite avec des opérandes de types adaptés. En C, 4 / 3 divise deux entiers et donne 1. 4.0 / 3.0 effectue la division réelle attendue. La multiplication s’écrit explicitement *.

À revoir dans les cours : Types de données · Entrées et sorties : printf et scanf · Constantes et littéraux.

L’unité est celle du rayon au cube : un rayon en centimètres donne un volume en cm³. On écrit r*r*r, et non r^3 : ^ est un opérateur binaire de ou exclusif sur les entiers, pas une puissance.

Version simple (naïve) : Volume de la sphère

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme. Le rayon est supposé positif ou nul.

#include <stdio.h>
#include <math.h>

int main(void) {
    double r, volume;
    const double pi = 3.141592653589793;
    puts("Entrer r :");
    scanf("%lf", &r);
    volume = (4.0 / 3.0) * pi * r * r * r;
    printf("Volume = %.6f\n", volume);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Volume de la sphère - version simple

Exécution 1. Le cube de 2 vaut 8 ; le volume vaut 32π/3.

Entrer r :
2
Volume = 33.510322

Exécution 2. Le produit contient un facteur nul.

Entrer r :
0
Volume = 0.000000

Version plus générale : Volume de la sphère

Volume de la sphère : programme complet

#include <stdio.h>
#include <math.h>

int main(void) {
    double r, volume;
    const double pi = 3.141592653589793;
    puts("Entrer r :");
    if (scanf("%lf", &r) != 1) {
        puts("Saisie invalide.");
        return 1;
    }
    if (!isfinite(r) || fabs(r) > 1000000.0) {
        puts("Valeur hors domaine.");
        return 1;
    }
    if (r < 0.0) {
        puts("Rayon invalide.");
        return 1;
    }
    volume = (4.0 / 3.0) * pi * r * r * r;
    printf("Volume = %.6f\n", volume);
    return 0;
}
Comprendre Volume de la sphère
Instruction ou étapeExplication et état du programme
const double piUne constante typée donne une approximation de π avec davantage de précision que 3.14. On ne modifie pas pi après son initialisation.
Lecture de r%lf convient à l’adresse d’un double ; r est ensuite contrôlé comme réel fini de module au plus un million.
Garde r < 0Rejette le rayon négatif avant tout calcul. Le rayon nul est autorisé.
volume = ...Avec r=2 : 2*2*2=8 ; 4/3 réel ≈1.333333 ; multiplication par π donne ≈33.510321638.
%.6fArrondit l’affichage à six chiffres décimaux, sans transformer volume en un entier.
Exécutions commentées : Volume de la sphère
SaisieRésultat après l’invitePourquoi ce résultat ?
2Volume = 33.510322Le cube de 2 vaut 8 ; le volume vaut 32π/3.
0Volume = 0.000000Le produit contient un facteur nul.
-2Rayon invalide.Le rayon géométrique est refusé.

Sorties écran : Volume de la sphère

Exécution 1. Le cube de 2 vaut 8 ; le volume vaut 32π/3.

Entrer r :
2
Volume = 33.510322

Exécution 2. Le produit contient un facteur nul.

Entrer r :
0
Volume = 0.000000

Exécution 3. Le rayon géométrique est refusé.

Entrer r :
-2
Rayon invalide.

Exercice 4 : Convertir Fahrenheit en Celsius

Lire TF et calculer TC = (5/9) × (TF − 32).

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Les parenthèses doivent conserver la soustraction avant le changement d’échelle. Ici aussi, 5/9 entre entiers donne 0 : toute conversion serait alors fausse. On emploie 5.0/9.0. Une température négative est une donnée normale.

À revoir dans les cours : Types de données · Variables et affectation · Entrées et sorties : printf et scanf.

Un test qui n’utilise que TF=32 ne détecterait pas l’erreur 5/9, puisque le bon résultat est également zéro. TF=212 est donc un meilleur cas de vérification complémentaire.

Version simple (naïve) : Conversion de température

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme.

#include <stdio.h>
#include <math.h>

int main(void) {
    double tf, tc;
    puts("Entrer tf :");
    scanf("%lf", &tf);
    tc = (5.0 / 9.0) * (tf - 32.0);
    printf("Celsius = %.6f\n", tc);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Conversion de température - version simple

Exécution 1. 212−32=180 puis 180×5/9=100.

Entrer tf :
212
Celsius = 100.000000

Exécution 2. C’est le point où la différence vaut zéro.

Entrer tf :
32
Celsius = 0.000000

Exécution 3. −72×5/9=−40.

Entrer tf :
-40
Celsius = -40.000000

Version plus générale : Conversion de température

Conversion de température : programme complet

#include <stdio.h>
#include <math.h>

int main(void) {
    double tf, tc;
    puts("Entrer tf :");
    if (scanf("%lf", &tf) != 1) {
        puts("Saisie invalide.");
        return 1;
    }
    if (!isfinite(tf) || fabs(tf) > 1000000.0) {
        puts("Valeur hors domaine.");
        return 1;
    }
    tc = (5.0 / 9.0) * (tf - 32.0);
    printf("Celsius = %.6f\n", tc);
    return 0;
}
Comprendre Conversion de température
Instruction ou étapeExplication et état du programme
double tf, tctf stocke l’entrée Fahrenheit, tc le résultat Celsius ; les deux échelles n’ont pas le même zéro.
tf - 32.0Avec 212 °F, on commence par 212−32=180. Avec 32 °F, cette étape donne zéro.
(5.0 / 9.0) * (...)180 × 5/9 = 100. Le type double évite la division entière et permet les valeurs fractionnaires.
printfLa sortie est arrondie à six décimales ; le signe moins est conservé pour les températures négatives.
Exécutions commentées : Conversion de température
SaisieRésultat après l’invitePourquoi ce résultat ?
212Celsius = 100.000000212−32=180 puis 180×5/9=100.
32Celsius = 0.000000C’est le point où la différence vaut zéro.
-40Celsius = -40.000000−72×5/9=−40.

Sorties écran : Conversion de température

Exécution 1. 212−32=180 puis 180×5/9=100.

Entrer tf :
212
Celsius = 100.000000

Exécution 2. C’est le point où la différence vaut zéro.

Entrer tf :
32
Celsius = 0.000000

Exécution 3. −72×5/9=−40.

Entrer tf :
-40
Celsius = -40.000000

Exercice 5 : Image par une fonction à une variable

Lire x et afficher f(x) = x² + 2x + 1. Cet exercice apparaît sans numéro dans le support.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Une fonction mathématique associe une valeur à chaque entrée de son domaine. Il n’est pas nécessaire d’écrire une fonction C distincte pour calculer ici son image : une expression puis une affectation suffisent.

À revoir dans les cours : Variables et affectation · Types de données · Les bases du langage C.

On peut vérifier l’algèbre avec f(x)=(x+1)². Les deux expressions sont mathématiquement égales ; des calculs flottants peuvent néanmoins arrondir différemment. Le programme conserve la forme demandée par le sujet.

Version simple (naïve) : Calcul de f(x)

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme.

#include <stdio.h>
#include <math.h>

int main(void) {
    double x, image;
    puts("Entrer x :");
    scanf("%lf", &x);
    image = x * x + 2.0 * x + 1.0;
    printf("f(x) = %.6f\n", image);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Calcul de f(x) - version simple

Exécution 1. 9−6+1=4.

Entrer x :
-3
f(x) = 4.000000

Exécution 2. 0+0+1=1.

Entrer x :
0
f(x) = 1.000000

Exécution 3. 1−2+1=0.

Entrer x :
-1
f(x) = 0.000000

Version plus générale : Calcul de f(x)

Calcul de f(x) : programme complet

#include <stdio.h>
#include <math.h>

int main(void) {
    double x, image;
    puts("Entrer x :");
    if (scanf("%lf", &x) != 1) {
        puts("Saisie invalide.");
        return 1;
    }
    if (!isfinite(x) || fabs(x) > 1000000.0) {
        puts("Valeur hors domaine.");
        return 1;
    }
    image = x * x + 2.0 * x + 1.0;
    printf("f(x) = %.6f\n", image);
    return 0;
}
Comprendre Calcul de f(x)
Instruction ou étapeExplication et état du programme
x * xCalcule le carré. Pour x = −3, le produit de deux nombres négatifs vaut 9.
2.0 * xCalcule le terme linéaire : pour −3, il vaut −6. L’écrire 2x ne serait pas du C valide.
image = ...Additionne 9−6+1=4 puis mémorise 4 dans image. Les multiplications ont priorité sur les additions.
AffichageLa valeur de x n’est pas modifiée par le calcul ; image contient le résultat.
Exécutions commentées : Calcul de f(x)
SaisieRésultat après l’invitePourquoi ce résultat ?
-3f(x) = 4.0000009−6+1=4.
0f(x) = 1.0000000+0+1=1.
-1f(x) = 0.0000001−2+1=0.

Sorties écran : Calcul de f(x)

Exécution 1. 9−6+1=4.

Entrer x :
-3
f(x) = 4.000000

Exécution 2. 0+0+1=1.

Entrer x :
0
f(x) = 1.000000

Exécution 3. 1−2+1=0.

Entrer x :
-1
f(x) = 0.000000

Exercice 6 : Fonction définie par morceaux

Lire x et calculer h(x) : x²+2x+1 si x<4 ; x² si 4≤x≤40 ; x+1 si x>40. Le support emploie ensuite le nom f ; on garde h pour éviter la confusion avec l’exercice 5.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Les trois domaines sont disjoints et couvrent tous les réels. Un enchaînement if / else if / else garantit qu’un seul calcul sera exécuté. Les valeurs de frontière 4 et 40 doivent suivre le morceau central.

À revoir dans les cours : Structures de contrôle : if et else · Les bases du langage C · Types de données.

Ne pas écrire 4 <= x <= 40 en C. Le premier test donne 0 ou 1, qui est ensuite comparé à 40 : ce n’est pas une double inégalité mathématique. Si l’on teste les deux bornes explicitement, écrire x >= 4.0 && x <= 40.0.

Version simple (naïve) : Évaluer h(x)

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme.

#include <stdio.h>
#include <math.h>

int main(void) {
    double x, image;
    puts("Entrer x :");
    scanf("%lf", &x);
    if (x < 4.0) {
        image = x * x + 2.0 * x + 1.0;
    } else if (x <= 40.0) {
        image = x * x;
    } else {
        image = x + 1.0;
    }
    printf("h(x) = %.6f\n", image);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Évaluer h(x) - version simple

Exécution 1. Premier morceau.

Entrer x :
3
h(x) = 16.000000

Exécution 2. Borne inférieure incluse dans le deuxième morceau.

Entrer x :
4
h(x) = 16.000000

Exécution 3. Borne supérieure également incluse.

Entrer x :
40
h(x) = 1600.000000

Exécution 4. Troisième morceau.

Entrer x :
41
h(x) = 42.000000

Version plus générale : Évaluer h(x)

Évaluer h(x) : programme complet

#include <stdio.h>
#include <math.h>

int main(void) {
    double x, image;
    puts("Entrer x :");
    if (scanf("%lf", &x) != 1) {
        puts("Saisie invalide.");
        return 1;
    }
    if (!isfinite(x) || fabs(x) > 1000000.0) {
        puts("Valeur hors domaine.");
        return 1;
    }
    if (x < 4.0) {
        image = x * x + 2.0 * x + 1.0;
    } else if (x <= 40.0) {
        image = x * x;
    } else {
        image = x + 1.0;
    }
    printf("h(x) = %.6f\n", image);
    return 0;
}
Comprendre Évaluer h(x)
Instruction ou étapeExplication et état du programme
if (x < 4.0)Avec x=3, vrai : image=9+6+1=16. Les deux autres blocs sont sautés.
else if (x <= 40.0)Ce test n’est atteint que si x>=4. Il suffit donc de tester la borne supérieure. Avec x=4 : image=16.
elseLes tests précédents étant faux, x est strictement supérieur à 40. Avec x=41, image=42.
À x=4040<4 est faux, puis 40<=40 est vrai : le carré vaut 1600, et non 41.
printf placé après la chaîneToutes les branches affectent image ; on peut ensuite afficher un seul résultat commun.
Exécutions commentées : Évaluer h(x)
SaisieRésultat après l’invitePourquoi ce résultat ?
3h(x) = 16.000000Premier morceau.
4h(x) = 16.000000Borne inférieure incluse dans le deuxième morceau.
40h(x) = 1600.000000Borne supérieure également incluse.
41h(x) = 42.000000Troisième morceau.

Sorties écran : Évaluer h(x)

Exécution 1. Premier morceau.

Entrer x :
3
h(x) = 16.000000

Exécution 2. Borne inférieure incluse dans le deuxième morceau.

Entrer x :
4
h(x) = 16.000000

Exécution 3. Borne supérieure également incluse.

Entrer x :
40
h(x) = 1600.000000

Exécution 4. Troisième morceau.

Entrer x :
41
h(x) = 42.000000

Exercice 7 : Image par une fonction à deux variables

Lire x et y et afficher g(x,y)=x²+y²+2xy+1.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Deux variables doivent être lues et leur lecture doit réussir avant le calcul. Les produits implicites de l’écriture mathématique deviennent des * explicites. La valeur de g est scalaire : on affiche un résultat, même si l’entrée contient deux nombres.

À revoir dans les cours : Entrées et sorties : printf et scanf · Variables et affectation · Types de données.

Le signe du terme croisé dépend des signes de x et y. Par exemple x=2 et y=−2 ne donnent pas 17 : le produit 2xy vaut −8. Ce cas vérifie que l’on n’a pas oublié le terme croisé.

Version simple (naïve) : Calcul de g(x,y)

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme.

#include <stdio.h>
#include <math.h>

int main(void) {
    double x, y, image;
    puts("Entrer x y :");
    scanf("%lf %lf", &x, &y);
    image = x * x + y * y + 2.0 * x * y + 1.0;
    printf("g(x,y) = %.6f\n", image);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Calcul de g(x,y) - version simple

Exécution 1. 4+9+12+1=26.

Entrer x y :
2 3
g(x,y) = 26.000000

Exécution 2. 4+4−8+1=1.

Entrer x y :
2 -2
g(x,y) = 1.000000

Exécution 3. Seul le terme constant reste.

Entrer x y :
0 0
g(x,y) = 1.000000

Version plus générale : Calcul de g(x,y)

Calcul de g(x,y) : programme complet

#include <stdio.h>
#include <math.h>

int main(void) {
    double x, y, image;
    puts("Entrer x y :");
    if (scanf("%lf %lf", &x, &y) != 2) {
        puts("Saisie invalide.");
        return 1;
    }
    if (!isfinite(x) || fabs(x) > 1000000.0 || !isfinite(y) || fabs(y) > 1000000.0) {
        puts("Valeur hors domaine.");
        return 1;
    }
    image = x * x + y * y + 2.0 * x * y + 1.0;
    printf("g(x,y) = %.6f\n", image);
    return 0;
}
Comprendre Calcul de g(x,y)
Instruction ou étapeExplication et état du programme
scanf avec deux %lfDeux adresses sont passées, &x puis &y. Le retour doit valoir 2 ; un seul nombre ne suffit pas.
x*x et y*yPour x=2, y=3, les carrés valent 4 et 9.
2.0*x*yLe terme croisé vaut 2×2×3=12.
Somme et affectation4+9+12+1=26 ; image reçoit 26, x et y restent 2 et 3.
Vérification indépendante(x+y)²+1 = 5²+1=26. C’est une façon de contrôler le résultat, pas un second calcul nécessaire au programme.
Exécutions commentées : Calcul de g(x,y)
SaisieRésultat après l’invitePourquoi ce résultat ?
2 3g(x,y) = 26.0000004+9+12+1=26.
2 -2g(x,y) = 1.0000004+4−8+1=1.
0 0g(x,y) = 1.000000Seul le terme constant reste.

Sorties écran : Calcul de g(x,y)

Exécution 1. 4+9+12+1=26.

Entrer x y :
2 3
g(x,y) = 26.000000

Exécution 2. 4+4−8+1=1.

Entrer x y :
2 -2
g(x,y) = 1.000000

Exécution 3. Seul le terme constant reste.

Entrer x y :
0 0
g(x,y) = 1.000000

Partie 2 : Comparaisons et choix

Exercice 8 : Coût d’un transport

Lire le prix du transport par kilomètre et la distance en kilomètres, puis afficher le coût total. Le sujet donne 150 FCFA/km et 200 km, mais annonce 3 000 FCFA.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Un prix unitaire multiplié par une quantité donne un prix total. Les unités se simplifient : (FCFA/km) × km = FCFA. Le prix et la distance peuvent être décimaux ; on choisit donc double. Une distance ou un prix négatif est refusé.

À revoir dans les cours : Types de données · Entrées et sorties : printf et scanf · Structures de contrôle : if et else.

Correction de l’exemple : 150 × 200 = 30 000 FCFA. Le résultat 3 000 du support est une erreur arithmétique, pas une règle de tarification supplémentaire.

Version simple (naïve) : Coût total

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme. Le tarif et la distance sont supposés positifs ou nuls.

#include <stdio.h>
#include <math.h>

int main(void) {
    double prix_km, distance, cout;
    puts("Entrer prix_km distance :");
    scanf("%lf %lf", &prix_km, &distance);
    cout = prix_km * distance;
    printf("Cout total = %.2f FCFA\n", cout);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Coût total - version simple

Exécution 1. Le résultat correct de l’exemple est 30 000, pas 3 000.

Entrer prix_km distance :
150 200
Cout total = 30000.00 FCFA

Exécution 2. 3 000 correspondrait à 20 km.

Entrer prix_km distance :
150 20
Cout total = 3000.00 FCFA

Exécution 3. Aucune distance parcourue.

Entrer prix_km distance :
150 0
Cout total = 0.00 FCFA

Version plus générale : Coût total

Coût total : programme complet

#include <stdio.h>
#include <math.h>

int main(void) {
    double prix_km, distance, cout;
    puts("Entrer prix_km distance :");
    if (scanf("%lf %lf", &prix_km, &distance) != 2) {
        puts("Saisie invalide.");
        return 1;
    }
    if (!isfinite(prix_km) || fabs(prix_km) > 1000000.0 || !isfinite(distance) || fabs(distance) > 1000000.0) {
        puts("Valeur hors domaine.");
        return 1;
    }
    if (prix_km < 0.0 || distance < 0.0) {
        puts("Prix ou distance invalide.");
        return 1;
    }
    cout = prix_km * distance;
    printf("Cout total = %.2f FCFA\n", cout);
    return 0;
}
Comprendre Coût total
Instruction ou étapeExplication et état du programme
prix_km et distanceDeux variables pour deux grandeurs différentes : le tarif par kilomètre et le nombre de kilomètres.
prix_km < 0 || distance < 0|| est le ou logique : il suffit qu’un des deux nombres soit négatif pour arrêter le programme.
cout = prix_km * distancePour 150 et 200 : 150×2=300, puis ×100=30 000.
%.2fAffiche deux chiffres après le point. Il s’agit d’un choix d’affichage pédagogique ; un logiciel comptable devrait définir ses règles d’arrondi et son unité monétaire de stockage.
Exécutions commentées : Coût total
SaisieRésultat après l’invitePourquoi ce résultat ?
150 200Cout total = 30000.00 FCFALe résultat correct de l’exemple est 30 000, pas 3 000.
150 20Cout total = 3000.00 FCFA3 000 correspondrait à 20 km.
150 0Cout total = 0.00 FCFAAucune distance parcourue.
-1 200Prix ou distance invalide.Le prix est négatif.

Sorties écran : Coût total

Exécution 1. Le résultat correct de l’exemple est 30 000, pas 3 000.

Entrer prix_km distance :
150 200
Cout total = 30000.00 FCFA

Exécution 2. 3 000 correspondrait à 20 km.

Entrer prix_km distance :
150 20
Cout total = 3000.00 FCFA

Exécution 3. Aucune distance parcourue.

Entrer prix_km distance :
150 0
Cout total = 0.00 FCFA

Exécution 4. Le prix est négatif.

Entrer prix_km distance :
-1 200
Prix ou distance invalide.

Exercice 9 : Maximum, minimum et moyenne

Lire trois nombres et afficher leur maximum, leur minimum et leur moyenne.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Le maximum provisoire doit commencer par une valeur réellement lue, pas par zéro. Sinon trois valeurs négatives pourraient donner à tort un maximum nul. Le même principe vaut pour le minimum. La moyenne arithmétique est la somme divisée par le nombre de valeurs.

À revoir dans les cours : Variables et affectation · Structures de contrôle : if et else · Types de données.

L’invariant utile est le suivant : après chaque comparaison, maximum est le plus grand des nombres déjà examinés et minimum le plus petit. C’est la même idée que l’on retrouvera plus tard pour parcourir un tableau.

Version simple (naïve) : Trois statistiques

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme.

#include <stdio.h>
#include <math.h>

int main(void) {
    double a, b, c, maximum, minimum, moyenne;
    puts("Entrer a b c :");
    scanf("%lf %lf %lf", &a, &b, &c);
    maximum = a;
    minimum = a;
    if (b > maximum) { maximum = b; }
    if (c > maximum) { maximum = c; }
    if (b < minimum) { minimum = b; }
    if (c < minimum) { minimum = c; }
    moyenne = (a + b + c) / 3.0;
    printf("Maximum = %.6f\n", maximum);
    printf("Minimum = %.6f\n", minimum);
    printf("Moyenne = %.6f\n", moyenne);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Trois statistiques - version simple

Exécution 1. Trace détaillée : maximum reste 8 ; minimum devient −2.

Entrer a b c :
8 -2 5
Maximum = 8.000000
Minimum = -2.000000
Moyenne = 3.666667

Exécution 2. Trois entrées négatives : initialiser à zéro aurait été incorrect.

Entrer a b c :
-7 -2 -9
Maximum = -2.000000
Minimum = -9.000000
Moyenne = -6.000000

Exécution 3. Les égalités ne nécessitent aucune mise à jour.

Entrer a b c :
4 4 4
Maximum = 4.000000
Minimum = 4.000000
Moyenne = 4.000000

Version plus générale : Trois statistiques

Trois statistiques : programme complet

#include <stdio.h>
#include <math.h>

int main(void) {
    double a, b, c, maximum, minimum, moyenne;
    puts("Entrer a b c :");
    if (scanf("%lf %lf %lf", &a, &b, &c) != 3) {
        puts("Saisie invalide.");
        return 1;
    }
    if (!isfinite(a) || fabs(a) > 1000000.0 || !isfinite(b) || fabs(b) > 1000000.0 || !isfinite(c) || fabs(c) > 1000000.0) {
        puts("Valeur hors domaine.");
        return 1;
    }
    maximum = a;
    minimum = a;
    if (b > maximum) { maximum = b; }
    if (c > maximum) { maximum = c; }
    if (b < minimum) { minimum = b; }
    if (c < minimum) { minimum = c; }
    moyenne = (a + b + c) / 3.0;
    printf("Maximum = %.6f\n", maximum);
    printf("Minimum = %.6f\n", minimum);
    printf("Moyenne = %.6f\n", moyenne);
    return 0;
}
Comprendre Trois statistiques
Instruction ou étapeExplication et état du programme
maximum=a ; minimum=aPour a=8, les deux candidats valent initialement 8. On sait ainsi qu’ils proviennent bien des entrées.
if (b > maximum)Avec b=−2 : −2>8 est faux, maximum reste 8.
if (c > maximum)Avec c=5 : 5>8 est faux, maximum reste 8.
Deux comparaisons de minimum−2<8 est vrai, minimum devient −2 ; 5<−2 est faux, il reste −2.
(a+b+c)/3.0(8−2+5)/3 = 11/3 ≈3.666667. Les parenthèses imposent de diviser toute la somme.
Trois printfAffichent chacun leur propre résultat. Les comparaisons indépendantes ne doivent pas être reliées par else : plusieurs mises à jour peuvent être nécessaires.
Exécutions commentées : Trois statistiques
SaisieRésultat après l’invitePourquoi ce résultat ?
8 -2 5Maximum = 8.000000 Minimum = -2.000000 Moyenne = 3.666667Trace détaillée : maximum reste 8 ; minimum devient −2.
-7 -2 -9Maximum = -2.000000 Minimum = -9.000000 Moyenne = -6.000000Trois entrées négatives : initialiser à zéro aurait été incorrect.
4 4 4Maximum = 4.000000 Minimum = 4.000000 Moyenne = 4.000000Les égalités ne nécessitent aucune mise à jour.

Sorties écran : Trois statistiques

Exécution 1. Trace détaillée : maximum reste 8 ; minimum devient −2.

Entrer a b c :
8 -2 5
Maximum = 8.000000
Minimum = -2.000000
Moyenne = 3.666667

Exécution 2. Trois entrées négatives : initialiser à zéro aurait été incorrect.

Entrer a b c :
-7 -2 -9
Maximum = -2.000000
Minimum = -9.000000
Moyenne = -6.000000

Exécution 3. Les égalités ne nécessitent aucune mise à jour.

Entrer a b c :
4 4 4
Maximum = 4.000000
Minimum = 4.000000
Moyenne = 4.000000

Exercice 10 : Déterminer la parité

Lire un entier et indiquer s’il est pair ou impair.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Un entier est pair s’il est divisible par 2, donc si son reste modulo 2 vaut zéro. L’opérateur C % calcule un reste entier. Il ne faut pas le confondre avec le signe de pourcentage dans un format printf.

À revoir dans les cours : Structures de contrôle : if et else · Types de données · Les bases du langage C.

Ce programme accepte tout entier représentable dans int, y compris le plus petit. Il ne prend pas l’opposé de n et ne risque donc pas le débordement rencontré dans une valeur absolue naïve.

Version simple (naïve) : Pair ou impair

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme.

#include <stdio.h>

int main(void) {
    int n;
    puts("Entrer n :");
    scanf("%d", &n);
    if (n % 2 == 0) {
        puts("Pair");
    } else {
        puts("Impair");
    }
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Pair ou impair - version simple

Exécution 1. 14=2×7.

Entrer n :
14
Pair

Exécution 2. Le reste est 1.

Entrer n :
15
Impair

Exécution 3. Le reste vaut −1 et n’est pas nul.

Entrer n :
-3
Impair

Exécution 4. 0=2×0.

Entrer n :
0
Pair

Version plus générale : Pair ou impair

Pair ou impair : programme complet

#include <stdio.h>

int main(void) {
    int n;
    puts("Entrer n :");
    if (scanf("%d", &n) != 1) {
        puts("Saisie invalide.");
        return 1;
    }
    if (n % 2 == 0) {
        puts("Pair");
    } else {
        puts("Impair");
    }
    return 0;
}
Comprendre Pair ou impair
Instruction ou étapeExplication et état du programme
n % 2Avec 14, la division par 2 a un reste nul ; avec 15, le reste vaut 1.
== 0Compare le reste à zéro. On n’écrit pas =, qui serait une affectation.
if puis elseLe cas reste nul est pair ; tous les restes non nuls sont impairs.
Entiers négatifs−3 % 2 vaut −1 selon les règles C modernes. Il faut donc tester l’égalité à 0, pas imposer que le reste soit 1.
Exécutions commentées : Pair ou impair
SaisieRésultat après l’invitePourquoi ce résultat ?
14Pair14=2×7.
15ImpairLe reste est 1.
-3ImpairLe reste vaut −1 et n’est pas nul.
0Pair0=2×0.

Sorties écran : Pair ou impair

Exécution 1. 14=2×7.

Entrer n :
14
Pair

Exécution 2. Le reste est 1.

Entrer n :
15
Impair

Exécution 3. Le reste vaut −1 et n’est pas nul.

Entrer n :
-3
Impair

Exécution 4. 0=2×0.

Entrer n :
0
Pair

Exercice 11 : Attribuer une mention

Lire une moyenne : moins de 10, Médiocre ; [10,12[, Passable ; [12,14[, Assez bien ; [14,16[, Bien ; [16,18[, Très bien ; [18,20], Excellent.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

La notation [a,b[ inclut a et exclut b. Une chaîne de tests croissants permet de représenter ces intervalles sans répéter les deux bornes. On considère une moyenne sur 20, donc les valeurs hors de [0,20] sont refusées.

À revoir dans les cours : Structures de contrôle : if et else · Entrées et sorties : printf et scanf · Types de données.

Une suite de six if indépendants utilisant seulement moyenne < seuil pourrait afficher plusieurs mentions. La chaîne else if s’arrête après la première catégorie satisfaite.

Version simple (naïve) : Mention sur 20

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme. La moyenne est supposée comprise entre 0 et 20. Les seuils de mention restent nécessaires.

#include <stdio.h>
#include <math.h>

int main(void) {
    double moyenne;
    puts("Entrer moyenne :");
    scanf("%lf", &moyenne);
    if (moyenne < 10.0) {
        puts("Mediocre");
    } else if (moyenne < 12.0) {
        puts("Passable");
    } else if (moyenne < 14.0) {
        puts("Assez bien");
    } else if (moyenne < 16.0) {
        puts("Bien");
    } else if (moyenne < 18.0) {
        puts("Tres bien");
    } else {
        puts("Excellent");
    }
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Mention sur 20 - version simple

Exécution 1. 9.5 < 10.

Entrer moyenne :
9.5
Mediocre

Exécution 2. 10 est inclus dans [10,12[.

Entrer moyenne :
10
Passable

Exécution 3. 12 n’appartient plus à l’intervalle précédent.

Entrer moyenne :
12
Assez bien

Exécution 4. Début de [14,16[.

Entrer moyenne :
14
Bien

Exécution 5. Début de [16,18[.

Entrer moyenne :
16
Tres bien

Exécution 6. Début de [18,20].

Entrer moyenne :
18
Excellent

Exécution 7. 20 est inclus dans la dernière mention.

Entrer moyenne :
20
Excellent

Version plus générale : Mention sur 20

Mention sur 20 : programme complet

#include <stdio.h>
#include <math.h>

int main(void) {
    double moyenne;
    puts("Entrer moyenne :");
    if (scanf("%lf", &moyenne) != 1) {
        puts("Saisie invalide.");
        return 1;
    }
    if (!isfinite(moyenne) || fabs(moyenne) > 1000000.0) {
        puts("Valeur hors domaine.");
        return 1;
    }
    if (moyenne < 0.0 || moyenne > 20.0) {
        puts("Moyenne invalide.");
        return 1;
    }
    if (moyenne < 10.0) {
        puts("Mediocre");
    } else if (moyenne < 12.0) {
        puts("Passable");
    } else if (moyenne < 14.0) {
        puts("Assez bien");
    } else if (moyenne < 16.0) {
        puts("Bien");
    } else if (moyenne < 18.0) {
        puts("Tres bien");
    } else {
        puts("Excellent");
    }
    return 0;
}
Comprendre Mention sur 20
Instruction ou étapeExplication et état du programme
Contrôle [0,20]Une note négative ou supérieure à 20 est exclue avant de classer les mentions.
moyenne < 10À 9.5, vrai : Médiocre. Aucun autre test de mention n’est ensuite exécuté.
else if (moyenne < 12)Atteint seulement si moyenne>=10. À 10 : vrai, donc Passable.
Les seuils 14, 16 et 18Chaque test exclut sa borne supérieure ; le test suivant la prend en charge. Ainsi 14 est Bien et 16 est Très bien.
Dernier elseOn sait déjà que moyenne>=18 et <=20. Il représente exactement [18,20], sans dépasser 20.
Exécutions commentées : Mention sur 20
SaisieRésultat après l’invitePourquoi ce résultat ?
9.5Mediocre9.5 < 10.
10Passable10 est inclus dans [10,12[.
12Assez bien12 n’appartient plus à l’intervalle précédent.
14BienDébut de [14,16[.
16Tres bienDébut de [16,18[.
18ExcellentDébut de [18,20].
20Excellent20 est inclus dans la dernière mention.
20.1Moyenne invalide.Hors de l’échelle sur 20.
-1Moyenne invalide.La moyenne ne peut pas être négative.

Sorties écran : Mention sur 20

Exécution 1. 9.5 < 10.

Entrer moyenne :
9.5
Mediocre

Exécution 2. 10 est inclus dans [10,12[.

Entrer moyenne :
10
Passable

Exécution 3. 12 n’appartient plus à l’intervalle précédent.

Entrer moyenne :
12
Assez bien

Exécution 4. Début de [14,16[.

Entrer moyenne :
14
Bien

Exécution 5. Début de [16,18[.

Entrer moyenne :
16
Tres bien

Exécution 6. Début de [18,20].

Entrer moyenne :
18
Excellent

Exécution 7. 20 est inclus dans la dernière mention.

Entrer moyenne :
20
Excellent

Exécution 8. Hors de l’échelle sur 20.

Entrer moyenne :
20.1
Moyenne invalide.

Exécution 9. La moyenne ne peut pas être négative.

Entrer moyenne :
-1
Moyenne invalide.

Exercice 12 : Reconnaître une année bissextile

Lire une année et appliquer la règle donnée : divisible par 4, sauf les années divisibles par 100 mais pas par 400. Vérifier 1901, 2004, 2100 et 2000.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

La divisibilité se teste avec le reste : annee % 4 == 0. && signifie et, || signifie ou, != signifie différent. Les parenthèses rendent la règle lisible. Le programme applique la règle grégorienne fournie à une année entière strictement positive.

À revoir dans les cours : Structures de contrôle : if et else · Les bases du langage C · Types de données.

Vérifier séparément les trois divisibilités
Année%4%100%400Conclusion
190111301Non
2004044Oui
210000100Non
2000000Oui

Écrire seulement annee % 4 == 0 donne une mauvaise réponse pour 2100. La branche des siècles est donc indispensable.

Version simple (naïve) : Année bissextile

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme. L'année est supposée strictement positive ; la regle des siècles reste indispensable.

#include <stdio.h>

int main(void) {
    int annee;
    puts("Entrer annee :");
    scanf("%d", &annee);
    if (annee % 400 == 0 ||
        (annee % 4 == 0 && annee % 100 != 0)) {
        puts("Bissextile");
    } else {
        puts("Non bissextile");
    }
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Année bissextile - version simple

Exécution 1. 1901 % 4 = 1.

Entrer annee :
1901
Non bissextile

Exécution 2. Divisible par 4, pas par 100.

Entrer annee :
2004
Bissextile

Exécution 3. Divisible par 100 mais pas par 400.

Entrer annee :
2100
Non bissextile

Exécution 4. Divisible par 400.

Entrer annee :
2000
Bissextile

Version plus générale : Année bissextile

Année bissextile : programme complet

#include <stdio.h>

int main(void) {
    int annee;
    puts("Entrer annee :");
    if (scanf("%d", &annee) != 1) {
        puts("Saisie invalide.");
        return 1;
    }
    if (annee <= 0) {
        puts("Annee invalide.");
        return 1;
    }
    if (annee % 400 == 0 ||
        (annee % 4 == 0 && annee % 100 != 0)) {
        puts("Bissextile");
    } else {
        puts("Non bissextile");
    }
    return 0;
}
Comprendre Année bissextile
Instruction ou étapeExplication et état du programme
annee % 400 == 0Une année divisible par 400 satisfait directement la règle : c’est le cas de 2000.
annee % 4 == 0Pour les autres années, la divisibilité par 4 est nécessaire.
annee % 100 != 0Écarte les siècles qui ne sont pas divisibles par 400, comme 2100.
|| avec (...)La règle complète est : divisible par 400 OU (divisible par 4 ET pas par 100).
Évaluation à court-circuitSi le premier terme du ou est vrai, le second n’a pas besoin d’être évalué. Pour &&, un premier terme faux suffit à rendre l’ensemble faux.
Exécutions commentées : Année bissextile
SaisieRésultat après l’invitePourquoi ce résultat ?
1901Non bissextile1901 % 4 = 1.
2004BissextileDivisible par 4, pas par 100.
2100Non bissextileDivisible par 100 mais pas par 400.
2000BissextileDivisible par 400.
0Annee invalide.Le domaine choisi exclut zéro.

Sorties écran : Année bissextile

Exécution 1. 1901 % 4 = 1.

Entrer annee :
1901
Non bissextile

Exécution 2. Divisible par 4, pas par 100.

Entrer annee :
2004
Bissextile

Exécution 3. Divisible par 100 mais pas par 400.

Entrer annee :
2100
Non bissextile

Exécution 4. Divisible par 400.

Entrer annee :
2000
Bissextile

Exécution 5. Le domaine choisi exclut zéro.

Entrer annee :
0
Annee invalide.

Exercice 13 : Écrire un chiffre en toutes lettres

Lire un chiffre de 0 à 9 et afficher son nom français. Les autres nombres sont hors du domaine.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Un switch choisit un bloc à partir de la valeur d’une expression entière. Chaque case correspond à une constante entière. break termine le switch ; sans lui, l’exécution continuerait dans les cases suivants.

À revoir dans les cours : Structures de contrôle : if et else · Constantes et littéraux · Types de données.

Une chaîne de if / else if conviendrait également. Ici, le switch exprime clairement une correspondance entre un petit ensemble de valeurs exactes et leurs libellés.

Version simple (naïve) : Un chiffre, un mot

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme. Le chiffre est supposé compris entre 0 et 9.

#include <stdio.h>

int main(void) {
    int chiffre;
    puts("Entrer chiffre :");
    scanf("%d", &chiffre);
    switch (chiffre) {
        case 0: puts("zero"); break;
        case 1: puts("un"); break;
        case 2: puts("deux"); break;
        case 3: puts("trois"); break;
        case 4: puts("quatre"); break;
        case 5: puts("cinq"); break;
        case 6: puts("six"); break;
        case 7: puts("sept"); break;
        case 8: puts("huit"); break;
        case 9: puts("neuf"); break;
        default:
            puts("Chiffre invalide.");
            return 1;
    }
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Un chiffre, un mot - version simple

Exécution 1. case 9 est sélectionné.

Entrer chiffre :
9
neuf

Exécution 2. Le chiffre zéro fait bien partie du domaine.

Entrer chiffre :
0
zero

Version plus générale : Un chiffre, un mot

Un chiffre, un mot : programme complet

#include <stdio.h>

int main(void) {
    int chiffre;
    puts("Entrer chiffre :");
    if (scanf("%d", &chiffre) != 1) {
        puts("Saisie invalide.");
        return 1;
    }
    switch (chiffre) {
        case 0: puts("zero"); break;
        case 1: puts("un"); break;
        case 2: puts("deux"); break;
        case 3: puts("trois"); break;
        case 4: puts("quatre"); break;
        case 5: puts("cinq"); break;
        case 6: puts("six"); break;
        case 7: puts("sept"); break;
        case 8: puts("huit"); break;
        case 9: puts("neuf"); break;
        default:
            puts("Chiffre invalide.");
            return 1;
    }
    return 0;
}
Comprendre Un chiffre, un mot
Instruction ou étapeExplication et état du programme
int chiffreOn lit un entier. Il s’agit du nombre 9, pas de la chaîne de caractères "9".
switch (chiffre)Pour 9, le contrôle va directement à case 9 ; les cases 0 à 8 ne sont pas exécutés.
puts("neuf"); break;Affiche neuf, puis sort du switch. Le return 0 commun termine ensuite main.
defaultTraite toute valeur qui n’a pas de case, comme −1 ou 10. Le programme signale une erreur.
Exécutions commentées : Un chiffre, un mot
SaisieRésultat après l’invitePourquoi ce résultat ?
9neufcase 9 est sélectionné.
0zeroLe chiffre zéro fait bien partie du domaine.
10Chiffre invalide.Aucun case ne correspond.

Sorties écran : Un chiffre, un mot

Exécution 1. case 9 est sélectionné.

Entrer chiffre :
9
neuf

Exécution 2. Le chiffre zéro fait bien partie du domaine.

Entrer chiffre :
0
zero

Exécution 3. Aucun case ne correspond.

Entrer chiffre :
10
Chiffre invalide.

Exercice 14 : Associer une description à un niveau

Lire un niveau entier et afficher la description du tableau du sujet : de 1 (micro, non ressenti) à 9 (dévastateur). En dessous de 1, signaler une erreur ; au-dessus de 9, reprendre le libellé « apocalypse » demandé par le TD.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

On sépare la validation de la sélection. Les tests niveau < 1 et niveau > 9 éliminent les valeurs hors domaine ; le switch ne traite ensuite que les neuf niveaux autorisés.

À revoir dans les cours : Structures de contrôle : if et else · Entrées et sorties : printf et scanf · Constantes et littéraux.

Cadre de l’exercice : on programme une table de correspondance entre entiers et textes. Les catégories, distances et le terme « apocalypse » sont ceux du support pédagogique ; ils ne constituent pas un modèle scientifique de prédiction des dommages sismiques. Le programme n’interprète pas les magnitudes décimales.

Version simple (naïve) : Table de descriptions du sujet

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme. Le niveau est supposé compris entre 1 et 9.

#include <stdio.h>

int main(void) {
    int niveau;
    puts("Entrer niveau :");
    scanf("%d", &niveau);
    switch (niveau) {
        case 1: puts("Micro tremblement de terre, non ressenti"); break;
        case 2: puts("Tres mineur, non ressenti mais detecte"); break;
        case 3: puts("Mineur, causant rarement des dommages"); break;
        case 4: puts("Leger, secousses notables d'objets a l'interieur des maisons"); break;
        case 5: puts("Modere, legers dommages aux edifices bien construits"); break;
        case 6: puts("Fort, destructeur jusqu'a 180 kilometres a la ronde si les zones sont peuplees"); break;
        case 7: puts("Majeur, dommages moderes a severes dans des zones plus vastes"); break;
        case 8: puts("Important, dommages serieux a des centaines de kilometres a la ronde"); break;
        case 9: puts("Devastateur, dommages sur des milliers de kilometres a la ronde"); break;
    }
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Table de descriptions du sujet - version simple

Exécution 1. Premier niveau du tableau.

Entrer niveau :
1
Micro tremblement de terre, non ressenti

Exécution 2. Le case 6 est le seul exécuté.

Entrer niveau :
6
Fort, destructeur jusqu'a 180 kilometres a la ronde si les zones sont peuplees

Exécution 3. Dernier niveau autorisé.

Entrer niveau :
9
Devastateur, dommages sur des milliers de kilometres a la ronde

Version plus générale : Table de descriptions du sujet

Table de descriptions du sujet : programme complet

#include <stdio.h>

int main(void) {
    int niveau;
    puts("Entrer niveau :");
    if (scanf("%d", &niveau) != 1) {
        puts("Saisie invalide.");
        return 1;
    }
    if (niveau < 1) {
        puts("Erreur de saisie : niveau inferieur a 1.");
        return 1;
    }
    if (niveau > 9) {
        puts("Apocalypse (libelle du TD) : niveau superieur a 9.");
        return 1;
    }
    switch (niveau) {
        case 1: puts("Micro tremblement de terre, non ressenti"); break;
        case 2: puts("Tres mineur, non ressenti mais detecte"); break;
        case 3: puts("Mineur, causant rarement des dommages"); break;
        case 4: puts("Leger, secousses notables d'objets a l'interieur des maisons"); break;
        case 5: puts("Modere, legers dommages aux edifices bien construits"); break;
        case 6: puts("Fort, destructeur jusqu'a 180 kilometres a la ronde si les zones sont peuplees"); break;
        case 7: puts("Majeur, dommages moderes a severes dans des zones plus vastes"); break;
        case 8: puts("Important, dommages serieux a des centaines de kilometres a la ronde"); break;
        case 9: puts("Devastateur, dommages sur des milliers de kilometres a la ronde"); break;
    }
    return 0;
}
Comprendre Table de descriptions du sujet
Instruction ou étapeExplication et état du programme
Les deux gardesUn niveau 0 déclenche la première erreur ; un niveau 10 déclenche le message hors échelle. Le return 1 arrête immédiatement le traitement.
switch (niveau)Pour 6, on entre au case 6 et on affiche le libellé Fort.
Les neuf casesIls conservent les neuf catégories et descriptions du tableau de l’énoncé, avec une orthographe ASCII dans le code.
break après chaque caseUne seule description est affichée. Un niveau 6 ne doit pas aussi imprimer les niveaux 7, 8 et 9.
Pas de default iciLes gardes précédentes garantissent que niveau est entre 1 et 9 ; les neuf cases couvrent donc tout le domaine restant.
Exécutions commentées : Table de descriptions du sujet
SaisieRésultat après l’invitePourquoi ce résultat ?
1Micro tremblement de terre, non ressentiPremier niveau du tableau.
6Fort, destructeur jusqu'a 180 kilometres a la ronde si les zones sont peupleesLe case 6 est le seul exécuté.
9Devastateur, dommages sur des milliers de kilometres a la rondeDernier niveau autorisé.
0Erreur de saisie : niveau inferieur a 1.Première garde.
10Apocalypse (libelle du TD) : niveau superieur a 9.Deuxième garde ; libellé de l’exercice.

Sorties écran : Table de descriptions du sujet

Exécution 1. Premier niveau du tableau.

Entrer niveau :
1
Micro tremblement de terre, non ressenti

Exécution 2. Le case 6 est le seul exécuté.

Entrer niveau :
6
Fort, destructeur jusqu'a 180 kilometres a la ronde si les zones sont peuplees

Exécution 3. Dernier niveau autorisé.

Entrer niveau :
9
Devastateur, dommages sur des milliers de kilometres a la ronde

Exécution 4. Première garde.

Entrer niveau :
0
Erreur de saisie : niveau inferieur a 1.

Exécution 5. Deuxième garde ; libellé de l’exercice.

Entrer niveau :
10
Apocalypse (libelle du TD) : niveau superieur a 9.

Partie 3 : Affectations et problèmes complets

Exercice 15 : Échanger deux variables

Deux variables entières a et b sont déjà déclarées et affectées. Écrire les instructions qui échangent leur contenu. La solution complète ci-dessous lit leurs valeurs pour permettre un essai autonome.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Une affectation copie une valeur vers la variable située à gauche. Elle n’établit pas une égalité permanente entre deux variables. Pour échanger deux contenus, il faut conserver l’ancien a avant de l’écraser. Une variable temporaire est le moyen le plus lisible.

À revoir dans les cours : Variables et affectation · Types de données · Les bases du langage C.

La suite a=b; b=a; est incorrecte : après la première affectation, les deux variables contiennent l’ancien b. Éviter aussi l’échange par additions et soustractions sur des int : des valeurs parfaitement valides peuvent faire déborder les sommes intermédiaires. La variable temporaire fonctionne sans calcul arithmétique.

Version simple (naïve) : Échange avec une variable temporaire

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme.

#include <stdio.h>

int main(void) {
    int a, b, temporaire;
    puts("Entrer a b :");
    scanf("%d %d", &a, &b);
    temporaire = a;
    a = b;
    b = temporaire;
    printf("a = %d, b = %d\n", a, b);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Échange avec une variable temporaire - version simple

Exécution 1. Trois copies conservent les deux valeurs initiales.

Entrer a b :
3 8
a = 8, b = 3

Exécution 2. Les signes n’affectent pas la méthode.

Entrer a b :
-2 5
a = 5, b = -2

Exécution 3. L’échange de deux valeurs égales ne produit aucun changement visible.

Entrer a b :
4 4
a = 4, b = 4

Version plus générale : Échange avec une variable temporaire

Échange avec une variable temporaire : programme complet

#include <stdio.h>

int main(void) {
    int a, b, temporaire;
    puts("Entrer a b :");
    if (scanf("%d %d", &a, &b) != 2) {
        puts("Saisie invalide.");
        return 1;
    }
    temporaire = a;
    a = b;
    b = temporaire;
    printf("a = %d, b = %d\n", a, b);
    return 0;
}
Comprendre Échange avec une variable temporaire
Instruction ou étapeExplication et état du programme
État initialPour a=3 et b=8, temporaire n’a pas encore de valeur utilisable.
temporaire = aCopie 3 dans temporaire : a=3, b=8, temporaire=3.
a = bCopie 8 dans a : a=8, b=8, temporaire=3. L’ancien a n’est pas perdu, car il est sauvegardé.
b = temporaireCopie 3 dans b : a=8, b=3, temporaire=3. L’échange est terminé.
printfAffiche les deux nouvelles valeurs. Le contenu de temporaire n’a plus d’importance pour le résultat.
Exécutions commentées : Échange avec une variable temporaire
SaisieRésultat après l’invitePourquoi ce résultat ?
3 8a = 8, b = 3Trois copies conservent les deux valeurs initiales.
-2 5a = 5, b = -2Les signes n’affectent pas la méthode.
4 4a = 4, b = 4L’échange de deux valeurs égales ne produit aucun changement visible.

Sorties écran : Échange avec une variable temporaire

Exécution 1. Trois copies conservent les deux valeurs initiales.

Entrer a b :
3 8
a = 8, b = 3

Exécution 2. Les signes n’affectent pas la méthode.

Entrer a b :
-2 5
a = 5, b = -2

Exécution 3. L’échange de deux valeurs égales ne produit aucun changement visible.

Entrer a b :
4 4
a = 4, b = 4
Corrigé vidéo

La vidéo explique la méthode de résolution du thème indiqué. Adaptez-la aux données et aux consignes de cet exercice.

Algorithme 2 : Algorithme de permutation

Voir sur YouTube

Exercice 16 : Résoudre ax + b = 0

Lire a et b. Si a=b=0, il existe une infinité de solutions ; si a=0 et b≠0, aucune ; sinon une solution x=−b/a.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

On doit examiner le coefficient de x avant de diviser. Si a vaut zéro, l’équation ne dépend plus de x : elle devient b=0. La solution est recherchée dans les réels.

À revoir dans les cours : Structures de contrôle : if et else · Types de données · Entrées et sorties : printf et scanf.

Le test exact à zéro est adapté ici aux coefficients explicitement saisis comme zéro. Une application numérique alimentée par des mesures ou des calculs devrait choisir une stratégie de tolérance et d’échelle ; remplacer arbitrairement tout petit coefficient par zéro changerait le problème posé. Les exemples visent des coefficients courants et non des valeurs proches des limites du type double.

Version simple (naïve) : Équation du premier degré

On suppose deux réels valides et a non nul. Il suffit alors de calculer -b/a. Les cas a=0 ne sont pas couverts par cette version simple ; ils restent expliques et traites dans la version générale.

#include <stdio.h>

int main(void) {
    double a, b, x;
    puts("Entrer a b :");
    scanf("%lf %lf", &a, &b);
    x = -b / a;
    printf("x = %.6f\n", x);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Équation du premier degré - version simple

Exécution 1. La seule solution est 3.

Entrer a b :
2 -6
x = 3.000000

Exécution 2. Le seul réel vérifiant −4x=0 est zéro.

Entrer a b :
-4 0
x = 0.000000

Version plus générale : Équation du premier degré

Équation du premier degré : programme complet

#include <stdio.h>
#include <math.h>

int main(void) {
    double a, b, x;
    puts("Entrer a b :");
    if (scanf("%lf %lf", &a, &b) != 2) {
        puts("Saisie invalide.");
        return 1;
    }
    if (!isfinite(a) || fabs(a) > 1000000.0 || !isfinite(b) || fabs(b) > 1000000.0) {
        puts("Valeur hors domaine.");
        return 1;
    }
    if (a == 0.0) {
        if (b == 0.0) {
            puts("Infinite de solutions reelles.");
        } else {
            puts("Aucune solution reelle.");
        }
    } else {
        x = -b / a;
        printf("x = %.6f\n", x);
    }
    return 0;
}
Comprendre Équation du premier degré
Instruction ou étapeExplication et état du programme
a == 0.0À a=0, on ne calcule jamais -b/a. On entre dans le traitement de l’équation constante.
b == 0.0 dans ce blocSi b=0 aussi, 0=0 est vrai pour tout x : infinité de solutions.
else interneAvec a=0 et b=5, l’égalité 5=0 est impossible : aucune solution.
else externeAvec a=2 et b=−6, a n’est pas nul : x=−(−6)/2=3.
Vérification de la racineRemplacer x par 3 donne 2×3−6=0. Ce contrôle permet de repérer une erreur de signe.
Exécutions commentées : Équation du premier degré
SaisieRésultat après l’invitePourquoi ce résultat ?
2 -6x = 3.000000La seule solution est 3.
0 0Infinite de solutions reelles.L’équation est toujours vraie.
0 5Aucune solution reelle.L’équation est toujours fausse.
-4 0x = 0.000000Le seul réel vérifiant −4x=0 est zéro.

Sorties écran : Équation du premier degré

Exécution 1. La seule solution est 3.

Entrer a b :
2 -6
x = 3.000000

Exécution 2. L’équation est toujours vraie.

Entrer a b :
0 0
Infinite de solutions reelles.

Exécution 3. L’équation est toujours fausse.

Entrer a b :
0 5
Aucune solution reelle.

Exécution 4. Le seul réel vérifiant −4x=0 est zéro.

Entrer a b :
-4 0
x = 0.000000

Exercice 17 : Résoudre ax² + bx + c = 0

Lire a, b et c, puis traiter tous les cas du support : équation toujours vraie, impossible, linéaire, ou quadratique. Pour a≠0, calculer Δ=b²−4ac et distinguer Δ<0, Δ=0 et Δ>0.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Les formules quadratiques supposent a≠0. Si a=0, on retombe sur bx+c=0, même si le titre dit « deuxième degré ». Pour a≠0 et Δ≥0, la racine carrée est définie dans les réels ; sqrt est déclarée dans math.h.

À revoir dans les cours : Structures de contrôle : if et else · Types de données · Structure d’un programme C.

L’arbre des cas à ne pas oublier
ConditionÉquation restanteRésultat
a=0, b=0, c=00=0Tout réel
a=0, b=0, c≠0c=0Aucun réel
a=0, b≠0bx+c=0−c/b
a≠0, Δ<0Vraie équation quadratiqueAucune racine réelle
a≠0, Δ=0Racine double−b/(2a)
a≠0, Δ>0Deux racines réelles(−b±√Δ)/(2a)

Précision numérique : cette solution pédagogique utilise les formules classiques pour des coefficients de taille courante. Les doubles ne représentent pas exactement tous les réels ; un discriminant calculé très proche de zéro et la soustraction de nombres proches peuvent demander une méthode numérique plus robuste. Le programme n’introduit pas de tolérance arbitraire et ne prétend pas être un solveur scientifique universel. Pour Δ<0, « aucune solution » signifie aucune solution réelle.

Version simple (naïve) : Tous les cas de l’équation

On suppose trois coefficients réels valides, de taille courante, et a non nul : il s'agit vraiment d'une équation du deuxième degre. On conserve les trois cas du discriminant pour ne jamais prendre la racine carrée d'un nombre négatif. Les équations dégénérées sont réservées a la version générale.

#include <stdio.h>
#include <math.h>

int main(void) {
    double a, b, c, delta, x1, x2;
    puts("Entrer a b c :");
    scanf("%lf %lf %lf", &a, &b, &c);
    delta = b * b - 4.0 * a * c;
    printf("Delta = %.6f\n", delta);
    if (delta < 0.0) {
        puts("Aucune solution reelle.");
    } else if (delta == 0.0) {
        x1 = -b / (2.0 * a);
        printf("Racine double = %.6f\n", x1);
    } else {
        x1 = (-b - sqrt(delta)) / (2.0 * a);
        x2 = (-b + sqrt(delta)) / (2.0 * a);
        printf("x1 = %.6f\nx2 = %.6f\n", x1, x2);
    }
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Tous les cas de l’équation - version simple

Exécution 1. Cas D, Δ<0 : pas de solution dans R.

Entrer a b c :
1 0 1
Delta = -4.000000
Aucune solution reelle.

Exécution 2. Cas D, Δ=0.

Entrer a b c :
1 -2 1
Delta = 0.000000
Racine double = 1.000000

Exécution 3. Cas D, Δ>0.

Entrer a b c :
1 -3 2
Delta = 1.000000
x1 = 1.000000
x2 = 2.000000

Version plus générale : Tous les cas de l’équation

Tous les cas de l’équation : programme complet

#include <stdio.h>
#include <math.h>

int main(void) {
    double a, b, c, delta, x1, x2;
    puts("Entrer a b c :");
    if (scanf("%lf %lf %lf", &a, &b, &c) != 3) {
        puts("Saisie invalide.");
        return 1;
    }
    if (!isfinite(a) || fabs(a) > 1000000.0 || !isfinite(b) || fabs(b) > 1000000.0 || !isfinite(c) || fabs(c) > 1000000.0) {
        puts("Valeur hors domaine.");
        return 1;
    }
    if (a == 0.0) {
        if (b == 0.0) {
            if (c == 0.0) {
                puts("Infinite de solutions reelles.");
            } else {
                puts("Aucune solution reelle.");
            }
        } else {
            x1 = -c / b;
            printf("x = %.6f\n", x1);
        }
    } else {
        delta = b * b - 4.0 * a * c;
        printf("Delta = %.6f\n", delta);
        if (delta < 0.0) {
            puts("Aucune solution reelle.");
        } else if (delta == 0.0) {
            x1 = -b / (2.0 * a);
            printf("Racine double = %.6f\n", x1);
        } else {
            x1 = (-b - sqrt(delta)) / (2.0 * a);
            x2 = (-b + sqrt(delta)) / (2.0 * a);
            printf("x1 = %.6f\nx2 = %.6f\n", x1, x2);
        }
    }
    return 0;
}
Comprendre Tous les cas de l’équation
Instruction ou étapeExplication et état du programme
Premier if : a==0On supprime le terme quadratique. Le code ne divise jamais par 2a dans cette branche.
b==0 puis c==0Si a=b=c=0 : infinité de solutions. Si a=b=0 mais c≠0 : aucune.
b!=0 avec a==0L’équation est bx+c=0 ; la racine est -c/b. Ne pas réutiliser -b/a du problème précédent.
delta=b*b-4.0*a*cPour a=1,b=−3,c=2 : 9−8=1. Le programme affiche ce discriminant pour suivre la décision.
delta<0Aucune racine réelle ; le code n’appelle pas sqrt sur un nombre négatif.
delta==0Une racine réelle double : −b/(2a). Pour 1,−2,1 : Δ=0 et racine=1.
delta>0Pour 1,−3,2 : sqrt(1)=1 ; x1=(3−1)/2=1 ; x2=(3+1)/2=2.
Parenthèses des dénominateursÉcrire / (2.0*a) divise par tout le produit. Écrire /2.0*a multiplierait ensuite par a et donnerait une autre formule.
VérificationPour x=1 : 1−3+2=0 ; pour x=2 : 4−6+2=0. x1 et x2 sont des noms, pas une garantie d’ordre croissant quand a est négatif.
Exécutions commentées : Tous les cas de l’équation
SaisieRésultat après l’invitePourquoi ce résultat ?
0 0 0Infinite de solutions reelles.Cas A : tout réel convient.
0 0 2Aucune solution reelle.Cas B : 2=0 est impossible.
0 2 -6x = 3.000000Cas C : 2x−6=0.
1 0 1Delta = -4.000000 Aucune solution reelle.Cas D, Δ<0 : pas de solution dans R.
1 -2 1Delta = 0.000000 Racine double = 1.000000Cas D, Δ=0.
1 -3 2Delta = 1.000000 x1 = 1.000000 x2 = 2.000000Cas D, Δ>0.

Sorties écran : Tous les cas de l’équation

Exécution 1. Cas A : tout réel convient.

Entrer a b c :
0 0 0
Infinite de solutions reelles.

Exécution 2. Cas B : 2=0 est impossible.

Entrer a b c :
0 0 2
Aucune solution reelle.

Exécution 3. Cas C : 2x−6=0.

Entrer a b c :
0 2 -6
x = 3.000000

Exécution 4. Cas D, Δ<0 : pas de solution dans R.

Entrer a b c :
1 0 1
Delta = -4.000000
Aucune solution reelle.

Exécution 5. Cas D, Δ=0.

Entrer a b c :
1 -2 1
Delta = 0.000000
Racine double = 1.000000

Exécution 6. Cas D, Δ>0.

Entrer a b c :
1 -3 2
Delta = 1.000000
x1 = 1.000000
x2 = 2.000000

Exercice 18 : Construire une calculatrice

Lire deux nombres a et b puis un caractère opérateur parmi +, −, * et /. Afficher le résultat ; vérifier notamment 14,10,* et 14,20,+.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Un opérateur lu au clavier est stocké dans un char. On peut l’utiliser dans un switch avec des constantes caractères comme '+'. Le caractère est lu après les nombres : le retour à la ligne restant dans le flux doit être ignoré.

À revoir dans les cours : Entrées et sorties : printf et scanf · Structures de contrôle : if et else · Constantes et littéraux.

Le signe * doit être tapé pendant l’exécution. Si l’on transmet des entrées par un terminal ou un script, attention aux caractères spéciaux du shell : une entrée standard contenant 14 10 * évite d’interpréter l’étoile comme un motif de fichiers. Le programme attend un seul caractère opérateur et n’est pas un analyseur d’expressions complètes comme 2+3*4.

Version simple (naïve) : Calculatrice à quatre opérations

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme. L'opérateur est supposé valide et, pour une division, b est supposé non nul. L'espace avant %c reste nécessaire, meme dans un code simple.

#include <stdio.h>
#include <math.h>

int main(void) {
    double a, b, resultat;
    char op;
    puts("Entrer a b :");
    scanf("%lf %lf", &a, &b);
    puts("Entrer un operateur :");
    scanf(" %c", &op);
    switch (op) {
        case '+': resultat = a + b; break;
        case '-': resultat = a - b; break;
        case '*': resultat = a * b; break;
        case '/':
            resultat = a / b;
            break;
        default:
            puts("Operateur invalide.");
            return 1;
    }
    printf("Resultat = %.6f\n", resultat);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Calculatrice à quatre opérations - version simple

Exécution 1. Produit de l’exemple du sujet.

Entrer a b :
14 10
Entrer un operateur :
*
Resultat = 140.000000

Exécution 2. Somme de l’exemple.

Entrer a b :
14 20
Entrer un operateur :
+
Resultat = 34.000000

Exécution 3. La soustraction conserve l’ordre a−b.

Entrer a b :
14 20
Entrer un operateur :
-
Resultat = -6.000000

Exécution 4. Division réelle, pas division entière.

Entrer a b :
5 2
Entrer un operateur :
/
Resultat = 2.500000

Version plus générale : Calculatrice à quatre opérations

Calculatrice à quatre opérations : programme complet

#include <stdio.h>
#include <math.h>

int main(void) {
    double a, b, resultat;
    char op;
    puts("Entrer a b :");
    if (scanf("%lf %lf", &a, &b) != 2) {
        puts("Saisie invalide.");
        return 1;
    }
    if (!isfinite(a) || fabs(a) > 1000000.0 || !isfinite(b) || fabs(b) > 1000000.0) {
        puts("Valeur hors domaine.");
        return 1;
    }
    puts("Entrer un operateur :");
    if (scanf(" %c", &op) != 1) {
        puts("Saisie invalide.");
        return 1;
    }
    switch (op) {
        case '+': resultat = a + b; break;
        case '-': resultat = a - b; break;
        case '*': resultat = a * b; break;
        case '/':
            if (b == 0.0) {
                puts("Division par zero interdite.");
                return 1;
            }
            resultat = a / b;
            break;
        default:
            puts("Operateur invalide.");
            return 1;
    }
    printf("Resultat = %.6f\n", resultat);
    return 0;
}
Comprendre Calculatrice à quatre opérations
Instruction ou étapeExplication et état du programme
scanf(" %c", &op)L’espace avant %c consomme les blancs en attente. Sans cet espace, op pourrait recevoir le retour à la ligne qui suit b.
case '*'Avec a=14 et b=10 : resultat=140. Le break évite d’entrer dans la division.
case '+'Avec a=14 et b=20 : resultat=34.
case '/' et garde b==0On vérifie le diviseur avant de l’utiliser. Avec b=0, le return empêche toute division.
defaultLe symbole % ou toute autre opération non prévue est refusé. % ne doit pas être appliqué à des doubles.
printf après le switchToute branche qui arrive ici a affecté resultat. Les branches d’erreur ont déjà terminé le programme.
Exécutions commentées : Calculatrice à quatre opérations
SaisieRésultat après l’invitePourquoi ce résultat ?
14 10 *Resultat = 140.000000Produit de l’exemple du sujet.
14 20 +Resultat = 34.000000Somme de l’exemple.
14 20 -Resultat = -6.000000La soustraction conserve l’ordre a−b.
5 2 /Resultat = 2.500000Division réelle, pas division entière.
5 0 /Division par zero interdite.La garde est exécutée avant a/b.
2 3 %Operateur invalide.Aucun case n’accepte %.

Sorties écran : Calculatrice à quatre opérations

Exécution 1. Produit de l’exemple du sujet.

Entrer a b :
14 10
Entrer un operateur :
*
Resultat = 140.000000

Exécution 2. Somme de l’exemple.

Entrer a b :
14 20
Entrer un operateur :
+
Resultat = 34.000000

Exécution 3. La soustraction conserve l’ordre a−b.

Entrer a b :
14 20
Entrer un operateur :
-
Resultat = -6.000000

Exécution 4. Division réelle, pas division entière.

Entrer a b :
5 2
Entrer un operateur :
/
Resultat = 2.500000

Exécution 5. La garde est exécutée avant a/b.

Entrer a b :
5 0
Entrer un operateur :
/
Division par zero interdite.

Exécution 6. Aucun case n’accepte %.

Entrer a b :
2 3
Entrer un operateur :
%
Operateur invalide.

Exercice 19 : Calculer un salaire, préciser la règle

Lire le nombre de points d’indice d’un salarié. Le support indique une valeur du point de 3 000 F en dessous de 1 000 points et de 2 500 F au-dessus de 1 000 points.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Avant de programmer une règle, son domaine et ses frontières doivent être définis. Le texte ne dit pas ce qui arrive exactement à 1 000 points et ne précise pas si les tarifs s’appliquent à tous les points ou seulement par tranches. Il n’existe donc pas de réponse unique sans une hypothèse explicite.

À revoir dans les cours : Structures de contrôle : if et else · Types de données · Variables et affectation.

Hypothèses annoncées : la première solution suit une lecture à tarif global et choisit 3 000 F pour le point au seuil exact de 1 000. La seconde montre l’autre interprétation courante, par tranches. Le document ne permet pas de trancher ; en évaluation, annoncer sa convention et demander la règle attendue. Ce sont les règles fictives du TD, pas une grille salariale réelle.

Version simple (naïve) : Hypothèse A, un tarif pour tous les points

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme. Le nombre de points est supposé entier, positif ou nul et assez petit pour le produit. La convention de tarif annoncée reste la meme.

#include <stdio.h>

int main(void) {
    long long points, salaire;
    puts("Entrer points :");
    scanf("%lld", &points);
    if (points <= 1000) {
        salaire = points * 3000LL;
    } else {
        salaire = points * 2500LL;
    }
    printf("Salaire = %lld F\n", salaire);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Hypothèse A, un tarif pour tous les points - version simple

Exécution 1. 800 points au tarif global de 3 000 F.

Entrer points :
800
Salaire = 2400000 F

Exécution 2. Le seuil est inclus dans le premier tarif par convention.

Entrer points :
1000
Salaire = 3000000 F

Exécution 3. Tous les points passent à 2 500 F : cette règle crée une baisse au seuil.

Entrer points :
1001
Salaire = 2502500 F

Exécution 4. 1 200×2 500.

Entrer points :
1200
Salaire = 3000000 F

Version plus générale : Hypothèse A, un tarif pour tous les points

Hypothèse A : un tarif pour tous les points : programme complet

#include <stdio.h>

int main(void) {
    long long points, salaire;
    puts("Entrer points :");
    if (scanf("%lld", &points) != 1) {
        puts("Saisie invalide.");
        return 1;
    }
    if (points < 0 || points > 1000000) {
        puts("Nombre de points invalide.");
        return 1;
    }
    if (points <= 1000) {
        salaire = points * 3000LL;
    } else {
        salaire = points * 2500LL;
    }
    printf("Salaire = %lld F\n", salaire);
    return 0;
}
Comprendre Hypothèse A : un tarif pour tous les points
Instruction ou étapeExplication et état du programme
long long points, salaireLe salaire est entier. Le type long long permet les produits de plusieurs milliards sans dépendre d’un int à 32 bits.
Garde de domaineCette version choisit un nombre entier de points entre 0 et 1 000 000. La borne pédagogique évite un produit démesuré.
points <= 1000Convention ajoutée : 1 000 est inclus dans le tarif 3 000 F. Avec 800 points : 800×3000=2 400 000.
elseAu-delà de 1 000, tous les points valent ici 2 500 F. Avec 1 200 : 1 200×2 500=3 000 000.
3000LL et 2500LLLe suffixe LL donne des constantes long long. %lld correspond au type du salaire.
Exécutions commentées : Hypothèse A, un tarif pour tous les points
SaisieRésultat après l’invitePourquoi ce résultat ?
800Salaire = 2400000 F800 points au tarif global de 3 000 F.
1000Salaire = 3000000 FLe seuil est inclus dans le premier tarif par convention.
1001Salaire = 2502500 FTous les points passent à 2 500 F : cette règle crée une baisse au seuil.
1200Salaire = 3000000 F1 200×2 500.
-1Nombre de points invalide.Les points négatifs sont refusés.

Sorties écran : Hypothèse A, un tarif pour tous les points

Exécution 1. 800 points au tarif global de 3 000 F.

Entrer points :
800
Salaire = 2400000 F

Exécution 2. Le seuil est inclus dans le premier tarif par convention.

Entrer points :
1000
Salaire = 3000000 F

Exécution 3. Tous les points passent à 2 500 F : cette règle crée une baisse au seuil.

Entrer points :
1001
Salaire = 2502500 F

Exécution 4. 1 200×2 500.

Entrer points :
1200
Salaire = 3000000 F

Exécution 5. Les points négatifs sont refusés.

Entrer points :
-1
Nombre de points invalide.

Version simple (naïve) : Hypothèse B, tarification par tranches

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme. Le nombre de points est supposé entier, positif ou nul et assez petit pour le produit. La convention de tarif annoncée reste la meme.

#include <stdio.h>

int main(void) {
    long long points, salaire;
    puts("Entrer points :");
    scanf("%lld", &points);
    if (points <= 1000) {
        salaire = points * 3000LL;
    } else {
        salaire = 1000LL * 3000LL + (points - 1000LL) * 2500LL;
    }
    printf("Salaire = %lld F\n", salaire);
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Hypothèse B, tarification par tranches - version simple

Exécution 1. Première tranche uniquement.

Entrer points :
800
Salaire = 2400000 F

Exécution 2. La première tranche est pleine.

Entrer points :
1000
Salaire = 3000000 F

Exécution 3. Un seul point supplémentaire à 2 500 F.

Entrer points :
1001
Salaire = 3002500 F

Exécution 4. 3 000 000 pour la première tranche +500 000 pour la seconde.

Entrer points :
1200
Salaire = 3500000 F

Version plus générale : Hypothèse B, tarification par tranches

Hypothèse B : tarification par tranches : programme complet

#include <stdio.h>

int main(void) {
    long long points, salaire;
    puts("Entrer points :");
    if (scanf("%lld", &points) != 1) {
        puts("Saisie invalide.");
        return 1;
    }
    if (points < 0 || points > 1000000) {
        puts("Nombre de points invalide.");
        return 1;
    }
    if (points <= 1000) {
        salaire = points * 3000LL;
    } else {
        salaire = 1000LL * 3000LL + (points - 1000LL) * 2500LL;
    }
    printf("Salaire = %lld F\n", salaire);
    return 0;
}
Comprendre Hypothèse B : tarification par tranches
Instruction ou étapeExplication et état du programme
Même lecture et même domaineOn ne change pas la saisie ni les contrôles ; seule la règle au-delà du seuil est différente.
points <= 1000Les 1 000 premiers points valent 3 000 F chacun, exactement comme dans l’hypothèse A.
1000LL * 3000LLLa première tranche entière vaut 3 000 000 F et reste acquise.
(points - 1000LL) * 2500LLSeuls les points supplémentaires valent 2 500 F. Pour 1 200 : 200×2 500=500 000.
Somme des tranches3 000 000+500 000=3 500 000. Ce salaire ne baisse pas quand on dépasse 1 000 points.
Exécutions commentées : Hypothèse B, tarification par tranches
SaisieRésultat après l’invitePourquoi ce résultat ?
800Salaire = 2400000 FPremière tranche uniquement.
1000Salaire = 3000000 FLa première tranche est pleine.
1001Salaire = 3002500 FUn seul point supplémentaire à 2 500 F.
1200Salaire = 3500000 F3 000 000 pour la première tranche +500 000 pour la seconde.

Sorties écran : Hypothèse B, tarification par tranches

Exécution 1. Première tranche uniquement.

Entrer points :
800
Salaire = 2400000 F

Exécution 2. La première tranche est pleine.

Entrer points :
1000
Salaire = 3000000 F

Exécution 3. Un seul point supplémentaire à 2 500 F.

Entrer points :
1001
Salaire = 3002500 F

Exécution 4. 3 000 000 pour la première tranche +500 000 pour la seconde.

Entrer points :
1200
Salaire = 3500000 F

Exercice 20 : Intersection de deux intervalles

Lire quatre entiers A, B, C et D, puis calculer l’intersection des intervalles fermés [A,B] et [C,D]. Distinguer un intervalle, un point et l’ensemble vide.

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Un réel x appartient aux deux intervalles si A≤x≤B et C≤x≤D. Il doit donc être au moins égal à max(A,C) et au plus égal à min(B,D). Les intervalles sont fermés : leurs extrémités sont incluses.

À revoir dans les cours : Structures de contrôle : if et else · Variables et affectation · Les bases du langage C.

Visualiser les trois situations
Intervalle 1Intervalle 2Borne gaucheBorne droiteIntersection
[1,5][3,8]35[3,5]
[1,3][3,7]33{3}
[1,2][3,4]32Vide

L’opérateur ?: n’est pas obligatoire : on peut obtenir gauche avec gauche=A; if(C>gauche) gauche=C; et droite avec droite=B; if(D<droite) droite=D;. Les deux méthodes comparent des valeurs ; elles n’ont pas besoin d’additionner les bornes, donc évitent un débordement arithmétique inutile.

Version simple (naïve) : Intersection d’intervalles fermés

On suppose les saisies numériques correctes, représentables dans leurs types et dans le domaine de la formule. Cette version ne vérifie pas le retour de scanf ni les bornes de saisie. Le coeur du calcul reste le meme. Les intervalles sont supposés correctement ordonnés : A<=B et C<=D.

#include <stdio.h>

int main(void) {
    int A, B, C, D, gauche, droite;
    puts("Entrer A B C D :");
    scanf("%d %d %d %d", &A, &B, &C, &D);
    gauche = (A > C) ? A : C;
    droite = (B < D) ? B : D;
    if (gauche < droite) {
        printf("Intersection = [%d, %d]\n", gauche, droite);
    } else if (gauche == droite) {
        printf("Intersection = {%d}\n", gauche);
    } else {
        puts("Intersection vide.");
    }
    return 0;
}

Lecture du code simple : les déclarations donnent les variables nécessaires ; scanf remplit ces variables sans vérifier son résultat ; les instructions suivantes effectuent le calcul ou choisissent la branche. Les tableaux de la version générale, plus bas, détaillent le même coeur de calcul et les évolutions des valeurs. Les cas exclus par les hypothèses ne doivent pas etre testes avec cette version.

Sorties écran : Intersection d’intervalles fermés - version simple

Exécution 1. max(1,3)=3 ; min(5,8)=5.

Entrer A B C D :
1 5 3 8
Intersection = [3, 5]

Exécution 2. Les deux intervalles se touchent en 3, qui est inclus.

Entrer A B C D :
1 3 3 7
Intersection = {3}

Exécution 3. gauche=3 > droite=2.

Entrer A B C D :
1 2 3 4
Intersection vide.

Exécution 4. Le deuxième intervalle est inclus dans le premier.

Entrer A B C D :
2 8 3 4
Intersection = [3, 4]

Exécution 5. Deux singletons identiques.

Entrer A B C D :
2 2 2 2
Intersection = {2}

Version plus générale : Intersection d’intervalles fermés

Intersection d’intervalles fermés : programme complet

#include <stdio.h>

int main(void) {
    int A, B, C, D, gauche, droite;
    puts("Entrer A B C D :");
    if (scanf("%d %d %d %d", &A, &B, &C, &D) != 4) {
        puts("Saisie invalide.");
        return 1;
    }
    if (A > B || C > D) {
        puts("Intervalle invalide.");
        return 1;
    }
    gauche = (A > C) ? A : C;
    droite = (B < D) ? B : D;
    if (gauche < droite) {
        printf("Intersection = [%d, %d]\n", gauche, droite);
    } else if (gauche == droite) {
        printf("Intersection = {%d}\n", gauche);
    } else {
        puts("Intersection vide.");
    }
    return 0;
}
Comprendre Intersection d’intervalles fermés
Instruction ou étapeExplication et état du programme
A>B || C>DVérifie l’ordre des bornes dans chaque intervalle. La solution refuse un intervalle inversé plutôt que de le réordonner silencieusement.
gauche = (A > C) ? A : COpérateur conditionnel : si A>C, choisir A ; sinon choisir C. C’est max(A,C). Avec [1,5] et [3,8], gauche=3.
droite = (B < D) ? B : DChoisit min(B,D). Dans le même exemple, droite=5.
gauche < droiteIl reste un intervalle non réduit : [3,5].
gauche == droiteUne extrémité commune reste dans les deux intervalles fermés. On affiche un singleton {gauche}, équivalent à [gauche,gauche].
gauche > droiteAucune valeur ne peut être à la fois au-dessus de gauche et en dessous de droite : intersection vide.
Exécutions commentées : Intersection d’intervalles fermés
SaisieRésultat après l’invitePourquoi ce résultat ?
1 5 3 8Intersection = [3, 5]max(1,3)=3 ; min(5,8)=5.
1 3 3 7Intersection = {3}Les deux intervalles se touchent en 3, qui est inclus.
1 2 3 4Intersection vide.gauche=3 > droite=2.
2 8 3 4Intersection = [3, 4]Le deuxième intervalle est inclus dans le premier.
2 2 2 2Intersection = {2}Deux singletons identiques.
5 1 2 8Intervalle invalide.Les bornes A et B sont inversées.

Sorties écran : Intersection d’intervalles fermés

Exécution 1. max(1,3)=3 ; min(5,8)=5.

Entrer A B C D :
1 5 3 8
Intersection = [3, 5]

Exécution 2. Les deux intervalles se touchent en 3, qui est inclus.

Entrer A B C D :
1 3 3 7
Intersection = {3}

Exécution 3. gauche=3 > droite=2.

Entrer A B C D :
1 2 3 4
Intersection vide.

Exécution 4. Le deuxième intervalle est inclus dans le premier.

Entrer A B C D :
2 8 3 4
Intersection = [3, 4]

Exécution 5. Deux singletons identiques.

Entrer A B C D :
2 2 2 2
Intersection = {2}

Exécution 6. Les bornes A et B sont inversées.

Entrer A B C D :
5 1 2 8
Intervalle invalide.

Partie 4 : Questions de cours

Question de cours 1 : Types des constantes et des littéraux

Donner le type de chacune des écritures : '1', "1", 1, "1234", "jean".

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

En C, l’apparence d’un symbole ne suffit pas à déterminer son type. Les délimiteurs font partie de la syntaxe. Une constante caractère ordinaire telle que '1' a le type int en C, même si elle sert souvent à initialiser un char. Une chaîne littérale ordinaire a un type tableau de char, avec un élément nul final.

À revoir dans les cours : Types de données · Constantes et littéraux.

La réponse complète aux cinq types
ÉcritureType en CSens
'1'intConstante caractère : code du caractère chiffre 1, pas la valeur numérique un.
"1"char[2]Tableau contenant '1' puis '\0'.
1intConstante entière décimale de valeur 1.
"1234"char[5]Quatre caractères chiffres puis '\0'.
"jean"char[5]Quatre lettres puis '\0'.

Dans beaucoup d’expressions, un tableau est converti en pointeur vers son premier élément. Cela n’en fait pas le type initial du littéral : "jean" a bien le type char[5], même si son utilisation dans un appel fournit souvent un char *. L’opérateur sizeof fait partie des cas où cette conversion n’a pas lieu.

Le code du caractère '1' est souvent 49 dans un environnement ASCII, mais on n’a pas besoin de supposer ASCII pour répondre : il faut distinguer le caractère chiffre et l’entier 1. Une chaîne littérale ne doit pas être modifiée, même si son type en C n’est pas un tableau de const char.

Observer les tailles sans supposer la taille d’int : programme complet

#include <stdio.h>

int main(void) {
    printf("'1' a la taille de int : %d\n",
           sizeof('1') == sizeof(int));
    printf("Taille de \"1\" = %zu\n", sizeof("1"));
    printf("Taille de \"1234\" = %zu\n", sizeof("1234"));
    printf("Taille de \"jean\" = %zu\n", sizeof("jean"));
    return 0;
}
Comprendre Observer les tailles sans supposer la taille d’int
Instruction ou étapeExplication et état du programme
sizeof('1') == sizeof(int)La comparaison est vraie : une constante caractère ordinaire a le type int en C. Le résultat affiché vaut 1.
sizeof("1")Le tableau contient le caractère '1' puis le caractère nul final : deux char, donc taille 2.
sizeof("1234")Quatre chiffres visibles et un nul final : taille 5. C’est du texte, pas l’entier 1234.
sizeof("jean")Même longueur de quatre caractères visibles et un nul final : taille 5.
%zuLe résultat de sizeof a le type size_t ; le format adapté est %zu. On ne suppose pas que int occupe exactement quatre octets.
Exécutions commentées : Observer les tailles sans supposer la taille d’int
SaisieRésultat après l’invitePourquoi ce résultat ?
(aucune)'1' a la taille de int : 1 Taille de "1" = 2 Taille de "1234" = 5 Taille de "jean" = 5sizeof sur un littéral chaîne mesure le tableau complet, y compris son nul final.

Sorties écran : Observer les tailles sans supposer la taille d’int

Exécution 1. sizeof sur un littéral chaîne mesure le tableau complet, y compris son nul final.

'1' a la taille de int : 1
Taille de "1" = 2
Taille de "1234" = 5
Taille de "jean" = 5

Question de cours 2 : Caractère, chaîne et écriture 'jean'

Quelle différence syntaxique et sémantique existe entre une constante caractère et une chaîne ? L’écriture 'jean' est-elle correcte ?

Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

Les apostrophes simples délimitent une constante caractère ; les guillemets doubles délimitent une chaîne. Il faut distinguer une constante, son type et la variable qui la reçoit. char lettre = 'j'; déclare une variable char initialisée à partir d’une constante de type int.

À revoir dans les cours : Constantes et littéraux · Types de données · Variables et affectation.

Même texte apparent, objets différents
ÉcritureCatégorieUsage à retenir
'j'Constante caractère ordinaire, type intUn caractère ; peut initialiser un char.
"j"Littéral chaîne, type char[2]Un caractère visible et un nul final.
"jean"Littéral chaîne, type char[5]Un mot de quatre lettres.
'jean'Constante caractère multicaractère, type intConstruction permise par la syntaxe C ; valeur dépendant de l’implémentation, ce n’est pas une chaîne.

Réponse précise à « est-elle correcte ? » : 'jean' n’est pas la bonne écriture pour représenter le prénom. En C, elle peut néanmoins être acceptée comme constante multicaractère de type int, avec une valeur définie par l’implémentation et éventuellement un avertissement du compilateur. La déclarer simplement « impossible en C » serait inexact ; lui attribuer une valeur portable le serait aussi. Pour le texte souhaité, écrire "jean".

Pour stocker une chaîne modifiable, écrire par exemple char nom[] = "jean"; : le tableau possède cinq éléments et reçoit une copie des lettres et du nul final. Pour pointer vers le littéral sans chercher à le modifier, écrire const char *nom = "jean";. Un unique char ne peut pas contenir les quatre lettres et le terminateur.

Les exemples de syntaxe ci-dessus sont des déclarations isolées pour comparer les notions ; le programme complet de la question 1 permet de vérifier les tailles. On n’exécute pas une valeur multicaractère pour la présenter comme universelle.

Question de cours 3 : Pré-incrémentation, post-incrémentation et séquencement

Distinguer ++i et i++, puis analyser :

i = 4;
x = 4 + ++i;
y = 4 + i++;
z = ++i + i++;
Voir le corrigé détailléMasquer le corrigé

Rappel des notions utiles

++i augmente i puis produit la nouvelle valeur ; i++ produit l’ancienne valeur et augmente i comme effet de bord. Quand ces expressions se trouvent seules dans des instructions successives, leur effet est simple à suivre. Cela ne permet pas de modifier i plusieurs fois sans séquencement dans une même expression.

À revoir dans les cours : Les bases du langage C · Variables et affectation · Types de données.

Suivre les trois premières instructions en les isolant du code invalide
Instruction du préfixe isoléValeur produite par l’incrémentationAffectationi après l’instruction
i=4-i reçoit 44
x=4 + ++i5x=95
y=4 + i++5y=96

La dernière instruction du sujet a un comportement indéfini : z = ++i + i++; modifie i deux fois dans des évaluations non séquencées l’une par rapport à l’autre. L’opérateur + ne fournit pas l’ordre nécessaire. On ne peut attribuer aucune valeur finale garantie à z ou à i pour le programme original, ni déduire un résultat fiable en essayant plusieurs compilateurs. Les règles de priorité et d’associativité ne résolvent pas ce problème.

Le tableau de préfixe décrit les instructions bien définies considérées sans la dernière ligne. Si l’on conserve la ligne au comportement indéfini dans le programme complet, le langage ne garantit pas son comportement global. La bonne correction consiste à signaler ce défaut, puis à proposer une autre suite d’instructions qui impose l’intention souhaitée. Les deux programmes ci-dessous ne contiennent aucune expression indéfinie.

Préfixe bien défini, sans la dernière instruction : programme complet

#include <stdio.h>

int main(void) {
    int i = 4;
    int x, y;
    x = 4 + ++i;
    y = 4 + i++;
    printf("x = %d, y = %d, i = %d\n", x, y, i);
    return 0;
}
Comprendre Préfixe bien défini, sans la dernière instruction
Instruction ou étapeExplication et état du programme
int i=4État initial : i=4 ; x et y ne sont pas encore affectés.
x=4 + ++ii passe d’abord à 5 ; l’expression ++i produit 5 ; x reçoit 4+5=9. Fin de l’instruction : i=5.
y=4 + i++i++ produit l’ancienne valeur 5 ; y reçoit 4+5=9 ; l’effet de l’incrémentation est accompli à la fin de l’instruction : i=6.
printf séparéIl est placé après les instructions et ne modifie pas i : x=9, y=9, i=6 sont des valeurs déterminées pour ce programme.
Exécutions commentées : Préfixe bien défini, sans la dernière instruction
SaisieRésultat après l’invitePourquoi ce résultat ?
(aucune)x = 9, y = 9, i = 6Ce résultat appartient au préfixe isolé, qui ne contient pas l’expression indéfinie.

Sorties écran : Préfixe bien défini, sans la dernière instruction

Exécution 1. Ce résultat appartient au préfixe isolé, qui ne contient pas l’expression indéfinie.

x = 9, y = 9, i = 6

Une réécriture explicitement séquencée : programme complet

#include <stdio.h>

int main(void) {
    int i = 4;
    int x, y, z, gauche, droite;
    x = 4 + ++i;
    y = 4 + i++;
    ++i;
    gauche = i;
    droite = i;
    ++i;
    z = gauche + droite;
    printf("x = %d, y = %d, z = %d, i = %d\n", x, y, z, i);
    return 0;
}
Comprendre Une réécriture explicitement séquencée
Instruction ou étapeExplication et état du programme
Après le calcul de yLe préfixe reste identique : x=9, y=9, i=6.
++i ; gauche=iOn impose maintenant un choix : faire l’incrémentation de gauche en premier. i devient 7, gauche reçoit 7.
droite=i ; ++iOn mémorise 7 dans droite puis on augmente i à 8.
z=gauche+droiteLes deux variables ne sont plus modifiées pendant l’addition : z=7+7=14.
Différence avec le sujetCette nouvelle suite d’instructions définit un ordre. Ce n’est pas une preuve que le programme original produit 14.
Exécutions commentées : Une réécriture explicitement séquencée
SaisieRésultat après l’invitePourquoi ce résultat ?
(aucune)x = 9, y = 9, z = 14, i = 8La réécriture impose un séquencement ; son résultat est défini.

Sorties écran : Une réécriture explicitement séquencée

Exécution 1. La réécriture impose un séquencement ; son résultat est défini.

x = 9, y = 9, z = 14, i = 8

Références techniques et conventions

Les règles délicates ont été vérifiées dans le projet public C11 du WG14, N1570 : §6.4.4.4 et §6.4.5 (caractères et chaînes), §6.5 (effets non séquencés), §6.5.5 (division et reste), §6.8.4.2 (switch), §7.21.6.1 et §7.21.6.2 (formats des entrées/sorties). Les programmes corrigés ont été compilés et leurs cas d’essai exécutés localement ; aucun résultat n’est attribué aux expressions au comportement indéfini.

Questions / Réponses

Aucune question. Soyez le premier à poser une question.
★ ★ ★ ★ ★
Aucune note. Soyez le premier à attribuer une note !

Ajouter un commentaire

Anti-spam