Exercices en langage C/Structures de contrôle
Structures conditionnelles
modifierAnalyse de programme : choix multiple
modifierCes exercices d'analyses doivent être réalisés sans compilateurs, à la main à l'aide d'un crayon et d'une feuille de papier.
Soit le programme suivant :
#include <stdio.h>
#include <stdlib.h>
int main(void) {
unsigned char i=7;
i=i/2; //"/": division entière...
switch(i) {
case 1 : (void)printf("Premier\n");break;
case 2 : (void)printf("Deuxième\n");break;
case 3 : (void)printf("Troisième\n");break;
default : (void)printf("Non classe\n");
}
return EXIT_SUCCESS;
}
Qu'est ce qui sera affiché à l'écran lors de l'exécution de ce programme ?
Même question pour le programme :
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int i=18;
i=i-(--i);
switch(i) {
case 1 : (void)printf("Premier\n");
case 2 : (void)printf("Deuxième\n");
case 3 : (void)printf("Troisième\n");
default : (void)printf("Non classe\n");
}
return EXIT_SUCCESS;
}
- pour le premier programme :
Comme i vaut 7 au début du programme, on pourrait penser que i/2 donne 3.5 qui est arrondi à 4 (puisqu'affecté à une variable entière sur un octet). Mais il n'en est rien : 8/3 = 2.66 ne donne pas plus 3 mais seulement 2... parce que 8/3 donne un quotient de 2 avec 2 comme reste et que l'on s'intéresse à ce quotient justement ! C'est cela la division entière. Don quand on arrive au switch
, i vaut 3 et donc
Troisième
s'affiche à l'écran.
- Pour le deuxième programme, c'est
Non classe
qui s'affiche car :
int i=18;
i=i-(--i);
est la même chose que :
int i=18;
i--; // i passe à 17
i=i-(i); // 17-17 donne 0
Remarquons que
int i=18;
i=i-(i--);
donnerait exactement le même résultat (dans le switch).
La suppression des break a des conséquences importantes. Par exemple
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int i=2;
switch(i) {
case 1 : (void)printf("Premier\n");
case 2 : (void)printf("Deuxième\n");
case 3 : (void)printf("Troisième\n");
default : (void)printf("Non classe\n");
}
return EXIT_SUCCESS;
}
afficherait :
Deuxième Troisième Non classe
Test simple : programme non complet
modifierÉcrire la partie de programme réalisant la structure de contrôle demandée. On ne demande pas le programme en entier : pas les "include" pas le main()...
Un test sur une valeur dans la variable i : si i vaut 19 on écrit "OK" autrement on écrit "Rejet"
Cet exercice n'est qu'un échauffement pour l'exercice suivant.
/******* langage C *********/
if (i==19)
printf("OK");
else
printf("Rejet");
L'erreur la plus fréquente est d'écrire :
/******* langage C *********/
if (i=19)
printf("OK");
else
printf("Rejet");
qui affichera toujours OK quelle que soit la valeur de i ! Pouvez-vous comprendre pourquoi ? Parce que i=19 met 19 dans i qui n'est pas zéro et donc est vrai... et toujours vrai !!!
Tester votre âge
modifierÉcrire un programme testage.c contenant une fonction main
qui :
- lit sur le clavier l'âge de l'utilisateur avec la fonction
scanf
; - teste si la réponse est valide par analyse du code retour de
scanf
et teste si la valeur est comprise entre 0 et 130; - affiche si l'utilisateur est majeur (>= 18 ans) ou mineur.
/*
Nom : testage.c
Auteur : Camille36
But : Demande l'âge de l'utilisateur,
teste si la réponse est valide et
affiche si l'utilisateur est majeur (>= 18 ans).
Paramètres : non pris en compte.
Code retour : 0 (EXIT_SUCCESS)
Pour produire un exécutable avec le compilateur libre GCC :
gcc -Wall -std=c99 -o testage.exe testage.c
Pour exécuter, tapez : ./testage.exe
Version : 1.0 du 18/1/2008
Licence : GNU GPL
*/
#include <stdio.h>
#include <stdlib.h>
/* Constantes symboliques pour changement plus facile :
pas besoin de chercher toutes les occurrences de la constante littérale
dans le corps de la fonction. */
#define AGE_MAJEUR 18
#define AGE_MINI 0
#define AGE_MAXI 130
int main(void)
{
// Declare les variables
int ageLu = 0;
int nbChampLu = 0;
int codeRetour = EXIT_SUCCESS; // Valeur par defaut
// Ecrit la question à l'écran
(void)puts("Quel est votre age :");
// Lit la réponse de l'utilisateur : un entier
nbChampLu = scanf("%d", &ageLu);
// Teste si scanf a bien converti le nombre entré
if (nbChampLu != 1)
{
(void)fputs("\aErreur : Vous devez entrer un nombre entier !\n", stderr);
codeRetour = EXIT_FAILURE;
}
else if (ageLu < AGE_MINI || ageLu>AGE_MAXI)
{
(void)fprintf(stderr,
"\aErreur : Vous devez entrer un nombre entier compris entre %d et %d !\n",
AGE_MINI, AGE_MAXI);
codeRetour = EXIT_FAILURE;
}
else
{
// Le test se fait à l'appel de puts
(void)puts((ageLu < AGE_MAJEUR) ? "Tu es mineur !" : "Vous ne pouvez pas voter !");
}
return codeRetour;
} // int main(...
Structures répétitives
modifierAnalyse de programme : boucle simple
modifierCet exercice d'analyse doit être réalisé sans compilateur, à la main à l'aide d'un crayon et d'une feuille de papier. Soit le programme suivant :
#include <stdio.h>
#include <stdlib.h>
int main(void){
int i;
for (i=0;i<5;i++) {// "/": division entiere
(void)printf("Module EC%d\n",(i+9)/(i+1));
}
return EXIT_SUCCESS;
}
Qu'affichera à l'écran l'exécution de ce programme ?
La seule difficulté de ce programme est la division entière, d'ailleurs précisée dans le commentaire. C'est la division que l'on a appris à l'école primaire : 5 / 2 : quotient = 2 et reste =1. La division entière donne le quotient c'est à dire 2 (et non pas 2,5 !!!)
Module EC9 Module EC5 Module EC3 Module EC3 Module EC2
Analyse de programme : boucle simple et tableau
modifierCet exercice d'analyse doit être réalisé sans compilateur, à la main à l'aide d'un crayon et d'une feuille de papier.
Soit le programme suivant :
#include <stdio.h>
#include <stdlib.h>
int main(void){
unsigned char i;
unsigned char tab[5];
//initialisation du tableau
tab[0]=1;tab[1]=2;tab[2]=4;tab[3]=8;tab[4]=16;
for (i=0;i<5;i++) {
(void)printf("Le %d° elt est %d\n",i+1,tab[i]);
}
return EXIT_SUCCESS;
}
Qu'affichera à l'écran l'exécution de ce programme ?
Tableau de 5 cases numérotées de 0 à 4. Le programme affiche tout simplement son contenu :
Le 1° elt est 1 Le 2° elt est 2 Le 3° elt est 4 Le 4° elt est 8 Le 5° elt est 16
Remarque : Il est possible de remplacer :
//********** Langage C ***************
unsigned char tab[5];
//initialisation du tableau
tab[0]=1;tab[1]=2;tab[2]=4;tab[3]=8;tab[4]=16;
//**********Fin Langage C ***************
par
//********** Langage C ***************
//déclaration du tableau avec initialisation
unsigned char tab[5]={1,2,4,8,16};
//**********Fin Langage C ***************
Voir même par :
//********** Langage C ***************
//déclaration du tableau avec initialisation
unsigned char tab[10]={1,2,4,8,16};
//**********Fin Langage C ***************
pour lequel on déclare un tableu de 10 case mais on en initialise que 5 ! Les autres seront initialisées à 0 !
Somme d'entiers (boucle simple)
modifierÉcrire un programme somme demandant à l'utilisateur de taper 10 entiers et qui affiche leur somme. Le programme ne devra utiliser que 3 variables et ne devra pas utiliser de tableau.
/*
Programme : somme
Role : demander 10 entiers et afficher leur somme
Compilation :
gcc -Wall -std=c99 -o somme.exe somme.c
Execution :
./somme.exe
Version 2.0 du 9/1/2008
*/
#include <stdio.h>
#include <stdlib.h>
#define NB_ENTIERS 10
int main(void)
{
int valeurLue = 0;
int somme = 0;
// Lecture des valeurs
(void)printf("Vous allez devoir entrer %d entiers\n", NB_ENTIERS);
for (int i = 0; i < NB_ENTIERS ; i++)
{
// Demande et lecture de l'entier
(void)printf("Tapez l'entier de rang %d : ", i+1);
(void)scanf("%d", &valeurLue);
somme = somme + valeurLue;
} // for (int i = 0...
// Affichage du résultat
(void)printf("la somme des %d entiers vaut : %d\n",
NB_ENTIERS, somme);
return EXIT_SUCCESS;
} // int main(...
Remarques qualité sur la correction proposée :
- Un cartouche d'entête indique des informations utiles dont les particularités de la compilation (-std=c99), la version.
- Les messages guident l'utilisateur : il sait ou il en est dans sa saisie.
- Utilisation d'une constante symbolique NB_ENTIERS au lieu de valeurs littérales 10 : si le nombre d'entier à demander change, il n'y aura qu'une seule ligne à modifier au lieu de 3 dans notre cas.
- for (int i = 0 : c'est une syntaxe autorisée en C99. Avec les anciennes normes C, La variable aurait dû être déclarée hors de la boucle. Cette nouvelle syntaxe permet d'avoir des variables d'itération locales à la boucle for.
- (void)printf(... : la fonction
printf
retourne le nombre de caractères écrit dans le flux de sortie. Comme j'ai décidé de ne pas utiliser ce code retour, je le signale en castant àvoid
la fonction. L'outil qualité lint signale toute fonction retournant une valeur qui n'est pas affectée à une variable ou ignoré explicitement. Cela permet d'éviter l'erreur qui consiste à oublier de traiter un code retour de fonction. - Le code retour de scanf est ignoré. Il renvoie le nombre de valeurs correctement récupérée. Dans un code opérationnel robuste, tous les codes retours doivent être traités et un traitement doit être prévu en cas d'erreur. Le type void n'existait pas en C K&R.
Analyse de programme (double boucle)
modifierCes exercices d'analyses doivent être réalisés sans compilateurs, à la main à l'aide d'un crayon et d'une feuille de papier.
- Soit le programme suivant :
#include <stdio.h> // pour printf
#include <stdlib.h> // pour system
main() {
int i,j;
system("clear"); //efface ecran sous linux (system("cls"); sous Windows)
for(i=0;i<5;i++){
for(j=i;j<5;j++)
(void)printf("**");
(void)printf("\n");
}
return EXIT_SUCCESS;
}
Que sera-t-il affiché à l'écran lors d'une exécution de ce programme ?
- Même question pour le programme suivant :
#include <stdio.h> // pour printf
#include <stdlib.h> // pour system
main() {
int i,j;
system("clear"); //efface ecran sous linux (system("cls"); sous Windows)
for(i=0;i<5;i++){
for(j=5-i;j<5;j++)
(void)printf("++");
(void)printf("\n");
}
return EXIT_SUCCESS;
}
- Pour le premier programme :
********** ******** ****** **** **
- Pour le deuxième programme :
++ ++++ ++++++ ++++++++
À noter la ligne vide en tout début.
Triangle d'étoiles (double boucles)
modifierCompléter la fonction afficherTriangle dans le programme ci-dessous : cette fonction devra afficher un triangle rempli d'étoiles (*) sur un nombre de lignes donné passé en paramètre, exemple :
* ** *** **** ***** ****** ******* ********
- 1ère version : sans utiliser de tableau à l'aide de deux boucles for imbriquées.
- 2ème version : avec une seule boucle for et un tableau de chaîne de caractère où vous accumulerez des étoiles.
/*
Nom : etoile.c
Compilation : gcc -Wall -std=c99 -o etoile.exe etoile.c
Exécution : ./etoile.exe
*/
#include <stdio.h>
#include <stdlib.h>
/*
Nom ... : afficherTriangle
Role .. : Afficher un triangle d'etoiles
Parametre :
nbLignes : nombre de lignes du triangle
*/
static void afficherTriangle(const int nbLignes)
{
// Partie à compléter
} // static void afficherTriangle(...
// Fonction principale pour test
int main(void)
{
int nbLignes = 0;
int nbChampsLu = 0;
int codeRetour = EXIT_SUCCESS;
(void)fputs("Lignes ? ", stdout);
nbChampsLu = scanf("%u", &nbLignes);
if (nbChampsLu == 1 && nbLignes > 0)
{
afficherTriangle(nbLignes);
}
else
{
(void)fputs("Erreur : Vous devez entrer un entier strictement positif !\n",
stderr);
codeRetour = EXIT_FAILURE;
}
return codeRetour;
} // int main(...
1ère version : sans utiliser de tableau à l'aide de deux boucles for imbriquées.
/*
Nom ... : afficherTriangle
Role .. : Afficher un triangle d'etoiles
Parametre :
nbLignes : nombre de lignes du triangle
Version 1 du 9/1/2008
*/
static void afficherTriangle(const int nbLignes)
{
for (int numLigne = 1; numLigne <= nbLignes; numLigne++)
{
for (int numColonne = 1; numColonne <= numLigne; numColonne++)
{
(void)putchar('*');
}
(void)putchar('\n');
}
} // static void afficherTriangle(...
Remarque :
- for (int i = 0 : c'est une syntaxe introduite en C99. Avec les anciennes normes C, La variable aurait dû être déclarée hors de la boucle. Cette nouvelle syntaxe permet d'avoir des variables d'itération locales à la boucle for.
- (void)putchar('*'); : la fonction putchar retourne le caractère écrit dans le flux de sortie ou EOF en cas d'erreur. Comme j'ai décidé de ne pas utiliser ce code retour, je le signale en castant à
void
la fonction. L'outil qualité lint signale toute fonction retournant une valeur qui n'est pas affectée à une variable ou ignoré explicitement. Cela permet d'éviter l'erreur qui consiste à oublier de traiter un code retour de fonction.
Dans un code opérationnel robuste, tous les codes retours doivent être traités et un traitement doit être prévu en cas d'erreur. Le type void n'existait pas en C K&R.
2ème version : avec une seule boucle for et un tableau de chaîne de caractère où vous accumulerez des étoiles.
/*
Nom ... : afficherTriangle
Rôle .. : Afficher un triangle d’étoiles
Paramètre :
nbLignes : nombre de lignes du triangle
Version 2 du 9/1/2008
*/
static void afficherTriangle(const int nbLignes)
{
// Déclaration de la chaîne de caractère (voir ma remarque)
char chaineEtoiles[nbLignes+1];
// Initialisation du tableau avec un terminateur de chaîne
chaineEtoiles[0] = '\0';
for (int numLigne = 1; numLigne <= nbLignes; numLigne++)
{
(void)strcat(chaineEtoiles, "*");
(void)puts(chaineEtoiles);
}
} // static void afficherTriangle(...
Remarque :
- Le tableau chaineEtoiles est de type VLA (Variable-Length Arrays) introduite par C99. Avec les versions de C précédente, il aurait fallu allouer dynamiquement le tableau au début du programme, puis le désalloué à la fin.