Objectifs pédagogiques
Les principaux objectifs de cette feuille d'exercices sont :
- de découvrir la syntaxe du langage C à travers des exercices de lecture, de mise en forme et de recherche d'erreurs ;
- de coder des premiers programmes élémentaires « classiques » (afficher la valeur d'une fonction, une table de multiplication, etc.).
Pour traiter ces exercices, il serait idéal d'avoir étudié le cours jusqu'au chapitre C2‑VII inclus. Néanmoins, cela obligerait à retarder considérablement les séances de travaux pratiques en classe. De plus, certains exercices ne font appel qu'à un nombre ciblé de connaissances et des renvois aux principaux éléments de cours requis sont indiqués au fur et à mesure des questions, de sorte qu'il est possible d'aborder les questions dans un état d'esprit de découverte, sans objectif de maîtrise.
Travail demandé
Les exercices sont presque tous indépendants les uns des autres, mais ordonnés par niveaux croissants de difficultés et de connaissances requises. Certaines questions peuvent être traitées sur cahier, mais la plupart nécessitent l'emploi d'un ordinateur.
Pour coder les programmes, il est vivement recommandé d'utiliser un éditeur de code comme, typiquement, Sublime Text, à paramétrer pour obtenir une coloration syntaxique adaptée au langage C (cf. chap. C1‑II ).
- Au fur et à mesure, enregistrer chaque fichier source principal en le nommant conformément à la numérotation des exercices, c'est‑à‑dire par exemple pour l'exercice 1, question a :
C2exo1_a.c - Tous les fichiers, y compris les exécutables générés par la chaîne de compilation locale, doivent être regroupés dans un répertoire d'exercices nommé par exemple
C2_EXOS, lui‑même placé dans le répertoire principal de programmation, lui‑même placé dans répertoire personnel d'étudiant.
Répondre aux questions supplémentaires sur feuille ou cahier.
- Analyse syntaxique d'un programme simple
- Syntaxiquement, comment s'appelle l'élément de code des lignes nº 1 et 2 ? À quoi sert‑il ? (cf. chap. C2‑II )
- Syntaxiquement, comment s'appelle l'élément de code de la ligne nº 3 ? À quoi sert‑il ? (cf. chap. C2‑II )
- Syntaxiquement, comment s'appelle l'élément de code de la ligne nº 7 ? À quoi sert‑il ? (cf. chap. C2‑III )
- De quoi est composée l'instruction de la ligne nº 12 ? Quel est son but ? (cf. chap. C2‑VII )
- De combien d'instructions le bloc de définition de la fonction
mainest‑il constitué ? Indiquer le nº de la première ligne de chacune de ces instructions (cf. chap. C2‑II ). - De combien d'instructions le bloc de l'instruction
do… whileest‑il constitué ? Indiquer le nº de la première ligne de chacune de ces instructions (cf. chap. C2‑II ). - Syntaxiquement, comment s'appelle l'élément de code à la ligne nº 24, placé entre le mot‑clef
whileet le symbole;? De combien d'atomes est‑il constitué ? En établir la liste (cf. chap. C2‑II ). - Syntaxiquement, comment s'appelle l'élément de code
&&? Que code‑t‑il ? (cf. chap. C2‑IV ) - Mise en forme et analyse sémantique d'un code source
- Indenter les instructions conformément à la structure du programme et avec la même convention d'indentation que celle du programme de l'exercice 1 (cf. chap. C2‑X ).
- Ajouter judicieusement des lignes d'aération pour séparer les différentes parties du programme et les rendre ainsi plus facile à repérer (cf. chap. C2‑X ).
- À quoi servent les deux constantes déclarées
C_TO_F_RATEetC_TO_F_OFFSETau tout début du bloc de définition de la fonctionmain? (cf. l'article Wikipedia W) - Pourquoi l'ordre de saisie des deux valeurs limites du tableau n'a‑t‑il pas d'incidence sur l'affichage ? Que se passe‑t‑il si l'utilisateur tape deux valeurs limites égales ?
- * En observant la sortie du programme sur le terminal, analyser l'instruction d'affichage des valeurs de températures (appel de la fonction
printfà la ligne nº 20 dans le code supra – cf. chap. C2‑VII ). L'affichage est‑il satisfaisant pour les valeurs de températures usuelles ? - * Observer les constantes littérales (c'est‑à‑dire les valeurs numériques) employées dans le code source. En déduire une modification pour rendre le programme plus facile à faire évoluer (cf. chap. C2‑III ).
- Ajouter une ligne de commentaire avant chaque groupe d'instructions constituant une partie distincte du programme.
- Recherche d'erreurs de syntaxe
- Une erreur de syntaxe (cf. chap. C2‑I ) :
- Deux fois la même erreur lexicographique (cf. chap. C2‑II ) :
- Deux erreurs (cf. chap. C2‑II et et chap. C2‑VII ) :
- Trois erreurs de syntaxe (cf. chap. C2‑V ) et :
- Trois erreurs de syntaxe (cf. chap. C2‑VII et chap. C2‑V ) :
- Recherche d'erreurs de codage
- qu'il peut y avoir plusieurs erreurs dans chaque extrait de code ;
- qu'il peut s'agir d'erreurs d'exécution et non pas seulement de syntaxe (pour le programme de la question d), il faut le tester, en ayant compris son but).
-
if (currentDate = birthdayDate) { print("Joyeux anniversaire !\n"); } else { print("Joyeux non-anniversaire !\n"); } -
if (x >= 0) printf("x positif"); if (y >= 0) printf(" et y aussi.\n"); -
for (int p = 10; p <= 0; p--) { printf("L'inverse de %d est %g\n.", p, 1.0/p); } -
#include <stdio.h> void Main(){} int newAttempt = 3; int secretCode = 1234; do { int typedCode = 0; if (newAttempt <= 2) { printf("Il vous reste %d tentatives.\n"); } printf("Tapez votre code secret : "); scanf("%d", &typedCode) newAtempt--; } while (typedCode == secretCode || newAttemps > 0); if (typedCode == secretcode) { printf("Bienvenue !\n"); } else { printf("Code incorrect, contactez votre administrateur !\n"); return -1; } return 0; } - de commencer par corriger immédiatement les erreurs évidentes de syntaxe ;
- puis de recourir à un compilateur pour trouver toutes les erreurs restantes et les corriger ;
- et enfin, d'exécuter le programme avec divers scénarios et constater les dysfonctionnements à corriger…
- Codage de fonctions mathématiques
- demande à l'utilisateur de saisir un entier positif n,
- affiche la valeur du nombre f(n) où f est une fonction spécifiée aux questions a) et b) ci‑après,
- si une valeur négative est saisie, un bref message s'affiche pour signaler l'impossibilité du calcul ;
- l'exécution du programme s'achève lorsque l'utilisateur saisit la valeur
0. - Traiter l'exercice avec la fonction f(n) = √n (racine carrée, en anglais square root). On exploitera le fichier d'en‑tête de la bibliothèque standard
math.h– cf. chap. C2‑IV . - Traiter l'exercice avec la fonction f(n) = n! (factorielle) W. On codera le calcul de la valeur de cette fonction avec une boucle
for(cf. chap. C2‑V ) en stockant le résultat dans une variablefdéclarée de typeintjuste avant la boucle. - Pour le programme de la question b), que se passe‑t‑il lorsque n > 12 ? Pour cela, tester la valeur obtenue pour n = 13 n = 14, etc. puis consulter le cours, chap. C3‑II .
- Codage de bifurcations multiples
- Recoder le programme de l'exercice 1 en employant une bifurcation multiple
switch(cf. chap. C2‑V ) à la place de la succession de bifurcations simplesif…else if… (lignes nº 13 à 21) – ne rien changer aux autres lignes du code source. - Dans le programme de l'exercice précédent, question a), est‑il possible de traiter les trois cas – valeur négative, positive ou nulle – à l'aide d'une bifurcation
switchpour la saisie de l'utilisateur ? Sinon, pourquoi ? - Codage d'un jeu de devinette
- Affichage d'une table de multiplication
-
NUMBERpour le nombre n qui fait l'objet de la table de multiplication ; -
MAX_FACTORpour la valeur maximale du facteur multiplicatif m de la table. - Affichage d'une liste de multiples
-
FACTORpour le nombre n dont on veut afficher les multiples ; -
MAX_NUMBERpour le nombre maximal m en dessous duquel on cherche les multiples de n. - * Affichage d'une liste de nombres premiers
- hormis 2, aucun nombre pair n'est premier, donc on peut ne tester que les nombres impairs à partir de
3; - de même, pour chaque nombre testé, on peut ne chercher ses éventuels diviseurs que parmi les nombres impairs, et s'arrêter lorsqu'on a atteint sa racine carrée.
- * Décomposition d'un entier en facteurs premiers
- 2 000 003,
- 20 000 003,
- 200 000 033,
- 2 000 000 011.
/* Academic dull program to discover C language elements
*/
#include <stdio.h>
int main(void)
{
int typedNumber = 0;
printf("Tapez un chiffre compris entre 1 et 3,\n");
printf("ou tout autre chiffre pour quitter.\n");
do {
printf("> ");
scanf("%d", &typedNumber);
if (typedNumber == 1) {
printf(" C'est peu !\n");
}
else if (typedNumber == 2) {
printf(" C'est moyen...\n");
}
else if (typedNumber == 3) {
printf(" C'est beaucoup !\n");
}
}
while (typedNumber >= 1 && typedNumber <= 3);
printf("Au revoir.\n");
return 0;
}
/* Academic program to convert temperatures from °C to °F */
#include <stdio.h>
int main(void) {
const float C_TO_F_RATE = 9.0/5.0;
const float C_TO_F_OFFSET = 32.0;
int maxTemp_C = 0;
int minTemp_C = 0;
printf(" -- Tableau de conversion °C -> °F -- \n");
printf("Tapez les deux valeurs limites du tableau en °C : ");
scanf("%d %d", &minTemp_C, &maxTemp_C);
if (maxTemp_C < minTemp_C) {
int bufferVar = minTemp_C;
minTemp_C = maxTemp_C;
maxTemp_C = bufferVar;}
if (maxTemp_C < minTemp_C + 10) {
maxTemp_C = minTemp_C + 10;}
int deltaTemp_C = (maxTemp_C - minTemp_C) / 10;
for (int temp_C = minTemp_C; temp_C <= maxTemp_C; temp_C += deltaTemp_C) {
float temp_F = temp_C * C_TO_F_RATE + C_TO_F_OFFSET;
printf("%4d °C = %4.0f °F\n", temp_C, temp_F);
} return 0; }
Dans les extraits de code source ci‑dessous, rechercher les erreurs de syntaxe (et uniquement de syntaxe) dans chaque extrait, sachant que le nombre d'erreurs est indiqué pour aider. Proposer une correction à chaque erreur.
Remarque. Sauf lorsque le cadre commence en ligne nº 1, le code n'est pas complet. Le fait qu'un identificateur apparaisse sans avoir été déclaré n'est a priori pas une erreur (la déclaration est supposée codée précédemment).
Pour tester un tel extrait de code et sa correction avec un compilateur, il est donc indispensable :
#include <stdio.h>
int main
{
printf("Hello, Wordl\nn");
return 0;
}
float price_in_£ = raw_price * (1.0 + VAT_RATE);
printf("The item costs %5.2f pounds.\n", price_in_£);
globalTempVariation = lastMeasure - 1stMeasure;
printf("The temperature has varied of %d °C.\n, globalTempVariation");
if (measuredPressure > LOW_PRESSURE) && (measuredPressure < HIGH_PRESSURE) {
compressorRelayState = LOW;
alertSignal = LOW;
}
else if (measuredPressure ≤ LOW_PRESSURE) {
compressorRelayState = HIGH;
alertSignal = LOW;
else {
compressorRelayState = LOW;
alertSignal = HIGH;
}
int counter = 0;
char typedChar;
do {
printf("Compteur de boucle = \n", ++counter);
printf("Souhaitez-vous continuer, tapez "o", sinon toute autre touche : ");
scanf(" %c", &typedChar);
}
while (typedChar == 'o')
Dans les extraits de code source ci‑dessous, rechercher les erreurs de codage, sachant :
Pour chaque erreur, proposer une correction.
Remarque. Comme pour l'exercice précédent, sauf lorsque le cadre commence en ligne nº 1, le code n'est pas complet. Le fait qu'un identificateur apparaisse sans avoir été déclaré n'est a priori pas une erreur (la déclaration est supposée codée précédemment).
En s'inspirant du programme analysé à l'exercice 1, coder un programme qui, en boucle :
sachant que :
Dans le code source ci‑dessous, les lignes nº 11 & 12 génèrent un nombre entier aléatoire x compris entre 1 et 100. En s'inspirant des exercices précédents (notamment l'exercice 1), compléter ce code pour composer un programme qui demande à l'utilisateur de deviner ce nombre, en affichant à chaque tentative si le nombre qu'il a saisi est plus petit ou plus grand que x. La valeur 0 sera utilisée pour permettre au joueur d'abandonner le jeu.
/* Simple guess game program
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
const int MAX_NUMBER = 100;
int main(void)
{
srand(time(NULL));
int x = 1 + rand() % MAX_NUMBER; // number to be guessed
int typedNumber = 0;
printf("%d\n", x); // uncomment for debuggging - comment for playing
printf("Try to guess the hidden number between 1 and %d (0 to quit):\n", MAX_NUMBER);
do {
printf("> ");
scanf("%d", &typedNumber);
// ... to be completed
}
Coder à l'aide d'une boucle for (cf. chap. C2‑V ) un programme qui affiche la table de multiplication d'un nombre entier positif n jusqu'à un facteur multiplicatif m donnés. Par exemple, si n = 7 et m = 10, le programme doit afficher :
0 x 7 = 0 1 x 7 = 7 2 x 7 = 14 3 x 7 = 21 4 x 7 = 28 5 x 7 = 35 6 x 7 = 42 7 x 7 = 49 8 x 7 = 56 9 x 7 = 63 10 x 7 = 70
On déclarera les constantes entières nommées :
Tester le bon fonctionnement du programme pour diverses valeurs de n et m.
Coder un programme qui affiche la liste de tous les multiples d'un nombre entier positif n qui sont inférieurs à une valeur maximale m, avec un saut de ligne à chaque dizaine. Par exemple, si n = 4 et m = 100, le programme doit afficher :
4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68 72 76 80 84 88 92 96 100
On pourra procéder à l'aide d'une boucle for (cf. chap. C2‑V ) par incrémentation unitaire d'un nombre k, en testant sa divisibilité par n. On s'aidera pour cela de l'opérateur modulo : l'expression k % n == 0 est vraie si et seulement si k est divisible par n (cf. chap. C2‑IV et chap. C3‑II ).
On déclarera les constantes entières nommées :
Tester le bon fonctionnement du programmes pour diverses valeurs de n et m. Pour les valeurs de n supérieures à 10, prendre m = 1000 et effectuer un saut de ligne à chaque centaine.
En s'inspirant de l'exercice précédent, coder un programme qui affiche la liste de tous les nombres premiers W inférieurs à une valeur maximale m = 1000 avec un saut de ligne à chaque centaine, comme ci‑dessous :
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997
Remarque. L'algorithme le plus « simple » pour déterminer si un nombre est premier – à savoir tester tous les nombres en recherchant leurs diviseurs parmi tous les nombres inférieurs – peut quand même faire l'objet d'optimisations. En effet :
En s'inspirant des programmes précédents, coder un programme qui demande à l'utilisateur de saisir un nombre entier positif et qui affiche sa décomposition en facteurs premiers W. Par exemple, si l'utilisateur saisit le nombre 120, le programme doit afficher :
120 = 2 x 2 x 2 x 3 x 5
De plus, si l'utilisateur saisit un nombre premier, alors le programme produire un affichage de la forme :
nombre = nombre it's a prime number!
Rappel : Le nombre 1 n'est pas premier ; le nombre 2 est premier.
Tester ce programme avec tous les nombres de 1 à 13, puis avec le nombre 2 000 000 000 puis avec les nombres premiers :