ESTI 2008-2009
M. Sammoud
EXERCICES DE REVISION PROGRAMMATION I
1
ESTI 2008-2009
M. Sammoud
Les Chaînes de caractères L’objectif de ces exercices est la manipulation des chaînes de caractères en utilisant les fonctions offertes par la bibliothèque <string.h> Exercice 1 a) Ecrire une fonction déterminant le nombre de lettres e (minuscules) présentes dans un texte (supposée ne pas dépasser 132 caractères) fourni au clavier. En utilisant la fonction strchr de string.h b) Ecrire une procédure qui supprime toutes les lettres e (minuscules) d'un texte (supposée ne pas dépasser 132 caractères) fourni au clavier. Le texte ainsi modifié sera créé, en mémoire, à la place de l'ancien. c) Ecrire une procédure qui lit au clavier un texte et qui l'affiche "à l'envers ‘‘. Pour tester ces sous programmes on vous demande d’écrire un programme principal qui permet de saisir un texte (supposée ne pas dépasser 132 caractères), on utilisera les tableaux dynamiques pour la déclaration du texte et la fonction gets pour saisir le texte qui doit se terminer par return. Quelle est la différence entre scanf et gets (pour répondre, testez avec les deux) Faites appel à toutes les fonctions et procédures implémentées au dessus dans votre main. /******************************************************* CORRECTION AUTEUR : M. SAMMOUD 1ERE ANNEE INFORMATIQUE APPLIQUEE (ESTI) *********************************************************/ Exercice 1 #include<stdio.h> #include<string.h> #include<stdlib.h> #define CAR 'e' #define LGMAX 132 /*
Exercice 1 a) en utilisant la fonction
strchr
****Que fait strchr ?****** Find a character in a string. char *strchr( const char *string, int c ); Return Value : Each of these functions returns a pointer to the first occurrence of c in string, or NULL if c is not found.*/ int compter_apparition_car (char * texte) { char* adr=texte;
2
ESTI 2008-2009
M. Sammoud
int nbre=0; while((adr=strchr(adr,CAR))!= NULL) /*strchr retourne un pointeur vers la première occurrence de CAR, on teste si c'est Différent de NULL on continu l'itération sinon on sort de la boucle */ { nbre++; adr++; }
return nbre; } // b) supprimer toutes les apparitions de CAR ('e') void supprimer_apparition_car (char * texte) { char*adr=texte; while((adr=strchr(adr,CAR))!= NULL) strcpy(adr, adr+1); //faire le décalage en supprimant le ième CAR }
// c) affichage à l'envers void afficher_envers (char * texte) { int i; for(i=strlen(texte);i>=0;i--) putchar(texte[i]); }
// programme principal void main() { int cmpt=0; char * texte=(char*)malloc(LGMAX*sizeof(char)); // Saisir le texte printf("\n
Donnez un texte termine par return \n");
gets(texte);// // question a) cmpt= compter_apparition_car (texte); printf ("\n le nombre d'apparition de %c est %d \n ",CAR, cmpt) ; //Question b) supprimer_apparition_car (texte); printf ("\n Voici votre texte prive des caractères %c \n ",CAR) ; puts(texte);
3
ESTI 2008-2009
M. Sammoud
//Question c) afficher_envers (texte); }
Exercice 2 Ecrire une procédure qui lit un verbe du premier groupe et qui en affiche la conjugaison au présent de l'indicatif, sous la forme : Je programme Tu programmes Il programme Nous programmons Vous programmez Ils programment Le programme devra vérifier que le mot fourni se termine bien par ''er". On supposera qu'il ne peut comporter plus de 26 lettres et qu'il s'agit d'un verbe régulier. Autrement dit, on admettra que l'utilisateur ne fournira pas un verbe tel que "manger" (le programme afficherait alors : ''nous mangons" !). Testez cette procédure en implémentant un programme principal. Exercice 2 #include<stdio.h> #include<string.h> #include<stdlib.h> #define LGMAX 26 /*
Exercice 2*/
void conjuger( char* verbe) { char * adr_fin; char * terminaison[6]={"e","es","e","ons","ez","ent"}; /*initialisation du tableau*/ char * deb[6]={"je","tu","il","nous","vous","ils"}; int i; printf("\n indicatif present : \n"); adr_fin=verbe+strlen(verbe)-2; for(i=0;i<6;i++) { strcpy(adr_fin,terminaison[i]); printf("\n %s %s \n", deb[i], verbe); } }
void main() { char * verbe=(char*)malloc(LGMAX*sizeof(char));
4
ESTI 2008-2009
M. Sammoud
char *fin; // Saisir le verbe do { printf("\n Donnez un verbe du premier groupe \n"); gets(verbe); fin=verbe+strlen(verbe)-2;
}while(strcmp(fin,"er")); conjuger( verbe);
}
Exercices de l’examen 2007-2008 Exercice 2
(3 points)
Soit le programme suivant : typedef enum boolean {FALSE, TRUE}; boolean test ( ) { int i, nbre; boolean Ok; do { printf (" donnez un nombre entier compris entre 0 et 100") ; scanf ("%d", &nbre) ; }while((nbre<0)||(nbre>100)) ; /* initialisation du traitement */ /* initialisation de l’avancement */ i=2 ; /* initialisation de l’arret */ Ok=TRUE; while ((i
5
ESTI 2008-2009
M. Sammoud
a) Que fait la fonction test ? (1 point)
La fonction test permet de déterminer si un nombre entier compris entre 0 et 100 est premier ou non. b) Reprenez cette fonction en remplaçant la boucle while par la boucle for (1 point) typedef enum boolean {FALSE, TRUE}; boolean test ( ) { int i, nbre; boolean Ok; do { printf (" donnez un nombre entier compris entre 0 et 100") ; scanf ("%d", &nbre) ; }while((nbre<0)||(nbre>100)) ; /* initialisation du traitement */ /* initialisation de l’avancement */ /* initialisation de l’arret */ Ok=TRUE;
For(i=2;i100)) ;
/* initialisation du traitement */ /* initialisation de l’avancement */ i=2 ; (0.25 points) /* initialisation de l’arret */ Ok=TRUE; (0.25 points) do {(0.25 points) if ((nbre % i) ==0) /*arret*/ Ok=FALSE; else i++; /* traitement et avancement*/
6
ESTI 2008-2009
}
M. Sammoud
while ((i
return Ok; } Exercice 3 (5 points) Soit CH une chaine de caractère qui contiendra 50 caractères. (On rappelle qu’une chaîne de caractère est un tableau de caractères.) a) Proposer une déclaration de CH en utilisant les tableaux dynamiques. (0.5 point)
Char * CH ;
(0.5 points)
b) Quelle est l’instruction qu’on doit ajouter après la déclaration de CH pour pouvoir la saisir ? pourquoi ? (1 point)
CH= (char*)malloc(51 * sizeof(char)) ; (0.5 points) Puisque la déclaration (sans malloc) nous réserve de l’espace mémoire pour seulement une seule case celle de la première lettre de la chaîne mais avec malloc on réserve de l’espace mémoire pour 51 cases, pour pouvoir écrire dans CH (0.5 points) c) Implémenter la procédure suivante : void saisir_chaine (char * chaine) ; (0.5 point)
void saisir_chaine (char * chaine) { printf("veuillez saisir la chaine de caractère") ; scanf("%s", chaine) ; } d) Implémenter la fonction calculer_nbre_occ qui permet de calculer le nombre d’occurrences d’un caractère dans une chaine de caractère (sans faire la distinction entre majuscule ou minuscule), en utilisant la boucle adéquate, cette fonction aura le prototype suivant : (2 points) int calculer_nbre_occ (char c, char*chaine) ;
#include int calculer_nbre_occ (char c, char*chaine) { int i, nbre ; (0.25 point) nbre=0 ; (0.25 point) for ( i=0 ; chaine[i] !=’\0’ ;i++)(0.25 point) { if(toupper(chaine[i])==toupper(c)) (0.75 point) /*ou bien if(tolower(chaine[i])==tolower(c))*/
nbre++;(0.25 point) } return (nbre); (0.25 point) } e) Proposer un programme principale qui permet de saisir une chaine de caractères CH puis d’afficher les occurrences des voyelles qui figurent dans CH (1 point)
#include<stdio.h> #include<stdlib.h> void main() { int n,i; char tab[6]={'a','e','u','i','y','o'};0.25 char * ch=(char*)malloc(51 * sizeof(char)) ; saisir_chaine (ch); 0.25 for(i=0;i<6;i++) {
7
ESTI 2008-2009
M. Sammoud
n=calculer_nbre_occ (tab[i], ch);0.25 if(n !=0) 0.25 printf("L'occurrence de %c est : %d",tab[i],n) ; } } Exemple : si CH est la chaine : "chainEdecarActEre" Le résultat de l’exécution du programme donnera : L’occurrence de ‘a’ est : 3 L’occurrence de ‘e’ est : 4 L’occurrence de ‘i’ est : 1 Problème (9 points) Un nombre complexe est défini par une partie réelle et une partie imaginaire. (Exemple : 3.5+5*i est un nombre complexe) On se propose dans cet exercice d’effectuer quelques opérations sur ces nombres. 1) Définir une structure de données en C permettant de représenter un nombre complexe. (1 point)
Un nombre complexe peut être représenté en utilisant les enregistrements : 0.5 point struct Complexe { float reel ; float imaginaire ; } ; 0.5 point 2)
Que doit on ajouter à cette structure de données pour qu’elle devienne un type ?
On ajoute l’instruction typedef pour que complexe se comporte comme un type. (0.5 point) 3)
Pour saisir un nombre complexe on vous demande d’écrire une procédure appelée Saisir_Complexe. En supposant qu’on a définit un type tComplex voici des propositions de prototypes pour cette procédure, une seule est correcte indiquez laquelle, en justifiant votre réponse et en indiquant quels sont les erreurs dans les trois propositions fausses. (2.5 points) a) tComplex Saisir_Complexe( ) ; b) void Saisir_Complexe(
) ;
c) void Saisir_Complexe(tComplex C1) ; d) void Saisir_Complexe(tComplex * C1) ; d)
void
correcte
Saisir_Complexe(tComplex (0.5
point),
c’est
une
*
C1) ;est
procédure
et
le
prototype
le
passage
de
paramètre est par adresse donc tout changement sera sauvegardé (0.5 point) e) tComplex
Saisir_Complexe(
d’implémenter
une
procédure
) ; et
on e)
demande est
une
fonction puisqu’elle a une valeur de retour (0.5 point)
8
ESTI 2008-2009
M. Sammoud
f) void
Saisir_Complexe(
paramètres
entrés
donc
) ;il la
n’y
saisie
a
ne
pas sera
de pas
sauvegardée (0.5 point) g) void Saisir_Complexe(tComplex C1) ; le passage de paramètre
est
fait
par
valeur
donc
tout
changement est fait sur des copies des paramètres donc en sortant de la fonction la valeur saisie sera perdu (0.5 point)
4) implémenter la procédure Saisir_Complexe
(2 points)
void Saisir_Complexe(tComplex * C1) { Printf("Donnez la partie reelle") ; Scanf("%f",C1Æreel) ;/* ou Scanf("%f",&((*C1).reel) ;*/0.5 Printf("Donnez la partie imaginaire") ; Scanf("%f",C1Æimaginaire) ;/* ou bien
bien
Scanf("%f",&((*C1).
imaginaire) ;*/0.5
} 5)
implémenter les fonctions suivantes : a) est_reel_pure : retourne 0 (ou faux) si le complexe possède une partie imaginaire différente de 0 (0.25 point)
int est_reel_pure (tComplex C) { if (C.imaginaire==0) return 1 ; else return 0 ; }/* ou bien directement return (C.imaginaire==0)*/ b) est_egale : retourne 1 (ou vrai) si C1 et C2 sont égaux avec C1 et C2 sont deux nombres complexes (0.25 point) int est_egale (tComplex C1, tComplex C2) { if ((C1.imaginaire==C2.imaginaire)&&( C1.reel==C2.reel)) return 1 ; else return 0 ; } c) additionner : retourne un nombre complexe qui est C1 + C2 (0.25 point) tComplex additionner (tComplex C1, tComplex C2) { tComplex Res ; Res. Reel= C1.reel+C2.reel;
9
ESTI 2008-2009
M. Sammoud
Res. Imaginaire= C1.imaginaire+C2.imaginaire ; return Res ; } d) soustraire : retourne un nombre complexe qui est C1 - C2 (0.25 point) tComplex soustraire (tComplex C1, tComplex C2) { tComplex Res ; Res. Reel= C1.reel-C2.reel; Res. Imaginaire= C1.imaginaire-C2.imaginaire ; return Res ; }
6)
proposez un programme principal qui testera toutes les fonctions et les procédures décrites au dessus, on utilisera le menu suivant : (2 points)
1-
Saisir un nombre complexe
2-
Comparer deux nombres complexes
3-
Réel pure
4-
Somme
5-
Soustraction
6-
Quitter DONNEZ VOTRE CHOIX :
Indication : - utilisez la structure conditionnelle switch…case pour le menu. - Le programme ne s’arrête que si le choix entré est 6, utilisez la boucle adéquate. void main() { int choix ; tComplex C1,C2, som, dif ; do { Printf("\n 1- Saisir un nombre complexe\n \n 2- Comparer deux nombres complexes\n \n 3- Réel pure\n \n 4- Somme\n \n 5- Soustraction\n \n 6- Quitter\n \n \t\t\t\tDONNEZ VOTRE CHOIX : Scanf("%d",&choix) ;0.25 switch (choix) {
10
") ;
ESTI 2008-2009
M. Sammoud
case 1 : Saisir_Complexe(& C1) ; break ; case 2 : { Saisir_Complexe(& C2) ; if(est_egale(C1,C2)==1) printf("les deux nombre complexes sont egaux") ; else printf("les deux nombre complexes ne sont pas egaux") ; break ; } case 3 : if(est_reel_pure(C1)==1) printf("le nombre complexes %d + %d*i est reel pure", C1.reel, C1.imaginaire) ; else printf("le nombre complexes %d + %d*i n’est pas reel pure", C1.reel, C1.imaginaire) ; break ; case 4 : som= additionner (C1,C2); printf("la somme de (%d + %d*i) et (%d + %d*i)est (%d + %d*i)", C1.reel, C1.imaginaire, C2.reel,C2.imaginaire, som.reel, som.imaginaire) ; break ; case 5 : dif=soustraire (C1,C2); printf("la soustraction de (%d + %d*i) et (%d + %d*i)est (%d + %d*i)", C1.reel, C1.imaginaire, C2.reel, C2.imaginaire, som.reel, som.imaginaire) ; break ; default : printf("choix erronné") ; } }while(choix !=6) ;0.25 }
11
ESTI 2008-2009
M. Sammoud
Les Tableaux Exercice 1 : Ecrire une fonction qui permet de rechercher le nombre le plus proche d'un nombre donné x dans un tableau de 20 entiers donné tab. Correction : #include<stdlib> #define MAX 20 int chercher ( int x, int tab [ MAX]) { int i, buff,diff; diff =abs(x-tab[0]); for( i=1;i<MAX;i++) { if ( abs(x-tab[i])<= diff) { diff= abs(x-tab[i]); buff=tab[i]; } } return buff; } Exercice 2 Afficher les entiers n'apparaissant qu'une fois dans un tableau d'entiers donné tab de longueur ltab (1000 ≥ ltab ≥ 1). #define MAX 1000 int nbre_occ( int val, int tab[], int n)/// fct qui calcule le nbre d’apparition d’un entier ds un tab { int i, nbre=0 ; for (i=0 ;i
12
ESTI 2008-2009
M. Sammoud
do { Printf(″donnez la taille du tableau″) ; Scanf(″%d″,&n) ; }while ((n>1000)||(n<1)) ; tab= (int *)malloc( n* sizeof(int)); saisir (tab,n); for (i=0 ;i
Exercice 4 Soit t un tableau de 6 nombres entiers, initialisé avec le mois et l’année de VOTRE date de naissance (mmaaaa). Par exemple, si vous êtes n´e(e) en avril 1995, le tableau t contient : 0 4 1 9 9 5. On suppose : - que la fonction afficher(t,n) affiche sur une même ligne les n éléments du tableau t séparés par un espace ; - que la fonction permuter(t,i,j) permute les éléments d’indices i et j du tableau t ; - que les variables i, j, k représentent des nombres entiers. Rappel : si i et j sont deux entiers, la valeur de i%j est le reste de la division entière de i par j. Décrire les affichages produits lorsque l’on exécute les instructions suivantes : afficher(t,6) ; for (i=0;i<6;i++) t[i] = t[i]%4 ; afficher(t,6) ; for (i=0 ; i<5 ; i++) { k=i; for (j=i+1 ; j<6 ; j++)
13
ESTI 2008-2009
M. Sammoud
{ if (t[j] > t[k]) k=j; } permuter (t,i,k) ; afficher(t,6) ; }
Calcul de l'écart entre deux dates A partir de deux dates la fonction Diff calcul le nombre de jours qui séparent ces deux dates, en tenant compte des années bissextiles. /* l'année est-elle bissextile*/ int Bissextile (int A) { return ((A % 4 == 0) && (A % 100 != 0 || A % 400 == 0)); } /*combien de jours se sont écoules depuis le début de l'année donnée*/ int Nb_Jours (int J, int M, int A) { int i, D = 0; const int Mois[12]= {31,28,31,30,31,30,31,31,30,31,30,31}; if (M == 1) { D = J; } else { for (i = 0; i < (M-1); i++) { D += Mois[i]; } D+=J; } if ((M > 2) && (Bissextile(A))) { D++; } return D; } /*la fonction diff proprement dite*/ int Diff (int j1, int m1, int a1, int j2, int m2, int a2) { int NJ = 0, NJ1, NJ2, i; NJ1 = Nb_Jours (j1, m1, a1); NJ2 = Nb_Jours (j2, m2, a2); if (a2 == a1) { NJ = NJ2 - NJ1; } else { for (i = 0; i < (a2-a1); i++) { NJ += 364;
14
ESTI 2008-2009
M. Sammoud
if (Bissextile (a1+i)) { NJ++; } } NJ -= NJ1; NJ += NJ2+1; } return NJ;
}
15
ESTI 2008-2009
M. Sammoud
Comparateur de dates Cette fonction permet de comparer deux dates sous forme de chaînes de caractères /** Positif si DateToCompare > Reference. Precision = 16 par defaut (resultat a la milliseconde pres). Pour utiliser la valeur par defaut, mettre 0 ou une valeur négative. */
double CompareDates (char *Reference, char *DateToCompare, int Precision) { int i, Longueur, Puissance; double d;
if ((Reference != NULL) && (DateToCompare != NULL)) { Longueur = strlen(DateToCompare); d = 0.0; if (Precision <= 0) Puissance = 16; else Puissance = Precision;
for ( i = 0 ; i < Longueur ; i++ ) { if ( (int)DateToCompare[i] != (int)Reference[i] ) { d = pow(10.0,(double)(Puissance - i)) ; d = d * (double)((int)DateToCompare[i] - (int)Reference[i]) ; break ; } } }
return d;
}
16
ESTI 2008-2009
M. Sammoud
Modifier la casse d'une chaîne de caractère Le fichier d'entête ctype.h propose les fonctions tolower et toupper pour mettre un caractère respectivement en minuscule et en majuscule, il est intéressant de proposer la même chose mais pour une chaîne de caractères #include <stdlib.h> #include <string.h> #include char *str_tolower (const char *ct) { char *s = NULL; if (ct) { int i; s = malloc (sizeof (*s) * (strlen (ct) + 1)); if (s) { for (i = 0; ct[i]; i++) { s[i] = tolower (ct[i]); } s[i] = '\0'; } } return s; } char *str_toupper (const char *ct) { char *s = NULL; if (ct) { int i; s = malloc (sizeof (*s) * (strlen (ct) + 1)); if (s) { for (i = 0; ct[i]; i++) { s[i] = toupper (ct[i]); } s[i] = '\0'; } }
return s; }
17
ESTI 2008-2009
M. Sammoud
Comment savoir si un nombre est premier ? Ressemblant à un hybride entre le crible d'Eratosthène et la méthode classique, cette solution utilise les nombres premiers en dessous de 100 pour savoir si un nombre est premier ou non. Si jamais on a fini le parcours, on utilise la méthode classique... On suppose que nbr >= 1 #include <stdio.h> #include <math.h> int estPremier (int nbr) { /*Les nombres premiers < 100*/ static int prems[] = {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}; int i, n; double d;
/* On suppose que 1 est premier */ if (nbr == 1) { return 1; } n = sizeof (prems) / sizeof (*prems); /* D'abord on regarde si n est divisible par les nombres premiers dans le tableau */ for (i = 0; i < n; i++) { if (nbr == prems[i]) { return 1; } if (nbr % prems[i] == 0) { return 0; } } /* Ensuite, on doit regarder a partir du dernier element du tableau+2 jusqu'a sqrt(nbr)... */ d = sqrt (nbr) + 0.5; /* Le 0.5 permet de tester si c'est un carre parfait... */ i = prems[i-1] + 2; while (i < d) { if (nbr % i == 0) { return 0; } i += 2; } return 1; }
18
ESTI 2008-2009
M. Sammoud
int main(void) { int i; for (i = 101; i < 500; i++) { if (estPremier (i)) { printf ("%d\n",i); } } return 0; }
Calcul du plus grand diviseur commun de deux entiers relatifs Cette fonction renvoie le PGCD (plus grand diviseur commun) de deux entiers relatifs, selon l'algorithme d'Euclide. La division euclidienne s'écrit comme suit : dividende = diviseur * quotient + reste avec 0 <= reste < diviseur Exemple : Division euclidienne de 20 par 3 20 = 3 * 6 + 2 avec 0 <= 2< 3
En utilisant des divisions euclidiennes successives, on peut trouver le PGCD de deux entiers relatifs. unsigned long pgcd(long a, long b, int voirAlgorithme) { long dividende = labs(a); /* le dividende contient la valeur absolue de a */ long diviseur = labs(b); /* le diviseur contient la valeur absolue de b */ long quotient; long reste; int fin = 0;
/* * on ne calcule le pgcd de deux nombres que s'ils sont différents de zéro */ if(a != 0 && b != 0) { while(!fin) { /* On applique la division euclidienne */ reste = dividende % diviseur;
19
ESTI 2008-2009
M. Sammoud
quotient = (dividende - reste) / diviseur;
/* * Si l'affichage de l'algorithme est souhaité, * on affiche chaque ligne */ if (voirAlgorithme) { printf ( "%ld = %ld * %ld + %ld\n", dividende, diviseur, quotient, reste ); } /* Si le reste est différent de 0, on continue l'algorithme */ if(reste !=0) { dividende = diviseur; diviseur = reste; } else { fin = 1; } } } else { /* Erreur ... */ diviseur = 0; }
return diviseur; }
20