Exercices en langage C. C. Delannoy

Dimension: px
Commencer à balayer dès la page:

Download "Exercices en langage C. C. Delannoy"

Transcription

1 Exercices en langage C C. Delannoy

2 2 Exercices en langage C PREMIERE PARTIE : EXERCICES D'APPLICATION Cette premiè re partie vous propose des exercices, à résoudre, de préférence, pendant la phase d'étude du langage C luimême. E le épouse la structure d'un cours "classique" 1, sous la forme de 7 chapitres : types de base, opérateurs et expressions ;entrées-sorties conversationne les ;instructions de contrôle ;les fonctions ;les tableaux et les pointeurs ; les chaînes de caractères ;les structures. Chaque chapitre comporte : - des exercices d'application immédiate destinés à faciliter l'assimilation du cours correspondant, - des exercices, sans grande difficulté algorithmique mettant en oeuvre les différentes notions acquises au cours des précédents chapitres. Notez que l'utilisation des fichiers, ainsi que la gestion dynamique ne sont pas abordés dans cette premiè re partie ;ces deux points feront chacun l'objet d'un chapitre approprié dans la seconde partie de l'ouvrage. 1 Un tel cours vous est proposé, par exemple, dans "Apprendre à programmer en Turbo C" ou dans "C norme ANSI - Guide complet de programmation" du même auteur, également aux éditions Eyro les.

3 I : TYPES DE BASE, OPERATEURS ET EXPRESSIONS Exercice I.1 Enoncé Eliminer les parenthèses superflues dans les expressions suivantes : a = (x+5) /* expression 1 */ a = (x=y) + 2 /* expression 2 */ a = (x==y) /* expression 3 */ (a<b) && (c<d) /* expression 4 */ (i++) * (n+p) /* expression 5 */ Solution a = x+5 /* expression 1 */ L'opérateur + est prioritaire sur l'opérateur d'affectation =. a = (x=y) + 2 /* expression 2 */ Ici, l'opérateur + étant prioritaire sur =, les parenthèses sont indispensables. a = x==y /* expression 3 */

4 4 Exercices en langage C L'opérateur == est prioritaire sur =. a<b && c<d /* expression 4 */ L'opérateur & & est prioritaire sur l'opérateur <. i++ * (n+p) /* expression 5 */ L'opérateur + + est prioritaire sur *;en revanche, *est prioritaire sur + ;de sorte qu'on ne peut éliminer les derniè res parenthèses. Exercice I.2 Enoncé Soient les déclarations : char c = '\x01' ; short int p = 10 ; Quels sont le type et la valeur de chacune des expressions suivantes : p + 3 /* 1 */ c + 1 /* 2 */ p + c /* 3 */ 3 * p + 5 * c /* 4 */ Solution 1) p est d'abord soumis à la conversion "systématique" short -> int, avant d'ê tre ajouté à la valeur 3 (int). Le résultat 13 est de type int. 2) c est d'abord soumis à la conversion "systématique" char -> int (ce qui aboutit à la valeur 1), avant d'ê tre ajouté à la valeur 1 (int). Le résultat 2 est de type int.

5 I. Types de base, opérateurs et expressions 5 3) p est d'abord soumis à la conversion systématique short -> int, tandis que c est soumis à la conversion systématique char -> int ;les résultats sont alors additionnés pour aboutir à la valeur 11 de type int. 4) p et c sont d'abord aux mêmes conversions systématiques que ci-dessus ;le résultat 35 est de type int. Exercice I.3 Enoncé Soient les déclarations : char c = '\x05' ; int n = 5 ; long p = 1000 ; float x = 1.25 ; double z = 5.5 ; Quels sont le type et la valeur de chacune des expressions suivantes : n + c + p /* 1 */ 2 * x + c /* 2 */ (char) n + c /* 3 */ (float) z + n / 2 /* 4 */ Solution 1) c est tout d'abord converti en int, avant d'ê tre ajouté à n. Le résultat (10), de type int, est alors converti en long, avant d'ê tre ajouté à p. On obtient finalement la valeur 1010, de type long. 2) On évalue d'abord la valeur de 2*x, en convertissant 2 (int) en float, ce qui fournit la valeur 2.5 (de type float). Par ai leurs, c est converti en int (conversion systématique). On évalue ensuite la valeur de 2*x, en convertissant 2 (int) en float, ce qui fournit la valeur 2.5 (de type float). Pour effectuer l'addition, on convertit alors la valeur entiè re 5 (c) en float, avant de l'ajouter au résultat précédent. On obtient finalement la valeur 7.75, de type float.

6 6 Exercices en langage C 3) n est tout d'abord converti en char (à cause de l'opérateur de "cast"), tandis que c est converti (conversion systématique) en int. Puis, pour procéder à l'addition, il est nécessaire de reconvertir la valeur de (char) n en int. Finalement, on obtient la valeur 10, de type int. 4) z est d'abord converti en float, ce qui fournit la valeur 5.5 (approximative, car, en fait, on obtient une valeur un peu moins précise que ne le serait 5.5 exprimé en double). Par ai leurs, on procè de à la division entiè re de n par 2, ce qui fournit la valeur entiè re 2. Cette derniè re est ensuite convertie en float, avant d'ê tre ajoutée à 5.5, ce qui fournit le résultat 7.5, de type float. Remarque : Dans la premiè re définition de Kernighan et Ritchie, les valeurs de type float étaient, e les aussi, soumises à une conversion systématique en double. Dans ce cas, les expressions 3 et 4 étaient alors de type double. Exercice I.4 Enoncé Soient les déclarations suivantes : int n = 5, p = 9 ; int q ; float x ; Que le est la valeur affectée aux différentes variables concernées par chacune des instructions suivantes : q = n < p ; /* 1 */ q = n == p ; /* 2 */ q = p % n + p > n ; /* 3 */ x = p / n ; /* 4 */ x = (float) p / n ; /* 5 */ x = (p + 0.5) / n ; /* 6 */ x = (int) (p + 0.5) / n ; /* 7 */ q = n * (p > n? n : p) ; /* 8 */ q = n * (p < n? n : p) ; /* 9 *:

7 I. Types de base, opérateurs et expressions 7 Solution 1) 1 2) 0 3) 5 (p%n vaut 4, tandis que p> n vaut 1) 4) 1 (p/n est d'abord évalué en int, ce qui fournit 1 ;puis le résultat est converti en float, avant d'ê tre affecté à x). 5) 1.8 (p est converti en float, avant d'ê tre divisé par le résultat de la conversion de n en float). 6) 1.9 (p est converti en float, avant d'ê tre ajouté à 0.5 ;le résultat est divisé par le résultat de la conversion de n en float). 7) 1 (p est converti en float, avant d'ê tre ajouté à 0.5 ;le résultat (5.5) est alors converti en int avant d'ê tre divisé par n). 8) 25 9) 45 Exercice I.5 Enoncé Quels résultats fournit le programme suivant : #include <stdio.h> main () { int i, j, n ; i = 0 ; n = i++ ; printf ("A : i = %d n = %d \n", i, n ) ; i = 10 ; n = ++ i ; printf ("B : i = %d n = %d \n", i, n ) ;

8 8 Exercices en langage C i = 20 ; j = 5 ; n = i++ * ++ j ; printf ("C : i = %d j = %d n = %d \n", i, j, n ) ; i = 15 ; n = i += 3 ; printf ("D : i = %d n = %d \n", i, n) ; i = 3 ; j = 5 ; n = i *= --j ; printf ("E : i = %d j = %d n = %d \n", i, n) ; Solution A : i = 1 n = 0 B : i = 11 n = 11 C : i = 21 j = 6 n = 120 D : i = 18 n = 18 E : i = 12 j = 12 n = 6 Exercice I.6 Enoncé Quels résultats fournira ce programme : #include <stdio.h> main() { int n=10, p=5, q=10, r ; r = n == (p = q) ; printf ("A : n = %d p = %d q = %d r = %d\n", n, p, q, r) ; n = p = q = 5 ; n += p += q ; printf ("B : n = %d p = %d q = %d\n", n, p, q) ;

9 q = n < p? n++ : p++ ; printf ("C : n = %d p = %d q = %d\n", n, p, q) ; q = n > p? n++ : p++ ; printf ("D : n = %d p = %d q = %d\n", n, p, q) ; I. Types de base, opérateurs et expressions 9 Solution A : n = 10 p = 10 q = 10 r = 1 B : n = 15 p = 10 q = 5 C : n = 15 p = 11 q = 10 D : n = 16 p = 11 q = 15 Exercice I.7 Enoncé Quels résultats fournira ce programme : #include <stdio.h> main() { int n, p, q ; n = 5 ; p = 2 ; /* cas 1 */ q = n++ >p p++!= 3 ; printf ("A : n = %d p = %d q = %d\n", n, p, q) ; n = 5 ; p = 2 ; /* cas 2 */ q = n++<p p++!= 3 ; printf ("B : n = %d p = %d q = %d\n", n, p, q) ; n = 5 ; p = 2 ; /* cas 3 */

10 10 Exercices en langage C q = ++n == 3 && ++p == 3 ; printf ("C : n = %d p = %d q = %d\n", n, p, q) ; n = 5 ; p = 2 ; /* cas 4 */ q = ++n == 6 && ++p == 3 ; printf ("D : n = %d p = %d q = %d\n", n, p, q) ; Solution Ilne faut pas oublier que les opérateurs && et n'évaluent leur deuxiè me opérande que lorsque cela est nécessaire. Ainsi, ici, iln'est pas évalué dans les cas 1 et 3. Voici les résultats fournis par le programme : A : n = 6 p = 2 q = 1 B : n = 6 p = 3 q = 1 C : n = 6 p = 2 q = 0 D : n = 6 p = 3 q = 1

11 II : LES ENTREES-SORTIES CONVERSATIONNELLES Exercice II.1 Enoncé Quels seront les résultats fournis par ce programme : #include <stdio.h> main () { int n = 543 ; int p = 5 ; float x = ; printf ("A : %d %f\n", n, x) ; printf ("B : %4d %10f\n", n, x) ; printf ("C : %2d %3f\n", n, x) ; printf ("D : %10.3f %10.3e\n", x, x) ; printf ("E : %-5d %f\n", n, x) ; printf ("F : %*d\n", p, n) ; printf ("G : %*.*f\n", 12, 5, x) ; printf ("H : %x : %8x :\n", n, n) ; printf ("I : %o : %8o :\n", n, n) ; Solution A : B :

12 12 Exercices en langage C C : D : e+01 E : F : 543 G : H : 21f : 21f : I : 1037 : 1037 : Exercice II.2 Enoncé Quels seront les résultats fournis par ce programme : #include <stdio.h> main() { char c ; int n ; c = 'S' ; printf ("A : %c\n", c) ; n = c ; printf ("B : %c\n", n) ; printf ("C : %d %d\n", c, n) ; printf ("D : %x %x\n", c, n) ; Solution A : S B : S C : D : 53 53

13 II. Les entrées-sorties conversationnelles 13 Exercice II.3 Enoncé Que les seront les valeurs lues dans les variables n et p (de type int), par l'instruction suivante : scanf ("%d %d", &n, &p) ; lorsqu'on lui fournit les données suivantes (le symbole ^ représente un espace et le représente une fin de ligne, c'est-à -dire une "validation") : a) 253^45@ b) ^253^@ ^^ 4 ^ Solution a) n = 243, p = 45 b) n = 253, p = 4 (les derniers caractères de la deuxiè me ligne pourront éventue lement être utilisés par une instruction de lecture ultérieure). Exercice II.4 Enoncé Que les seront les valeurs lues dans les variables n et p (de type int), par l'instruction suivante : scanf ("%4d %2d", &n, &p) ; lorsqu'on lui fournit les données suivantes (le symbole ^ représente un espace et le représente une fin de ligne, c'est-à -dire une "validation") :

14 14 Exercices en langage C a) 12^45@ b) @ c) ^7@ d) 1^458@ e) ^^^4567^^8912@ Solution Rappelons que lorsqu'une indication de longueur est présente dans le code format fourni à scanf (comme, par exemple, le 4 de %4d), scanf interrompt son exploration si le nombre correspondant de caractères a été exploré, sans qu'un séparateur (ou "espace blanc") n'ait été trouvé. Notez bien, cependant, que les éventuels caractères séparateurs "sautés" auparavant ne sont pas considérés dans ce compte. Voici les résultats obtenus : a) n=12, p=45 b) n=1234, p=56 c) n=1234, p=56 d) n=1, p=45 e) n=4567, p=89 En a, on obtiendrait exactement les mêmes résultats sans indication de longueur (c'est-à -dire avec %d %d). En b, en revanche, sans l'indication de longueur 4, les résultats seraient différents (n vaudrait , tandis qu'ilmanquerait des informations pour p). En c, les informations ^ et 7 ne sont pas prises en compte par scanf (e les le seront éventue lement par une prochaine lecture!) ;sans la premiè re indication de longueur, les résultats seraient différents : pour n (en supposant que cela ne conduise pas à une valeur non représentable dans le type int) et 7 pour p. En d, cette fois, c'est l'indication de longueur 2 qui a de l'importance ;en son abscence, n vaudrait effectivement 1, mais p vaudrait 458. Enfin, en e, les deux indications de longueur sont importantes ; notez bien que les trois espaces placés avant les caractères pris en compte pour n, ainsi que les 2 espaces placés avant les caractères pris en compte pour p ne sont pas comptabilisés dans la longueur imposée. Exercice II.5

15 Enoncé II. Les entrées-sorties conversationnelles 15 Soit le programme suivant : #include <stdio.h> main() { int n, p ; do { printf ("donnez 2 entiers (0 pour finir) : ") ; scanf("%4d%2d", &n, &p) ; printf ("merci pour : %d %d\n", n, p) ; while (n) ; Quels résultats fournira-t-il, en supposant qu'on lui entre les données suivantes (attention, on supposera que les données sont frappées au clavier et les résultats affichés à l'écran, ce qui signifie qu'ily aura "mixage" entre ces deux sortes d'informations) : Solution Ici, on retrouve le mécanisme lié à l'indication d'une longueur maximale dans le code format, comme dans l'exercice précédent. De plus, on exploite le fait que les informations d'une ligne qui n'ont pas été prises en compte lors d'une lecture restent disponibles pour la lecture suivante. Enfin, rappelons que, tant que scanf n'a pas reçu suffisamment d'information, compte tenu des différents codes format spécifiés (et non pas des variables indiquées), e le en attend de nouve les. Voici finalement les résultats obtenus : donnez 2 entiers (0 pour finir) 1 2 merci pour : 1 2

16 16 Exercices en langage C donnez 2 entiers (0 pour finir) 3 4 merci pour : 3 4 donnez 2 entiers (0 pour finir) merci pour : donnez 2 entiers (0 pour finir) merci pour : donnez 2 entiers (0 pour finir) merci pour : 34 5 donnez 2 entiers (0 pour finir) merci pour : 6 7 donnez 2 entiers (0 pour finir) merci pour : 8 9 donnez 2 entiers (0 pour finir) 0 merci pour : 10 0 donnez 2 entiers (0 pour finir) 0 12 merci pour : 0 12

17 III :LES INSTRUCTIONS DE CONTROLE Exercice III.1 Enoncé Que les erreurs ont été commises dans chacun des groupes d'instructions suivants : 1) if (a<b) printf ("ascendant") else printf ("non ascendant") ; 2) int n ;... switch (2*n+1) { case 1 : printf ("petit") ; case n : printf ("moyen") ; 3) #define LIMITE 100 int n ;... switch (n) { case LIMITE-1 : printf ("un peu moins") ; case LIMITE : printf ("juste") ; case LIMITE+1 : printf ("un peu plus") ; 4) const int LIMITE=100 int n ;

18 18 Exercices en langage C... switch (n) { case LIMITE-1 : printf ("un peu moins") ; case LIMITE : printf ("juste") ; case LIMITE+1 : printf ("un peu plus") ; Solution 1) Ilmanque un point-virgule à la fin du premier printf : if (a<b) printf ("ascendant") ; else printf ("non ascendant") ; 2) Les valeurs suivant le mot case doivent obligatoirement être des "expressions constantes", c'est-à -dire des expressions calculables par le compilateur lui-même. Ce n'est pas le cas de n. 3) Aucune erreur, les expressions te les que LIMITE-1 étant bien des expressions constantes. 4) Ici, les expressions suivant le mot case ne sont plus des expressions constantes, car le symbole LIMITE a été défini sous forme d'une "constante symbolique" (en C+ +, cependant, ces instructions seront correctes). Exercice III.2 Enoncé Soit le programme suivant : #include <stdio.h> main() { int n ; scanf ("%d", &n) ; switch (n) { case 0 : printf ("Nul\n") ; case 1 :

19 case 2 : printf ("Petit\n") ; break ; case 3 : case 4 : case 5 : printf ("Moyen\n") ; default : printf ("Grand\n") ; III. Les instructions de contrôle 19 Quels résultats affiche-t-illorsqu'on lui fournit en donnée : a) 0 b) 1 c) 4 d) 10 e) -5 Solution a) Nul Petit b) Petit c) Moyen Grand d) Grand e) Grand Exercice III.3

20 20 Exercices en langage C Enoncé Que les erreurs ont été commises dans chacune des instructions suivantes : a) do c = getchar() while (c!= '\n') ; b) do while ( (c = getchar())!= '\n') ; c) do { while (1) ; Solution a) Ilmanque un point-virgule : do c = getchar() ; while (c!= '\n') ; b) Ilmanque une instruction (éventue lement "vide") aprè s le mot do. On pourrait écrire, par exemple : do { while ( (c = getchar())!= '\n') ; ou : do ; while ( (c = getchar())!= '\n') ; c) Iln'y aura pas d'erreur de compilation ;toutefois, ils'agit d'une "boucle infinie". Exercice III.4 Enoncé Ecrire plus lisiblement : do { while (printf("donnez un nombre >0 "), scanf ("%d", &n), n<=0) ;

21 III. Les instructions de contrôle 21 Solution Plusieurs possibilités existent, puisqu'il "suffit" de reporter, dans le corps de la boucle, des instructions figurant "artificie lement" sous forme d'expressions dans la condition de poursuite : do printf("donnez un nombre >0 ") ; while (scanf ("%d", &n), n<=0) ; ou, mieux : do { printf("donnez un nombre >0 ") ; scanf ("%d", &n) ; while (n<=0) ; Exercice III.5 Enoncé Soit le petit programme suivant : #include <stdio.h> main() { int i, n, som ; som = 0 ; for (i=0 ; i<4 ; i++) { printf ("donnez un entier ") ; scanf ("%d", &n) ; som += n ; printf ("Somme : %d\n", som) ; Ecrire un programme réalisant exactement la même chose, en employant, à la place de l'instruction for :

22 22 Exercices en langage C a) une instruction while, b) une instruction do... while. Solution a) b) #include <stdio.h> main() { int i, n, som ; som = 0 ; i = 0 ; /* ne pas oublier cette "initialisation" */ while (i<4) { printf ("donnez un entier ") ; scanf ("%d", &n) ; som += n ; i++ ; /* ni cette "incrémentation" */ printf ("Somme : %d\n", som) ; #include <stdio.h> main() { int i, n, som ; som = 0 ; i = 0 ; /* ne pas oublier cette "initialisation" */ do { printf ("donnez un entier ") ; scanf ("%d", &n) ; som += n ; i++ ; /* ni cette "incrémentation" */ while (i<4) ; /* attention, ici, toujours <4 */ printf ("Somme : %d\n", som) ;

23 Exercice III.6 III. Les instructions de contrôle 23 Enoncé Quels résultats fournit le programme suivant : #include <stdio.h> main() { int n=0 ; do { if (n%2==0) { printf ("%d est pair\n", n) ; n += 3 ; continue ; if (n%3==0) { printf ("%d est multiple de 3\n", n) ; n += 5 ; if (n%5==0) { printf ("%d est multiple de 5\n", n) ; break ; n += 1 ; while (1) ; Solution 0 est pair 3 est multiple de 3 9 est multiple de 3 15 est multiple de 3 20 est multiple de 5

24 24 Exercices en langage C Exercice III.7 Enoncé Quels résultats fournit le programme suivant : #include <stdio.h> main() { int n, p ; n=0 ; while (n<=5) n++ ; printf ("A : n = %d\n", n) ; n=p=0 ; while (n<=8) n += p++ ; printf ("B : n = %d\n", n) ; n=p=0 ; while (n<=8) n += ++p ; printf ("C : n = %d\n", n) ; n=p=0 ; while (p<=5) n+= p++ ; printf ("D : n = %d\n", n) ; n=p=0 ; while (p<=5) n+= ++p ; printf ("D : n = %d\n", n) ; Solution A : n = 6 B : n = 10 C : n = 10 D : n = 15

25 D : n = 21 III. Les instructions de contrôle 25 Exercice III.8 Enoncé Quels résultats fournit le programme suivant : #include <stdio.h> main() { int n, p ; n=p=0 ; while (n<5) n+=2 ; p++ ; printf ("A : n = %d, p = %d \n", n, p) ; n=p=0 ; while (n<5) { n+=2 ; p++ ; printf ("B : n = %d, p = %d \n", n, p) ; Solution A : n = 6, p = 1 B : n = 6, p = 3 Exercice III.9

26 26 Exercices en langage C Enoncé Quels résultats fournit le programme suivant : #include <stdio.h> main() { int i, n ; for (i=0, n=0 ; i<5 ; i++) n++ ; printf ("A : i = %d, n = %d\n", i, n) ; for (i=0, n=0 ; i<5 ; i++, n++) { printf ("B : i = %d, n = %d\n", i, n) ; for (i=0, n=50 ; n>10 ; i++, n-= i ) { printf ("C : i = %d, n = %d\n", i, n) ; for (i=0, n=0 ; i<3 ; i++, n+=i, printf ("D : i = %d, n = %d\n", i, n) ) ; printf ("E : i = %d, n = %d\n", i, n) ; Solution A : i = 5, n = 5 B : i = 5, n = 5 C : i = 9, n = 5 D : i = 1, n = 1 D : i = 2, n = 3 D : i = 3, n = 6 E : i = 3, n = 6

27 Exercice III.10 III. Les instructions de contrôle 27 Enoncé Ecrire un programme qui calcule les racines carrées de nombres fournis en donnée. Ils'arrê tera lorqu'on lui fournira la valeur 0. Ilrefusera les valeurs négatives. Son exécution se présentera ainsi : donnez un nombre positif : 2 sa racine carrée est : e+00 donnez un nombre positif : -1 svp positif donnez un nombre positif : 5 sa racine carrée est : e+00 donnez un nombre positif : 0 Rappelons que la fonction sqrt fournit la racine carrée (double) de la valeur (double) qu'on lui fournit en argument. Solution Ilexiste beaucoup de "rédactions possibles" ;en voici 3 : #include <stdio.h> #include <math.h> /* indispensable pour sqrt (qui fourni un résultat */ /* de type double */ main() { double x ; do { printf ("donnez un nombre positif : ") ; scanf ("%le", &x) ; if (x < 0) printf ("svp positif \n") ; if (x <=0) continue ; printf ("sa racine carrée est : %le\n", sqrt (x) ) ; while (x) ;

28 28 Exercices en langage C #include <stdio.h> #include <math.h> main() { double x ; do { printf ("donnez un nombre positif : ") ; scanf ("%le", &x) ; if (x < 0) { printf ("svp positif \n") ; continue ; if (x>0) printf ("sa racine carrée est : %le\n", sqrt (x) ) ; while (x) ; #include <stdio.h> #include <math.h> main() { double x ; do { printf ("donnez un nombre positif : ") ; scanf ("%le", &x) ; if (x < 0) { printf ("svp positif \n") ; continue ; if (x>0) printf ("sa racine carrée est : %le\n", sqrt (x) ) ; if (x==0) break ; while (1) ; Remarque : Ilne faut surtout pas oublier #include <math.h> car, sinon, le compilateur considè re (en l'abscence du prototype) que sqrt fournit un résultat de type int.

29 Exercice III.11 III. Les instructions de contrôle 29 Enoncé Calculer la somme des n premiers termes de la "série harmonique", c'est-à -dire la somme : 1 + 1/2 + 1/3 + 1/ /n La valeur de n sera lue en donnée. Solution #include <stdio.h> main() { int nt ; /* nombre de termes de la série harmonique */ float som ; /* pour la somme de la série */ int i ; do { printf ("combien de termes : ") ; scanf ("%d", &nt) ; while (nt<1) ; for (i=1, som=0 ; i<=nt ; i++) som += (float)1/i ; printf ("Somme des %d premiers termes = %f", nt, som) ; Remarques : 1) Rappelons que dans : som += (float)1/i l'expression de droite est évaluée en convertissant d'abord 1 et i en float.

30 30 Exercices en langage C Ilfaut éviter d'écrire : som += 1/i auquelcas, les valeurs de 1/i seraient toujours nu les (sauf pour i=1) puique l'opérateur /, lorsqu'il porte sur des entiers, correspond à la division entiè re. De même, en écrivant : som += (float) (1/i) le résultat ne serait pas plus satisfaisant puisque la conversion en flottant n'aurait lieu qu'aprè s la division (en entier). En revanche, on pourrait écrire : som += 1.0/i ; 2) Si l'on cherchait à exécuter ce programme pour des valeurs élevées de n (en prévoyant alors une variable de type float ou double), on constaterait que la valeur de la somme semble "converger" vers une limite (bien qu'en théorie la série harmonique "diverge"). Cela provient tout simplement de ce que, dè s que la valeur de 1/i est "petite" devant som, le résultat de l'addition de 1/i et de som est exactement som. On pourrait toutefois améliorer le résultat en effectuant la somme "à l'envers" (en effet, dans ce cas, le rapport entre la valeur à ajouter et la somme courante serait plus faible que précédemment).. Exercice III.12 Enoncé Afficher un triangle isocè le formé d'étoiles. La hauteur du triangle (c'est-à -dire le nombre de lignes) sera fourni en donnée, comme dans l'exemple ci-dessous. On s'arrangera pour que la derniè re ligne du triangle s'affiche sur le bord gauche de l'écran. combien de lignes? 10 * *** ***** ******* ********* *********** ************* ***************

31 ***************** ******************* III. Les instructions de contrôle 31 Solution #include <stdio.h> #define car '*' /* caractère de remplissage */ main() { int nlignes ; /* nombre total de lignes */ int nl ; /* compteur de ligne */ int nesp ; /* nombre d'espaces précédent une étoile */ int j ; printf ("combien de lignes? ") ; scanf ("%d", &nlignes) ; for (nl=0 ; nl<nlignes ; nl++) { nesp = nlignes - nl - 1 ; for (j=0 ; j<nesp ; j++) putchar (' ') ; for (j=0 ; j<2*nl+1 ; j++) putchar (car) ; putchar ('\n') ; Exercice III.13 Enoncé Afficher toutes les maniè res possibles d'obtenir un franc avec des piè ces de 2 centimes, 5 centimes et 10 centimes. Dire combien de possibilités ont été ainsi trouvées. Les résultats seront affichés comme suit : 1 F = 50 X 2c

32 32 Exercices en langage C 1 F = 45 X 2c 2 X 5c 1 F = 40 X 2c 4 X 5c 1 F = 35 X 2c 6 X 5c 1 F = 30 X 2c 8 X 5c 1 F = 25 X 2c 10 X 5c 1 F = 20 X 2c 12 X 5c 1 F = 15 X 2c 14 X 5c 1 F = 10 X 2c 16 X 5c 1 F = 5 X 2c 18 X 5c 1 F = 20 X 5c 1 F = 45 X 2c 1 X 10c 1 F = 40 X 2c 2 X 5c 1 X 10c 1 F = 35 X 2c 4 X 5c 1 X 10c 1 F = 10 X 2c 2 X 5c 7 X 10c 1 F = 5 X 2c 4 X 5c 7 X 10c 1 F = 6 X 5c 7 X 10c 1 F = 10 X 2c 8 X 10c 1 F = 5 X 2c 2 X 5c 8 X 10c 1 F = 4 X 5c 8 X 10c 1 F = 5 X 2c 9 X 10c 1 F = 2 X 5c 9 X 10c 1 F = 10 X 10c En tout, il y a 66 façons de faire 1 F Solution #include <stdio.h> main() { int nbf ; /* compteur du nombre de façons de faire 1 F */ int n10 ; /* nombre de pièces de 10 centimes */ int n5 ; /* nombre de pièces de 5 centimes */ int n2 ; /* nombre de pièces de 2 centimes */ nbf = 0 ; for (n10=0 ; n10<=10 ; n10++) for (n5=0 ; n5<=20 ; n5++) for (n2=0 ; n2<=50 ; n2++)

33 if ( 2*n2 + 5*n5 + 10*n10 == 100) { nbf ++ ; printf ("1 F = ") ; if (n2) printf ("%2d X 2c ", n2 ) ; if (n5) printf ("%2d X 5c ", n5 ) ; if (n10) printf ("%2d X 10c", n10) ; printf ("\n") ; III. Les instructions de contrôle 33 printf ("\nen tout, il y a %d façons de faire 1 F\n", nbf) ; Exercice III.14 Enoncé Ecrire un programme qui détermine la n ieme valeur u n ( n étant fourni en donnée) de la "suite de Fibonacci" définie comme suit : u1 = 1 u2 = 1 u n = u n-1 + u n-2 pour n> 2 Solution #include <stdio.h> main() { int u1, u2, u3 ; /* pour "parcourir" la suite */ int n ; /* rang du terme demandé */ int i ; /* compteur */

34 34 Exercices en langage C do { printf ("rang du terme demandé (au moins 3)? ") ; scanf ("%d", &n) ; while (n<3) ; u2 = u1 = 1 ; /* les deux premiers termes */ i = 2 ; while (i++ < n) /* attention, l'algorithme ne fonctionne */ { u3 = u1 + u2 ; /* que pour n > 2 */ u1 = u2 ; u2 = u3 ; /* autre formulation possible : */ /* for (i=3 ; i<=n ; i++, u1=u2, u2=u3) u3 = u1 + u2 ; */ printf ("Valeur du terme de rang %d : %d", n, u3) ; Notez que, comme à l'accoutumée en C, beaucoup de formulations sont possibles. Nous en avons d'ai leurs placé une seconde en commentaire de notre programme. Exercice III.15 Enoncé Ecrire un programme qui trouve la plus grande et la plus petite valeur d'une succession de notes (nombres entiers entre 0 et 20) fournies en données, ainsi que le nombre de fois où ce maximum et ce minimum ont été attribués. On supposera que les notes, en nombre non connu à l'avance, seront terminées par une valeur négative. On s'astreindra à ne pas utiliser de "tableau". L'exécution du programme pourra se présenter ainsi : donnez une note (-1 pour finir) : 12 donnez une note (-1 pour finir) : 8 donnez une note (-1 pour finir) : 13 donnez une note (-1 pour finir) : 7

35 donnez une note (-1 pour finir) : 11 donnez une note (-1 pour finir) : 12 donnez une note (-1 pour finir) : 7 donnez une note (-1 pour finir) : 9 donnez une note (-1 pour finir) : -1 III. Les instructions de contrôle 35 note maximale : 13 attribuée 1 fois note minimale : 7 attribuée 2 fois Solution #include <stdio.h> main() { int note ; /* note "courante" */ int max ; /* note maxi */ int min ; /* note mini */ int nmax ; /* nombre de fois où la note maxi a été trouvée */ int nmin ; /* nombre de fois où la note mini a été trouvée */ max = -1 ; /* initialisation max (possible car toutes notes >=0 */ min = 21 ; /* initialisation min (possible car toutes notes < 21) */ while (printf ("donnez une note (-1 pour finir) : "), scanf ("%d", &note), note >=0) { if (note == max) nmax++ ; if (note > max) { max = note ; nmax = 1 ; if (note == min) nmin++ ; if (note < min) { min = note ; nmin = 1 ; /* attention, si aucune note (cad si max<0) */ /* les résultats sont sans signification */ if (max >= 0) { printf ("\nnote maximale : %d attribuée %d fois\n", max, nmax) ;

36 36 Exercices en langage C printf ("note minimale : %d attribuée %d fois\n", min, nmin) ; Exercice III.16 Enoncé Ecrire un programme qui affiche la "table de multiplication" des nombres de 1 à 10, sous la forme suivante : I I I I I I I I I I I Solution #include <stdio.h> #define NMAX 10 /* nombre de valeurs */ main() { int i, j ; /* affichage ligne en-tête */ printf (" I") ; for (j=1 ; j<=nmax ; j++) printf ("%4d", j) ;

37 printf ("\n") ; printf (" ") ; for (j=1 ; j<=nmax ; j++) printf ("----") ; printf ("\n") ; III. Les instructions de contrôle 37 /* affichage des différentes lignes */ for (i=1 ; i<=nmax ; i++) { printf ("%4d I", i) ; for (j=1 ; j<=nmax ; j++) printf ("%4d", i*j) ; printf ("\n") ;

38

39 IV :LES FONCTIONS N.B. Ici, on ne trouvera aucun exercice faisant intervenir des pointeurs, et par conséquent aucun exercice mettant en oeuvre une transmission d'arguments par adresse. De tels exercices apparaîtront dans le chapitre suivant. Exercice IV.1 Enoncé a) Que fournit le programme suivant : #include <stdio.h> main() { int n, p=5 ; n = fct (p) ; printf ("p = %d, n = %d\n", p, n) ; int fct (int r) { return 2*r ; b) Ajouter une déclaration convenable de la fonction fct : - sous la forme la plus brè ve possible (suivant la norme ANSI),

40 40 Exercices en langage C - sous forme d'un "prototype". Solution a) Bien qu'ilne possède pas de déclaration de la fonction fct, le programme main est correct. En effet, la norme ANSI autorise qu'une fonction ne soit pas déclarée, auquelcas e le est considérée comme fournissant un résultat de type int. Cette facilité est toutefois fortement déconsei lée (et e le ne sera plus acceptée de C+ +). Voici les résultats fournis par le programme : p = 5, n = 10 b) La déclaration la plus brè ve sera : int fct () ; La déclaration (vivement consei lée), sous forme de prototype sera : int fct (int) ; ou, éventue lement, sous forme d'un prototype "complet" : int fct (int r) ; Dans ce dernier cas, le nom r n'a aucune signification : on utilise souvent le même nom (lorsqu'on le connaît!) que dans l'en-tête de la fonction, mais ilpourrait s'agir de n'importe quelautre nom de variable). Exercice IV.2 Enoncé Ecrire :

41 IV. Les fonctions 41 - une fonction, nommée f1, se contentant d'afficher "bonjour" (e le ne possédera aucun argument, ni valeur de retour), - une fonction, nommée f2, qui affiche "bonjour" un nombre de fois égalà la valeur reçue en argument (int) et qui ne renvoie aucune valeur, - une fonction, nommée f3, qui fait la même chose que f2, mais qui, de plus, renvoie la valeur (int) 0. Ecrire un petit programme appelant successivement chacune de ces 3 fonctions, aprè s les avoir convenablement déclarées sous forme d'un prototype. Solution #include <stdio.h> void f1 (void) { printf ("bonjour\n") ; void f2 (int n) { int i ; for (i=0 ; i<n ; i++) printf ("bonjour\n") ; int f3 (int n) { int i ; for (i=0 ; i<n ; i++) printf ("bonjour\n") ; return 0 ; main() { void f1 (void) ; void f2 (int) ; int f3 (int) ; f1 () ; f2 (3) ; f3 (3) ;

42 42 Exercices en langage C Exercice IV.3 Enoncé Quels résultats fournira ce programme : #include <stdio.h> int n=10, q=2 ; main() { int fct (int) ; void f (void) ; int n=0, p=5 ; n = fct(p) ; printf ("A : dans main, n = %d, p = %d, q = %d\n", n, p, q) ; f() ; int fct (int p) { int q ; q = 2 * p + n ; printf ("B : dans fct, n = %d, p = %d, q = %d\n", n, p, q) ; return q ; void f (void) { int p = q * n ; printf ("C : dans f, n = %d, p = %d, q = %d\n", n, p, q) ;

43 IV. Les fonctions 43 Solution B : dans fct, n = 10, p = 5, q = 20 A : dans main, n = 20, p = 5, q = 2 C : dans f, n = 10, p = 20, q = 2 Exercice IV.4 Enoncé Ecrire une fonction qui reçoit en arguments 2 nombres flottants et un caractère et qui fournit un résultat correspondant à l'une des 4 opérations appliquées à ses deux premiers arguments, en fonction de la valeur du dernier, à savoir : addition pour le caractère +, soustraction pour -, multiplication pour *et division pour / (tout autre caractère que l'un des 4 cités sera interprété comme une addition). On ne tiendra pas compte des risques de division par zéro. Ecrire un petit programme (main) utilisant cette fonction pour effectuer les 4 opérations sur deux nombres fournis en donnée. Solution #include <stdio.h> float oper (float v1, float v2, char op) { float res ; switch (op) { case '+' : res = v1 + v2 ; break ; case '-' : res = v1 - v2 ; break ; case '*' : res = v1 * v2 ; break ; case '/' : res = v1 / v2 ;

44 44 Exercices en langage C break ; default : res = v1 + v2 ; return res ; main() { float oper (float, float, char) ; /* prototype de oper */ float x, y ; printf ("donnez deux nombres réels : ") ; scanf ("%e %e", &x, &y) ; printf ("leur somme est : %e\n", oper (x, y, '+') ) ; printf ("leur différence est : %e\n", oper (x, y, '-') ) ; printf ("leur produit est : %e\n", oper (x, y, '*') ) ; printf ("leur quotient est : %e\n", oper (x, y, '/') ) ; Exercice IV.5 Enoncé Transformer le programme (fonction + main) écrit dans l'exercice précédent de maniè re à ce que la fonction ne dispose plus que de 2 arguments, le caractère indiquant la nature de l'opération à effectuer étant précisé, cette fois, à l'aide d'une variable globale. Solution #include <stdio.h>

45 IV. Les fonctions 45 char op ; /* variable globale pour la nature de l'opération */ /* attention : doit être déclarée avant d'être utilisée */ float oper (float v1, float v2) { float res ; switch (op) { case '+' : res = v1 + v2 ; break ; case '-' : res = v1 - v2 ; break ; case '*' : res = v1 * v2 ; break ; case '/' : res = v1 / v2 ; break ; default : res = v1 + v2 ; return res ; main() { float oper (float, float) ; /* prototype de oper */ float x, y ; printf ("donnez deux nombres réels : ") ; scanf ("%e %e", &x, &y) ; op = '+' ; printf ("leur somme est : %e\n", oper (x, y) ) ; op = '-' ; printf ("leur différence est : %e\n", oper (x, y) ) ; op = '*' ; printf ("leur produit est : %e\n", oper (x, y) ) ; op = '/' ; printf ("leur quotient est : %e\n", oper (x, y) ) ; Remarque : Il s'agissait ici d'un exercice d'"école" destiné à forcer l'utilisation d'une variable globale. Dans la pratique, on évitera le plus possible ce genre de programmation qui favorise trop largement les risques d'"effets de bord".

46 46 Exercices en langage C Exercice IV.6 Enoncé Ecrire une fonction, sans argument ni valeur de retour, qui se contente d'afficher, à chaque appel, le nombre totalde fois où e le a été appelée sous la forme : appel numéro 3 Solution La mei leure solution consiste à prévoir, au sein de la fonction en question, une variable de classe statique. E le sera initialisée une seule fois à zéro (ou à toute autre valeur éventue lement explicitée) au début de l'exécution du programme. Ici, nous avons, de plus, prévu un petit programme d'essai. #include <stdio.h> void fcompte (void) { static int i ; /* il est inutile, mais pas défendu, d'écrire i=0 */ i++ ; printf ("appel numéro %d\n", i) ; /* petit programme d'essai de fcompte */ main() { void fcompte (void) ; int i ; for (i=0 ; i<3 ; i++) fcompte () ; Là encore, la démarche consistant à utiliser comme compteur d'appels une variable globale (qui devrait alors être connue du programme utilisateur) est à proscrire.

47 Exercice IV.7 IV. Les fonctions 47 Enoncé Ecrire 2 fonctions à un argument entier et une valeur de retour entiè re permettant de préciser si l'argument reçu est multiple de 2 (pour la premiè re fonction) ou multiple de 3 (pour la seconde fonction). Utiliser ces deux fonctions dans un petit programme qui lit un nombre entier et qui précise s'ilest pair, multiple de 3 et/ou multiple de 6, comme dans cet exemple (ily a deux exécutions) : donnez un entier : 9 il est multiple de 3 donnez un entier : 12 il est pair il est multiple de 3 il est divisible par 6 Solution #include <stdio.h> int mul2 (int n) { if (n%2) return 0 ; else return 1 ; int mul3 (int n) { if (n%3) return 0 ; else return 1 ; main() { int mul2 (int) ;

48 48 Exercices en langage C int mul3 (int) ; int n ; printf ("donnez un entier : ") ; scanf ("%d", &n) ; if (mul2(n)) printf ("il est pair\n") ; if (mul3(n)) printf ("il est multiple de 3\n") ; if (mul2(n) && mul3(n)) printf ("il est divisible par 6\n") ;

49 V : TABLEAUX ET POINTEURS Exercice V.1 Enoncé Quels résultats fournira ce programme : #include <stdio.h> main() { int t [3] ; int i, j ; int * adt ; for (i=0, j=0 ; i<3 ; i++) t[i] = j++ + i ; /* 1 */ for (i=0 ; i<3 ; i++) printf ("%d ", t[i]) ; /* 2 */ printf ("\n") ; for (i=0 ; i<3 ; i++) printf ("%d ", *(t+i)) ; /* 3 */ printf ("\n") ; for (adt = t ; adt < t+3 ; adt++) printf ("%d ", *adt) ; /* 4 */ printf ("\n") ; for (adt = t+2 ; adt>=t ; adt--) printf ("%d ", *adt) ; /* 5 */

50 50 Exercices en langage C printf ("\n") ; Solution /*1*/ remplit le tableau avec les valeurs 0 (0+ 0), 2 (1+ 1) et 4 (2+ 2) ;on obtiendrait plus simplement le même résultat avec l'expression 2*i. /*2 */ affiche "classiquement" les valeurs du tableau t, dans l'ordre "naturel". /* 3 */ fait la même chose, en utilisant le formalisme pointeur au lieu du formalisme tableau. Ainsi, *(t+i) est parfaitement équivalent à t[i]. /*4 */ fait la même chose, en utilisant la "lvalue" adt (à laque le on a affecté initialement l'adresse t du tableau) et en "l'incrémentant" pour parcourir les différentes adresses des 4 éléments du tableau. /*5 */ affiche les valeurs de t, à l'envers, en utilisant le même formalisme pointeur que dans 4. On aurait pu écrire, de façon équivalente : for (i=2 ; i>=0 ; i--) printf ("%d ", t[i]) ; Voici les résultats fournis par ce programme : Exercice V.2

51 Enoncé V. Tableaux et pointeurs 51 Ecrire, de deux façons différentes, un programme qui lit 10 nombres entiers dans un tableau avant d'en rechercher le plus grand et le plus petit : a) en utilisant uniquement le "formalisme tableau", b) en utilisant le "formalisme pointeur", à chaque fois que cela est possible Solution a) La programmation est, ici, "classique". Nous avons simplement défini un symbole NVAL destiné à contenir le nombre de valeurs du tableau. Notez bien que la déclaration int t[nval] est acceptée puisque NVAL est une "expression constante". En revanche, e le ne l'aurait pas été si nous avions défini ce symbole NVAL par une "constante symbolique" (const int NVAL = 10). #include <stdio.h> #define NVAL 10 /* nombre de valeurs du tableau */ main() { int i, min, max ; int t[nval] ; printf ("donnez %d valeurs\n", NVAL) ; for (i=0 ; i<nval ; i++) scanf ("%d", &t[i]) ; max = min = t[0] ; for (i=1 ; i<nval ; i++) { if (t[i] > max) max = t[i] ; /* ou max = t[i]>max? t[i] : max */ if (t[i] < min) min = t[i] ; /* ou min = t[i]<min? t[i] : min */ printf ("valeur max : %d\n", max) ; printf ("valeur min : %d\n", min) ; b) On peut remplacer systématiquement, t[i] par *(t+i)./ De plus, dans scanf, on peut remplacer & t[i] par t+i. Voici finalement le programme obtenu : #include <stdio.h> #define NVAL 10 /* nombre de valeurs du tableau */ main() { int i, min, max ;

52 52 Exercices en langage C int t[nval] ; printf ("donnez %d valeurs\n", NVAL) ; for (i=0 ; i<nval ; i++) scanf ("%d", t+i) ; /* attention t+i et non *(t+i) */ max = min = *t ; for (i=1 ; i<nval ; i++) { if (*(t+i) > max) max = *(t+i) ; if (*(t+i) < min) min = *(t+i) ; printf ("valeur max : %d\n", max) ; printf ("valeur min : %d\n", min) ; Exercice V.3 Enoncé Soient deux tableaux t1 et t2 déclarés ainsi : float t1[10], t2[10] ; Ecrire les instructions permettant de recopier, dans t1, tous les éléments positifs de t2, en complétant éventue lement t1 par des zéros. Ici, on ne cherchera pas à fournir un programme complet et on utilisera systématiquement le formalisme tableau. Solution On peut commencer par remplir t1 de zéros, avant d'y recopier les éléments positifs de t2 : int i, j ; for (i=0 ; i<10 ; i++) t1[i] = 0 ; /* i sert à pointer dans t1 et j dans t2 */ for (i=0, j=0 ; j<10 ; j++)

53 if (t2[j] > 0) t1[i++] = t2[j] ; V. Tableaux et pointeurs 53 Mais, on peut recopier d'abord dans t1 les éléments positifs de t2, avant de compléter éventue lement par des zéros. Cette deuxiè me formulation, moins simple que la précédente, se révélerait toutefois plus efficace sur de grands tableaux : int i, j ; for (i=0, j=0 ; j<10 ; j++) if (t2[j] > 0) t1[i++] = t2[j] ; for (j=i ; j<10 ; j++) t1[j] = 0 ; Exercice V.4 Enoncé Quels résultats fournira ce programme : #include <stdio.h> main() { int t[4] = {10, 20, 30, 40 ; int * ad [4] ; int i ; for (i=0 ; i<4 ; i++) ad[i] = t+i ; /* 1 */ for (i=0 ; i<4 ; i++) printf ("%d ", * ad[i]) ; /* 2 */ printf ("\n") ; printf ("%d %d \n", * (ad[1] + 1), * ad[1] + 1) ; /* 3 */ Solution Le tableau ad est un tableau de 4 éléments ;chacun de ces éléments est un pointeur sur un int. L'instruction /* 1 */ remplit le tableau ad avec les adresses des 4 éléments du tableau t. L'instruction /*2 */ affiche finalement les 4 éléments du tableau t ;en effet, *ad[i] représente la valeur située à l'adresse ad[i]. /*2 */ est équivalente ici à : for (i=0 ; i<4 ; i++) printf ("%d", t[i]) ;

54 54 Exercices en langage C Enfin, dans l'instruction /*3 */, *(ad[1] + 1) représente la valeur située à l'entier suivant celui d'adresse ad[1] ;ils'agit donc de t[2]. En revanche, *ad[1] + 1 représente la valeur située à l'adresse ad[1] augmentée de 1, autrement dit t[1] + 1. Voici, en définitive, les résultats fournis par ce programme : Exercice V.5 Enoncé Soit le tableau t déclaré ainsi : float t[3] [4] ; Ecrire les (seules) instructions permettant de calculer, dans une variable nommée som, la somme des éléments de t : a) en utilisant le "formalisme usueldes tableaux à deux indices", b) en utilisant le "formalisme pointeur". Solution a) La premiè re solution ne pose aucun problème particulier : int i, j ; som = 0 ; for (i=0 ; i<3 ; i++) for (j=0 ; j<4 ; j++) som += t[i] [j] ; b) Le formalisme pointeur est ici moins facile à appliquer que dans le cas des tableaux à un indice. En effet, avec, par exemple, float t[4], t est de type int * et ilcorrespond à un pointeur sur le premier élément du tableau. Ilsuffit donc d'incrémenter convenablement t pour parcourir tous les éléments du tableau.

55 V. Tableaux et pointeurs 55 En revanche, avec notre tableau float t [3] [4], t est du type pointeur sur des tableaux de 4 flottants(type : float[4] *). La notation *(t+i) est généralement inutilisable sous cette forme puisque, d'une part, e le correspond à des valeurs de tableaux de 4 flottants et que, d'autre part, l'incrément i porte, non plus sur des flottants, mais sur des blocs de 4 flottants ;par exemple, t+2 représente l'adresse du huitiè me flottant, compté à partir de celui d'adresse t. Une solution consiste à "convertir" la valeur de t en un pointeur de type float *. On pourrait se contenter de procéder ainsi : float * adt ;... adt = t ; En effet, dans ce cas, l'affectation entraîne une conversion forcée de t en float *, ce qui ne change pas l'adresse correspondante 1 (seule la nature du pointeur a changé). Généralement, on y gagnera en lisibilité en explicitant la conversion mise en oeuvre à l'aide de l'opérateur de "cast". Notez que, d'une part, cela peut éviter certains messages d'avertissement ("warnings") de la part du compilateur. Voici finalement ce que pourraient être les instructions demandées : int i ; int * adt ; som = 0 ; adt = (float *) t ; for (i=0 ; i<12 ; i++) som += * (adt+i); Exercice V.6 Enoncé Ecrire une fonction qui fournit en valeur de retour la somme des éléments d'un tableau de flottants transmis, ainsi que sa dimension, en argument. Ecrire un petit programme d'essai. 1 Attention, cela n'est vrai que parce que l'on passe de pointeurs sur des groupes d'éléments à un pointeur sur ces éléments. Autrement dit, aucune "contrainte d'alignement" ne risque de nuire ici. Iln'en irait pas de même, par exemple, pour des conversions de char *en int *.

56 56 Exercices en langage C Solution En ce qui concerne le tableau de flottants reçu en argument, ilne peut être transmis que par adresse. Quant au nombre d'élément (de type int), nous le transmettrons classiquement par valeur. L'en-tête de notre fonction pourra se présenter sous l'une des formes suivantes : float somme (float t[], int n) float somme (float * t, int n) float somme (float t[5], int n) /* déconseillé car laisse croire que t */ /* est de dimension fixe 5 */ En effet, la dimension rée le de t n'a aucune incidence sur les instructions de la fonction e le-même (e le n'intervient pas dans le calculde l'adresse d'un élément du tableau 2 et e le ne sert pas à "a louer" un emplacement puisque le tableau en question aura été a loué dans la fonction appelant somme). Voici ce que pourrait être la fonction demandée : float somme (float t[], int n) /* on pourrait écrire somme (float * t,... */ /* ou encore somme (float t[4],... */ /* mais pas somme (float t[n],... */ { int i ; float s = 0 ; for (i=0 ; i<n ; i++) s += t[i] ; /* on pourrait écrire s += * (t+i) ; */ return s ; Pour ce qui est du programme d'utilisation de la fonction somme, on peut, là encore, écrire le "prototype" sous différentes formes : float somme (float [], int ) ; float somme (float *, int ) ; float somme (float [5], int ) ; /* déconseillé car laisse croire que t */ /* est de dimension fixe 5 */ Voici un exemple d'un telprogramme : #include <stdio.h> main() 2 Iln'en irait pas de même pour des tableaux à plusieurs indices.

57 { float somme (float *, int) ; float t[4] = {3, 2.5, 5.1, 3.5 ; printf ("somme de t : %f\n", somme (t, 4) ) ; V. Tableaux et pointeurs 57 Exercice V.7 Enoncé Ecrire une fonction qui ne renvoie aucune valeur et qui détermine la valeur maximale et la valeur minimale d'un tableau d'entiers (à un indice) de tai le quelconque. Ilfaudra donc prévoir 4 arguments : le tableau, sa dimension, le maximum et le minimum. Ecrire un petit programme d'essai. Solution En langage C, un tableau ne peut être transmis que par adresse (en toute rigueur, C n'autorise que la transmission par valeur mais, dans le cas d'un tableau, on transmet une valeur de type pointeur qui n'est rien d'autre que l'adresse du tableau!). En ce qui concerne son nombre d'éléments, on peut indifféremment en transmettre l'adresse (sous forme d'un pointeur de type int *), ou la valeur ;ici, la seconde solution est la plus normale. En revanche, en ce qui concerne le maximum et le minimum, ils ne peuvent pas être transmis par valeur, puisqu'ils doivent précisément être déterminés par la fonction. Ilfaut donc obligatoirement prévoir de passer des pointeurs sur des float. L'en-tête de notre fonction pourra donc se présenter ainsi (nous ne donnons plus toutes les écritures possibles) : void maxmin (int t[], int n, int * admax, int * admin) L'algorithme de recherche de maximum et de minimum peut être calqué sur celui de l'exercice V.2, en remplaçant max par *admax et min par *admin. Cela nous conduit à la fonction suivante : void maxmin (int t[], int n, int * admax, int * admin)

58 58 Exercices en langage C { int i ; *admax = t[1] ; *admin = t[1] ; for (i=1 ; i<n ; i++) { if (t[i] > *admax) *admax = t[i] ; if (t[i] < *admin) *admin = t[i] ; Si l'on souhaite éviter les "indirections" qui apparaissent systématiquement dans les instructions de comparaison, on peut "travai ler" temporairement sur des variables locales à la fonction (nommées ici max et min). Cela nous conduit à une fonction de la forme suivante : void maxmin (int t[], int n, int * admax, int * admin) { int i, max, min ; max = t[1] ; min = t[1] ; for (i=1 ; i<n ; i++) { if (t[i] > max) max = t[i] ; if (t[i] < min) min = t[i] ; *admax = max ; *admin = min ; Voici un petit exemple de programme d'utilisation de notre fonction : #include <stdio.h> main() { void maxmin (int [], int, int *, int *) ; int t[8] = { 2, 5, 7, 2, 9, 3, 9, 4 ; int max, min ; maxmin (t, 8, &max, &min) ; printf ("valeur maxi : %d\n", max) ; printf ("valeur mini : %d\n", min) ;

59 Exercice V.8 V. Tableaux et pointeurs 59 Enoncé Ecrire une fonction qui fournit en retour la somme des valeurs d'un tableau de flottants à deux indices dont les dimensions sont fournies en argument. Solution Par analogie avec ce que nous avions fait dans l'exercice V.6, nous pourrions songer à déclarer le tableau concerné dans l'en-tête de la fonction sous la forme t[][]. Mais, cela n'est plus possible car, cette fois, pour déterminer l'adresse d'un élément t[i][j] d'un teltableau, le compilateur doit en connaître la deuxiè me dimension. Une solution consiste à considérer qu'on reçoit un pointeur (de type float*) sur le début du tableau et d'en parcourir tous les éléments (au nombre de n*p si n et p désignent les dimensions du tableau) comme si l'on avait affaire à un tableau à une dimension. Cela nous conduit à cette fonction : float somme (float * adt, int n, int p) { int i ; float s ; for (i=0 ; i<n*p ; i++) s += adt[i] ; /* ou s += *(adt+i) */ return s ; Pour utiliser une te le fonction, la seule difficulté consiste à lui transmettre effectivement l'adresse de début du tableau, sous la forme d'un pointeur de type int *. Or, avec, par exemple t[3][4], t, s'ilcorrrespond bien à la bonne adresse, est du type "pointeur sur des tableaux de 4 flottants". A priori, toutefois, compte tenu de la présence du prototype, la conversion voulue sera mise en oeuvre automatiquement par le compilateur. Toutefois, comme nous l'avons déjà dit dans l'exercice V.5, on y gagnera en lisibilité (et en éventuels messages d'avertissement!) en faisant appelà l'opérateur de "cast". Voici finalement un exemple d'un telprogramme d'utilisation de notre fonction : #include <stdio.h> main() {

60 60 Exercices en langage C float somme (float *, int, int) ; float t[3] [4] = { {1,2,3,4, {5,6,7,8, {9,10,11,12 ; printf ("somme : %f\n", somme ((float *)t, 3, 4) ) ;

61 VI:LES CH AINES DE CARACTERES Exercice VI.1 Enoncé Quels résultats fournira ce programme : #include <stdio.h> main() { char * ad1 ; ad1 = "bonjour" ; printf ("%s\n", ad1) ; ad1 = "monsieur" ; printf ("%s\n", ad1) ; Solution L'instruction ad1 = "bonjour" place dans la variable ad1l'adresse de la chaîne constante "bonjour". L'instruction printf ("%s\n", ad1) se contente d'afficher la valeur de la chaîne dont l'adresse figure dans ad1, c'est-à -dire, en l'occurrence "bonjour". De maniè re comparable, l'instruction ad1 = "monsieur" place l'adresse de la chaîne constante "monsieur"

62 62 Exercices en langage C dans ad1 ;l'instruction printf ("%s\n", ad1) affiche la valeur de la chaîne ayant maintenant l'adresse contenue dans ad1, c'est-à -dire maintenant "monsieur". Finalement, ce programme affiche tout simplement : bonjour monsieur On aurait obtenu plus simplement le même résultat en écrivant : printf ("bonjour\nmonsieur\n") ; Exercice VI.2 Enoncé Quels résultats fournira ce programme : #include <stdio.h> main() { char * adr = "bonjour" ; /* 1 */ int i ; for (i=0 ; i<3 ; i++) putchar (adr[i]) ; /* 2 */ printf ("\n") ; i = 0 ; while (adr[i]) putchar (adr[i++]) ; /* 3 */ Solution La déclaration /*1 */ place dans la variable adr, l'adresse de la chaîne constante bonjour. L'instruction /* 2 */ affiche les caractères adr[0], adr[1] et adr[2], c'est-à -dire les 3 premiers caractères de cette chaîne. L'instruction /*3 */ affiche tous les caractères à partir de celui d'adresse adr, tant que l'on a pas affaire à un caractère nul;comme notre chaîne

63 VI. Les chaînes de caractères 63 "bonjour" est précisément terminée par un tel caractère nul, cette instruction affiche finalement, un par un, tous les caractères de "bonjour". En définitive, le programme fournit simplement les résultats suivants : bon bonjour Exercice VI.3 Enoncé Ecrire le programme précédent (Exercice VI.2), sans utiliser le "formalisme tableau" (ilexiste plusieurs solutions). Solution Voici deux solutions possibles : a) On peut remplacer systématiquement la notation adr[i] par *(adr+ i), ce qui conduit à ce programme : #include <stdio.h> main() { char * adr = "bonjour" ; int i ; for (i=0 ; i<3 ; i++) putchar (*(adr+i)) ; printf ("\n") ; i = 0 ; while (adr[i]) putchar (*(adr+i++)) ; b) On peut également parcourir notre chaîne, non plus à l'aide d'un "indice" i, mais en incrémentant un pointeur de type char *: ilpourrait s'agir tout simplement de adr, mais généralement, on préférera ne pas détruire cette information et en employer une copie :

64 64 Exercices en langage C #include <stdio.h> main() { char * adr = "bonjour" ; char * adb ; for (adb=adr ; adb<adr+3 ; adb++) putchar (*adb) ; printf ("\n") ; adb = adr ; while (*adb) putchar (*(adb++)) ; Notez bien que si nous incrémentions directement adr dans la premiè re instruction d'affichage, nous ne disposerions plus de la "bonne adresse" pour la deuxiè me instruction d'affichage. Exercice VI.4 Enoncé Ecrire un programme qui demande à l'utilisateur de lui fournir un nombre entier entre 1 et 7 et qui affiche le nom du jour de la semaine ayant le numéro indiqué (lundi pour 1, mardi pour 2,... dimanche pour 7). Solution Une démarche consiste à créer un "tableau de 7 pointeurs sur des chaînes", correspondant chacune au nom d'un jour de la semaine. Comme ces chaînes sont ici constantes, ilest possible de créer un teltableau par une déclaration comportant une intialisation de la forme : char * jour [7] = { "lundi", "mardi",... N'oubliez pas alors que jour[0] contiendra l'adresse de la premiè re chaîne, c'est-à -dire l'adresse de la chaîne constante "lundi" ;jour[1] contiendra l'adresse de "mardi",... Pour afficher la valeur de la chaîne de rang i, ilsuffit de remarquer que son adresse est simplement jour[i-1]. D'où le programme demandé :

65 VI. Les chaînes de caractères 65 #include <stdio.h> main() { char * jour [7] = { "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche" ; int i ; do { printf ("donnez un nombre entier entre 1 et 7 : ") ; scanf ("%d", &i) ; while ( i<=0 i>7) ; printf ("le jour numéro %d de la semaine est %s", i, jour[i-1]) ; Exercice VI.5 Enoncé Ecrire un programme qui lit deux nombres entiers fournis obligatoirement sur une même ligne. Le programme ne devra pas "se planter" en cas de réponse incorrecte (caractères invalides) comme le ferait scanf ("%d %d",...) mais simplement afficher un message et redemander une autre réponse. Ildevra en a ler de même lorsque la réponse fournie ne comporte pas assez d'informations. En revanche, lorsque la réponse comportera trop d'informations, les derniè res devront être ignorées. Le traitement (demande de 2 nombres et affichage) devra se poursuivre jusqu'à ce que le premier nombre fourni soit 0. Voici un exemple d'exécution d'un telprogramme : --- donnez deux entiers : é réponse erronée - redonnez-la : 2 15 merci pour donnez deux entiers : 5 réponse erronée - redonnez-la : 4 12 merci pour donnez deux entiers : merci pour donnez deux entiers : 5 é3

66 66 Exercices en langage C réponse erronée - redonnez-la : 5 23 merci pour donnez deux entiers : 0 0 merci pour 0 0 Remarque : on peut utiliser les fonctions gets et sscanf. Solution Comme le suggè re la remarque de l'énoncé, on peut résoudre les problèmes posés en effectuant en deux temps la lecture d'un couple d'entiers : -lecture d'une chaîne de caractères (c'est-à -dire une suite de caractères absolument quelconques, validée par "return") avec la fonction gets, - "décodage" de cette chaîne avec sscanf, suivant un "format", d'une maniè re comparable à ce que ferait scanf, à partir de son "tampon d'entrée". Rappelons que sscanf, tout comme scanf, fournit en retour le nombre d'informations correctement lues, de sorte qu'il suffit de répéter les deux opérations précédentes jusqu'à ce que la valeur fournie par sscanf soit égale à 2. L'énoncé ne fait aucune hypothèse sur le nombre maximalde caractères que l'utilisateur pourra ê tre amené à fournir. Ici, nous avons supposé qu'au plus 128 caractères seraient fournis ;ils'agit là d'une hypothèse qui, dans la pratique, s'avère réaliste, dans la mesure où on risque rarement de frapper des lignes plus longues ;de surcroît, il s'agit même d'une limitation "nature le" de certains environnements (DOS, en particulier). Voici le programme demandé : #include <stdio.h> #define LG 128 /* longueur maximale d'une ligne */ main() { int n1, n2 ; /* entiers à lire en donnée */ int compte ; /* pour la valeur de retour de sscanf */ char ligne [LG+1] ; /* pour lire une ligne (+1 pour \0) */ do /* boucle de lecture des différents couples de valeurs */ { /* boucle de lecture d'un couple de valeur jusqu'à OK */ printf ("--- donnez deux entiers : ") ; do { gets (ligne) ; compte = sscanf (ligne, "%d %d", &n1, &n2) ;

67 VI. Les chaînes de caractères 67 if (compte<2) printf ("réponse erronée - redonnez-la : ") ; while (compte < 2) ; printf ("merci pour %d %d\n", n1, n2) ; while (n1) ; Remarques 1) Si l'utilisateur fournit plus de caractères qu'iln'en faut pour former 2 nombres entiers, ces caractères (lus dans ligne) ne seront pas utilisés par sscanf ;malgré tout, ils ne seront pas exploités ultérieurement puisque, lorsque l'on redemandera 2 nouveaux entiers, on relira une nouve le chaîne par gets. 2) Si l'on souhaite absolument pouvoir limiter la longueur de la chaîne lue au clavier, en utilisant des instructions "portables", ilfaut se tourner vers la fonction fgets 1 destinée à lire une chaîne dans un fichier, et l'appliquer à stdin. On remplacera l'instruction gets (ligne) par fgets (ligne, LG, stdin) qui limitera à LG le nombre de caractères pris en compte. Notez toutefois que, dans ce cas, les caractères excédentaires (et donc non "vus" par fgets) resteront disponibles pour une prochaine lecture (ce qui n'est pas pire que dans la situation actue le où ces caractères viendraient écraser des emplacements mémoire situés au-delà du tableau ligne!). Dans certaines implémentations (Turbo/Borland C/C+ + et Quick C/C Microsoft), il existe une fonction (non portable, puisque non prévue par la norme ANSI) nommée cgets qui, utilisée à la place de gets (ou fgets) permet de régler le problème évoqué. En effet, cgets permet de lire une chaîne, en limitant le nombre de caractères effectivement fournis au clavier : iln'est pas possible à l'utilisateur d'en frapper plus que prévu, de sorte que le risque de caractères excédentaires n'existe plus! Exercice VI.6 1 Mais, si vous réalisez ces exercices en accompagnement d'un cours de langage C, ilest probable que vous n'aurez pas encore étudié la fonction fgets (en général, e le est introduite dans le chapitre relatif au traitement des fichiers). Certains exercices de la seconde partie de cet ouvrage feront appelà fgets, et/ou à sscanf.

68 68 Exercices en langage C Enoncé Ecrire un programme déterminant le nombre de lettres e (minuscule) contenues dans un texte fourni en donnée sous forme d'une seule ligne ne dépassant pas 128 caractères. On cherchera, ici, à n'utiliser aucune des fonctions de traitement de chaîne. Solution Compte tenu des contraintes imposées par l'énoncé, nous ne pouvons pas faire appelà la fonction strlen. Pour "explorer" notre chaîne, nous utiliserons le fait qu'e le est terminée par un caractère nul(\0]. D'où le programme proposé : #define LG_LIG 128 #include <stdio.h> main() { char ligne [LG_LIG+1] ; /* pour lire une ligne au clavier +1 pour \0 */ int i ; /* pour explorer les différents caractères de ligne */ int ne ; /* pour compter le nombre de 'e' */ printf ("donnez un texte de moins d'une ligne : \n") ; gets (ligne) ; ne = 0 ; i = 0 ; while (ligne[i]) if (ligne[i++] == 'e') ne++ ; printf ("votre texte comporte %d lettres e", ne) ; Exercice VI.7 Enoncé Ecrire un programme qui lit, en donnée, un verbe du premier groupe et qui en affiche la conjugaison au présent de l'indicatif, sous la forme :

69 je chante tu chantes il chante nous chantons vous chantez ils chantent VI. Les chaînes de caractères 69 On s'assurera que le mot fourni se termine bien par "er". On supposera qu'ils'agit d'un verbe régulier ;autrement dit, on admettra que l'utilisateur ne fournira pas un verbe telque manger (le programme afficherait alors : nous mangons!). Solution On lira "classiquement" un mot, sous forme d'une chaîne à l'aide de la fonction gets. Pour vérifier sa terminaison par "er", on comparera avec la chaîne constante "er", la chaîne ayant comme adresse l'adresse de fin du mot, diminuée de 2. L'adresse de fin se déduira de l'adresse de début et de la longueur de la chaîne (obtenue par la fonction strlen). Quant à la comparaison voulue, e le se fera à l'aide de la fonction strcmp ;rappelons que cette derniè re reçoit en argument 2 pointeurs sur des chaînes et qu'e le fournit en retour une valeur nu le lorsque les deux chaînes correspondantes sont égales et une valeur non nu le dans tous les autres cas. Les différentes personnes du verbe s'obtiennent en remplaçant, dans la chaîne en question, la terminaison "er" par une terminaison appropriée. On peut, pour cela, utiliser la fonction strcpy qui recopie une chaîne donnée (ici la terminaison) à une adresse donnée (ici, ce le déjà utilisée dans strcmp pour vérifier que le verbe se termine bien par "er"). Les différentes terminaisons possibles seront rangées dans un tableau de chaînes constantes (plus précisément, dans un tableau de pointeurs sur des chaînes constantes). Nous ferons de même pour les différents sujets (je, tu...) ;en revanche, ici, nous ne chercherons pas à les "concaténer" au verbe conjugué ;nous nous contentons de les écrire, au moment opportun. Voici finalement le programme demandé : #include <stdio.h> #include <string.h> #define LG_VERBE 30 /* longueur maximale du verbe fourni en donnée */ main() { char verbe [LG_VERBE+1] ; /* verbe à conjuguer +1 pour \0 */ char * sujet [6] = { "je", "tu", "il", "nous", "vous", "ils" ; /* sujets */ char * term [6] = { "e", "es", "e", "ons", "ez", "ent" ;/* terminaisons */ int i ; char * adterm ; /* pointeur sur la terminaison du verbe */

70 70 Exercices en langage C do { printf ("donnez un verbe régulier du premier groupe : ") ; gets (verbe) ; adterm = verbe + strlen(verbe) - 2 ; while (strcmp (adterm, "er") ) ; printf ("conjugaison à l\'indicatif présent :\n") ; for (i=0 ; i<6 ; i++) { strcpy (adterm, term[i]) ; printf ("%s %s\n", sujet[i], verbe) ; Remarque : rappelons que strcpy recopie (sans aucun contrôle) la chaîne dont l'adresse est fournie en premier argument (c'est-à -dire, en fait, tous les caractères à partir de cette adresse, jusqu'à ce que l'on rencontre un \0) à l'adresse fournie en second argument ;de plus, e le complète bien le tout avec un caractère nulde fin de chaîne. Exercice VI.8 Enoncé Ecrire un programme qui supprime toutes les lettres e (minuscule) d'un texte de moins d'une ligne (ne dépassant pas 128 caractères) fourni en donnée. On s'arrangera pour que le texte ainsi modifié soit créé en mémoire, à la place de l'ancien. N.B. on pourra utiliser la fonction strchr. Solution La fonction strchr permet de trouver un caractère donné dans une chaîne. E le est donc tout à fait appropriée pour localiser les 'e' ;il faut toutefois noter que, pour localiser tous les 'e', il est nécessaire de répéter l'appel de cette

71 VI. Les chaînes de caractères 71 fonction, en modifiant à chaque fois l'adresse de début de la chaîne concernée (ilfaut éviter de boucler sur la recherche du même caractère 'e'). La fonction strchr fournit l'adresse à laque le on a trouvé le premier caractère indiqué (ou la valeur 0 si ce caractère n'existe pas). La suppression du 'e' trouvé peut se faire en recopiant le "reste" de la chaîne à l'adresse où l'on a trouvé le 'e'. Voici une solution possible : #include <stdio.h> #include <string.h> #define LG_LIG 128 /* longueur maximum d'une ligne de données */ #define CAR 'e' /* caractère à supprimer */ main() { char ligne [LG_LIG+1] ; /* pour lire une ligne +1 pour \0 */ char * adr ; /* pointeur à l'intérieur de la ligne */ printf ("donnez un texte de moins d'une ligne : \n") ; gets (ligne) ; adr = ligne ; while (adr = strchr (adr,'e') ) strcpy (adr, adr+1) ; printf ("voici votre texte, privé des caractères %c :\n") ; puts (ligne) ;

72

73 VII :LES STRUCTURES Exercice VII.1 Enoncé Soit le modè le (type) de structure suivant : struct s_point { char c ; int x, y ; ; Ecrire une fonction qui reçoit en argument une structure de type s_point et qui en affiche le contenu sous la forme : point B de coordonnées a) En transmettant en argument la valeur de la structure concernée, b) En transmettant en argument l'adresse de la structure concernée. Dans les deux cas, on écrira un petit programme d'essai de la fonction ainsi réalisée. Solution a) Voici la fonction demandée : #include <stdio.h>

74 74 Exercices en langage C void affiche (struct s_point p) { printf ("point %c de coordonnées %d %d\n", p.c, p.x, p.y) ; Notez que sa compilation nécessite obligatoirement la déclaration du type s_point, c'est-à -dire les instructions : struct s_point { char c ; int x, y ; ; Voici un petit programme qui affecte les valeurs 'A', 10 et 12 aux différents champs d'une structure nommée s, avant d'en afficher les valeurs à l'aide de la fonction précédente : main() { void affiche (struct s_point) ; // déclaration (prototype) de affiche struct s_point s ; s.c = 'A' ; s.x = 10 ; s.y = 12 ; affiche (s) ; Nature lement, la remarque précédente s'applique également ici. En pratique, la déclaration de la structure s_point figurera dans un fichier d'extension h que l'on se contentera d'incorporer par #include au moment de la compilation. De même, ilest nécessaire d'inclure stdio.h. b) Voici la nouve le fonction demandée : #include <stdio.h> void affiche (struct s_point * adp) { printf ("point %c de coordonnées %d %d\n", adp->c, adp->x, adp->y) ; Notez que l'on doit, cette fois, faire appelà l'opérateur ->, à la place de l'opérateur point (.), puisque l'on "travai le" sur un pointeur sur une structure, et non plus sur la valeur de la structure e le-même. Toutefois l'usage de -> n'est pas totalement indispensable, dans la mesure où, par exemple, adp-> x est équivalent à (*adp).x. Voici l'adaptation du programme d'essai précédent : main() {

75 void affiche (struct s_point *) ; struct s_point s ; s.c = 'A' ; s.x = 10 ; s.y = 12 ; affiche (&s) ; VII. Les structures 75 Remarque : Au lieu d'affecter des valeurs aux champs c, x et y de notre structure s (dans les deux programmes d'essai), nous pourrions (ici) utiliser les possibilités d'initialisation offertes par le langage C, en écrivant : struct s_point s = {'A', 10, 12 ; Exercice VII.2 Enoncé Ecrire une fonction qui "met à zéro" les différents champs d'une structure du type s_point (défini dans l'exercice précédent) qui lui est transmise en argument. La fonction ne comportera pas de valeur de retour. Solution Ici, bien que l'énoncé ne le précise pas, ilest nécessaire de transmettre à la fonction concernée, non pas la valeur, mais l'adresse de la structure à "remettre à zéro". Voici la fonction demandée (ici, nous avons reproduit la déclaration de s_point) : #include <stdio.h> struct s_point { char c ; int x, y ; ; void raz (struct s_point * adr)

76 76 Exercices en langage C { adr->c = 0 ; adr->x = 0 ; adr->y = 0 ; Voici, à titre indicatif, un petit programme d'essai (sa compilation nécessite la déclaration de s_point, ainsi que le fichier stdio.h) : main() { struct s_point p ; void raz (struct s_point *) ; // déclaration de raz raz (&p) ; /* on écrit c en %d pour voir son code */ printf ("après : %d %d %d", p.c, p.x, p.y) ; Exercice VII.3 Enoncé Ecrire une fonction qui reçoit en argument l'adresse d'une structure du type s_point (défini dans l'exercice VII.1) et qui renvoie en résultat une structure de même type correspondant à un point de même nom (c) et de coordonnées opposées. Ecrire un petit programme d'essai. Solution Bien que l'énoncé ne précise rien, le résultat de notre fonction ne peut être transmis que par valeur. En effet, ce résultat doit être créé au sein de la fonction e le-même ;cela signifie qu'ilsera détruit dè s la sortie de la fonction ;en transmettre l'adresse reviendrait à renvoyer l'adresse de quelque chose destiné à disparaître... Voici ce que pourrait être notre fonction (ici, encore, nous avons reproduit la déclaration de s_point) : #include <stdio.h> struct s_point { char c ;

77 int x, y ; ; struct s_point sym (struct s_point * adr) { struct s_point res ; res.c = adr->c ; res.x = - adr->x ; res.y = - adr->y ; return res ; VII. Les structures 77 Notez la "dissymétrie" d'instructions te les que res.c = adr-> c ;on y fait appelà l'opérateur. à gauche et à l'opérateur -> à droite (on pourrait cependant écrire res.c = (*adr).c. Voici un exemple d'essai de notre fonction (ici, nous avons utilisé les possibilités d'initialisation d'une structure pour donner des valeurs à p1) : main() { struct s_point sym (struct s_point *) ; struct s_point p1 = {'P', 5, 8 ; struct s_point p2 ; p2 = sym (&p1) ; printf ("p1 = %c %d %d\n", p1.c, p1.x, p1.y) ; printf ("p2 = %c %d %d\n", p2.c, p2.x, p2.y) ; Exercice VII.4 Enoncé Soit la structure suivante, représentant un point d'un plan : struct s_point { char c ; int x, y ; ; 1) Ecrire la déclaration d'un tableau (nommé courbe) de NP points (NP supposé défini par une instruction #define) 2) Ecrire une fonction (nommée affiche) qui affiche les valeurs des différents "points" du tableau courbe, transmis en argument, sous la forme :

78 78 Exercices en langage C point D de coordonnées ) Ecrire un programme qui : -lit en données des valeurs pour le tableau courbe ; on utilisera de préférence les fonctions gets et sscanf, de préférence à scanf (voir éventue lement l'exercice VI.5) ;on supposera qu'une ligne de donnée ne peut pas dépasser 128 caractè res, - fait appelà la fonction précédente pour les afficher. Solution 1) Ilsuffit de déclarer un tableau de structures : struct s_point courbe [NP] ; 2) Comme courbe est un tableau, on ne peut qu'en transmettre l'adresse en argument de affiche. Il est préférable de prévoir également en argument le nombre de points. Voici ce que pourrait être notre fonction : void affiche (struct s_point courbe [], int np) /* courbe : adresse de la première structure du tableau */ /* (on pourrait écrire struct s_point * courbe) */ /* np : nombre de points de la courbe */ { int i ; for (i=0 ; i<np ; i++) printf ("point %c de coordonnées %d %d\n", courbe[i].c, courbe[i].x, courbe[i].x) ; Comme pour n'importe queltableau à une dimension transmis en argument, ilest possible de ne pas en mentionner la dimension dans l'en-tête de la fonction. Bien entendu, comme, en fait, l'identificateur courbe n'est qu'un pointeur de type s_point * (pointeur sur la premiè re structure du tableau), nous aurions pu également écrire s_point * courbe. Notez que, comme à l'accoutumée, le "formalisme tableau" et le "formalisme pointeur" peuvent être indifféremment utilisés (voire combinés). Par exemple, notre fonction aurait pu également s'écrire : void affiche (struct s_point * courbe, int np) { struct s_point * adp ; int i ;

79 VII. Les structures 79 for (i=0, adp=courbe ; i<np ; i++, adp++) printf ("point %c de coordonnées %d %d", courbe->c, courbe->x, courbe->y) ; 3) Comme nous avons appris à le faire dans l'exercice VI.5, nous lirons les informations relatives aux différents points à l'aide des deux fonctions : - gets, pour lire, sous forme d'une chaîne, une ligne d'information, - sscanf, pour décoder suivant un format le contenu de la chaîne ainsi lue. Voici ce que pourrait le programme demandé (ici, nous avons reproduit, à la fois la déclaration de s_point et la fonction affiche précédente) : #include <stdio.h> struct s_point { char c ; int x, y ; ; #define NP 10 /* nombre de points d'une courbe */ #define LG_LIG 128 /* longueur maximale d'une ligne de donnée */ main() { struct s_point courbe [NP] ; int i ; char ligne [LG_LIG+1] ; void affiche (struct s_point [], int) ; /* lecture des différents points de la courbe */ for (i=0 ; i<np ; i++) { printf ("nom (1 caractère) et coordonnées point %d : ", i+1) ; gets (ligne) ; sscanf (ligne, "%c %d %d", &courbe[i].c, &courbe[i].x, &courbe[i].y) ; affiche (courbe, NP) ; void affiche (struct s_point courbe [], int np) { int i ; for (i=0 ; i<np ; i++) printf ("point %c de coordonnées %d %d\n", courbe[i].c, courbe[i].x, courbe[i].x) ;

80 80 Exercices en langage C Exercice VII.5 Enoncé Ecrire le programme de la question 3 de l'exercice précédent, sans utiliser de structures. On prévoira toujours une fonction pour lire les informations relatives à un point. Solution Ici, ilnous faut obligatoirement prévoir 3 tableaux différents de même tai le : un pour les noms de points, un pour leurs abscisses et un pour leurs ordonnées. Le programme ne présente pas de difficultés particuliè res (son principalintérê t est d'ê tre comparé au précédent!). #include <stdio.h> #define NP 10 /* nombre de points d'une courbe */ #define LG_LIG 128 /* longueur maximale d'une ligne de donnée */ main() { char c [NP] ; /* noms des différents points */ int x [NP] ; /* abscisses des différents points */ int y [NP] ; /* ordonnées des différents points */ int i ; char ligne [LG_LIG+1] ; void affiche (char [], int[], int[], int) ; /* lecture des différents points de la courbe */ for (i=0 ; i<np ; i++) { printf ("nom (1 caractère) et coordonnées point %d : ", i+1) ; gets (ligne) ; sscanf (ligne, "%c %d %d", &c[i], &x[i], &y[i]) ; affiche (c, x, y, NP) ;

81 void affiche (char c[], int x[], int y[], int np) { int i ; for (i=0 ; i<np ; i++) printf ("point %c de coordonnées %d %d\n", c[i], x[i], x[i]) ; VII. Les structures 81 Exercice VII.6 Enoncé Soient les deux modè les de structure date et personne déclarés ainsi : #define LG_NOM 30 struct date { int jour ; int mois ; int annee ; ; struct personne { char nom [LG_NOM+1] ; /* chaîne de caractères représentant le nom */ struct date date_embauche ; struct date date_poste ; ; Ecrire une fonction qui reçoit en argument une structure de type personne et qui en remplit les différents champs avec un dialogue se présentant sous l'une des 2 formes suivantes : nom : DUPONT date embauche (jj mm aa) : date poste = date embauche? (O/N) : O nom : DUPONT date embauche (jj mm aa) : date poste = date embauche? (O/N) : N date poste (jj mm aa) :

82 82 Exercices en langage C Solution Notre fonction doit modifier le contenu d'une structure de type personne ;il est donc nécessaire qu'e le en reçoive l'adresse en argument. Ici, l'énoncé n'imposant aucune protection particuliè re concernant les lectures au clavier, nous lirons "classiquement" le nom par gets et les trois autres informations numériques par scanf. Voici ce que pourrait être la fonction demandée : void remplit (struct personne * adp) { char rep ; /* pour lire une réponse de type O/N */ printf ("nom : ") ; gets (adp->nom) ; /* attention, pas de contrôle de longueur */ printf ("date embauche (jj mm aa) : ") ; scanf ("%d %d %d", &adp->date_embauche.jour, &adp->date_embauche.mois, &adp->date_embauche.annee) ; printf ("date poste = date embauche? (O/N) : ") ; getchar () ; rep = getchar () ; /* premier getchar pour sauter \n */ if (rep == 'O') adp->date_poste = adp->date_embauche ; else { printf ("date poste (jj mm aa) : ") ; scanf ("%d %d %d", &adp->date_poste.jour, &adp->date_poste.mois, &adp->date_poste.annee) ; Notez que, comme à l'accoutumée, dè s lors qu'une lecture de valeurs numériques (ici par scanf) est suivie d'une lecture d'un caractère (ici par getchar, mais le même problème se poserait avec scanf et le code %c), il est nécessaire de "sauter" artificie lement le caractère ayant servi à la validation de la derniè re information numérique ;en effet, dans le cas contraire, c'est précisément ce caractère (\n) qui est pris en compte. En toute rigueur, la démarche ainsi utilisée n'est pas infai lible : si l'utilisateur fournit des informations supplémentaires aprè s la derniè re valeur numérique (ne serait-ce qu'un simple espace), le caractère lu ultérieurement ne sera pas celui attendu. Toutefois, ils'agit alors des "problèmes habituels" liés à la fourniture d'informations excédentaires. Ils peuvent être résolus par différentes techniques dont nous avons parlé, notamment, dans l'exercice VI.5.

83 VII. Les structures 83 Voici, à titre indicatif, un petit programme d'essai de notre fonction (sa compilation nécessite les déclarations des structures date et personne) : main() { struct personne bloc ; remplit (&bloc) ; printf ("nom : %s \n date embauche : %d %d %d \n date poste : %d %d %d", bloc.nom, bloc.date_embauche.jour, bloc.date_embauche.mois, bloc.date_embauche.annee, bloc.date_poste.jour, bloc.date_poste.mois, bloc.date_poste.annee ) ;

84 DEUXIEME PARTIE : EXERCICES TH EMATIQUES

85 INTRODUCTION A LA DEUXIEME PARTIE Ce chapitre vous fournit quelques explications concernant la maniè re dont sont conçus les problèmes proposés dans cette deuxiè me partie de l'ouvrage et les quelques rè gles que nous nous sommes fixées pour la rédaction des programmes correspondants. 1 - Canevas commun à ch aque exercice Pour chaque exercice, nous avons adopté le même canevas. a) L'exposé du problème Ilest constitué d'un énoncé accompagné d'un exemple. Cet ensemble constitue ce qu'ilest indispensable de lire avant de tenter de résoudre le problème. Certes, l'exemple permet d'i lustrer et de concrétiser l'énoncé mais, de plus, il le précise, en particulier en explicitant la maniè re dont le programme dialogue avec l'utilisateur. On notera que cet exemple correspond exactement à une image d'écran obtenue avec le programme proposé en solution. b) L'analyse E le spécifie (ou précise) les algorithmes à mettre en oeuvre pour aboutir à une solution. E le garde un caractère général; notamment, e le évite de s'intéresser à certains détails de programmation dont le choix est rejeté au moment de l'écriture du programme. A priori, e le fait déjà partie de la solution ;toutefois, si vous séchez sur l'énoncé lui-même, rien ne vous empê che, aprè s la lecture de cette analyse, de tenter d'écrire le programme correspondant. En effet, un telexercice, bien

86 86 Exercices en langage C que limité à la simple traduction d'un algorithme dans un langage, n'en possède pas moins un intérê t propre en ce qui concerne l'apprentissage du langage lui-même. c) Le programme Bien qu'ilsuive exactement l'analyse proposée, iln'en reste pas moins qu'ilfai le le considérer comme une rédaction possible parmi beaucoup d'autres. N'oubliez pas qu'à ce niveau ilest bien difficile de porter un jugement de valeur sur les qualités ou les défauts de te le ou te le rédaction, tant que l'on n'a pas précisé les critères retenus (vitesse d'exécution, tai le mémoire, clarté de la rédaction, respect de certaines rè gles de style,...) ;cela est d'autant plus vrai que certains de ces critères peuvent s'avérer incompatibles entre eux. Ces remarques s'appliquent d'ai leurs déjà aux exercices proposés précédemment dans la premiè re partie de cet ouvrage mais avec moins d'accuité. d) Les commentaires Ils fournissent certaines explications que nous avons jugées utiles à la compréhension du programme lui-même. Ilpeut, par exemple, s'agir : - de rappels concernant une instruction ou une fonction peu usue le, - de justifications de certains choix réalisés uniquement au moment de la rédaction du programme, - de mise en évidence de certaines particularités ou originalités du langage, - etc. e) La discussion E le constitue une sorte d'ouverture fondée sur une réflexion de caractère généralqui peut porter sur : -les insuffisances éventue les du programme proposé, notamment en ce qui concerne son comportement face à des erreurs de la part de l'utilisateur, -les améliorations qu'ilest possible de lui apporter, - une généralisation du problème posé, - etc.

87 Introduction à la deuxiè me partie Protection des programmes par rapport aux données Comme beaucoup d'autres langages, les instructions usue les de lecture au clavier du langage C ne sont pas totalement protégées d'éventue les réponses incorrectes de la part de l'utilisateur. Ce les-ci peuvent entraîner un comportement anorm aldu program m e. D'une maniè re générale, ce problème de contrôle des données peut être résolu par l'emploi de techniques appropriées te les que ce les que nous avons rencontrées dans l'exercice VI.5 de la premiè re partie. Toutefois, ce les-ci présentent l'inconvénient d'alourdir le texte du programme. C'est pourquoi nous avons évité d'introduire systématiquement de te les protections dans tous nos exemples, ce qui aurait manifestement masqué l'objectif essentielde l'exercice (bien entendu, ces protections pourraient devenir indispensables dans un programme réel). Notez toutefois que certains exercices, de par leur nature même, requiè rent une te le protection ;ce le-ci sera alors clairement demandée dans l'énoncé lui-même. 3 - A propos des structures de boucle En principe, lorsque l'analyse d'un problème fait intervenir une répétition, ilfaudrait, pour ê tre complet, en préciser le type : - répétition définie (ou avec compteur) : e le est réalisée en C avec l'instruction for, - répétition tant que, dans laque le le test de poursuite a lieu en début de boucle : e le est réalisée en C avec l'instruction while, - répétition jusqu'à dans laque le le test d'arrê t a lieu en fin de boucle : e le est réalisée en C avec l'instruction do... while. En fait, ilexiste plusieurs raisons de ne pas toujours spécifier le choix du type d'une répétition au niveau de l'analyse et de le reporter au niveau de l'écriture du programme : - d'une part, le choix d'un type de boucle n'est pas toujours dicté impérativement par le problème : par exemple, un algorithme utilisant une répétition de type jusqu'à peut toujours être transformé en un algorithme utilisant une répétition de type tant que, - d'autre part, comme nous l'avons déjà entrevu dans le chapitre III de la premiè re partie, le langage C autorise des formes de répétition plus variées que les trois que nous venons d'évoquer (et qui sont ce les proposées classiquement par la "programmation structurée") : ainsi, par exemple : *grâ ce à la notion d'opérateur séquentiel, on peut réaliser, à l'aide de l'instruction while, des boucles dans lesque les le test de poursuite a lieu, non plus en début, mais en cours de boucle, *l'instruction break autorise des boucles à sorties multiples.

88 88 Exercices en langage C Certes, on peut objecter que ce sont là des possibilités qui sont contraires à l'esprit de la programmation structurée. Cependant, utilisées à bon escient, e les peuvent améliorer la concision et le temps d'exécution des programmes. Compte tenu de l'orientation du langage C, ilne nous a pas paru opportun de nous priver totalement de ces facilités. En définitive, ilnous arrivera souvent, au cours de l'analyse, de nous contenter de préciser la (ou les) condition(s) d'arrê t d'une itération et de reporter au niveau de la programmation même le choix des instructions à utiliser. On notera qu'en procédant ainsi un effort de réflexion logique peut rester nécessaire au moment de la rédaction du programme, laque le, dans ce cas, se trouve être plus qu'une simple traduction littérale! 4 - A propos des fonctions a) Comme nous l'avons déjà remarqué dans l'avant-propos, la norme ANSI accepte deux formes de définition de fonctions. Voici, par exemple, deux façons d'écrire l'en-tête d'une fonction fct recevant deux arguments de type int et charet renvoyant une valeur de type double : double fct (int x, char * p) double fct (x, p) int x ; char * p ; Ilne s'agit là que de simples différences de rédaction, sans aucune incidence sur le plan fonctionnel. Ici, nous avons systématiquement employé la premiè re forme (on la nomme parfois forme "moderne"), dans la mesure où e le a tendance à se généraliser et où, de plus, ils'agit de la seule forme acceptée par le C+ +. b) Les fonctions ont toujours été déclarées dans les fonctions les utilisant bien qu'a priori : - cela ne soit pas obligatoire pour les fonctions fournissant un résultat de type int, - cela ne soit pas obligatoire lorsqu'une fonction a été définie, dans le même source, avant d'ê tre utilisée. c) Dans les déclarations des fonctions, nous avons utilisé la forme prototype autorisée par le standard ANSI. Ce le-ci se révèle surtout fort précieuse lorsque l'on exploite les possibilités de compilation séparée et que l'on a donc affaire à plusieurs fichiers source différents. Certes, ce n'est pas le cas ici, mais, compte tenu de ce qu'e le est pratiquement acceptée de tous les compilateurs actuels et que, de plus, e le est est obligatoire en C+ +, ilnous a paru judicieux d'en faire une habitude.

89 I : VARIATIONS ALGORITH MIQUES SUR LES INSTRUCTIONS DE BASE Ce chapitre vous propose des problèmes ne faisant appelqu'aux notions de base du langage C, à savoir : - entrées-sorties conversationne les (getchar, scanf, gets, putchar, printf), - instructions de contrôle, - tableaux, - chaînes, - fonctions. I-1 Triangle de Pascal Enoncé Afficher un "triangle de Pascal" dont le nombre de lignes est fourni en donnée. Nous vous rappelons que les "cases" d'un teltriangle contiennent les valeurs des coefficients du binome C n,p (ou nombre de combinaisons de n éléments pris p à p). Cette valeur est placée dans la case correspondant à l'intersection de la ligne de rang n et la colonne de rang p (la numérotation commençant à 0). On évitera de calculer chaque terme séparément ;au contraire, on cherchera à exploiter la relation de récurrence :

90 90 Exercices en langage C C i,j = C i-1, j + C i-1,j-1 On limitera à 15 le nombre de lignes demandées par l'utilisateur et on respectera la présentation proposée dans l'exemple ci-dessous. Exemple combien de lignes voulez vous? 12 p n ANALYSE A priori, nous pourrions utiliser un tableau t à deux dimensions comportant 15x15 éléments et décider (arbitrairement) que le premier indice correspond au rang d'une ligne du triangle, le second à celui d'une colonne. Nous remplirions alors partie lement ce tableau avec les valeurs C i,j voulues (i varierait de 0 à n-1 si n représente le nombre de lignes demandées et, pour chaque valeur de i, j varierait de 0 à i). Pour exploiter la récurrence proposée, ilnous suffirait alors de procéder comme suit : - placer la valeur 1 en t(0,0) (ce qui constitue la premiè re ligne), - pour chaque ligne de rang i, à partir de i=1, procéder ainsi : *placer la valeur 1 en t(i,0) et t(i,i) (extrémités de la ligne de rang i), *pour j variant de 1 à i-1, faire : t(i,j) = t(i-1,j) + t(i-1,j-1)

91 I. Variations algorithmiques sur les instructions de base 91 En fait, ilest possible de n'utiliser qu'un tableau à une seule dimension, dans lequelon vient calculer successivement chacune des lignes du triangle (ilfaut alors, bien sûr, afficher chaque ligne dè s qu'e le a été déterminée). Supposons, en effet, qu'à un instant donné, nous disposions dans ce tableau t des i+1 valeurs de la ligne de rang i et voyons comment déterminer ce les de la ligne de rang i+1. Nous constatons que la récurrence proposée permet de définir la nouve le valeur d'un élément de t en fonction de son ancienne valeur et de l'ancienne valeur de l'élément précédent. Certes, si nous répétions une affectation de la forme : t(j) = t(j) + t(j-1) en faisant varier j de 1 à i-1, nous n'aboutirions pas au résultat escompté puisqu'alors la valeur de t(j) dépendrait de la nouve le valeur préalablement attribuée à t(j-1). Mais, ilest facile de montrer qu'en explorant la ligne de droite à gauche, c'est-à -dire en répétant l'affectation ci-dessus en faisant décroître j de i-1 à 0, le problème ne se pose plus. Voici finalement l'algorithme que nous utiliserons : Faire varier i de 0 à n-1. Pour chaque valeur de i : - répéter, en faisant décroître j de i-1 à 1 : t(j) = t(j) + t(j-1) - placer la valeur 1 dans t(i). Remarques : 1) Telque l'algorithme vient d'ê tre énoncé, nous constatons que pour i=0, j doit décroître de -1 à 1! Nous admettrons que cela signifie en fait qu'aucun traitement n'est à réaliser dans ce cas (ce qui est normalpuisque alors notre ligne est réduite à la seule valeur 1, laque le sera placée par l'affectation t(i)=1). Ilen va de même pour i=1, j devant alors décroître de 0 à 1. On notera qu'en langage C la boucle for permet de tenir compte de ces cas particuliers (le test de poursuite de boucle étant réalisé en début). Ce n'est toutefois pas là une rè gle généralisable à tous les langages. 2) Avec les précautions que nous venons d'évoquer, l'algorithme "s'initialise" de lui-même. Programme #include <stdio.h> #define NMAX 15 /* nombre maximal de lignes */ main() { int t [NMAX], /* tableau représentant une ligne du triangle */

92 92 Exercices en langage C nl, /* nombre de lignes souhaitées */ i, /* indice de la ligne courante */ j ; /* indice courant de colonne */ /* lecture nombre de lignes souhaitées et affichage titres */ printf ("combien de lignes voulez vous? ") ; scanf ("%d", &nl) ; if (nl > NMAX) nl = NMAX ; printf ("\n\n p ") ; for (i=0 ; i<nl ;i++) printf ("%5d", i) ; printf ("\n n\n") ; for (i=0 ; i<=nl ; i++) printf ("-----") ; printf ("\n") ; /* création et affichage de chaque ligne */ for (i=0 ; i<nl ;i++) { t[i] = 1 ; for (j=i-1 ; j>0 ; j--) t[j] = t[j-1] + t[j] ; printf ("%2d --", i) ; for (j=0 ; j<=i ; j++) printf ("%5d", t[j]) ; printf ("\n") ; Commentaires *En langage C, les indices d'un tableau commencent à 0. Ici, cette particularité s'avère intéressante puisque nos numéros de lignes ou de colonnes doivent aussi commencer à 0. *Plutôt que d'utiliser directement la constante 15 dans notre programme, nous avons préféré faire appelà l'instruction #define du préprocesseur pour définir un symbole NMAX possédant cette valeur. Ilest ainsi beaucoup plus facile, le cas échéant, de modifier cette valeur (puisqu'ilsuffit alors d'intervenir en un seulendroit du programme). Notez que nous n'aurions pas pu utiliser la déclaration de constante symbolique (const int NMAX = 15), car, dans ce cas, NMAX n'aurait pas été une "expression constante", et nous n'aurions pas pu l'utiliser comme dimension d'un tableau. *Ne pas oublier que t[nmax] réserve NMAX éléments (c'est-à -dire 15), dont les indices varient de 0 à 14.

93 I. Variations algorithmiques sur les instructions de base 93 *Si l'utilisateur demande un nombre de lignes supérieur à NMAX, le programme se contente de limiter cette demande à la valeur NMAX. DISCUSSION *Nous aurions pu tenir compte de la symétrie de chaque ligne par rapport à son centre ; quelques instructions supplémentaires nous auraient alors permis une légè re réduction du temps de calcul. *L'énoncé limitait à 15 le nombre de lignes de notre triangle. En effet, au-delà, il n'est généralement plus possible d'afficher toutes les valeurs sur une seule ligne d'écran. *Notre programme n'est pas protégé dans le cas où l'utilisateur fournit une réponse non numérique à la question posée. Dans ce cas, toutefois, la situation n'est pas trè s grave ;en effet, la valeur de nl est, certes, aléatoire mais, de toute façon, e le sera limitée à 15 par le programme. Si vous souhaitiez quand même traiter ce type d'anomalie, ilvous suffirait d'examiner le code de retour de la fonction scanf (ilfournit le nombre de valeurs convenablement lues) et de vérifier qu'ilest bien égalà 1. I-2 Crible d'eratosthène Ilexiste une méthode de détermination de nombres premiers connue sous le nom de "crible d'erastothène". E le permet d'obtenir tous les nombres premiers inférieurs à une valeur donnée n. La méthode (manue le) consiste à dresser une liste des nombres considérés (de 1 à n) et à y rayer tous les nombres multiples d'autres entiers (de tels nombres sont nécessairement non premiers). Plus précisément, on procè de ainsi : 1 - on raye le 1 (qui, par définition, n'est pas un nombre premier). 2 - on recherche, à partir du dernier nombre premier considéré (la premiè re fois, on convient qu'il s'agit du 1), le premier nombre non rayé (on peut montrer qu'ilest premier). Ildevient, à son tour, le dernier nombre premier considéré et on raye tous ses multiples. 3 - on répè te le point 2 jusqu'à ce que le nombre premier considéré soit supérieur à la racine carrée de n. On peut alors montrer que tous les nombres non premiers ont été rayés de la liste.

94 94 Exercices en langage C Enoncé Ecrire un programme basé sur cette méthode recherchant tous les nombres premiers compris entre 1 et n (la valeur de n étant fixée dans le programme) Exemple entre 1 et 1000, les nombres premiers sont : ANALYSE La méthode manue le suggè re d'utiliser un tableau. Toutefois, devons-nous, par analogie, y ranger les nombres entiers de 1 à n?en fait, cela ne serait guè re utile puisque alors chaque nombre serait égalà son rang dans le tableau (du moins, à une unité prè s, suivant les conventions que l'on adopterait pour l'indice du premier élément). En réalité, le bon déroulement de l'algorithme nous impose seulement d'ê tre en mesure de faire correspondre à chaque entier entre 1 et n, une information précisant, à chaque instant, s'ilest rayé ou non (cette information pouvant évoluer au fildu déroulement du programme). Ils'agit là tout nature lement d'une information de type "logique" (vrai ou faux). Comme ce type n'existe pas en tant que telen langage C, nous le simulerons à l'aide de deux constantes entiè res : VRAI de valeur 1, FAUX de valeur 0. Notez que le choix de la valeur 0 pour FAUX est imposé par la maniè re dont le langage

95 I. Variations algorithmiques sur les instructions de base 95 C considè re une expression numérique apparaissant dans une condition ;la valeur 1, par contre, pourrait être, sans inconvénient, remplacée par n'importe que le valeur non nu le. Notons raye un tel tableau et supposons que raye[i] correspond à l'entier i (ce qui, compte tenu des conventions du langage C, signifie que raye[0] est inutilisé). Notre algorithme nous impose de garder la trace du dernier nombre premier considéré. Nous le nommerons prem. La démarche manue le se transpose alors comme suit : *Initialisation : - mettre à FAUX tous les éléments du tableau raye, - mettre à FAUX le premier élément de raye, et faire : *Itération : prem = 1 - rechercher, à partir de prem, le premier nombre non encore rayé, c'est-à -dire incrémenter la valeur de prem jusqu'à ce que t[prem] soit FAUX (en toute rigueur, ilfaut se demander s'ilexiste encore un tel nombre dans notre tableau, et donc limiter l'incrémentation de prem à N). - rayer tous les multiples de prem, dans le cas où un telnombre a été trouvé. *L'itération proposée peut être répétée, indifféremment (les deux formulations étant équivalentes dè s que N est supérieur ou égalà 1) : - jusqu'à ce que la valeur de prem soit supérieure à la racine carrée de N, - ou tant quela valeur de prem est inférieure ou égale à la racine carrée de N. Programme #include <stdio.h> #define N 1000 /* plus grand entier à examiner */ #define VRAI 1 /* pour "simuler" des... */ #define FAUX 0 /*... valeurs logiques */ main() { int raye [N+1], /* tableau servant de crible */ prem, /* dernier nombre premier considéré */ na, /* compteur de nombres affichés */ i ; /* initialisations */ for (i=1 ; i<=n ; i++) /* mise à zéro du crible */

96 96 Exercices en langage C raye[i] = FAUX ; raye[1] = VRAI ; /* on raye le nombre 1 */ /* passage au crible */ prem = 1 ; while (prem*prem <= N) { while (raye[++prem] && prem<n ) { /* recherche premier nombre non rayé */ for (i=2*prem ; i<=n ; i+=prem) /* on raye tous ses multiples */ raye[i] = VRAI ; /* affichage résultats */ printf ("entre 1 et %d, les nombres premiers sont :\n", N) ; na = 0 ; for (i=1 ; i<=n ; i++) if (!raye[i] ) { printf ("%7d",i) ; na++ ; if ( na%10 == 0) printf ("\n") ; /* 10 nombres par ligne */ Commentaires *La recherche du premier nombre non encore rayé est réalisée par la seule instruction : while (raye[++prem] && prem<n) { Notez bien la pré-incrémentation de prem ;une post-incrémentation : while (t[prem++] && prem<n) { aurait conduit à une boucle infinie sur le premier nombre premier trouvé, c'est-à -dire 2 (du moins si N est supérieur ou égalà 2). Ilsuffirait toutefois d'incrémenter prem une fois avant d'entrer dans la boucle pour que cela fonctionne. *Nous avons conservé le garde-fou : prem < N

97 I. Variations algorithmiques sur les instructions de base 97 On pourrait toutefois démontrer que, dè s que N est supérieur ou égalà 2, on est toujours assuré de trouver au moins un nombre non rayé avant la fin du tableau (compte tenu de ce que l'on commence l'exploration avec un nombre inférieur ou égalà la racine carrée de N). *Nous avons prévu d'afficher nos nombres premiers, à raison de 10 par ligne, chaque nombre occupant 7 caractères. Pour ce faire, nous utilisons une variable nommée na nous permettant de comptabiliser le nombre de nombres affichés. A chaque fois que na est multiple de 10, nous provoquons un saut de ligne. DISCUSSION *Telqu'ilest proposé ici, le programme traite le cas n=1000. Pour le faire fonctionner avec d'autres valeurs, il est nécessaire d'intervenir au niveau du programme lui-même et de le recompiler. Si vous souhaitez que la valeur de n puisse être fournie en donnée, ilfaut lui fixer une valeur maximale, afin de prévoir la réservation du tableau correspondant. Notez toutefois que les possibilités de gestion dynamique du langage C offrent une solution plus agréable à ce problème de dimensions variables. Vous en trouverez certains exemples dans le chapitre consacré à la gestion dynamique. *Le tableau raye, ainsi que les variables prem et i, ont été déclarés de type int, ce qui, dans certaines implémentations, peut limiter à les valeurs qu'ilest ainsi possible d'examiner. On peut toujours faire mieux, en utilisant le type unsigned int, ou mieux le type long ou unsigned long. Toutefois, dans ce cas, on s'assurera que l'on n'est pas soumis à des contraintes sur la tai le des différents modules objets, sur la tai le de la pile ou, encore, tout simplement, sur la tai le des différents objets qu'ilest possible de manipuler. Iln'est pas rare, en effet, que l'on rencontre des limitations à 64 KO (c'est le cas, actue lement, des compilateurs Borland/Turbo C/C+ + utilisés dans l'environnement DOS). I-3 Lettres communes à deux mots (1) Enoncé Réaliser un programme qui affiche les lettres communes à deux mots fournis au clavier. On prévoira d'afficher plusieurs fois une lettre qui apparaît à plusieurs reprises dans chacun des deux mots.

98 98 Exercices en langage C On supposera que ces mots ne peuvent pas comporter plus de 26 caractères et on les lira à l'aide de la fonctions gets. Exemples donnez un premier mot : monsieur donnez un deuxième mot : bonjour la lettre o est commune aux deux mots la lettre n est commune aux deux mots la lettre u est commune aux deux mots la lettre r est commune aux deux mots donnez un premier mot : barbara donnez un deuxième mot : ravage la lettre a est commune aux deux mots la lettre r est commune aux deux mots la lettre a est commune aux deux mots ANALYSE L'énoncé nous impose d'utiliser gets, donc de représenter nos mots sous forme de chaînes de caractères (suites de caractères terminées par le caractère nul, noté en C : \0). Nous utiliserons à cet effet des tableaux de caractères de dimension 27 (pour 26 lettres maximum et un caractère de fin). La recherche des lettres communes aux deux mots peut se faire en comparant chacun des caractères de la premiè re chaîne à chacun des caractères de la seconde. Cela nous conduit nature lement à l'utilisation de deux boucles avec compteur (instructions for) imbriquées. Toutefois, nous devons tenir compte de ce qu'une même lettre peut figurer plusieurs fois dans un même mot. Dans ces conditions, ilfaut éviter : *qu'une même lettre du premier mot ne puisse être trouvée en deux endroits différents du second. Par exemple, avec : et monsieur bonjour

99 I. Variations algorithmiques sur les instructions de base 99 aprè s avoir trouvé que le o de monsieur figurait en position 2 de bonjour, il faut éviter de signaler une nouve le coïncidence entre ce même o de monsieur et le second o de bonjour. Ilest donc nécessaire d'interrompre la comparaison entre une lettre du premier mot avec toutes ce les du second mot, dè s qu'une coïncidence a été détectée. *qu'une même lettre du second mot ne puisse coïncider avec deux lettres différentes du second. Par exemple, avec (attention à l'ordre des mots) : et bonjour monsieur il faut éviter de trouver une coïncidence entre le premier o de bonjour et l'unique o de monsieur et une autre coïncidence entre le second o de bonjour et le même o de monsieur. Pour ce faire, une démarche (parmi d'autres) consiste à éliminer dans le second mot la lettre ayant fait l'objet d'une coïncidence. Plus précisément, ilsuffit de remplacer une te le lettre par un caractère dont on est sûr qu'iln'apparaîtra pas dans un mot. Ici, nous avons choisi l'espace puisque nous sommes censés travai ler avec des mots. Programme #include <stdio.h> #include <string.h> #define LMAX 26 main() { char mot1 [LMAX+1], /* premier mot */ mot2 [LMAX+1] ; /* deuxième mot */ int i, j ; /* lecture des deux mots */ printf ("donnez un premier mot : ") ; gets (mot1) ; printf ("donnez un deuxième mot : ") ; gets (mot2) ; /* comparaison */ for (i=0 ; i<strlen(mot1) ; i++) for (j=0 ; j<strlen(mot2) ; j++) if (mot1[i] == mot2[j])

100 100 Exercices en langage C { printf ("la lettre %c est commune aux deux mots\n", mot1[i]) ; mot2[j] = ' ' ; break ; Commentaires *Nous avons utilisé le symbole LMAX pour représenter la longueur maximale d'un mot. Notez bien que les tableaux mot1 et mot2 ont dû ê tre prévus de dimension LMAX+1, afin de tenir compte de la présence du caractère de fin de chaîne. *Nous aurions pu utiliser, à la place de la seconde boucle avec compteur (en j), une boucle tant que (while). Certes, la programmation eût été plus structurée mais, néanmoins, moins concise. DISCUSSION Ce programme n'est pas protégé contre des réponses de plus de 26 caractères. Dans ce cas, en effet, les caractères superflus iront écraser les données se trouvant au-delà de l'un des tableaux mot1 ou mot2. Les conséquences peuvent être assez variées (vous pouvez expérimenter le présent programme dans diverses situations et tenter d'expliquer les comportements observés). Ilexiste différentes façons d'éviter ce risque. Citons, par exemple : -lire (toujours par gets), une chaîne comportant un nombre de caractères suffisamment élevé pour que l'utilisateur ne risque pas (trop!) d'en fournir plus. On pourrait choisir, par exemple 80 ou 128 caractères (dans certaines implémentations, iln'est jamais possible de taper des lignes de plus de 128 caractères). -limiter automatiquement la longueur de la chaîne lue, en utilisantla fonction fgets ;par exemple, avec fgets (mot1, LMAX, stdin), on limite à LMAXle nombre de caractères lus sur stdin. - utiliser, dans certaines implémentations (Turbo/Borland C/C+ +, C/Quick C Microsoft), une fonction (non portable!) nommée cgets.

101 I. Variations algorithmiques sur les instructions de base 101 I-4 Lettres communes à deux mots (2) Enoncé Réaliser un programme qui affiche les lettres communes à deux mots fournis en donnée. Cette fois, on n'imposera pas de limite à la tai le des mots fournis par l'utilisateur, mais on ne prendra en compte que les 26 premiers caractères. Quelque soit le nombre de caractères effectivement frappés, l'utilisateur devra toujours valider sa réponse par la frappe de la touche return. Là encore, on prévoira d'afficher plusieurs fois une lettre qui apparaît à plusieurs reprises dans chacun des mots. On s'astreindra à n'utiliser pour la lecture au clavier que la seule fonction getchar. De plus, on réalisera une fonction destinée à lire un mot dans un tableau qu'on lui transmettra en argument ;e le fournira, en retour, la longueur effective du mot ainsi lu. Exemples Voir ceux de l'exercice précédent ANALYSE L'énoncé nous impose l'emploi de getchar, ce qui signifie que chacun des deux mots devra ê tre lu caractère par caractère. Dans ces conditions, nous pouvons choisir de représenter nos mots : - soit sous forme d'une chaîne de caractères. Ilnous faudra alors introduire nous-mêmes le caractère de fin de chaîne (\0), ce que faisait automatiquement gets. - soit sous forme d'une simple suite de caractères (c'est-à -dire sans ce caractère de fin). Dans ce cas, ilnous faudra alors prévoir d'en déterminer la "longueur". Comme l'énoncé nous impose que la fonction de lecture d'un mot en restitue la longueur, nous choisirons la seconde solution. La lecture d'un mot consiste donc à lire des caractères au clavier jusqu'à ce que l'on rencontre une validation (\n) ou que l'on ait obtenu 26 caractères ;de plus, dans le cas où l'on a obtenu 26 caractères, il faut poursuivre la lecture de caractères au clavier, sans les prendre en compte, jusqu'à ce que l'on rencontre une validation.

102 102 Exercices en langage C Programme #include <stdio.h> #define LMAX 26 /* longueur maximale d'un mot */ main() { int lire(char []) ; /* déclaration (prototype) fonction lecture d'un mot */ char mot1 [LMAX], /* premier mot (sans '\0') */ mot2 [LMAX] ; /* deuxième mot (sans '\0') */ int l1, /* longueur premier mot */ l2, /* longueur deuxième mot */ i, j ; /* lecture des deux mots */ printf ("donnez un premier mot : ") ; l1 = lire (mot1) ; printf ("donnez un deuxième mot : ") ; l2 = lire (mot2) ; /* comparaison */ for (i=0 ; i<l1 ; i++) for (j=0 ; j<l2 ; j++) if (mot1[i] == mot2[j]) { printf ("la lettre %c est commune aux deux mots\n", mot1[i]) ; mot2[j] = ' ' ; break ; /* Fonction de lecture d'un mot */ int lire (char mot [LMAX]) { int i ; /* rang du prochain caractère à lire */ char c ; i = 0 ; while ( (c=getchar())!= '\n' && i<=lmax ) mot[i++] = c ; /* ici, soit on a lu \n, soit on a lu LMAX caractères */ /* dans tous les cas, c contient le premier caractère */ /* non pris en compte */ if (c!= '\n')

103 I. Variations algorithmiques sur les instructions de base 103 while (getchar()!= '\n') { /* recherche '\n' */ return(i) ; Commentaires *Là encore, nous avons utilisé le symbole LMAX pour représenter la tai le maximale d'un mot. Par contre, cette fois, la dimension des tableaux mot1 et mot2 est égale à LMAX (et non plus LMAX+ 1), puisque nous n'avons pas à y introduire le caractère supplémentaire de fin de chaîne. *En ce qui concerne la fonction (nommée lire) de lecture d'un mot au clavier, vous constatez que nous l'avons déclarée dans le programme principal(main), bien que cela soit facultatif, dans la mesure où e le fournit un résultat de type int (en effet, toute fonction qui n'est pas explicitement déclarée est supposée produire un résultat de type int). D'autre part, comme nous l'avons expliqué dans l'introduction de cette seconde partie, nous avons utilisé, dans cette déclaration, la forme "prototype" autorisée par la norme ANSI (ce prototype assure les contrôles de types d'arguments et met en place d'éventue les conversions). Par ai leurs, l'en-tête de notre fonction lire a été écrit suivant la forme "moderne". La norme ANSI aurait autorisé le remplacement de note en-tête par : int lire (mot) char mot [LMAX] ; *Le tableau de caractères représentant l'unique argument de lire doit obligatoirement être transmis par adresse puisque cette fonction doit être en mesure d'en modifier le contenu. N'oubliez pas cependant qu'en langage C un nom de tableau est interprété (par le compilateur) comme un pointeur (constant) sur son premier élément. C'est ce qui justifie la présence, dans les appels à la fonction lire, de mot1 ou mot2, et non de &mot1 ou & mot2. *La déclaration de l'argument de lire, dans son en-tête : char mot [LMAX] aurait pu également s'écrire : char mot [] ou même : char * mot

104 104 Exercices en langage C Dans le premier cas, on continue de spécifier (au lecteur du programme plus qu'au compilateur) que mot est un tableau de caractères mais que sa dimension n'a pas besoin d'ê tre connue au sein de lire. Dans le second cas, on exprime plus clairement que, finalement, l'argument reçu par lire n'est rien d'autre qu'un pointeur sur des caractères. Ces formulations sont totalement équivalentes pour le compilateur et, dans tous les cas (même le dernier), ilreste possible de faire appelau "formalisme" tableau au sein de lire, en utilisant une notation te le que : mot [i++] D'ai leurs, pour le compilateur, cette derniè re est équivalente à : * (mot + i++) Les même reflexions s'appliquent à l'écriture du prototype de lire. DISCUSSION *Le symbole LMAX est défini pour l'ensemble du source contenant, ici, le programme principal et la fonction lire. Mais, si cette fonction devait être compilée séparément du reste, ilserait alors nécessaire de faire figurer la définition (#define) dans les deux sources, ce qui comporte un risque d'erreur. Dans une situation "rée le", on pourrait avoir intérê t à faire appelà l'une des démarches suivantes : - transmettre la valeur de LMAX en argument de la fonction lire. - regrouper les définitions de symboles communs à plusieurs sources dans un fichier séparé que l'on appe le par #include dans chacun des sources concernés. *Contrairement au programme de l'exercice précédent, celui-ci se trouve protégé de réponses trop longues de la part de l'utilisateur. I-5 Comptage de lettres

105 Enoncé I. Variations algorithmiques sur les instructions de base 105 Réaliser un programme qui compte le nombre de chacune des lettres de l'alphabet d'un texte entré au clavier. Pour simplifier, on ne tiendra compte que des minuscules, mais on comptera le nombre des caractères non reconnus comme tels (quels qu'ils soient : majuscules, ponctuation, chiffres,...). Le programme devra accepter un nombre quelconque de lignes. L'utilisateur tapera une "ligne vide" pour signaler qu'ila terminé la frappe de son texte (ce qui revient à dire qu'ilfrappera donc deux fois de suite la touche return, aprè s la frappe de sa derniè re ligne). On supposera que les lignes frappées au clavier ne peuvent jamais dépasser 127 caractères. Par ai leurs, on fera l'hypothèse (peu restrictive en pratique) que les "codes" des lettres minuscules a à z sont consécutifs (ce qui est le cas, notamment, avec le code ASCII). Exemple donnez votre texte, en le terminant par une ligne vide je me figure ce zouave qui joue du xylophone en buvant du whisky votre texte comporte 63 caractères dont : 2 fois la lettre a 1 fois la lettre b 1 fois la lettre c 2 fois la lettre d 8 fois la lettre e 1 fois la lettre f fois la lettre u 2 fois la lettre v 1 fois la lettre w 1 fois la lettre x 2 fois la lettre y 1 fois la lettre z et 11 autres caractères

106 106 Exercices en langage C ANALYSE Ilnous faut donc utiliser un tableau de 26 entiers permettant de comptabiliser le nombre de fois où l'on a rencontré chacune des 26 lettres (minuscules) de l'alphabet. Nous le nommerons compte. Nous utiliserons également un compteur nommé ntot pour le nombre total de caractères et un autre nommé nautres pour les caractères différents d'une lettre minuscule. En ce qui concerne le comptage proprement dit, ilnous faut examiner chacune des lettres du texte. Pour ce faire, ilexiste (au moins) deux démarches possibles : - effectuer une répétition du traitement d'un caractère, - effectuer une répétition du traitement d'une ligne, lui-même constitué de la répétition du traitement de chacun des caractères qu'e le contient. a) La premiè re démarche aboutit à une simple boucle avec compteur. E le ne demande d'accéder qu'à un seulcaractère à la fois (par exemple, par getchar). E le nécessite, par contre, l'élimination des caractères de fin de ligne \n (qui sont transmis comme les autres par getchar), puisqu'ils ne font pas vraiment partie du texte. De surcroît, la détection de la fin du texte oblige à conserver en permanence le "caractère précédent". Lorsque ce caractère, ainsi que le caractère courant, sont égaux à \n, c'est que l'on a atteint la fin du texte. Ilsuffit d'initialiser artificie lement ce caractère précédent à une valeur quelconque (autre que \n) pour éviter de devoir effectuer un traitement particulier pour le premier caractère. b) La seconde démarche aboutit à deux boucles imbriquées. E le permet de lire directement chaque ligne par gets. E le rè gle de maniè re nature le les problèmes de fin de ligne et de fin de texte. Nous vous proposons ici deux programmes, correspondant à chacune de ces deux démarches. Programme basé sur la répétition du traitement d'un caractère #include <stdio.h> main() { char c, /* pour lire un caractère frappé au clavier */ cprec ; /* caractère précédent */ int compte[26] ; /* pour compter les différentes lettres */ int numl, /* rang lettre courante dans l'alphabet */ ntot, /* nombre de caractères du texte */ nautres, /* nb caractères autres qu'une lettre minuscule */ i ; /* initialisations */

107 I. Variations algorithmiques sur les instructions de base 107 cprec = ' ' ; ntot = 0 ; nautres = 0 ; for (i=0 ; i<26 ; i++) compte[i]=0 ; /* lecture texte et comptages */ printf ("donnez votre texte, en le terminant par une ligne vide\n") ; while ( (c=getchar())!= '\n' cprec!= '\n' ) { if (c!= '\n') { numl = c - 'a' ; /* on donne le rang 0 à la lettre 'a' */ if (numl >=0 && numl < 26) compte[numl]++ ; else nautres++ ; ntot++ ; cprec = c ; /* affichage résultats */ printf ("\n\nvotre texte comporte %d caractères dont :\n", ntot) ; for (i=0; i<26 ; i++) printf ("%d fois la lettre %c\n", compte[i], 'a'+i) ; printf ("\net %d autres caractères\n", nautres) ; Commentaires *L'expression : c - 'a' permet d'obtenir le "rang" dans l'alphabet du caractère contenu dans c. N'oubliez pas que le langage C considè re le type char comme numérique. Plus précisément, dans le cas présent, les valeurs de c et de 'a' sont converties en int (ce qui fournit la valeur numérique de leur code) avant que ne soit évaluée l'expression c-'a'. Comme nous avons supposé que les codes des minuscules sont consécutifs, nous obtenons bien le résultat escompté. *Les instructions : if (c!= '\n') { numl = c - 'a' ; if (numl >=0 && numl < 26) compte[numl]++ ; else nautres++ ; ntot++ ;

108 108 Exercices en langage C cprec = c; pourraient se condenser en : if ( (cprec=c)!= '\n') { numl = c - 'a' ; if (numl >=0 && numl < 26) compte[numl]++ ; else nautres++ ; ntot++ ; Programme basé sur la répétition du traitement d'une ligne #include <stdio.h> #include <string.h> main() { char ligne[128] ; /* pour lire une ligne frappée au clavier */ int compte[26] ; /* pour compter les différentes lettres */ int numl, /* rang lettre courante dans l'alphabet */ ntot, /* nombre de caractères du texte */ nautres, /* nombre de caractères autres qu'une lettre minuscule */ i ; /* initialisations */ ntot = 0 ; nautres = 0 ; for (i=0 ; i<26 ; i++) compte[i]=0 ; /* lecture texte et comptages */ printf ("donnez votre texte, en le terminant par une ligne vide\n") ; do { gets(ligne) ; for (i=0 ; i<strlen(ligne) ; i++, ntot++) { numl = ligne[i] - 'a' ;/* on donne le rang 0 à la lettre 'a' */ if (numl >=0 && numl < 26) compte[numl]++ ; else nautres++ ; while (strlen(ligne)) ; /* affichage résultats */

109 I. Variations algorithmiques sur les instructions de base 109 printf ("\n\nvotre texte comporte %d caractères dont :\n", ntot) ; for (i=0; i<26 ; i++) printf ("%d fois la lettre %c\n", compte[i], 'a'+i) ; printf ("\net %d autres caractères\n", nautres) ; DISCUSSION *Aucun des deux programmes proposés ne pose de problème de protection vis-à -vis des réponses fournies par l'utilisateur. I-6 Comptage de mots Enoncé Ecrire un programme permettant de compter le nombre de mots contenus dans un texte fourni au clavier. Le texte pourra comporter plusieurs lignes et l'utilisateur tapera une ligne "vide" pour signaler qu'ilen a terminé la frappe (ce qui revient à dire qu'ilfrappera deux fois de suite la touche return aprè s avoir fourni la derniè re ligne). On admettra que deux mots sont toujours séparés par un ou plusieurs des caractères suivants : - fin de ligne - espace - ponctuation : :., ;?! - parenthèses : ( ) - gui lemets : " - apostrophe : ' On admettra également, pour simplifier, qu'aucun mot ne peut être commencé sur une ligne et se poursuivre sur la suivante. On prévoira une fonction permettant de décider si un caractère donné transmis en argument est un des séparateurs mentionnés ci-dessus. E le fournira la valeur 1 lorsque le caractère est un séparateur et la valeur 0 dans le cas contraire.

110 110 Exercices en langage C Exemple donnez votre texte, en le terminant par une ligne vide Le langage C a été conçu en 1972 par Denis Ritchie avec un objectif très précis : écrire un "système d'exploitation" (UNIX). A cet effet, il s'est inspiré du langage B (créé par K. Thompson) qu'il a haussé au niveau de langage évolué, notamment en l'enrichissant de structures et de types, et ceci tout en lui conservant ses aptitudes de programmation proche de la machine. votre texte comporte 68 mots ANALYSE Comme dans l'exercice précédent, ilexiste (au moins) deux démarches possibles : - effectuer une répétition du traitement d'un caractère, - effectuer une répétition du traitement d'une ligne, lui-même constitué de la répétition du traitement de chacun des caractères qu'e le contient. La premiè re démarche aboutit à une simple boucle avec compteur. E le demande simplement d'accéder à un seul caractère (par exemple par getchar). La seconde démarche aboutit à deux boucles imbriquées. E le demande d'effectuer une lecture ligne par ligne (par exemple par gets). Là encore, nous examinerons les deux démarches et nous proposerons un programme correspondant à chacune d'entre e les. Dans les deux démarches, tous les caractères séparateurs jouent le même rôle, à condition d'y inclure \n (si l'on travai le avec getchar) ou \0 (si l'on travai le avec gets). On peut alors dire que l'on a progressé d'un mot dans le texte, chaque fois que l'on a réalisé la séquence suivante : - recherche du premier caractère différent d'un séparateur, - recherche du premier caractère égalà un séparateur.

111 I. Variations algorithmiques sur les instructions de base 111 On pourrait répéter successivement ces deux opérations, tant que que l'on n'est pas arrivé à la fin du texte. En fait, ilest plus simple d'utiliser un "indicateur logique" (nous l'appe lerons mot_en_cours) et d'effectuer pour chaque caractèrele traitement suivant : - sile caractère est un séparateur et si mot_en_cours est vrai, augmenter de un le compteur de mots et remettre mot_en_cours à faux. - si le caractère n'est pas un séparateur et si mot_en_cours est faux, mettre mot_en_cours à vrai. Quant à la condition d'arrê t, e le s'exprime différemment suivant la démarche adoptée : - deux caractères consécutifs égaux à \n pour la premiè re, ce qui impose de conserver en permanence la valeur du "caractère précédent" ;ce dernier sera initialisé à une valeur quelconque différente de \n pour éviter un traitement particulier du premier caractère du texte. -ligne vide pour la seconde. Programme basé sur la répétition du traitement d'un caractère #include <stdio.h> #define VRAI 1 /* pour "simuler" des... */ #define FAUX 0 /*... valeurs logiques */ main() { int sep(char) ; /* prototype fonction test "caractère séparateur?" */ char c, /* pour lire un caractère frappé au clavier */ cprec ; /* caractère précédent */ int nmots, /* compteur du nombre de mots */ fin_texte, /* indicateurs logiques : - fin texte atteinte */ mot_en_cours ; /* - mot trouvé */ cprec = ' ' ; fin_texte = FAUX ; mot_en_cours = FAUX ; nmots = 0 ; printf ("donnez votre texte, en le terminant par une ligne vide\n") ; while (!fin_texte) { if ( sep(c=getchar()) ) { if (mot_en_cours) { nmots++ ; mot_en_cours = FAUX ;

112 112 Exercices en langage C else mot_en_cours = VRAI ; if ( c=='\n' && cprec=='\n') fin_texte = VRAI ; cprec = c ; printf ("\n\nvotre texte comporte %d mots :\n", nmots) ; /*******************************************/ /* fonction d'examen d'un caractère */ /*******************************************/ int sep (char c) { char sep[12] = {'\n', /* fin de ligne */ ' ', /* espace */ ',', ';', ':', '.', '?', '!', /* ponctuation */ '(', ')', /* parenthèses */ '"', '\'' ; /* guillemets, apostrophe*/ int nsep=12, /* nombre de séparateurs */ i ; i = 0 ; while ( c!=sep[i] && i++<nsep-1 ) ; if (i == nsep) return (0) ; else return (1) ; Commentaires *Nous avons introduit une variable "logique" nommée fin_texte qui nous facilite la détection de la fin du texte. Nous aurions pu nous en passer en introduisant une instruction break au sein d'une boucle do... while {1(boucle infinie). *Dans le traitement de chaque caractère, nous n'avons pas respecté "à la lettre" l'algorithme proposé lors de l'analyse. En effet, nous exécutons l'instruction : mot_en_cours = VRAI même si l'indicateur mot_en_cours a déjà la valeur VRAI ;cela nous évite un test supplémentaire, sans modifier le comportement du programme (puisque la modification ainsi apportée consiste à mettre à VRAI l'indicateur alors qu'ily est déjà).

113 I. Variations algorithmiques sur les instructions de base 113 *Dans la fonction sep, la seule instruction : while ( c!=sep[i] && i++<nsep-1 ) ; permet de savoir si le caractère c est un séparateur. En effet, ilne faut pas oublier que l'opérateur & & n'évalue son second opérande que lorsque cela est nécessaire. Autrement dit, si la premiè re condition est fausse (c est donc égalà un séparateur), l'expression i++<nsep-1 n'est pas évaluée et i n'est donc pas incrémentée. Si, par contre, cette premiè re condition est vérifiée alors qu'on a exploré la totalité des séparateurs (i=11), la seconde condition est évaluée et e le est trouvée fausse, mais en même temps, i se trouve incrémentée (à 12). En définitive, on voit qu'à la fin de cette instruction, lorsque i vaut 12, cela signifie que c ne figure pas dans la liste des séparateurs. Programme basé sur la répétition du traitement d'une ligne #include <stdio.h> #include <string.h> #define VRAI 1 /* pour "simuler" des... */ #define FAUX 0 /*... valeurs logiques */ main() { int sep(char) ; /* prototype fonction test "caractère séparateur?" */ char ligne[128] ; /* pour lire une ligne frappée au clavier */ int nmots, /* compteur du nombre de mots */ mot_en_cours, /* indicateur logique : mot trouvé */ i ; nmots = 0 ; mot_en_cours = FAUX ; printf ("donnez votre texte, en le terminant par une ligne vide\n") ; do { gets(ligne) ; for (i=0 ; i<=strlen(ligne) ; i++) /* on traite aussi le '\0' */ if ( sep(ligne[i]) ) { if (mot_en_cours) { nmots++ ; mot_en_cours = FAUX ; else mot_en_cours = VRAI ; while (strlen(ligne)) ;

114 114 Exercices en langage C printf ("\n\nvotre texte comporte %d mots :\n", nmots) ; /********************************************/ /* fonction d'examen d'un caractère */ /********************************************/ int sep (char c) { char sep[12] = {'\0', /* fin de ligne (chaîne) */ ' ', /* espace */ ',', ';', ':', '.', '?', '!', /* ponctuation */ '(', ')', /* parenthèses */ '"', '\'' ; /* guillemets, apostrophe*/ int nsep=12, /* nombre de séparateurs */ i ; i = 0 ; while ( c!=sep[i] && i++<nsep-1 ) ; if (i == nsep) return (0) ; else return (1) ; Commentaires Nous avons dû : - d'une part, au sein de la fonction sep, remplacer le séparateur \n par \0, - d'autre part, dans la boucle de traitement des caractères d'une ligne, traiter comme les autres ce caractère de fin de ligne (c'est-à -dire faire varier i de 0 à strlen(ligne) et non strlen(ligne)-1), afin d'éviter de compter pour un seulmot le dernier mot d'une ligne (non suivi d'un séparateur) et le premier de la suivante. Discussion *En ce qui concerne la fonction d'examen d'un caractère (nommée sep), vous constatez (dans les deux versions proposées) que nous l'avons déclarée dans le programme principal(main), bien que cela soit facultatif, dans la mesure où e le fournit un résultat de type int. *Aucun des deux programmes proposés ne pose de problème de protection vis-à -vis des réponses fournies par l'utilisateur.

115 II : UTILISATION DE STRUCTURES Le chapitre I vous a proposé des exercices faisant appel aux instructions de base du langage C. Les exercices de ce chapitre font intervenir, en plus, la notion de structure sous des formes diverses (en particulier les tableaux de structures et leur initialisation). II-1 Signe du zodiaque Enoncé Afficher le signe du zodiaque correspondant à une date de naissance fournie en donnée, sous la forme : jour mois Les deux informations seront séparées par au moins un espace. La premiè re sera fournie sous forme numérique, tandis que la seconde le sera sous forme d'une chaîne de caractères. Nous vous rappelons que les périodes correspondant à chaque signe sont les suivantes : Capricorne Verseau Poisson 23 décembre - 19 janvier 20 janvier - 19 février 20 février - 20 mars

116 116 Exercices en langage C Bélier 21 mars - 19 avril Taureau 20 avril - 20 mai Gémeau 21 mai - 20 juin Cancer 21 juin - 21 juillet Lion 22 juillet - 22 août Vierge 23 août - 22 septembre Balance 23 septembre - 22 octobre Scorpion 23 octobre - 21 novembre Sagittaire 22 novembre - 22 décembre Exemples donnez votre jour et votre mois (sans accent) de naissance? 11 july *** erreur de nom de mois *** donnez votre jour et votre mois de naissance? 16 janvier vous êtes né sous le signe suivant : Capricorne ANALYSE Le programme doit être en mesure d'établir une correspondance entre le nom d'un signe et les deux dates limites correspondantes. On peut déjà noter que la date de fin d'un signe est la vei le de ce le de début du suivant. Nous nous contenterons donc de ne conserver qu'une seule de ces deux informations, par exemple la date de fin. La correspondance souhaitée peut être réalisée : - par plusieurs tableaux (jour, mois, signe) reliés par une valeur commune d'indice. - par un seultableau dans lequelchaque élément est une structure comportant un numéro de jour, un nom de mois et un nom de signe. Nous choisirons la seconde solution car e le permet de mieux mettre en évidence la correspondance entre les informations, au moment de l'initialisation au sein du programme. La recherche du signe correspondant à une date donnée se fait alors de la maniè re suivante : - On cherche tout d'abord l'élément (nous le nommerons x) appartenant à notre tableau de structures, dont le nom de mois correspond à celui proposé en donnée. S'iln'existe pas, on le signale par un message approprié.

117 II. Utilisation de structures On regarde ensuite si le numéro du jour proposé est inférieur ou égalà celui de l'élément x. Dans l'affirmative, on peut en conclure que la date proposée est antérieure à la date de fin du signe figurant dans l'élément x, ce qui fournit la réponse voulue. Dans le cas contraire, on en conclut que la date proposée est postérieure à la date de début du signe figurant dans l'élément x ;il suffit donc d'examiner l'élément suivant pour obtenir la réponse voulue. Toutefois, si x est le dernier élément de notre tableau, ilfaudra considérer que son suivant est en fait le premier élément du tableau. On remarquera que l'algorithme proposé fonctionne effectivement parce que chacun des 12 mois de l'année ne comporte qu'un seulchangement de signe. Si cela n'avait pas été le cas, ilaurait fa lu "encadrer" la date proposée par deux dates d'éléments consécutifs de notre tableau. Programme #include <stdio.h> #include <conio.h> #include <string.h> main() { struct s_date { int jour ; char mois [10] ; char signe [11] ; ; struct s_date date [12] = { 23, "decembre", "Sagittaire", 20, "janvier", "Capricorne", 20, "fevrier", "Verseau", 21, "mars", "Poisson", 20, "avril", "Bélier", 21, "mai", "Taureau", 21, "juin", "Gémeau", 22, "juillet", "Cancer", 23, "aout", "Lion", 23, "septembre", "Vierge", 23, "octobre", "Balance", 22, "novembre", "Scorpion" ; int jour_n ; /* jour de naissance */ char mois_n [10] ; /* mois de naissance */ int nbv, i ;

118 118 Exercices en langage C /* lecture date de naissance */ printf ("donnez votre jour et votre mois de naissance?\n") ; scanf ("%d %s", &jour_n, mois_n) ; /* recherche et affichage du signe correspondant */ i = 0 ; while ( stricmp(date[i].mois, mois_n) && i++<11 ) { if (i<12) { printf ("vous êtes né sous le signe suivant : ") ; if (jour_n >= date[i].jour) i = (i+1)%12 ; printf ("%s", date[i].signe) ; else printf ("*** erreur de nom de mois ***") ; Commentaires *Nous avons défini ici un modè le de structure nommé s_date, dans lequelnous trouvons un numéro de jour, un nom de mois et le signe correspondant. Nous avons prévu 10 caractères pour le nom de mois, ce qui autorise des chaînes de longueur inférieure ou égale à 9 (compte tenu du \0 de fin) ;de même, nous avons prévu 11 caractères pour le signe. Le tableau nommé date est un tableau de 12 éléments ayant chacun le type s_date. Nous l'avons initialisé dans sa déclaration, ce qui permet de mettre facilement en para lèle chaque signe et sa date de fin. *En ce qui concerne la lecture de la date au clavier, nous n'avons pas prévu, ici, de protection vis-à -vis d'éventue les erreurs de frappe de l'utilisateur (cela n'était pas demandé par l'énoncé). *Rappelons que la fonction stricmp compare, sans tenir compte de la distinction majuscules/minuscules, les deux chaînes dont on lui fournit l'adresse en argument. E le restitue une valeur non nu le (qu'on peut interpréter comme vrai) lorsque les deux chaînes sont différentes et une valeur nu le (faux) lorsqu'e les sont égales. *La recherche du nom de mois est réalisée par la seule instruction : while ( stricmp(date[i].mois, mois_n) && i++<11 ) { Ce le-ci possède un double avantage ;tout d'abord, celui de la concision ;ensuite, celui de nous permettre de savoir directement si la recherche a été fructueuse ou non.

119 II. Utilisation de structures 119 En effet, il ne faut pas oublier que l'opérateur & & n'évalue son second opérande que lorsque cela est nécessaire. Autrement dit, si la premiè re condition est fausse (ily a donc égalité des deux chaînes), l'expression i++<11 n'est pas évaluée et i n'est donc pas incrémentée. La valeur de i désigne alors l'élément voulu. Si, par contre, cette premiè re condition est vérifiée (iln'y a donc pas égalité des deux chaînes) alors qu'on est arrivé en fin de table (i=11), la seconde condition est évaluée et e le est trouvée fausse, mais en même temps i se trouve incrémentée (à 12). En définitive, on voit que, à la fin de cette instruction, lorsque i vaut 12, cela signifie que l'élément cherché ne figure pas dans la table. Dans le cas contraire (i<12), i désigne l'élément cherché. Bien entendu, cette "recherche en table" pouvait se programmer de beaucoup d'autres maniè res. Par exemple, nous aurions pu écrire : while ( stricmp(date[i].mois, mois_n) && i<11 ) i++ ; Toutefois, cette instruction n'est pas équivalente à la précédente. En effet, lorsque i vaut 11, cela peut signifier : - soit que l'élément cherché est en position 11 (premier test satisfait), - soit que l'élément cherché ne figure pas dans la table (second test satisfait). Pour trancher, ilest donc nécessaire, dans ce cas, d'effectuer une comparaison supplémentaire. Notez que, par contre, une instruction te le que : while ( stricmp(date[i].mois, mois_n) && i++ <= 11) { serait quelque peu erronée. En effet, dans le cas où l'élément cherché ne figurerait pas dans le tableau, on serait amené à évaluer l'expression : date[i].mois avec une valeur i égale à 12, c'est-à -dire désignant un élément situé en dehors du tableau. Certes, en général, cela ne serait guè re visible dans le comportement du programme, dans la mesure où ilest bien peu probable que cette valeur soit égale au nom de mois voulu... *Notez l'emploi de l'opérateur arithmétique % qui permet de régler le problème du signe suivant le dernier signe du tableau. DISCUSSION *Telqu'ila été prévu, notre programme accepte des noms de mois écrits en minuscules ou en majuscules mais sans accent. Dans un programme réel, ilserait souhaitable de faire preuve de plus de tolérance.

120 120 Exercices en langage C *Notre recherche du nom de mois a été réalisée ici par un algorithme dit de recherche séquentie le en table (algorithme qui, comme nous l'avons vu, peut se programmer en C à l'aide d'une seule instruction). D'autres algorithmes plus rapides existent, en particulier celui dit de recherche dichotomique. L'exercice IV-5 vous en proposera un exemple. II-2 Codage morse Enoncé Ecrire un programme affichant le codage en morse d'un texte fourni au clavier et ne dépassant pas une "ligne" de 127 caractères. Les caractères susceptibles d'ê tre codés en morse sont : -les 26 lettres de l'alphabet (supposées tapées en majuscules), -les 10 chiffres de 0 à 9, -le point, Si le texte contient d'autres caractères que ceux-ci, le programme affichera simplement des points d'interrogation à la place du code morse. Tableau des codes morses Exemple A.- B -... C -.-. D -.. E. F..-. G --. H... I.. J.--- K -.- L.-.. M -- N -. O --- P.--. Q --.- R.-. S... T - U..- V...- W.-- X -..- Y -.-- Z donnez votre message (1 ligne maxi) : LE LANGAGE C, CONCU EN 1972, EST L'OEUVRE DE DENIS RITCHIE. voici la traduction de votre message

121 II. Utilisation de structures ?????? ?????? -.-.???????????? ??????. -.?????? ????????????.... -??????.-..?????? ?????? -...?????? ?????? ANALYSE Le programme doit donc ê tre en mesure d'établir une correspondance entre un caractère et son code morse. Là encore, nous pourrions utiliser deux tableaux reliés par une valeur commune d'un indice. Mais l'emploi d'un tableau de structures permet de mieux mettre en évidence la correspondance entre les informations, lors de l'initialisation. Chaque élément (structure) du tableau contiendra : - un caractère, -le code morse correspondant, exprimé sous forme d'une chaîne. Le codage d'un caractère se fera alors simplement par sa localisation dans le tableau. Programme #include <stdio.h> #include <string.h> #define NL 37 /* nombre de caractères codés */ main() { struct code { char lettre ; char * morse ; ; struct code table[nl] = /* code morse */ { 'A', ".-", 'B', "-...", 'C', "-.-.", 'D', "-..", 'E', ".", 'F', "..-.", 'G', "--.", 'H', "...", 'I', "..", 'J', ".---", 'K', "-.-", 'L', ".-..", 'M', "--", 'N', "-.", 'O', "---", 'P', ".--.", 'Q', "--.-", 'R',".-.", 'S', "...", 'T', "-", 'U', "..-", 'V', "...-", 'W', ".--", 'X', "-..-",

122 122 Exercices en langage C 'Y', "-.--", 'Z', "--..", '.', ".-.-.-", '0', "-----", '1', ".----", '2', "..---", '3', "...--", '4', "...-", '5', "...", '6', "-...", '7', "--...", '8', "---..", '9', "----." ; char ligne[128] ; /* pour lire une ligne au clavier */ int i, j ; /* lecture message à traduire */ printf ("donnez votre message (1 ligne maxi) : \n") ; gets (ligne) ; printf ("\n\n voici la traduction de votre message\n") ; /* traduction lettre par lettre */ for (i=0 ; i<strlen(ligne) ; i++) { j=0 ; while (ligne[i]!= table[j].lettre && j++<nl-1) ; if (j<nl) printf ("%7s", table[j].morse) ; else printf ("??????") ; if (! ((i+1)%10) ) printf ("\n") ; /* 10 codes morse par ligne */ Commentaires *Nous avons défini un modè le de structure, nommé code, dans lequelnous trouvons : - un caractère, - un pointeur sur une chaîne de caractères destinée à contenir le code morse correspondant. Notez que, contrairement à ce que nous avions fait dans le programme de l'exercice précédent, nous avons prévu ici un pointeur sur une chaîne et non un tableau de caractères. Dans ces conditions, le tableau table occupera seulement 37 (valeur de NL) emplacements dont la tai le sera généralement de 3 ou 5 octets (1 pour le caractère et 2 ou 4 pour le pointeur). L'emplacement même des chaînes correspondantes se trouve cependant réservé à la compilation, de par le fait que nous avons initialisé ce tableau lors de sa déclaration. Ilne faut pas oublier, en effet, qu'une notation te le que : ".-.-."

123 II. Utilisation de structures 123 est interprétée par le compilateur comme représentant l'adresse de la chaîne fournie, mais qu'en même temps il lui réserve un emplacement. Cette façon de procéder peut se révèler plus économique en place mémoire que la précédente, dans la mesure où chaque chaîne n'occupe que l'espace qui lui est nécessaire (ilfaut toutefois ajouter, pour chaque chaîne, l'espace nécessaire à un pointeur). Remarque : En toute rigueur, le tableau table est de classe automatique (puisqu'il apparaît au sein d'une fonction - ici le programme principal). Son emplacement est donc a loué au moment de l'exécution du programme (c'est-à -dire, ici, dè s le début). Les constantes chaînes, par contre, voient leurs emplacements définis dè s la compilation. Si notre tableau table avait été déclaré de maniè re globale, ilaurait été de classe statique. Son emplacement aurait alors été réservé dè s la compilation. Une te le distinction est toutefois relativement forme le et e le n'a guè re d'incidence en pratique. Il est, en effet, généralement, assez tentant de considérer les variables déclarées dans le programme principal comme "quasi statiques", dans la mesure où, bien que non réservées à la compilation, e les n'en n'occupent pas moins de l'espace pendant toute la durée de l'exécution du programme. *La recherche du caractère dans notre tableau table est réalisée par la seule instruction : while (ligne[i]!= table[j].lettre && j++<nl-1) ; DISCUSSION Dans un programme "réel", ilfaudrait prévoir d'accepter un message de plus d'une ligne, ce qui poserait le problème de sa mémorisation. On pourrait être amené, soit à lui imposer une tai le maximale, soit à se tourner vers des méthodes de "gestion dynamique". II-3 Décodage morse

124 124 Exercices en langage C Enoncé Ecrire un programme permettant de décoder un message en morse fourni au clavier sous forme d'une suite de caractères. Ce le-ci pourra com porter : - des points et des tirets représentant les codes proprement dits, - un ou plusieurs espaces pour séparer les différents codes (on n'imposera donc pas à l'utilisateur d'employer un "gabarit" fixe pour chaque code). On supposera que le message fourni ne dépasse pas une ligne de 127 caractères. Les codes inexistants seront traduits par le caractère "?". On utilisera le tableau des codes morses fourni dans l'exercice précédent (II-2). Exemple donnez votre message (1 ligne maxi) : voici la traduction de votre message B O N J O U R.? ANALYSE Ce programme doit donc établir une correspondance entre un code morse et un caractère. Nous pouvons, pour ce faire, utiliser la même structure que dans l'exercice précédent. Le décodage d'un caractère se fera alors en explorant, non plus la partie caractère, mais la partie chaîne du tableau de structure. L'algorithme de recherche sera donc similaire, la comparaison de caractères étant remplacée par une comparaison de chaînes. En ce qui concerne le message en morse, nous pouvons le lire par gets dans un tableau de 128 caractères, nommé ligne. Le principalproblème qui se pose alors à nous est celui de l'accè s à chacun des codes morses contenus dans ligne ;en effet, ceux-ci sont écrits avec un gabarit variable et séparés par un nombre variable d'espaces. Nous proposons de répéter le traitement suivant, fondé sur l'emploi d'un pointeur de caractères (indice) dans le tableau ligne : - Avancer le pointeur, tant qu'ildésigne un espace.

125 II. Utilisation de structures Extraire, à partir de la position indiquée par le pointeur, à l'aide de sscanf, une chaîne de longueur maximale 7 (puisque aucun code morse ne dépasse cette longueur). Pour cela, nous ferons appel au code format %7s, lequel interrompt l'exploration, soit quand un séparateur est rencontré, soit lorsque la longueur indiquée (7) est atteinte. - Incrémenter le pointeur de la longueur de la chaîne ainsi lue (car, bien sûr, iln'aura pas été modifié par sscanf). Programme #include <stdio.h> #include <string.h> #define NL 37 /* nombre de caractères codés */ #define LG 127 /* longueur ligne clavier */ main() { struct code { char lettre ; char * morse ; ; struct code table[nl] = /* code morse */ { 'A', ".-", 'B', "-...", 'C', "-.-.", 'D', "-..", 'E', ".", 'F', "..-.", 'G', "--.", 'H', "...", 'I', "..", 'J', ".---", 'K', "-.-", 'L', ".-..", 'M', "--", 'N', "-.", 'O', "---", 'P', ".--.", 'Q', "--.-", 'R',".-.", 'S', "...", 'T', "-", 'U', "..-", 'V', "...-", 'W', ".--", 'X', "-..-", 'Y', "-.--", 'Z', "--..", '.', ".-.-.-", '0', "-----", '1', ".----", '2', "..---", '3', "...--", '4', "...-", '5', "...", '6', "-...", '7', "--...", '8', "---..", '9', "----." ; char ligne[lg+1] ; /* pour lire une ligne au clavier */ char code[7] ; /* code courant à traduire */ int i, j ; /* lecture message à traduire */ printf ("donnez votre message (1 ligne maxi) : \n") ; gets (ligne) ; printf ("\n\n voici la traduction de votre message\n") ;

126 126 Exercices en langage C /* traduction code par code */ i=0 ; while (i<strlen(ligne)) { while (ligne[i] == ' ') i++ ; /* saut des espaces éventuels */ if ( i<strlen(ligne) ) /* si pas en fin de ligne */ { sscanf (&ligne[i], "%6s", code); /* lecture code (6 car max) */ i += strlen(code) ; /* incrément pointeur dans ligne */ j=0 ; /* recherche code dans table */ while ( stricmp (code, table[j].morse) && j++<nl-1) ; if (j<nl) printf ("%2c", table[j].lettre) ; /* code trouvé */ else printf ("?") ; /* code non trouvé */ Commentaires * Dans la boucle de saut des espaces éventuels, on ne risque pas d'a ler au-delà de la fin de la chaîne contenue dans ligne, car le caractère de fin (\0), différent d'un espace, servira de "sentine le". *Par contre, avant d'extraire un nouveau code par sscanf, ilest nécessaire de s'assurer que l'on n'est pas parvenu en fin de ligne. En effet, dans ce cas, sscanf fournirait une suite de caractères constituée du caractère \0 (qui n'est pas considéré par cette fonction comme un séparateur) et des caractères suivants (prélevés en dehors du tableau ligne). Notez que, en l'absence d'un teltest, le malne serait pas trè s grave puisqu'ilreviendrait simplement à placer au plus 7 caractères dans code, com m ençant par \0. * La recherche du code morse dans le tableau table est réalisée par la seule instruction : while ( stricmp (code, table[j].morse) && j++<nl-1) ; Les remarques faites dans le quatriè me commentaire de l'exercice II-1, à propos de la recherche séquentie le en table, s'appliquent également ici. DISCUSSION

127 II. Utilisation de structures 127 Notre programme ne détecte pas le cas où l'utilisateur fournit un code morse de plus de 6 caractères. Dans ce cas, en effet, il se contente de le "découper" en tranches de 6 caractères (la derniè re tranche pouvant avoir une longueur inférieure). Si l'on souhaitait détecter ce genre d'anomalie, il faudrait, aprè s chaque examen d'un code, s'assurer qu'il est effectivement suivi d'un espace ou d'une fin de chaîne. II-4 Facturation par code Enoncé Réaliser un programme établissant une facture pouvant porter sur plusieurs articles. Pour chaque article à facturer, l'utilisateur ne fournira que la quantité et un numéro de code à partir duquelle programme devra retrouver à la fois le libe lé et le prix unitaire. Le programme devra refuser les codes inexistants. A la fin, ilaffichera un récapitulatif tenant lieu de facture. Les informations relatives aux différents articles seront définies dans le source même du programme (et non dans un fichier de données). E le seront toutefois placées à un niveau global, de maniè re à pouvoir, le cas échéant, faire l'objet d'un source séparé, appelable par #include. On prévoira deux fonctions : - une pour rechercher les informations relatives à un article, à partir de son numéro de code, - une pour afficher la facture récapitulative. Exemple combien d'articles à facturer? 3 code article? 25 quantité de Centrifugeuse au prix unitaire de ? 33 code article? 7 ** article inexistant - redonnez le code : 16

128 128 Exercices en langage C quantité de Grille-pain au prix unitaire de ? 12 code article? 26 quantité de Four à raclette 6P au prix unitaire de ? 6 FACTURE ARTICLE NBRE P-UNIT MONTANT Centrifugeuse Grille-pain Four raclette 6P TOTAL ANALYSE L'énoncé nous précise que les codes d'articles sont numériques, mais il ne dit pas qu'ils sont consécutifs. Dans ces conditions, ilest nécessaire de mémoriser les différentes valeurs possibles de ces codes. Comme nous devons pouvoir associer à chaque code un libe lé (chaîne) et un prix (réel), nous pouvons songer à utiliser un tableau de structures, dans lequel chaque élément contient les informations relatives à un article (code, libe lé, prix unitaire). Ce tableau sera, comme demandé par l'énoncé, déclaré à un niveau globalet initialisé dans sa déclaration. Le travailde la fonction de recherche (nous la nommerons recherche) consistera à vérifier la présence du code d'article dans le tableau de structure ainsi défini. En cas de succè s, e le en restituera le rang (ce qui sera suffisant au programme principalpour afficher les informations correspondantes). Dans le cas contraire, e le restituera la valeur -1. Notez que le code d'article sera le seulargument de cette fonction. Nous voyons donc déjà comment, pour chaque code (correct) fourni par l'utilisateur, afficher les informations correspondantes avant d'en demander la quantité. Mais, compte tenu de ce que l'édition de la facture doit être faite aprè s les saisies relatives à tous les articles, nous devons obligatoirement, pour chaque article à facturer, conserver : -la quantité, - une information permettant d'en retrouver le libe lé et le prix unitaire. Nous pourrions, certes, archiver ces informations dans un tableau. Mais, en fait, cela n'est pas nécessaire puisqu'ilest possible de les retrouver à partir du rang de l'article dans la structure (le code article conviendrait également, mais il nous faudrait alors explorer à nouveau notre tableau de structures lors de l'édition de la facture). Ces deux informations seront conservées dans deux tableaux (nommés qte et rangart) comportant autant d'éléments que d'articles à facturer (on en prévoira un nombre maximal).

129 II. Utilisation de structures 129 La fonction d'édition de la facture (nommée facture) se contentera alors d'explorer séquentie lement ces deux tableaux pour retrouver toutes les informations nécessaires. E le recevra, en argument, les adresses des deux tableaux (qte et rangart), ainsi que le nombre d'articles à facturer. Programme #include <stdio.h> /* structure contenant les informations relatives aux */ /* différents articles */ #define NBART 6 /* nombre total d'articles */ typedef struct { int code ; /* code article */ char * lib ; /* libellé */ float pu ; /* prix unitaire */ t_article ; t_article article [NBART] = { 11, "Gaufrier", 268.0, 14, "Cafetière 12 T", 235.0, 16, "Grille-pain", , 19, "Balance de ménage", 278.0, 25, "Centrifugeuse", 370.0, 26, "Four raclette 6P", ; /* */ #define NAFMAX 10 /* nombre maxi d'articles à facturer */ main() { int recherche(int) ; /* proto fonction de recherche d'un article */ void facture(int[], int[], int) ; /* proto fonction d'affichage de la facture */ int naf, /* nombre d'articles à facturer */ rang, /* rang courant d'un article */ codart, /* code courant d'un article */ i ; int rangart [NAFMAX], /* rang des articles à facturer */ qte [NAFMAX] ; /* quantité de chaque article à facturer */ /* entrée du nombre d'articles à facturer */ printf ("combien d'articles à facturer? ") ; scanf ("%d", &naf) ;

130 130 Exercices en langage C /* boucle de traitement de chaque article à facturer */ for (i=0 ; i<naf ; i++) { printf ("code article? ") ; do { scanf ("%d", &codart) ; rang = recherche (codart) ; if (rang == -1) printf (" ** article inexistant - redonnez le code : ") ; while (rang == -1) ; rangart[i] = rang ; printf ("quantité de %s au prix unitaire de %8.2f? ", article[rang].lib, article[rang].pu) ; scanf ("%d", &qte[i]) ; /* affichage facture */ facture (rangart, qte, naf) ; /***********************************************************/ /* fonction de recherche d'un code article */ /***********************************************************/ int recherche (int codart) { int rang ; /* rang courant d'un article */ rang = 0 ; while (article[rang].code!= codart && rang++ < NBART-1) { ; if (rang <NBART) return (rang) ; else return (-1) ; /***********************************************************/ /* fonction d'affichage de la facture */ /***********************************************************/ void facture(int rangart[], int qte[], int naf) /* rangart : tableau des rangs des codes articles */ /* qte :tableau des prix unitaires */ /* naf :nombre d'articles à facturer */ { float somme, /* total facture */ montant ; /* montant relatif à un article */ int i ;

131 II. Utilisation de structures 131 printf ("\n\n %32s\n\n", "FACTURE") ; printf ("%-20s%5s%10s%12s\n\n", "ARTICLE", "NBRE", "P-UNIT", "MONTANT") ; somme = 0 ; for (i=0 ; i<naf ; i++) { montant = article[rangart[i]].pu * qte[i] ; printf ("%-20s%5d%10.2f%12.2f\n", article[rangart[i]].lib, qte[i], article[rangart[i]].pu, montant) ; somme += montant ; printf ("\n\n%-35s%12.2f", " TOTAL", somme) ; Commentaires *Nous avons choisi ici d'utiliser typedef pour définir sous le nom t_articlela structure correspondant à un article. Vous constatez que le libe lé y apparaît sous la forme d'un pointeur sur une chaîne et non d'une chaîne ou d'un tableau de caractères. Dans ces conditions, le tableau article, déclaré de ce type, n'occupera que 6 emplacements de petite tai le (généralement 6 ou 8 octets) *Là encore, une seule instruction permet d'effectuer la recherche d'un code article dans le tableau article. Voyez, à ce propos, les remarques faites dans le quatriè me commentaire de l'exercice II-1. *Le code format %-20s, utilisé à deux reprises dans la fonction facture, permet de "cadrer" une chaîne à gauche. DISCUSSION *Notre programme n'est pas protégé contre des réponses incorrectes de la part de l'utilisateur. En particulier, une réponse non numérique peut entraîner un comportement assez désagréable. Dans un programme réel, ilserait nécessaire de régler convenablement ce type de problème, par exemple en utilisant fgets (..., stdin) et sscanf. *De même, dans un programme réel, ilpourrait être judicieux de demander à l'utilisateur de confirmer que le produit cherché est bien celui dont on vient de lui afficher les informations.

132 132 Exercices en langage C *La précision offerte par le type float (6 chiffres significatifs) peut se révéler insuffisante.

133 III : H ASARD ET RECREATIONS Ce chapitre vous propose un certain nombre d'exercices correspondant à la réalisation de programmes récréatifs, basés sur l'utilisation du hasard. Les deux premiers exercices sont essentie lement destinés à vous montrer comment générer des nombres aléatoires en langage C. III-1 Tirage aléatoire Enoncé Ecrire une fonction fournissant un nombre entier tiré au hasard entre 0 (inclus) et une valeur n (incluse) fournie en argument. Réaliser un programme principalutilisant cette fonction pour examiner la "distribution" des valeurs ainsi obtenues dans l'interva le [0, 10]. Le nombre de tirages à réaliser sera lu en donnée et le programme affichera le nombre de fois où chacune de ces valeurs aura été obtenue. Exemple combien de tirages : 1100 nombre de tirages obtenus 0 : : 95

134 134 Exercices en langage C 2 : : 95 4 : 91 5 : : : : 92 9 : : 105 Indication : ilest nécessaire de faire appelà la fonction rand de la "bibliothèque standard". ANALYSE Ilfaut faire appelà la fonction rand. Ce le-ci fournit un nombre entier, tiré de façon "pseudo-aléatoire" dans l'interva le [0, RAND_MAX], chaque nombre de cet interva le ayant quasiment la même probabilité d'ê tre tiré. Notez que la valeur de RAND_MAX est définie dans stdlib.h ;d'aprè s la norme, e le n'est jamais inférieure à la capacité minimale d'un int, c'est-à -dire Pour aboutir au résultat voulu, une démarche consiste à transformer un tel nombre en un nombre réel appartenant à l'interva le [0,1[. Ilsuffit ensuite de multiplier ce réelpar n+1 et d'en prendre la partie entiè re pour obtenir le résultat escompté. On peut alors montrer que les valeurs 0, 1,... n-1, n sont quasi équiprobables. Pour obtenir un telnombre aléatoire, nous pouvons diviser le nombre fourni par rand par la valeur RAND_MAX+ 1 (il faut éviter de diviser par RAND_MAX, car la valeur 1 risquerait alors d'ê tre obtenue - en moyenne une fois sur RAND_MAX!). Là encore, on peut, de maniè re forme le, montrer que si la loi de probabilité est uniforme sur [0,1[, ilen va de même de ce le du nombre ainsi fabriqué dans l'interva le d'entiers [0,n]. Programme #include <stdio.h> #include <stdlib.h> /* pour la fonction rand */ #define N 10 /* les tirages se feront entre 0 et N */ main() {

135 III. Hasard et récréations 135 int aleat (int) ; /* prototype fonction de tirage aléatoire */ int ntir, /* nombre de tirages requis */ t[n+1], /* tableau comptage tirages de chaque valeur */ i ; printf ("combien de tirages : ") ; scanf ("%d", &ntir) ; for (i=0 ; i<=n ; i++) t[i] = 0 ; for (i=1 ; i<=ntir ; i++) t[aleat(n)]++ ; printf ("nombre de tirages obtenus\n") ; for (i=0 ; i<=n ; i++) { printf ("%4d : ", i) ; printf ("%6d\n", t[i]) ; /********************************************************/ /* fonction de tirage aléatoire d'un nombre dans [0, n] */ /********************************************************/ int aleat (int n) { int i ; i = rand() / (RAND_MAX + 1.) * (n+1) ; return (i) ; Commentaires *Dans la fonction aleat, la division par RAND_MAX+ 1 doit bien sûr s'effectuer sur des valeurs rée les. Mais, de plus, il faut prendre garde à ne pas écrire le diviseur sous la forme RAND_MAX + 1. En effet, celui-ci serait évalué dans le type int et, dans le cas (fréquent) où la valeur de RAND_MAX est exactement la valeur maximale du type int, l'addition de 1 à RAND_MAX conduirait à la valeur (le dépassement de capacité n'étant jamais détecté en cas d'opérations sur des entiers). DISCUSSION

136 136 Exercices en langage C En général, la fonction rand fournit toujours la même suite de valeurs, d'une exécution à une autre. L'exercice suivant vous montre comment éviter ce phénomène. III-2 Tirage aléatoire variable Enonce Ecrire une fonction fournissant un nombre entier tiré au hasard entre 0 et une valeur n fournie en argument. La suite des valeurs restituées par cette fonction (lorsqu'on l'appe le à diverses reprises) devra ê tre différente d'une exécution à une autre et ne pas dépendre d'une quelconque information fournie par l'utilisateur. Comme dans l'exercice précédent, on réalisera un programme principal utilisant cette fonction pour examiner la "distribution" des valeurs ainsi obtenues dans l'interva le [0,10]. Pour ce faire, on lira en données le nombre de tirages à réaliser et le programme affichera le nombre de fois où chacune des valeurs aura été obtenue. Suggestion : ilfaut "initialiser" convenablement le "générateur de nombres aléatoire", en utilisant la fonction srand. La "graîne" nécessaire peut être fabriquée à l'aide de la fonction time, de façon à avoir un caractère suffisamment imprévisible. Exemples (ils'agit là des résultats correspondant à deux exécutions différentes du même programme) combien de tirages : 1100 nombre de tirages obtenus 0 : : : 97 3 : 97 4 : 89 5 : 93 6 : 105

137 7 : : : : 83 III. H asard et récréations 137 combien de tirages : 1100 nombre de tirages obtenus 0 : : 98 2 : 98 3 : : 98 5 : 97 6 : 99 7 : : 99 9 : : 96 ANALYSE En langage C, la fonction srand permet d'initialiser le générateur de nombres aléatoires. Ilfaut cependant lui fournir une "graîne", c'est-à -dire un nombre entier (de type unsigned int) qui déterminera le premier nombre tiré par rand. Cette méthode permet ainsi, si on le souhaite, d'obtenir à volonté une même suite de nombres aléatoires ;il faut d'ai leurs noter que, par défaut, tout se passe comme si srand était appelé, en début de l'exécution d'un programme, avec l'argument 1. Ici, par contre, nous souhaitons obtenir une suite différente d'une exécution à une autre. Une solution à ce problème consiste à choisir une "graîne" aléatoire. Bien sûr, iln'est pas question de faire appelà rand dans ce cas. Par contre, la fonction time fournit une date, exprimée en secondes. Ce le-ci possède un caractère suffisamment imprévisible pour ê tre utilisée comme graîne. Cette initialisation du générateur de nombres aléatoires doit toutefois n'ê tre réalisée qu'une seule fois pour une exécution donnée. Dans le cas contraire, on risquerait, en effet, d'obtenir plusieurs fois de suite les mêmes nombres. Si l'on souhaite que ce problème soit pris en charge par la fonction de tirage d'un nombre e le-même, ilest nécessaire que cette derniè re soit capable de le faire lors de son premier appel(et uniquement à ce moment-là). Ce mécanisme passe par l'emploi d'une variable de classe statique.

138 138 Exercices en langage C Programme #include <stdio.h> #include <stdlib.h> /* pour la fonction rand */ #include <time.h> /* pour la fonction time */ #define N 10 /* les tirages se feront entre 0 et N */ main() { int aleat (int) ; /* prototype fonction de tirage aléatoire */ int ntir, /* nombre de tirages requis */ t[n+1], /* tableau comptage tirages de chaque valeur */ i ; printf ("combien de tirages : ") ; scanf ("%d", &ntir) ; for (i=0 ; i<=n ; i++) t[i] = 0 ; for (i=1 ; i<=ntir ; i++) t[aleat(n)]++ ; printf ("nombre de tirages obtenus\n") ; for (i=0 ; i<=n ; i++) { printf ("%4d : ", i) ; printf ("%6d\n", t[i]) ; /********************************************************/ /* fonction de tirage aléatoire d'un nombre dans [0, n] */ /********************************************************/ int aleat (int n) { int i ; static int prem = 1 ; /* drapeau premier appel */ time_t date ; /* pour l'argument de time */ /* time_t est un type entier défini dans time.h */ /* initialisation générateur au premier appel */ if (prem) { srand (time(&date)) ; prem = 0 ;

139 III. H asard et récréations 139 /* génération nombre */ i = rand() / (RAND_MAX + 1.) * (n+1) ; return (i) ; Commentaires *Le mécanisme du traitement particulier à effectuer au premier appelest réalisé grâ ce à la variable prem, déclarée de classe statique. Cette variable est initialisée à un, lors de la compilation. Dès le premier appel, e le est mise à zéro et e le gardera ensuite cette valeur jusqu'à la fin de l'exécution du programme. Ainsi, la fonction srand n'est effectivement appelée qu'une seule fois, lors du premier appelde notre fonction aleat. *La fonction time fournit en retour le temps (exprimé en secondes) écoulé depuis une certaine "origine" dépendant de l'implémentation. Le type de cette valeur dépend, lui aussi, de l'implémentation ;toutefois, la norme prévoit qu'ilexiste, dans time.h, un symbole time_t (défini par typedef) précisant le type effectivement employé. Ici, lorsque nous transmettons cette valeur à srand, ilest possible qu'apparaisse une conversion du type time_t dans le type unsigned int ; ici, cela n'a guè re d'importance, dans la mesure où, même si cette conversion est "dégradante", la valeur obtenue restera imprévisible pour l'utilisateur. D'autre part, la fonction time ne se contente pas de fournir une "heure" en retour ;e le range également cette même information à l'adresse qu'on lui fournit (obligatoirement) en argument ;c'est ce qui justifie l'existence de la variable date (qui n'est pas utilisée par ai leurs) et qui doit, ici, absolument être déclarée dans le "bon type", sous peine de risquer d'aboutir à un écrasement intempestif de données (dans le cas où on aurait déclaré date d'un type "plus petit" que le type effectif). III-3 Aléa d'étoiles

140 140 Exercices en langage C Enoncé Afficher au hasard un certain nombre d'étoiles (caractère "*") à l'intérieur d'un rectangle. Le nombre d'étoiles souhaitées, ainsi que le nombre de lignes et de colonnes du rectangle seront fournis en données. Le programme vérifiera que la zone est assez grande pour recevoir le nombre d'étoiles requis. On évitera que plusieurs étoiles ne soient superposées. Exemple combien de lignes : 10 combien de colonnes : 45 combien de points : 200 ** * **** ** *** * ** *** * *** ** * * * ** * ** * * ****** * ** ** * * ** * * * ***** *** ** * *** * * * *** * * * * * ** * * ** * * ** ** ** **** ** ** ** ** * * * * * * ** *** * * * ** * * * * ** *** ** ** * ** * * * * ** * * * * * ***** ** ** * * * * ***** ** *** * ** * ***** **** * * *** * ** **** * ***** ANALYSE Nous utiliserons un tableau de caractères à deux dimensions, dans lequelchaque élément représentera une case de notre rectangle. Nous conviendrons que le premier indice représente le rang de la ligne et que le second indice représente le rang de la colonne. Comme l'utilisateur doit pouvoir choisir les dimensions du rectangle concerné, nous prévoirons de donner à notre tableau une tai le suffisante pour couvrir tous les cas possibles (nous avons choisi, ici, 25 lignes de 80 caractères) ;cela signifie que, la plupart du temps, le programme n'utilisera qu'une partie de ce tableau. Au départ, nous initialiserons tous les éléments de la "partie utile" de ce tableau avec le caractère espace. Nous choisirons ensuite au hasard les éléments dans lesquels nous devrons placer un caractère "*". Pour ce faire, ilnous suffira de tirer au hasard un numéro de ligne et un numéro de colonne jusqu'à ce que l'emplacement correspondant soit disponible (caractère espace). L'algorithme de tirage au hasard d'un nombre entier appartenant à un interva le donné a été exposé dans l'analyse de l'exercice III-1.

141 III. H asard et récréations 141 Ilne nous restera plus qu'à afficher, par exemple avec la fonction putchar, les différents éléments de notre tableau, en prévoyant un "changement de ligne" aux moments opportuns. Programme #include <stdio.h> #include <stdlib.h> #include <string.h> /* pour memset */ #include <time.h> #define NY 25 /* nombre total de lignes de l'écran */ #define NX 80 /* nombre total de colonnes de l'écran */ main() { int aleat (int) ; /* prototype fonction tirage aléatoire */ int ny, /* nombre de lignes du rect. considéré */ nx, /* nombre de col. du rect. considéré */ ix, /* colonne courante */ iy, /* ligne courante */ nb_points, /* nombre de points à tirer */ i, j ; char ecran [NX] [NY] ; /* image de l'écran */ const char blanc = ' ', /* caractère de remplissage */ point = '*' ; /* représentation d'un point */ /* entrée des dimensions du rectangle considéré et du nombre de points souhaités */ do { printf ("combien de lignes : ") ; scanf ("%d", &ny) ; while (ny<=0 ny>=ny) ; do { printf ("combien de colonnes : ") ; scanf ("%d", &nx) ; while (nx<=0 nx>=nx) ; do { printf ("combien de points : ") ; scanf ("%d", &nb_points) ; while (nb_points > nx*ny nb_points < 1 ) ;

142 142 Exercices en langage C /* remplissage aléatoire du tableau image d'écran */ memset (ecran, blanc, NX*NY) ; for (i=1 ; i<=nb_points ; i++) { do { ix = aleat (nx-1) ; iy = aleat (ny-1) ; while ( ecran [ix] [iy]!= blanc) ; ecran [ix] [iy] = point ; /* affichage du tableau image d'écran */ for (j=0 ; j<ny ; j++) { for (i=0 ; i<nx ; i++) putchar ( ecran [i] [j] ) ; printf ("\n") ; /*******************************************************/ /* fonction de tirage aléatoire d'un nombre dans [0,n] */ /*******************************************************/ int aleat (int n) { int i ; static int prem = 1 ; /* drapeau premier appel */ time_t date ; /* pour l'argument de time */ /* initialisation générateur au premier appel */ if (prem) { srand (time(&date) ) ; prem = 0 ; /* génération nombre aléatoire */ i = rand() / (RAND_MAX + 1.) * (n+1) ; return (i) ; Commentaires

143 III. H asard et récréations 143 *L'initialisation de la partie utile du tableau avec le caractère espace aurait pu se programmer ainsi : for (i=0 ; i<nx ; i++) for (j=0 ; j<ny ; j++) ecran [i][j] = ' ' ; Ici, nous avons préféré faire appel à la fonction memset, d'exécution plus rapide. Toutefois, ce le-ci remplit d'un caractère donné une suite d'octets consécutifs ;ceci exclut donc de limiter l'initialisation à la partie utile du tableau. Ilne faut pas oublier, en effet, que ce le-ci n'est pas formée de nx*ny octets consécutifs (quoique, en toute rigueur, en tenant compte de la maniè re dont sont rangés en mémoire les différents éléments d'un tableau, il soit possible de limiter l'initialisation à nx*ny éléments consécutifs). *Nous avons repris la fonction aleat de l'exercice précédent. Ce le-ci tire une valeur entiè re au hasard entre 0 et une limite qu'on lui fournit en argument ;de plus, lors de son premier appel, e le effectue une initialisation du générateur de nombres aléatoires. III-4 Estimation de pi Enoncé Calculer une valeur approchée de pi, par la méthode suivante : - on tire un certain nombre de points au hasard dans un carré donné. - on détermine le rapport entre le nombre de ces points appartenant au cercle inscrit dans le carré et le nombre total de points tirés. Ce rapport fournit une estimation de la valeur de pi/4. Le nombre totalde points à tirer sera fourni en donnée. Exemple combien de points? estimation de pi avec points : e+000

144 144 Exercices en langage C ANALYSE Nous choisirons un carré de côté unité. Nous conviendrons de prendre son coin bas gauche comme origine d'un repè re cartésien. Nous tirerons alors au hasard le nombre de points voulus, à l'intérieur de ce carré. Plus précisément, pour chaque point, nous déterminerons au hasard ses deux coordonnées, en tirant deux nombres réels appartenant à l'interva le [0,1]. A cet effet, nous ferons appelà la méthode exposée dans l'analyse de l'exercice III-1. Pour chaque point, nous calculerons sa distance au centre du carré (de coordonnées : 0.5, 0.5) et nous considérerons qu'il appartient au cercle inscrit si cette distance est inférieure à 0.5 (notez que, par souci de simplicité, nous travai lerons en fait avec le carré de cette distance). Programme #include <stdio.h> #include <stdlib.h> main() { float caleat(void) ; /* prototype fonction de tirage valeur aléatoire */ float x, y, /* coordonnées d'un point courant */ d2, /* distance (au carré) d'un point courant au centre */ pi ; /* valeur approchée de pi */ int np, /* nombre de points à tirer */ nc, /* nombre de points à l'intérieur du cercle */ i ; printf ("combien de points? ") ; scanf ("%d", &np) ; for (nc=0, i=1 ; i<=np ; i++) { x = caleat() ; y = caleat() ; d2 = (x-0.5) * (x-0.5) + (y-0.5) * (y-0.5) ; if (d2 <= 0.25) nc++ ; pi = (4.0 * nc) / np ;

145 printf ("estimation de pi avec %d points : %e", np, pi) ; III. H asard et récréations 145 float caleat (void) /* fonction fournissant une valeur aléatoire */ /* appartenant à l'intervalle [0-1] */ { return ( (float) rand() / (RAND_MAX + 1.0) ) ; DISCUSSION Notre fonction de tirage aléatoire d'un entier fournit toujours la même suite de valeurs. Ce qui signifie que, pour un nombre donné de points, nous obtiendrons toujours la même estimation de pi. Vous pouvez éviter ce phénomène en utilisant la fonction réalisée dans l'exercice III-2. III-5 Jeu du devin Enoncé Ecrire un programme qui choisit un nombre entier au hasard entre 0 et 1000 et qui demande à l'utilisateur de le "deviner". A chaque proposition faite par le joueur, le programme répondra en situant le nombre proposé par rapport à celui à deviner (plus grand, plus petit ou gagné). Lorsque le joueur aura deviné le nombre choisi, ou lorsqu'un nombre maximal de coups (10) aura été dépassé, le programme affichera la récapitulation des différentes propositions. Exemple Devinez le nombre que j'ai choisi (entre 1 et 1000) votre proposition : trop grand

146 146 Exercices en langage C votre proposition : trop grand votre proposition : trop grand votre proposition : trop grand votre proposition : trop grand votre proposition : trop grand votre proposition : trop petit votre proposition : trop grand votre proposition : vous avez gagné en 9 coups ---- Récapitulation des coups joués trop grand 250 trop grand 125 trop grand 64 trop grand 32 trop grand 16 trop grand 8 trop petit 12 trop grand 10 exact ANALYSE Le programme commencera par tirer un nombre entier au hasard, suivant la démarche exposée dans l'analyse de l'exercice III-1. Ildevra ensuite répéter l'action : faire jouer le joueur jusqu'à ce que le joueur ait gagné ou qu'ilait dépassé le nombre maximalde coups permis. L'action en question consiste simplement à :

147 - Demander au joueur de proposer un nombre. III. H asard et récréations Conserver ce nombre dans un tableau (pour pouvoir établir la récapitulation finale). Notez que, compte tenu de ce qu'un nombre de coups maximalest imposé, ce dernier fournira le nombre maximald'éléments de notre tableau. - Comparer le nombre fourni avec la valeur choisie par le programme et afficher le message correspondant. Programme #include <stdio.h> #include <stdlib.h> #define NCOUPS 15 /* nombre maximal de coups autorisés */ #define NMAX 1000 /* valeur maximale du nombre à deviner */ main() { int aleat(int) ; /* prototype fonction de tirage d'un nombre au hasard */ int nc, /* compteur du nombre de coups joués */ ndevin, /* nombre à deviner */ n, /* nombre courant proposé par le joueur */ prop[nmax], /* tableau récapitulatif des nombres proposés */ i ; /* initialisations et tirage du nombre secret */ nc = 0 ; printf ("Devinez le nombre que j'ai choisi (entre 1 et %d)\n", NMAX) ; ndevin = aleat(nmax) ; /* déroulement du jeu */ do { printf ("votre proposition : ") ; scanf ("%d",&n) ; prop [nc++] = n ; if (n < ndevin) printf (" trop petit\n") ; else if (n > ndevin) printf (" trop grand\n") ; while (n!= ndevin && nc < NCOUPS) ; /* affichage résultats */

148 148 Exercices en langage C if (n == ndevin) printf ("\n\n++++ vous avez gagné en %d coups\n", nc) ; else { printf ("\n\n---- vous n'avez pas trouvé\n") ; printf ("le nombre choisi était %d\n", ndevin) ; /* affichage récapitulation */ printf ("\n ---- Récapitulation des coups joués ----\n\n") ; for (i=0 ; i<nc ; i++) { printf ("%4d ", prop[i]) ; if (prop[i] > ndevin) printf ("trop grand \n") ; else if (prop[i] < ndevin) printf ("trop petit\n") ; else printf ("exact\n") ; /*******************************************************/ /* fonction de tirage aléatoire d'un nombre dans [0,n] */ /*******************************************************/ int aleat(int n) { int i = rand() / (RAND_MAX + 1.) * (n+1) ; return i ; DISCUSSION Notre fonction de tirage aléatoire d'un nombre entier fournit toujours la même valeur, ce qui gâ che quelque peu l'intérê t du jeu. Dans la pratique, ilserait nécessaire de remplacer la fonction aleat de ce programme par ce le proposée dans l'exercice III-2, laque le permet d'obtenir un nombre différent d'une exécution à une autre. III-6 Mastermind

149 Enoncé III. H asard et récréations 149 Réaliser un programme qui choisit au hasard une combinaison de 5 chiffres (compris entre 1 et 8) et qui demande à l'utilisateur de la deviner. A chaque proposition, le programme précisera : -le nombre de chiffres exacts proposés à la bonne place, -le nombre de chiffres exacts mais proposés à la mauvaise place. Les différentes propositions du joueur seront fournies sous la forme de 5 chiffres consécutifs (sans aucun séparateur), validés par return. Le programme devra traiter convenablement le cas des réponses incorrectes : lettre à la place d'un chiffre, réponse trop courte ou trop longue, chiffre incorrect (nulou supérieur à 8). On prévoira un nombre limite d'essais, au-delà duquel le programme s'interrompra en indiquant que le était la combinaison à deviner. Exemple proposition? : P 0 C proposition? : P 1 C proposition? : P 1 C proposition? : P 0 C proposition? : ** incorrect ** proposition? : 1133é ** incorrect ** proposition? : P 1 C proposition? : P 0 C proposition? : P 0 C vous avez trouvé en 7 coups

150 150 Exercices en langage C ANALYSE Ilparaît assez natureld'utiliser un tableau à 5 éléments pour y ranger la combinaison tirée au hasard. Notez que nous pourrions également tirer au hasard un nombre de 5 chiffres, mais ilfaudrait, de toute façon, en extraire chacun des chiffres ;de plus, la méthode serait difficilement généralisable à un nombre quelconque de positions. La principale difficulté réside dans l'analyse de la proposition du joueur. Dans la comparaison des deux tableaux (combinaison tirée par le programme et combinaison proposée par le joueur), il faudra tenir compte des remarques suivantes : - Un chiffre compté "à sa place" ne doit pas pouvoir ê tre également compté comme "exact, mais malplacé". - Lorsqu'un tirage comporte plusieurs chiffres identiques, il ne faut pas qu'un même chiffre de la proposition du joueur puisse être compté plusieurs fois comme exact. - Lorsqu'une proposition comporte plusieurs chiffres identiques, il ne faut pas les considérer tous comme correspondant à un même chiffre du tirage. Nous vous proposons la méthode suivante : 1 - Nous recherchons tout d'abord les chiffres exacts placés en bonne position. A chaque fois qu'une coïncidence est relevée, nous supprimonsle chiffre, à la fois dans la proposition du joueur et dans le tirage (en le remplaçant, par exemple, par la valeur 0). 2 - Nous reprenons ensuite, un à un, chacun des chiffres du tirage qui n'ont pas été supprimés (c'est-à -dire qui sont différents de 0). Nous les comparons à chacun des chiffres de la proposition. Là encore, si une coïncidence est relevée, nous supprimons les chiffres correspondants, à la fois dans la proposition et dans le tirage. Notez bien qu'il faut absolument éviter de considérer les chiffres déjà supprimés du tirage : ils risqueraient d'ê tre trouvés égaux à d'autres chiffres supprimés de la proposition. Cette méthode qui détruit le tirage nous oblige nécessairement à en faire une copie avant d'entamer l'analyse de la proposition. Nous avons choisi de réaliser trois fonctions : - tirage : tirage au hasard de la combinaison (tableau de 5 entiers) à deviner. - entree : entrée de la proposition du joueur. Ilparaît logique que cette fonction fournisse cette proposition dans un tableau d'entiers. Toutefois, afin de traiter convenablement les cas de réponses incorrectes, la proposition du joueur sera tout d'abord lue dans une chaîne à l'aide de la fonction cgets (son mécanisme est décrit dans l'exercice II-4). - analyse : analyse de la proposition du joueur, suivant l'algorithme décrit précédemment. Programme

151 #include <stdio.h> #include <stdlib.h> #include <string.h> III. H asard et récréations 151 #define NPOS 5 /* nombre de positions */ #define NCHIF 8 /* nombre de chiffres (ici, de 1 a 8) */ #define NCMAX 12 /* nombre maximal de coups */ main() { void tirage (int []) ; /*****************************/ int entree (int []) ; /* prototypes fonctions */ void analyse(int [], int[], int[], int []) ; /*****************************/ int tir[npos], /* combinaison tirée par le programme */ prop[npos], /* proposition du joueur */ ncoup, /* compteur de coups joués */ bpos, /* nombre de chiffres bien placés */ bchif ; /* nombre de chiffres exacts mais mal placés */ /* initialisations */ tirage (tir) ; ncoup = 0 ; /* déroulement du jeu */ do { while (printf ("proposition? : "), entree(&prop) ) printf ("\n** incorrect **\n") ; analyse (prop, tir, &bpos, &bchif) ; printf ("\n %22d P %d C\n", bpos, bchif) ; ncoup++ ; while (bpos < NPOS && ncoup < NCMAX) ; /* affichage résultats */ if (bpos == NPOS) printf ("vous avez trouvé en %d coups", ncoup) ; else { int i ; printf ("vous n'avez pas trouvé en %d coups\n", NCMAX) ; printf ("la bonne combinaison était : ") ; for (i=0 ; i<npos ; i++) printf ("%d",tir[i]) ; printf ("\n") ;

152 152 Exercices en langage C /*************************************************/ /* fonction de tirage de la combinaison secrète */ /*************************************************/ void tirage (int tir []) { int i ; for (i=0 ; i<npos ; i++) tir[i] = rand() / (RAND_MAX + 1.) * NCHIF + 1 ; /*************************************************/ /* fonction de lecture de la proposition du joueur */ /*****************************************************/ int entree (int prop []) { char ch[npos+3] ; /* chaîne où sera lue la proposition du joueur */ int i ; /* lecture proposition joueur dans chaîne ch */ ch[0] = NPOS+1 ; /* préparation longueur maxi chaîne lue */ cgets (ch) ; /* contrôles */ if (strlen (&ch[2])!= NPOS) return(-1) ; for (i=2 ; i<npos+2 ; i++) if (ch[i] < '1' ch[i] > '1'+NCHIF-1) return(-1) ; /* extraction des chiffres choisis */ for (i=0 ; i<npos ; i++) prop[i] = ch[2+i] -'0' ; return (0) ; /**************************************************/ /* fonction d'analyse de la proposition du joueur */ /**************************************************/ void analyse (int prop [], int tir [], int bpos [], int bchif []) { int tirbis[npos], /* double de la combinaison secrète */ i, j ; /* recopie de la combinaison secrète */ for (i=0 ; i<npos ; i++) tirbis[i] = tir[i] ;

153 III. H asard et récréations 153 /* comptage bonnes positions */ *bpos = 0 ; for (i=0 ; i<npos ; i++) if (prop[i] == tirbis[i]) { (*bpos)++ ; tirbis[i] = prop[i] = 0 ; /* comptage bons chiffres mal placés */ *bchif = 0 ; for (i=0 ; i<npos ; i++) for (j=0 ; j<npos ; j++) if (prop[i]!=0 && prop[i] == tirbis[j]) { (*bchif)++ ; prop[i] = tirbis[j] = 0 ; Commentaires *Le nombre de positions (NPOS) et le nombre de chiffres (NCH IF) ont été définis par #define, ce qui en facilite l'éventue le modification. *La fonction tirage fait appelà l'algorithme de tirage au hasard d'un entier, telque nous l'avons exposé dans l'exercice III-1. Toutefois, ici, le nombre tiré doit appartenir à l'interva le [1,NCH IF] et non à l'interva le [0,NCH IF]. C'est ce qui explique que le nombre réeltiré dans l'interva le [0,1[ soit multiplié par NCH IF et que l'on ajoute 1 au résultat. *La fonction entreelit, comme prévu, la proposition du joueur sous forme d'une chaîne. E le en effectue les contrôles requis en restituant la valeur 0 lorsque la réponse est valide et la réponse -1 dans le cas contraire. Notez que la décision de demander, en cas d'erreur, une nouve le proposition au joueur est prise dans le programme principalet non dans la fonction e le-même. *Les arguments de la fonction analyse sont transmis par leur adresse, afin que leur valeur puisse être modifiée. C'est ce qui justifie leur déclaration sous forme de pointeurs sur des entiers. N'oubliez pas que les noms de tableaux correspondent à leur adresse ;c'est ce qui justifie que dans l'appelde analyse, on trouve effectivement les symboles prop et tir, alors que, par ai leurs, on y trouve &bpos et &bchif. *Dans la boucle suivante (du programme principal) :

154 154 Exercices en langage C while (printf ("proposition? : "), entree(&prop) ) printf ("\n** incorrect **\n") ; l'expression figurant dans while utilise un "opérateur séquentiel", ce qui permet ainsi de simplifier quelque peu l'écriture. A titre indicatif, voici deux constructions équivalentes, l'une parfaitement structurée, l'autre basée sur l'utilisation de break (les valeurs des symboles VRAI et FAUX étant respectivement 1 et 0) : ok = FAUX ; while (!ok) { printf ("proposition? : ") ; if (entree(&prop)) ok = VRAI ; else printf ("\n** incorrect **\n") ; do { printf ("proposition? : ") ; if (entree(&prop)) break ; else printf ("\n** incorrect **\n) ; while(1) ; DISCUSSION *Ici, la saisie de la proposition du joueur est parfaitement satisfaisante, même pour un programme "réel". En particulier, e le autorise les corrections, même aprè s que l'utilisateur a frappé le dernier chiffre. *Par contre, telqu'ilest proposé ici, ce programme choisit toujours la même combinaison, ce qui enlève quelque intérê t à la pratique réguliè re du jeu (mais qui peut faciliter la mise au point du programme). Pour rémédier à cette lacune, il suffit d'introduire, dans la fonction tirage, une initialisation du générateur de nombres aléatoires, lors de son premier appel, comme nous l'avons fait dans l'exercice III-2. *Le programme supporte, sans aucune modification, des valeurs quelconques de NPOS et des valeurs de NCH IF inférieures à 10. Ilest facile d'a ler au-delà, en modifiant simplement la fonction entree.

155 IV : TRIS, FUSIONS ET RECH ERCH E EN TABLE Nous vous proposons ici des exercices de programmation d'algorithmes classiques ayant trait aux tris et fusions de tableaux, ainsi qu'à la recherche en table. IV-1 Tri par extraction simple Enoncé Réaliser un programme de tri par valeurs décroissantes d'un tableau d'entiers, en utilisant l'algorithme dit "par extraction simple" qui se définit de la maniè re suivante : - On recherche le plus grand des n éléments du tableau. - On échange cet élément avec le premier élément du tableau. - Le plus petit élément se trouve alors en premiè re position. On peut alors appliquer les deux opérations précédentes aux n-1 éléments restants, puis aux n-2,... et cela jusqu'à ce qu'ilne reste plus qu'un seulélément (le dernier - qui est alors le plus petit). Le programme affichera tous les "résultats intermédiaires", c'est-à -dire les valeurs du tableau, aprè s chaque échange de deux éléments. Exemple combien de valeurs à trier : 8 donnez vos valeurs à trier

156 156 Exercices en langage C valeurs à trier valeurs triées ANALYSE L'algorithme proposé par l'énoncé peut se formaliser comme suit, en tenant compte des conventions d'indices propres au langage C : Répéter, pour i variant de 0 à n-2 : - rechercher k m telque t(k m ) soit le plus grand des t(k), pour k a lant de i à n-1, - échanger les valeurs de t(k m ) et de t(i). Programme #include <stdio.h> #define NMAX 100 /* nombre maximal de valeurs à trier */ main() { int t [NMAX], /* tableau contenant les valeurs à trier */ nval, /* nombre de valeurs à trier */ kmax, /* position du maximum temporaire */ tempo, /* valeur temporaire pour échange valeurs */

157 IV. Tris, fusions et recherche en table 157 i, j, k ; /* lecture des valeurs à trier */ printf ("combien de valeurs à trier : ") ; scanf ("%d", &nval) ; if (nval > NMAX) nval = NMAX ; printf ("donnez vos valeurs à trier\n") ; for (k=0 ; k<nval ; k++) scanf ("%d", &t[k]) ; printf ("\n ---- valeurs à trier ----\n") ; for (k=0 ; k<nval ; k++) printf ("%5d", t[k]) ; printf ("\n\n") ; /* tri des valeurs */ for (i=0 ; i<nval-1 ; i++) /* recherche maxi partiel pour chaque i */ { kmax = i ; /* init recherche maxi partiel */ for (j=i+1 ; j<nval ; j++) /* recherche maxi partiel */ if (t[j] > t[kmax]) kmax = j ; tempo = t[kmax] ; /* mise en place maxi partiel */ t[kmax] = t[i] ; t[i] = tempo ; for (k=0 ; k<nval ; k++) /* affichage intermédiaire */ printf ("%5d", t[k]) ; printf ("\n") ; /* affichage valeurs triées */ printf ("\n ---- valeurs triées ----\n") ; for (k=0 ; k<nval ; k++) printf ("%5d", t[k]) ; printf ("\n") ; Commentaires Ce programme fonctionne pour toutes les valeurs de NMAX, en particulier : - pour NMAX inférieur ou égalà 0, ilne fait rien, - pour NMAX = 1, illit une valeur qu'ilaffiche te le que le.

158 158 Exercices en langage C IV-2 Tri par permutation simple Enoncé Ecrire une fonction réalisant le tri par valeurs croissantes d'un tableau d'entiers, en utilisant l'algorithme de tri par permutation simple (dit de "la bu le"), qui se définit ainsi (n représentant le nombre d'éléments du tableau) : On parcourt l'ensemble du tableau, depuis sa fin jusqu'à son début, en comparant deux éléments consécutifs, en les inversant s'ils sont malclassés. On se retrouve ainsi avec le plus petit élément placé en tête du tableau. On renouve le une te le opération (dite "passe") avec les n-1 éléments restants, puis avec les n-2 éléments restants, et ainsi de suite... jusqu'à ce que : - soit l'avant-dernier élément ait été classé (le dernier étant alors obligatoirement à sa place), - soit qu'aucune permutation n'ait eu lieu pendant la derniè re passe (ce qui prouve alors que l'ensemble du tableau est convenablement ordonné). On prévoira en arguments : -l'adresse du tableau à trier, - son nombre d'éléments, - un indicateur précisant si l'on souhaite que la fonction affiche les valeurs du tableau aprè s chaque permutation (0 pour non, 1 pour oui). Exemple combien de valeurs à trier : 6 donnez vos valeurs à trier valeurs à trier

159 IV. Tris, fusions et recherche en table valeurs triées ANALYSE L'algorithme nous est indiqué par l'énoncé. Nous utiliserons cependant une répétition de type tant que (instruction while) qui permet de prendre convenablement en compte le cas où l'on appe le la fonction de tri en lui fournissant en argument un nombre de valeurs inférieur ou égalà 1. Dans la mise en oeuvre de cet algorithme, nous ferons appelà un entier i spécifiant le rang à partir duquelle tableau n'est pas encore trié. Initialement, il faudra prévoir qu'aucun élément n'est encore à sa place, ce qui conduira à l'initialisation artificie le de i à -1 (puisque en C, le premier élément d'un tableau porte le numéro 0). D'autre part, un indicateur logique nommé permut nous servira à préciser si au moins une permutation a eu lieu au cours de la derniè re passe. Si nous notons nvalle nombre de valeurs de notre tableau, l'algorithme de tri peut alors s'énoncer comme suit : Tant que i ne désigne pas le dernier élément du tableau (c'est-à -dire i < nval-1) et que permut est VRAI, nous effectuons une passe. Cette derniè re consiste en une succession de comparaisons des éléments de rang j et j+1, j décrivant tous les éléments depuis l'avant-dernier jusqu'à celui de rang i+1 (autrement dit, j décroissant de nval-2 à i+1). A chaque permutation, nous donnons à permutla valeur VRAI ;nous aurons, bien sûr, pris soin d'initialiser permut à FAUX au début de chaque passe. Notez que l'utilisation d'une répétition de type tant que (dans laque le la condition de poursuite fait intervenir l'indicateur permut) nous oblige à initialiser artificie lement permut à VRAI, en tout début de travail. Programme #include <stdio.h> #define VRAI 1 /* pour "simuler" des... */ #define FAUX 0 /*... valeurs logiques */ #define NMAX 100 /* nombre maximal de valeurs à trier */ main() {

160 160 Exercices en langage C void bulle(int [], int, int ) ; /* prototype fonction de tri */ int t [NMAX], /* tableau contenant les valeurs à trier */ nval, /* nombre de valeurs à trier */ k ; /* lecture des valeurs à trier */ printf ("combien de valeurs à trier : ") ; scanf ("%d", &nval) ; if (nval > NMAX) nval = NMAX ; printf ("donnez vos valeurs à trier\n") ; for (k=0 ; k<nval ; k++) scanf ("%d", &t[k]) ; printf ("\n ---- valeurs à trier ----\n") ; for (k=0 ; k<nval ; k++) printf ("%5d", t[k]) ; printf ("\n\n") ; /* tri des valeurs */ bulle (t, nval, 1) ; /* affichage valeurs triées */ printf ("\n ---- valeurs triées ----\n") ; for (k=0 ; k<nval ; k++) printf ("%5d", t[k]) ; printf ("\n") ; /**************************************************/ /* fonction de tri par la méthode de la bulle */ /**************************************************/ void bulle (int t[], int nval, int affich) /* t : tableau à trier */ /* nval : nombre de valeurs à trier */ /* affich : indicateur affichages intermédiaires */ { int i, /* rang à partir duquel le tableau n'est pas trié */ j, /* indice courant */ tempo, /* pour l'échange de 2 valeurs */ k ; int permut ; /* indicateur logique précisant si au moins une */ /* permutation a eu lieu lors de la précédente passe */ i = -1 ; permut = VRAI ; while (i < nval-1 && permut)

161 IV. Tris, fusions et recherche en table 161 { permut = FAUX ; for (j=nval-2 ; j>i ; j--) { if ( t[j] > t[j+1] ) { permut = VRAI ; tempo = t[j] ; t[j] = t[j+1] ; t[j+1] = tempo ; if (affich) { for (k=0 ; k<nval ; k++) printf ("%5d", t[k]) ; printf ("\n") ; i++ ; Commentaires Dans la fonction bullle, la déclaration : int * t ; est équivalente à : int t[] ; DISCUSSION Les deux algorithmes proposés dans l'exercice précédent et dans celui-ci correspondent à ce que l'on appe le des "méthodes directes". D'une maniè re générale, ce sont des algorithmes simples à programmer, mais qui nécessitent un nombre de comparaisons de l'ordre de n 2 (notez qu'ilexiste une troisiè me méthode directe dite "tri par insertion"). En fait, ilexiste des méthodes dites "évoluées" qui conduisent à un nombre de comparaisons de l'ordre de n * log n. Ce les-ci débouchent sur des programmes plus complexes et les opérations qu'e les font intervenir sont e les-mêmes plus gourmandes en temps que ce les des méthodes directes. Aussi, les méthodes évoluées ne prennent véritablement d'intérê t que pour des valeurs élevées de n.

162 162 Exercices en langage C A titre indicatif, nous vous fournissons ici l'algorithme relatif à la méthode évoluée la plus performante, nommée "Tri rapide" (Quicksort), inventée par C. A. R. H oare. Cet algorithme, délicat à programmer, est basé sur l'opération de "segmentation" d'un tableau ;ce le-ci consiste à partager un tableau en deux parties, nommées segments, te les que tout élément de l'une soit inférieur ou égal à tout élément de l'autre. Une te le segmentation peut être réalisée par l'algorithme suivant : - Prendre un élément au hasard (on peut prendre l'élément du milieu). Soit m sa valeur. - Rechercher, depuis le début du tableau, le premier élément t(i) telque t(i)>m. - Rechercher, depuis la fin du tableau, le premier élément t(j) telque t(j)<m. - Permuter t(i) et t(j). - Poursuivre ce "parcours" du tableau jusqu'à ce que i et j se rencontrent. Le tri proprement dit s'effectue en appliquant à nouveau l'opération de segmentation à chaque segment obtenu, puis aux segments obtenus par segmentation de ces segments,... et ainsi de suite jusqu'à ce que chaque segment ne contienne plus qu'un seulélément. Notez qu'une te le méthode se prê te particuliè rement bien à une programmation récursive. IV-3 Tri d'un tableau de ch aînes Enoncé Ecrire une fonction utilisant la méthode de tri par extraction simple (décrite dans l'exercice IV-1) pour trier un tableau de chaînes, par ordre alphabétique (sans distinction entre majuscules et minuscules). Cette fonction recevra, en argument : -l'adresse d'un tableau de pointeurs sur les chaînes concernées, -le nombre de chaînes à trier. Le tri proprement dit portera, non sur les valeurs des chaînes e les-mêmes, mais uniquement sur le tableau de pointeurs. On testera cette fonction à l'aide d'un programme principalcréant un simple tableau de chaînes (ayant donc chacune une longueur maximale donnée).

163 IV. Tris, fusions et recherche en table 163 Exemple combien de chaînes à trier? 7 donnez vos 7 chaînes (validez chacune par 'return') C Turbo C Basic Pascal Turbo Pascal Fortran ADA voici vos chaînes triées ADA Basic C Fortran Pascal Turbo C Turbo Pascal ANALYSE La méthode de tri a été décrite dans l'exercice IV-1. Il est cependant nécessaire de procéder à plusieurs sortes d'adaptations : - ilfaut en faire une fonction, -la relation d'ordre qui sert au tri ne porte plus sur des entiers, mais sur des chaînes de caractères ;cela implique de recourir à la fonction stricmp (et non strcmp, puisque l'on souhaite ne pas distinguer les majuscules des minuscules), -les éléments à permuter seront des pointeurs et non plus des entiers.

164 164 Exercices en langage C Programme #include <stdio.h> #include <string.h> #define NCHMAX 100 /* nombre maximal de chaînes à traiter */ #define LGMAX 25 /* longueur maximale d'une chaîne (sans \0) */ main() { void trichaines (char * *, int ) ; /* prototype fonction de tri */ char chaines [NCHMAX] [LGMAX+1] ; /* tableau des chaînes */ char * adr [NCHMAX] ; /* tableau pointeurs sur les chaînes */ int nch, /* nombre de chaîne à trier */ i ; /* lecture des chaînes et préparation du tableau de pointeurs */ printf ("combien de chaînes à trier? ") ; scanf ("%d", &nch) ; if (nch > NCHMAX) nch = NCHMAX ; getchar() ; /* pour forcer la lecture de fin de ligne */ printf ("donnez vos %d chaînes (validez chacune par 'return')\n", nch) ; for (i=0 ; i<nch ; i++) { fgets (chaines[i], LGMAX+1, stdin) ; /* lit au maximum LGMAX caractères */ adr[i] = chaines[i] ; /* tri des pointeurs sur les chaînes */ trichaines (adr, nch) ; /* affichage des chaînes après tri */ /* attention aux chaînes de longueur maximum!! */ printf ("\n\nvoici vos chaînes triées\n") ; for (i=0 ; i<nch ; i++) printf ("%s", adr[i]) ; void trichaines (char * * adr, int nch) /* adr : adresse tableau de pointeurs sur chaînes à trier */ /* nch : nombre de chaînes */ { char * tempo ; /* pointeur temporaire pour l'échange de 2 pointeurs */ int kmax,

165 IV. Tris, fusions et recherche en table 165 i, j ; for (i=0 ; i<nch-1 ; i++) { kmax = i ; for (j=i+1 ; j<nch ; j++) if ( stricmp (adr[kmax], adr[j]) > 0 ) kmax = j ; tempo = adr[kmax] ; adr[kmax] = adr[i] ; adr[i] = tempo ; Commentaires *Ici, les chaînes à trier ont été placées (par le programme principal) dans un tableau de caractères (nommé chaines) à deux dimensions. Notez bien qu'ilne serait pas possible d'en inverser l'ordre des dimensions ;ilest en effet nécessaire que tous les caractères d'une même chaîne soient consécutifs. *Bien que cela n'ait pas été explicitement demandé par l'énoncé, nous avons prévu un contrôle sur la longueur des chaînes fournies au clavier ;pour ce faire, nous avons fait appel à la fonction fgets, en l'appliquant au fichier stdin. L'instruction : fgets (chaines[i], LGMAX+1, stdin) ; lit au maximum LGMAX caractères sur stdin et les range à l'adresse chaine[i], en complétant le tout par un zéro de fin de chaîne. Ainsi, on évite les risques de débordement mémoire que présente gets. Toutefois un léger inonvénient apparaît. En effet, tant que le nombre de caractères maximal(lgmax) n'est pas atteint, le caractère \n qui a servi à délimiter la chaîne lue est rangé en mémoire, au même titre que les autres. En revanche, lorsque le nombre maximalde caractères a été atteint, alors précisément que ce caractère \n n'a pas été rencontré, on ne trouve plus ce caractère en mémoire (le caractère nulde fin de chaîne, quant à lui, est bien toujours présent). Cet inconvénient est surtout sensible lorsque l'on affiche à nouveau les chaînes par printf aprè s leur tri : les chaînes de longueur maximale ne seront pas suivies d'un changement de ligne. Notez bien qu'en employant puts on obtiendrait, en revanche, 1 caractère de changement de ligne pour les chaînes de longueur maximale (transmis par la fonction puts même) et 2 caractères de changement de ligne pour les autres chaînes (celui figurant dans la chaîne et celui transmis par puts). Dans un "programme opérationnel", ilfaudrait gérer convenablement cette situation, ce que nous n'avons pas fait ici.

166 166 Exercices en langage C *Rappelons que, aprè s la lecture par scanf du nombre de chaînes à traiter, le pointeur reste (comme à l'accoutumée) positionné sur le dernier caractère non encore utilisé ;dans le mei leur des cas, il s'agit de \n (mais il peut y avoir d'autres caractères avant si l'utilisateur a été distrait). Dans ces conditions, la lecture ultérieure d'une chaîne par gets conduira à lire... une chaîne vide. Pour éviter ce problème, nous avons placé une instruction getchar qui absorbe ce caractère \n. En toute rigueur, si l'on souhaitait traiter correctement le cas où l'utilisateur a fourni trop d'information pour le scanf précédent, il serait nécessaire d'opérer une lecture d'une chaîne par gets (ilfaudrait prévoir un emplacement à cet effet!). *Dans la fonction trichaines, le premier argument adr a été déclaré par : char * * adr Ils'agit d'un pointeur sur le tableau de pointeurs sur les différentes chaînes. Nous aurions pu également le déclarer par : char * adr[] Notez d'ai leurs que nous avons utilisé le "formalisme" tableau au sein de la fonction e le-même. Ainsi : adr[i] = adr[j] aurait pu se formuler : * (adr+i) = * (adr+j) *Nous vous rappelons que la fonction stricmp compare les deux chaînes dont on lui fournit les adresses et e le fournit une valeur entiè re définie comme étant : - positive si la premiè re chaîne arrive aprè s la seconde, au sens de l'ordre défini par le code des caractères (sans tenir compte de la différence entre majuscules et minuscules pour les 26 lettres de l'alphabet), - nu le si les deux chaînes sont égales, - négative si la premiè re chaîne arrive avant la seconde. DISCUSSION D'une maniè re générale, iln'est pas nécessaire que les chaînes à trier soient, comme ici, implantées en mémoire de maniè re consécutive. De même, la fonction trichaines proposée pourrait tout aussi bien opérer sur des chaînes dont les emplacements auraient été a loués "dynamiquement" (le chapitre V vous propose d'ai leurs un exercice dans ce sens).

167 IV. Tris, fusions et recherche en table 167 IV-4 Fusion de deux tableaux ordonnés La fusion consiste à rassembler en un seultableau ordonné les éléments de deux tableaux, eux-mêmes ordonnés. Enoncé Réaliser une fonction qui fusionne deux tableaux d'entiers ordonnés par valeurs croissantes. On prévoira en arguments : - les adresses des trois tableaux concernés, - le nombre de valeurs de chacun des deux tableaux à fusionner. Pour tester cette fonction, on écrira un programme principalqui lit au clavier deux ensembles de valeurs que l'on triera au préalable à l'aide de la fonction bulle réalisée dans l'exercice IV-2. Exemple combien de valeurs pour le premier tableau? 5 donnez vos valeurs combien de valeurs pour le second tableau? 7 donnez vos valeurs premier tableau à fusionner second tableau à fusionner résultat de la fusion des deux tableaux

168 168 Exercices en langage C ANALYSE La démarche, assez simple, s'inspire de ce le que l'on adopterait pour résoudre "à la main" un telproblème. Ilsuffit, en effet, d'avancer en para lèle dans chacun des deux tableaux à fusionner (t1 et t2), en prélevant, à chaque fois, le plus petit des deux éléments et en l'introduisant dans le tableau résultant t. Plus précisément, nous sommes amenés à utiliser trois indices : - i1 : premier élément de t1 non encore pris en compte, - i2 : premier élément de t2, non encore pris en compte, - i : emplacement du prochain élément à introduire dans t. Nous initialisons ces trois indices à zéro (compte tenu des conventions du C). Nous répétons alors le traitement suivant : Choisir le plus petit des éléments t1(i1) et t2(i2) et le placer en t(i). Incrémenter de 1 la valeur de l'indice correspondant à l'élément extrait (i1 ou i2), ainsi que ce le de i. Nous poursuivons ainsi jusqu'à ce que l'un des deux tableaux soit épuisé. Ilne reste plus alors qu'à recopier la fin de l'autre tableau. Programme #include <stdio.h> #define NMAX1 100 /* nombre maximal de valeurs du premier tableau */ #define NMAX2 100 /* nombre maximal de valeurs du second tableau */ main() { void fusion(int [], int [], int [], int, int ) ; /* proto fonction de fusion */ void bulle(int [], int) ; /* proto fonction servant à assurer l'ordre des tableaux */ int t1 [NMAX1], /* premier tableau à fusionner */ t2 [NMAX2], /* second tablleau à fusionner */ t [NMAX1+NMAX2] ; /* tableau résultant de la fusion */ int nval1, /* nombre de valeurs à prélever dans t1 */ nval2, /* nombre de valeurs à prélever dans t2 */ k ; /* lecture des valeurs des deux ensembles à fusionner */ printf ("combien de valeurs pour le premier tableau? ") ; scanf ("%d", &nval1) ;

169 IV. Tris, fusions et recherche en table 169 if (nval1 > NMAX1) nval1 = NMAX1 ; printf ("donnez vos valeurs\n") ; for (k=0 ; k<nval1 ; k++) scanf ("%d", &t1[k]) ; printf ("combien de valeurs pour le second tableau? ") ; scanf ("%d", &nval2) ; if (nval2 > NMAX2) nval2 = NMAX2 ; printf ("donnez vos valeurs\n") ; for (k=0 ; k<nval2 ; k++) scanf ("%d", &t2[k]) ; /* tri préalable et affichage des valeurs à fusionner */ bulle (t1, nval1) ; bulle (t2, nval2) ; printf ("\npremier tableau à fusionner\n") ; for (k=0 ; k<nval1 ; k++) printf ("%5d", t1[k]) ; printf ("\nsecond tableau à fusionner\n") ; for (k=0 ; k<nval2 ; k++) printf ("%5d", t2[k]) ; /* fusion et affichage résultats */ fusion (t, t1, t2, nval1, nval2) ; printf ("\n\n résultat de la fusion des deux tableaux\n") ; for (k=0 ; k<nval1+nval2 ; k++) printf ("%5d", t[k]) ; /********************************************************/ /* fonction de fusion de deux tableaux */ /********************************************************/ void fusion (int t[], int t1[], int t2[], int nval1, int nval2) /* t1 et t2 : tableaux à fusionner */ /* t :tableau résultant */ /* nval1 : nombre de valeurs du premier tableau t1 */ /* nval2 : nombre de valeurs du second tableau t2 */ { int i1, i2, /* indices courants dans les tableaux à fusionner */ i, /* indice courant dans le tableau résultant */ k ;

170 170 Exercices en langage C i = 0 ; i1 = 0 ; i2 = 0 ; while (i1 < nval1 && i2 < nval2) { if ( t1[i1] < t2[i2] ) t[i++] = t1[i1++] ; else t[i++] = t2[i2++] ; if (i1 == nval1) for (k=i2 ; k<nval2 ; k++) t[i++] = t2[k] ; else for (k=i1 ; k<nval1 ; k++) t[i++] = t1[k] ; /*******************************************************/ /* fonction de tri d'un tableau (méthode de la bulle) */ /*******************************************************/ void bulle (int t[], int nval) { int i, j, tempo, k, permut ; i = -1 ; permut = 1 ; while (i < nval-1 && permut) { permut = 0 ; for (j=nval-2 ; j>i ; j--) if ( t[j] > t[j+1]) { permut = 1 ; tempo = t[j] ; t[j] = t[j+1] ; t[j+1] = tempo ; i++ ; Commentaires *Pour effectuer le tri préalable des deux tableaux fournis en donnée, nous avons repris la fonction bulle réalisée dans l'exercice IV-2. Nous en avons toutefois supprimé les instructions permettant d'afficher, sur demande, les impressions intermédiaires.

171 IV-5 Rech erch e dich otomique IV. Tris, fusions et recherche en table 171 L'exercice II-4 de facturation par code faisait intervenir un algorithme séquentiel de recherche en table. Nous vous proposons ici de réaliser un algorithme plus performant de recherche par "dichotomie". Enoncé Ecrire un programme qui recherche, à partir d'un code d'article (numérique), l'information qui lui est associée, à savoir un libe lé (chaîne) et un prix unitaire (réel). Comme dans l'exercice II-4, le programme utilisera un tableau de structures, déclaré à un niveau global, pour conserver les informations requises. Cette fois, par contre, ces derniè res seront rangées par ordre croissant du numéro de code. La localisation d'un numéro de code donné se fera par une recherche dichotomique. Ce le-ci consiste à profiter de l'ordre du tableau pour accélérer la recherche en procédant comme suit : - On considè re l'élément figurant au "milieu" du tableau. Si le code cherché lui est égal, la recherche est terminée. S'illui est inférieur, on en conclut que le code recherché ne peut se trouver que dans la premiè re moitié du tableau ; dans le cas contraire, on en conclut qu'ilse trouve dans la seconde moitié. - On recommence alors l'opération sur la "moitié" concernée, puis sur la moitié de cette moitié, et ainsi de suite... jusqu'à ce que l'une des conditions suivantes soit satisfaite : *on a trouvé l'élément cherché, *on est sûr qu'ilne figure pas dans le tableau. Exemples code article recherché : 24 le code 24 n'existe pas code article recherché : 19 article de code 19 libellé : Balance de ménage prix :

172 172 Exercices en langage C ANALYSE L'algorithme proposé par l'énoncé suggè re d'utiliser trois variables permettant de spécifier, à un instant donné, la partie du tableau dans laque le s'effectue la recherche : gauche : début de la partie restant à explorer, droite : fin de la partie restant à explorer, milieu : position choisie pour le "milieu" de cette partie restant à explorer. Notez déjà que cette notion de milieu est quelque peu ambiguë. Nous conviendrons qu'e le correspond à la partie entiè re de la moyenne des indices gauche et droite. L'algorithme de recherche par dichotomie peut alors s'énoncer ainsi (t désignant le tableau, n le nombre de codes et x l'élément cherché) : - Initialiser gauche et droite de façon qu'ils désignent l'ensemble du tableau. - Répéter le traitement suivant : *Déterminer le milieu de la partie à explorer : milieu = (gauche + droite) / 2 *Comparer l'élément cherché x avec t(milieu) : + S'ils sont égaux, l'élément cherché est localisé en position milieu, + Si x est supérieur à t(milieu),l'élément cherché ne peut se situer que dans la partie droite ;on réalise l'affectation : debut = milieu dans le cas contraire, l'élément cherché ne peut se situer que dans la partie gauche ;on réalise l'affectation : fin = milieu - 1 Il nous reste à spécifier la condition d'arrê t (ou de poursuite) de cette répétition. On peut déjà noter que, à chaque parcours de la boucle, soit la valeur de gauche augmente, soit ce le de droite diminue. Ainsi, on est sûr qu'au bout d'un nombre fini de tours on aboutira à l'une des situations suivantes : -l'élément a été localisé. -la valeur de gauche est supérieure à ce le de droite. E les nous fournissent donc tout nature lement la condition de fin de notre boucle.

173 IV. Tris, fusions et recherche en table 173 Notez que, dans un premier temps, la valeur de gauche devient égale à ce le de droite ;mais, dans ce cas, nous ne savons pas encore si le seul élément restant à examiner est ou non égal à x ;aussi est-il nécessaire de faire un tour supplémentaire pour s'en assurer. Programme #include <stdio.h> /* structure contenant les informations relatives aux */ /* différents articles */ #define NBART 6 /* nombre total d'articles */ typedef struct { int code ; /* code article */ char * lib ; /* libellé */ float pu ; /* prix unitaire */ t_article ; t_article article [NBART] = { 11, "Gaufrier", 268.0, 14, "Cafetière 12 T", 235.0, 16, "Grille-pain", , 19, "Balance de ménage", 278.0, 25, "Centrifugeuse", 370.0, 26, "Four raclette 6P", ; /* */ #define VRAI 1 /* pour "simuler" des... */ #define FAUX 0 /*... valeurs logiques */ main() { int coderec, /* code article recherché */ codecour, /* code courant */ gauche, /* limite gauche de la recherche */ droite, /* limite droite de la recherche */ milieu, /* nouvelle limite (droite ou gauche */ trouve ; /* indicateur code trouvé/non trouvé */ printf ("code article recherché : ") ; scanf ("%d", &coderec) ; gauche = 0 ; droite = NBART-1 ; trouve = FAUX ;

174 174 Exercices en langage C while (gauche <= droite &&!trouve) { milieu = (gauche+droite) / 2 ; codecour = article[milieu].code ; if ( codecour == coderec ) trouve = VRAI ; else if ( codecour < coderec) gauche = milieu + 1 ; else droite = milieu - 1 ; if (trouve) printf ("article de code %d\nlibellé : %s\nprix : %10.2f", coderec, article[milieu].lib, article[milieu].pu) ; else printf ("le code %d n'existe pas", coderec) ; Commentaires *Notez bien la condition régissant la boucle while : gauche <= droite &&!trouve - D'une part, comme nous l'avons dit dans notre analyse, nous poursuivons notre exploration, même quand les valeurs de gauche et droite sont égales, de maniè re à savoir si le seulélément restant à examiner convient ou non. - D'autre part, nous y faisons intervenir un indicateur logique (trouve). Nous aurions pu nous en passer, à condition de placer un break dans la boucle. Toutefois, dans ce cas, il aurait fa lu prévoir, en fin de boucle, un test supplémentaire permettant de savoir si la recherche avait été fructueuse ou non. DISCUSSION Ilfaut prendre garde, dans le déroulement de l'algorithme, à ne pas se contenter de prendre comme nouve le borne de la partie de tableau à explorer la valeur de milieu, en écrivant : ou : debut = milieu fin = milieu En effet, dans ce cas, on ne peut plus prouver que la boucle s'achève en un nombre fini de tours. Certaines situations conduisent d'ai leurs à une boucle infinie.

175 V : GESTION DYNAMIQUE Les données d'un programme se répartissent en trois catégories : statiques, automatiques et dynamiques. Les données statiques sont définies dè s la compilation ;la gestion des données automatiques reste transparente au programmeur et seules les données dynamiques sont véritablement créées (dans le tas) sur son initiative. D'une maniè re générale, l'utilisation de données dynamiques fournit des solutions à des problèmes tels que : - gestion de données dont l'ampleur n'est pas connue lors de la réalisation du programme, - mise en oeuvre de structures dites dynamiques, te les que les listes chaînées ou les arbres binaires. Ce chapitre vous en propose quelques exemples. V-1 Crible dynamique Enoncé Réaliser un programme qui détermine les premiers nombres premiers par la méthode du crible d'eratosthène, exposée dans l'exercice I-2. Cette fois, par contre, le nombre d'entiers à considérer ne sera pas fixé par le programme, mais fourni en donnée. Le programme a louera dynamiquement l'emplacement mémoire nécessaire au déroulement de l'algorithme. En cas de mémoire insuffisante, ildemandera à l'utilisateur de formuler une demande moins importante. On s'astreindra ici à utiliser la fonction ma loc. Exemple combien d'entiers voulez-vous examiner : 200

176 176 Exercices en langage C entre 1 et 200 les nombres premiers sont : ANALYSE L'algorithme lui-même a déjà été exposé dans l'exercice I-2. La nouveauté réside ici dans l'a location dynamique de l'espace imparti au tableau d'entiers. Pour ce faire, la démarche la plus classique consiste à faire appel à la fonction malloc, comme nous le préconise l'énoncé. Programme #include <stdio.h> #include <stdlib.h> #define VRAI 1 /* pour simuler des...*/ #define FAUX 0 /*... valeurs "logiques" */ main() { unsigned n, /* nombre d'entiers à considérer */ * raye, /* pointeur sur tableau servant de crible */ prem, /* dernier nombre premier considéré */ i ; int na ; /* compteur de nombres premiers affichés */ /* lecture du nombre d'entiers à considérer et allocation dynamique du tableau correspondant */ do { printf ("combien d'entiers voulez-vous examiner : ") ; scanf ("%u", &n) ; raye = (unsigned *) malloc ( (n+1)*sizeof(unsigned) ) ; if (raye == NULL) printf ("** mémoire insuffisante ") ; while (raye == NULL) ;

177 V.Gestion dynamique 177 /* initialisations du crible */ for (i=1 ; i<=n ; i++) /* mise à "zéro" du crible */ raye[i] = FAUX ; raye[1] = VRAI ; /* on raye le nombre 1 */ /* passage au crible */ prem = 1 ; while (prem*prem <= n) { while (raye[++prem] && prem<n ) { /* recherche premier nb prem non rayé */ for (i=2*prem ; i<=n ; i+=prem) /* on raye tous ses multiples */ raye[i] = VRAI ; /* affichage résultats */ printf ("entre 1 et %u les nombres premiers sont :\n", n) ; na = 0 ; for (i=1 ; i<=n ; i++) if (!raye[i] ) { printf ("%7u", i) ; if (++na%10 == 0) printf ("\n") ; /* 10 nombres par ligne */ Commentaires *L'a location de l'espace mémoire nécessaire au tableau d'entiers est réalisée par l'instruction : raye = (unsigned *) malloc ( (n+1)*sizeof(unsigned) ) ; dans laque le raye est un pointeur sur des entiers non signés. Or, le prototype de malloc est précisément : void * malloc (size_t) ; Le résultat fourni par malloc est un "pointeur générique" qui peut être converti implicitement en un pointeur de n'importe queltype. Aussi, l'opérateur de "cast" (unsigned *) n'est pas indispensable ici. Notre instruction d'a location mémoire aurait pu s'écrire : raye = malloc ( (n+1) * sizeof(unsigned) ) ;

178 178 Exercices en langage C En ce qui concerne l'argument de malloc, celui-ci est a priori d'un type size_t défini (par typedef) dans stdlib.h. Le type exact correspondant dépend de l'implémentation (mais ilest toujours non signé - en général, ils'agit de unsigned int). Notez que le résultat fourni par sizeof est du même type size_t. Rappelons que malloc fournit en résultat un pointeur sur le début de la zone concernée lorsque l'a location a réussi et un pointeur nuldans le cas contraire (notez que le symbole NULL est défini dans stdlib.h). *En ce qui concerne l'algorithme de passage au crible, vous remarquez que nous avons employé exactement les mêmes instructions que dans le programme de l'exercice I-2. Pourtant, dans ce dernier, le symbole raye désignait un tableau d'entiers, tandis qu'ici ildésigne un pointeur sur des entiers. Cela est possible parce qu'en langage C, un nom de tableau est un pointeur (constant). DISCUSSION *Le choix du type unsigned pour n est quelque peu arbitraire ;ilest guidé par le fait que malloc admet généralement un argument de ce type. En supposant que telest le cas, on constate qu'alors l'expression : (n+1) * sizeof (unsigned) conduit à des valeurs erronées dès que la valeur de n*sizeof(int) dépasse la capacité du type int (n'oubliez pas qu'iln'y a pas de détection de dépassement de capacité pour les opérations portant sur des entiers). Le résultat peut alors être catastrophique car le nombre d'octets demandés à malloc se trouve être inférieur à celui rée lement utilisé. Le problème se complique encore un peu si l'on tient compte de ce que, dans certaines implémentations, le type size_t peu correspondre à autre chose que unsigned int. En toute rigueur, il faudrait donc s'assurer que le nombre de valeurs demandées par l'utilisateur est effectivement inférieur à une certaine limite à fixer en fonction de l'implémentation concernée. V-2 Création dynamique de ch aînes Lorsqu'un programme doit traiter un grand nombre de chaînes de longueur variable et que ce nombre n'est pas connu a priori, ilpeut s'avérer intéressant de faire a louer dynamiquement (par le programme) l'espace mémoire nécessaire au stockage des chaînes. C'est ce que vous propose cet exercice qui peut être considéré comme préalable à un traitement ultérieur de ces chaînes (par exemple un tri comme vous le proposera l'exercice V-3).

179 V.Gestion dynamique 179 Enoncé Ecrire un programme qui lit un nombre quelconque de chaînes au clavier et qui les range en mémoire dans des emplacements a loués dynamiquement au fur et à mesure des besoins. Les adresses de chacune des chaînes seront conservées dans un tableau de pointeurs. Ce dernier sera réservé dans le programme (en classe automatique) et sa tai le (fixe) imposera donc une valeur maximale au nombre de chaînes qu'ilsera ainsi possible de traiter. L'utilisateur signalera qu'ila fourni sa derniè re chaîne en la faisant suivre d'une chaîne "vide". Le programme affichera ensuite les chaînes lues, à titre de simple contrôle. Remarque : on utilisera la fonction malloc et on supposera que les lignes lues au clavier ne peuvent jamais dépasser 127 caractères. Exemple chaîne numéro 1 (return pour finir) C chaîne numéro 2 (return pour finir) Turbo C chaîne numéro 3 (return pour finir) Basic chaîne numéro 4 (return pour finir) Pascal chaîne numéro 5 (return pour finir) Turbo Pascal chaîne numéro 6 (return pour finir) fin création liste des chaînes créées chaîne numéro 1 C chaîne numéro 2 Turbo C chaîne numéro 3

180 180 Exercices en langage C Basic chaîne numéro 4 Pascal chaîne numéro 5 Turbo Pascal ANALYSE L'énoncé nous impose donc de définir, au sein du programme, un tableau de pointeurs destiné à contenir les adresses des chaînes à créer. Chaque chaîne sera d'abord lue dans une zone intermédiaire (non dynamique). On lui a louera ensuite, dynamiquement, à l'aide de la fonction malloc, un emplacement dont la tai le correspond exactement à sa longueur ;l'adresse ainsi obtenue sera mémorisée dans le tableau de pointeurs. Le traitement sera interrompu : - soit quand le tableau de pointeurs est plein, - soit quand l'utilisateur fournit une chaîne vide. De plus, à chaque a location réalisée par malloc, on s'assurera que l'espace mémoire nécessaire a pu ê tre obtenu. Dans le cas contraire, on prévoira d'interrompre le programme. Programme #include <stdio.h> #include <stdlib.h> /* pour la fonction exit */ #include <string.h> #define NCHMAX 1000 /* nombre maximal de chaînes */ #define LGLIGNE 127 /* longueur maximale d'une ligne d'écran */ main() { char ligne [LGLIGNE+1], /* chaîne servant à lire une ligne écran */ * adr [NCHMAX], /* tableau de pointeurs sur les chaînes */ * ptr ; /* pointeur courant sur une chaîne */ int nch, /* compteur du nombre de chaînes */ i ; /* mise à zéro du tableau de pointeurs */

181 V.Gestion dynamique 181 for (i=0 ; i<nchmax ; i++) adr[i] = NULL ; /* boucle de création dynamique des chaînes */ nch=0 ; while (nch < NCHMAX) /* tant que nb max chaînes non atteint */ { printf ("----- chaîne numéro %d (return pour finir)\n", nch+1) ; gets (ligne) ; if ( strlen(ligne) ) { if ( (ptr = malloc (strlen(ligne)+1))!= NULL) strcpy (adr[nch++]=ptr, ligne) ; else { printf ("\n\n*** erreur allocation dynamique") ; exit(-1) ; /* arrêt si erreur alloc dynam */ else break ; /* sortie boucle si réponse vide */ printf ("\nfin création\n") ; /* liste des chaînes ainsi créées */ printf ("\n\nliste des chaînes crées\n\n") ; i = 0 ; for (i=0 ; i<nch ; i++) printf (" chaîne numéro %d\n%s\n", i+1, adr[i]) ; Commentaires *Ici, compte tenu de ce que nous précisait l'énoncé, nous avons choisi de lire nos chaînes dans un tableau de 128 caractères, à l'aide de la fonction gets. *Nous avons remis à "zéro" le tableau de pointeurs sur nos chaînes. Ils'agit là d'une opération superflue mais qui peut s'avérer utile pendant la phase de mise au point du programme. Notez l'usage du symbole NULL ;prédéfini dans le fichier stdlib.h, ilcorrespond à la constante pointeur nu le. *La création des chaînes est réalisée par une boucle tant que (instruction while), dans laque le nous avons prévu deux autres sorties :

182 182 Exercices en langage C - une sortie par break, dans le cas où l'utilisateur a fourni une chaîne vide, - un arrê t exceptionneldu programme par exit, dans le cas où l'a location dynamique a échoué. Cette fonction (dont le prototype figure dans stdlib.h) requiert un argument ; sa valeur est transmise au système et e le pourrait éventue lement être récupérée par d'autres programmes. Notez que, en l'absence de l'instruction #include relative à stdlib.h, le compilateur accepte un appelde exit sans argument (ilest incapable de détecter l'erreur - laque le n'a d'ai leurs aucune incidence sur l'exécution du programme lui-même). Nature lement, beaucoup d'autres formulations seraient possibles. DISCUSSION *Le fait de réserver le tableau dans le programme (en classe automatique) impose une limite au nombre de chaînes qu'il est ainsi possible de traiter ;cette limite est indépendante de la mémoire rée lement disponible. On peut améliorer quelque peu la situation en faisant également a louer dynamiquement l'espace nécessaire à ce tableau de pointeurs. Il faut toutefois en connaître la tai le (ou du moins une valeur maximale) lors de l'exécution du programme. Cela peut faire l'objet d'une donnée fournie par l'utilisateur comme dans l'exercice suivant. V-3 Tri dynamique de ch aînes Enoncé Ecrire un programme permettant de trier par ordre alphabétique des chaînes fournies en donnée. Comme dans l'exercice précédent, on a louera dynamiquement des emplacements mémoire aux chaînes, au fur et à mesure de leur lecture, et leurs adresses seront conservées dans un tableau de pointeurs. Par contre, ici, ce dernier verra, lui aussi, son emplacement a loué dynamiquement en début de programme ;pour ce faire, on demandera à l'utilisateur de fournir une valeur maximale du nombre de chaînes qu'ilsera amené à fournir. On utilisera l'algorithme de "tri par extraction simple" exposé dans l'exercice V-1 et on fera appelà la fonction malloc. Exemple

183 V.Gestion dynamique 183 nombre maximal de chaînes? chaîne numéro 1 (return pour finir) C chaîne numéro 2 (return pour finir) Turbo C chaîne numéro 3 (return pour finir) Basic chaîne numéro 4 (return pour finir) Pascal chaîne numéro 5 (return pour finir) Turbo Pascal chaîne numéro 6 (return pour finir) Fortran chaîne numéro 7 (return pour finir) ADA chaîne numéro 8 (return pour finir) fin création liste triée des chaînes crées ADA Basic C Fortran Pascal Turbo C Turbo Pascal ANALYSE Ilnous suffit en fait d'adapter le programme de l'exercice précédent, en lui adjoignant : -la réservation dynamique du tableau de pointeurs, -le tri du tableau de chaînes ainsi créé, par réorganisation des pointeurs. Nous utiliserons pour cela l'algorithme de tri par extraction simple Celui-ci a été exposé dans l'énoncé de l'exercice V-1 et son adaptation au tri de chaînes a été expliquée dans l'analyse de l'exercice V-2.

184 184 Exercices en langage C Programme #include <stdio.h> #include <stdlib.h> #include <string.h> #define LGLIGNE 127 /* longueur maximale d'une ligne d'écran */ main() { char ligne [LGLIGNE+1], /* chaîne servant à lire une ligne écran */ * * adr, /* adresse tableau pointeurs sur les chaînes */ * ptr, /* pointeur courant sur une chaîne */ * tempo ; /* pointeur temporaire pour éch. 2 pointeurs */ unsigned nchmax, /* nombre maximal de chaînes */ nch, /* compteur du nombre de chaînes */ i, j, kmax ; /* création et mise à zéro du tableau de pointeurs */ printf ("nombre maximum de chaînes? ") ; scanf ("%d", &nchmax) ; getchar() ; /* pour sauter la validation */ if ( (adr = malloc (nchmax*sizeof(char*)) ) == NULL) { printf ("\n\n*** erreur allocation dynamique") ; exit(-1) ; /* arrêt si erreur alloc dynam */ for (i=0 ; i<nchmax ; i++) adr[i] = NULL ; /* boucle de création dynamique des chaînes */ nch = 0 ; while (nch < nchmax) /* tant que nb max de chaînes non atteint */ { printf (" chaîne numéro %d (return pour finir)\n", nch+1) ; gets (ligne) ; if ( strlen(ligne) ) { if ( ( ptr = malloc (strlen(ligne)+1))!= NULL) strcpy (adr[nch++]=ptr, ligne) ; else { printf ("\n\n*** erreur allocation dynamique") ; exit(-1) ; /* arrêt si erreur alloc dynam */ else break ; /* sortie boucle si réponse vide */

185 V.Gestion dynamique 185 printf ("\nfin création\n") ; /* tri des chaînes par réarrangement des pointeurs */ for (i=0 ; i<nch-1 ; i++) { kmax = i ; for (j=i+1 ; j<nch ; j++) if ( stricmp (adr[kmax], adr[j]) > 0 ) kmax = j ; tempo = adr[kmax] ; adr[kmax] = adr[i] ; adr[i] = tempo ; /* liste triées des chaînes ainsi créées */ printf ("\n\nliste triée des chaînes crées\n\n") ; for (i=0 ; i<nch ; i++) puts ( adr[i] ) ; Commentaires *Dans le programme de l'exercice V-2, le symbole adr désignait un tableau de pointeurs. Ici, ce même symbole désigne un pointeur sur un tableau de pointeurs. Or, malgré cette différence apparente, vous constatez que nous employons toujours la notation : adr[i] avec la même signification dans les deux cas. En fait, dans le précédent programme, adr était une constante pointeur dont la valeur était ce le de l'adresse de début du tableau de pointeurs. Dans le présent programme, adr est une variable pointeur dont la valeur est également ce le de début du tableau de pointeurs. Ainsi, dans les deux cas : adr[i] est équivalent à : *(adr + i) Notez cependant que l'équivalence entre les deux programmes n'est pas totale. En effet, dans le premier cas, adr n'est pas une lvalue (mot anglais dont une traduction approchée pourrait être : valeur à gauche) ;par exemple, l'expression adr++ serait incorrecte. Dans le second cas, par contre, adr est bien une lvalue.

186 186 Exercices en langage C *Nous n'avons pris aucune précaution particuliè re en ce qui concerne les lectures au clavier qui sont réalisées ici par gets et scanf. Indépendamment des anomalies habitue les encourues en cas de données incorrectes (chaîne trop longue pour gets, donnée non numérique pour scanf), un problème supplémentaire apparaît, lié au fait qu'aprè s une lecture par scanf, le pointeur reste positionné sur le dernier caractère non encore utilisé, à savoir ici le \n (du moins si l'utilisateur a validé normalement, sans fournir d'informations supplémentaires). Si la lecture suivante est, à son tour, effectuée par scanf, aucun problème particulier ne se pose, le caractère \n étant simplement ignoré. Iln'en va plus de même lorsque la lecture suivante est effectuée par gets ;dans ce cas, en effet, ce caractère est interprété comme un caractère de "fin" et gets fournit... une chaîne vide. C'est pour éviter ce phénomène que nous avons dû introduire une instruction getchar pour absorber le \n. DISCUSSION Pour pouvoir a louer convenablement l'emplacement du tableau de pointeurs, notre programme a besoin que l'utilisateur lui fournisse une valeur maximale du nombre de chaînes. Si nous souhaitions qu'ilen soit autrement, ilserait nécessaire de pouvoir a louer provisoirement un emplacement à ce tableau, quitte à l'étendre ensuite au fur et à mesure des besoins à l'aide de la fonction realloc. Une te le extension pourrait être réalisée, soit à chaque nouve le chaîne entrée, soit par blocs de tai le fixe (par exemple toutes les 100 chaînes). V-4 Création d'une liste ch aînée On appe le liste chaînée ou liste liée une suite ordonnée d'éléments dans laque le chaque élément, sauf le dernier, comporte un pointeur sur l'élément suivant. Enoncé Ecrire un programme qui crée une liste chaînée d'éléments comportant chacun : - un nom (chaîne) d'au maximum 10 caractères, - un â ge. Les informations correspondantes seront lues au clavier et l'utilisateur frappera un nom "vide" aprè s les données relatives au dernier élément.

187 V.Gestion dynamique 187 Le programme affichera ensuite les informations contenues dans la liste ainsi créée, dans l'ordre inverse de celui dans lequele les auront été fournies. On prévoira deux fonctions :l'une pour la création, l'autre pour la liste. E les posséderont comme unique argument l'adresse de début de la liste (pointeur sur le premier élément). Exemple om : Laurence age : 19 nom : Yvette age : 35 nom : Catherine age : 20 nom : Sebastien age : 21 nom : NOM AGE Sebastien 21 Catherine 20 Yvette 35 Laurence 19 ANALYSE Chaque élément de notre liste sera représenté par une structure. Nous voyons que ce le-ci doit contenir un pointeur sur un élément de même type. Cela fait intervenir une certaine "récursivité" dans la déclaration correspondante, ce qui est accepté en C. En ce qui concerne l'algorithme de création de la liste, deux possibilités s'offrent à nous : - Ajouter chaque nouvelélément à la fin de la liste. Le parcours ultérieur de la liste se fera alors dans le même ordre que celui dans lequelles données correspondantes ont été introduites. - Ajouter chaque nouvelélément en début de liste. Le parcours ultérieur de la liste se fera alors dans l'ordre inverse de celui dans lequelles données correspondantes ont été introduites.

188 188 Exercices en langage C Compte tenu de ce que l'énoncé nous demande d'afficher la liste à l'envers, aprè s sa création, ilparaît plus apportun de choisir la seconde méthode. Comme demandé, la création de la liste sera réalisée par une fonction. Le programme principalse contentera de réserver un pointeur (nommé debut) destiné à désigner le premier élément de la liste. Sa valeur effective sera fournie par la fonction de création. L'algorithme de création, quant à lui, consistera à répéter le traitement d'insertion d'un nouvelélément en début de liste, à savoir : - créer dynamiquement un emplacement pour un nouvelélément et y ranger les informations fournies au clavier, - affecter au pointeur contenu dans ce nouvelélément l'ancienne valeur de debut, - affecter à debutl'adresse de ce nouvelélément. Nous conviendrons, de plus, que le dernier élément de la liste possède un pointeur nul, ce qui nous facilitera l'initialisation de l'algorithme ;en effet, ce le-ci se ramène alors à l'affectation à debut d'une valeur nu le. Programme #include <stdio.h> #include <stdlib.h> #include <string.h> #define LGNOM 20 /* longueur maximale d'un nom */ typedef struct element /* définition du type élément */ { char nom [LGNOM+1] ; /* nom */ int age ; /* age */ struct element * suivant ; /* pointeur element suivant */ t_element ; main() { void creation (t_element * *) ; /* fonction de création de la liste */ void liste (t_element *) ; /* fonction de liste de la liste */ t_element * debut ; /* pointeur sur le début de la liste */ creation (&debut) ; liste (debut) ;

189 V.Gestion dynamique 189 /****************************************************/ /* fonction de création d'une liste chaînée */ /****************************************************/ void creation (t_element * * adeb) { char nomlu [LGNOM+1] ; /* pour lire un nom au clavier */ t_element * courant ; /* pour l'échange de valeurs de pointeurs */ * adeb = NULL ; /* liste vide au départ */ while (1) /* boucle de création apparemment infinie... */ { /*... mais, en fait, interrompue sur "nom vide" */ printf ("nom : ") ; gets (nomlu) ; if (strlen(nomlu)) { courant = (t_element *) malloc (sizeof(t_element)) ; strcpy (courant->nom, nomlu) ; printf ("age : ") ; scanf ("%d", &courant->age) ; getchar() ; /* pour sauter le \n */ courant->suivant = * adeb ; * adeb = courant ; else break ; /* sortie boucle si nom vide */ /******************************************************/ /* fonction de liste d'une liste chaînée */ /******************************************************/ void liste (t_element * debut) { printf ("\n\n NOM AGE\n\n") ; while (debut) { printf ("%15s %3d\n", debut->nom, debut->age) ; debut = debut->suivant ;

190 190 Exercices en langage C Commentaires *Nous avons ici choisi de déclarer notre structure à un niveau globalet de faire appelà typedef. Cette déclaration à un niveau globalévite de devoir décrire la même structure en différents endroits, ce qui serait, non seulement laborieux mais, de surcroît, source d'erreurs. Par contre, le recours à typedef n'apporte qu'une simplification des déclarations des éléments de ce type (dans le cas contraire, ilsuffirait de remplacer t_element par struct element). Notez bien, par contre, qu'iln'est pas possible de remplacer, au sein de la définition de notre structure, l'écriture : par : struct element * suivant t_element * suivant *La fonction de création reçoit en argument l'adresse du pointeur debut, car e le doit pouvoir lui attribuer une valeur. La fonction de liste, quant à e le, se contente de la valeur de ce même pointeur. Cette différence se répercute nature lement sur la maniè re d'utiliser cet argument dans chacune des deux fonctions. Notez d'ai leurs que nous avons pu nous permettre, dans la fonction de liste, de modifier la valeur ainsi reçue (le pointeur debut y décrit successivement les différents éléments de la liste). *Là encore, les lectures au clavier ont été réalisées par scanf et gets, donc sans protections particuliè res. Comme nous l'avons déjà signalé dans le précédent exercice, l'utilisation conjointe de ces deux fonctions pose un problème lié au fait que, aprè s une lecture par scanf, le pointeur reste positionné sur le dernier caractère non encore utilisé, à savoir (généralement) \n. C'est ce qui justifie l'introduction d'une instruction getchar pour absorber ce caractère intempestif.

191 VI : RECURSIVITE La récursivité est une notion délicate mais qui a l'avantage de conduire souvent à des programmes simples. Les trois premiers exercices de ce chapitre sont plutôt des "exercices d'école" destinés à vous faire explorer différentes situations en vous forçant à écrire une fonction récursive, là où, en pratique, on ne serait pas amené à le faire. VI-1 lecture récursive (1) Enoncé Ecrire une fonction récursive de lecture d'une valeur entiè re au clavier. La fonction devra s'appeler e le-même dans le cas où l'information fournie est incorrecte (non numérique). On prévoira une fonction à un argument (l'adresse de la variable pour laque le on veut lire une valeur) et sans valeur de retour. On pourra faire appelà fgets et sscanf pour détecter convenablement les réponses incorrectes. Remarque Nous vous consei lons de comparer cet exercice au suivant dans lequelle même problème est résolu par l'emploi d'une fonction récursive sans argument et avec valeur de retour. Exemple

192 192 Exercices en langage C donnez un nombre entier : un ** réponse incorrecte - redonnez-la : 'à ** réponse incorrecte - redonnez-la : merci pour 40 ANALYSE Au sein de la fonction (que nous nommerons lecture), nous lirons la valeur attendue à l'aide de fgets (..., stdin), associé à sscanf, comme nous l'avons déjà fait dans certains des exercices précédents. Nous considérerons la réponse de l'utilisateur comme correcte lorsque le code de retour de sscanf sera égalà 1. Si tel n'est pas le cas, nous ferons à nouveau appelà la même fonction lecture. Programme #include <stdio.h> #define LG_LIG 20 /* longueur maxi information lue au clavier */ main() { void lecture (int *) ; /* prototype fonction (récursive) de lecture */ int n ; /* entier à lire */ printf ("donnez un nombre entier : ") ; lecture (&n) ; printf ("-- merci pour %d", n) ; void lecture (int *p) { int compte ; /* compteur du nb de valeurs OK */ char ligne[lg_lig+1] ; /* pour lire une ligne au clavier par fgets */ /* +1 pour tenir compte du \0 de fin */ fgets (ligne, LG_LIG, stdin) ; compte = sscanf (ligne, "%d", p) ; if (!compte) { printf ("** réponse incorrecte - redonnez la : ") ; lecture (p) ;

193 VI. Récursivité 193 Commentaires *Notez bien qu'au sein de la fonction lecture, au niveau de l'appel de sscanf, nous voyons apparaître p et non & p, puisque ici p est déjà un pointeur sur la variable dont on veut lire la valeur. *Si nous avions utilisé simplement gets (comme dans l'exercice VI.5 de la premiè re partie) au lieu de fgets (..., stdin), nous aurions pu également nous protéger de mauvaises réponses de l'utilisateur, mais nous aurions dû définir une tai le maximale pour la chaîne lue au clavier ;nous aurions couru le risque de "débordement mémoire", dans le cas où l'utilisateur aurait fourni une réponse trop longue. DISCUSSION Chaque nouvelappelde lecture entraîne l'a location automatique, sur la pile, d'emplacements pour : -l'argument p, -les objets locaux : compte et ligne. Or, en fait, ne sont nécessaires que les valeurs correspondant au dernier appel de lecture (celui où la lecture s'est convenablement déroulée) ;dans ces conditions, l'empilement des différents emplacements a loués au tableau ligne est superflu. Si l'on souhaite faire quelques économies d'espace mémoire à ce niveau, on peut s'arranger pour que cet emplacement ne soit réservé qu'une seule fois : - soit dans le programme appelant (ici le programme principal) ;dans ce cas, ilfaudra en transmettre l'adresse en argument, ce qui entraîne l'empilement d'une variable supplémentaire. - soit en classe globale ;dans ce cas, on peut également traiter de la sorte compte et p (c'est-à -dire, en fait, n), ce qui supprime du même coup tous les arguments et les objets locaux de lecture. Notez qu'ilrestera quand même, à chaque appel, une a location automatique d'espace pour l'adresse de retour. - soit en classe statique (static) au sein de la fonction. Là encore, nous pouvons traiter de la même maniè re la variable compte, la variable p, quant à e le, restant soumise aux empilements.

194 194 Exercices en langage C VI-2 Lecture récursive (2) Enoncé Ecrire une fonction récursive de lecture d'une valeur entiè re au clavier. La fonction devra s'appeler e le-même dans le cas où l'information fournie est incorrecte (non numérique). On prévoira cette fois une fonction dans laque le la valeur de retour est la valeur lue (iln'y aura donc pas d'arguments). Là encore, on pourra faire appelà fgets (..., stdin) et sscanf pour détecter convenablement les réponses incorrectes. Remarque Cet exercice est surtout destiné à être comparé au précédent dans lequelle même problème est résolu par l'emploi d'une fonction avec argument et sans valeur de retour. Exemple donnez un nombre entier : un ** réponse incorrecte - redonnez la : 'à ** réponse incorrecte - redonnez la : merci pour 40 ANALYSE Comme précédemment, au sein de notre fonction (nommée lecture), nous lirons la valeur attendue à l'aide de fgets associé à sscanf. Nous considérerons la réponse de l'utilisateur comme correcte lorsque le code de retour de sscanf sera égalà 1. Si cela n'est pas le cas, nous ferons de nouveau appelà la même fonction lecture.

195 VI. Récursivité 195 Programme #include <stdio.h> #define LG_LIG 20 /* longueur maxi information lue au clavier */ main() { int lecture (void) ; /* fonction (récursive) de lecture */ int n ; /* entier à lire */ printf ("donnez un nombre entier : ") ; n = lecture() ; printf ("-- merci pour %d", n) ; int lecture (void) { int compte, /* compteur du nb de valeurs OK */ p ; /* entier à lire */ char ligne[lg_lig+1] ; /* pour lire une ligne au clavier par fgets */ fgets (ligne, LG_LIG, stdin) ; compte = sscanf (ligne, "%d", &p) ; if (!compte) { printf ("** réponse incorrecte - redonnez-la : ") ; p = lecture() ; return(p) ; Commentaires *Cette fois, on notera que p désigne une variable locale de type int, dont l'emplacement est a loué automatiquement à chaque appelde la fonction lecture, de la même maniè re que pour les autres objets locaux compte et ligne. Par ai leurs, si aucun emplacement n'est a loué ici pour un quelconque argument, ilfaut en prévoir un pour la valeur de retour. On remarque d'ai leurs qu'ici cette valeur se trouve "propagée" de proche en proche, lors du "dépilement" des appels. *Prenez garde à ne pas écrire :

196 196 Exercices en langage C if (!compte) { printf ("** réponse incorrecte - redonnez-la : ") ; p = lecture() ; else return (p) ; car la fonction ne renverrait une valeur que lorsque la lecture se serait déroulée convenablement. Notez d'ai leurs que dans ce cas, bon nombre de compilateurs vous préviendrait par un message d'avertissement ("warning"). Par contre, ilserait tout à fait correct (et équivalent) d'écrire : if (!compte) { printf ("** réponse incorrecte - redonnez la : ") ; return (lecture()) ; else return (p) ; DISCUSSION Les remarques faites dans le précédent exercice à propos des empilements de ligne (et éventue lement compte) s'appliquent encore ici. VI-3 Lecture récursive (3) Enoncé Ecrire une fonction récursive de lecture d'un entier au clavier. La fonction devra s'appeler e le-même dans le cas où l'information fournie est incorrecte. Cette fois, la fonction possédera 3 arguments : -le message qu'e le doit imprimer avant de lire une valeur (le message "donnez un nombre entier :" ne sera donc plus affiché par le programme principal), -l'adresse de la variable dans laque le on doit lire une valeur, -le nombre maximald'essais autorisés.

197 VI. Récursivité 197 E le fournira un code de retour égalà 0 si la lecture a fini par aboutir et à -1 lorsque la lecture n'a pas pu aboutir dans le nombre d'essais impartis. Comme dans les deux précédents exercices, on fera appelà fgets associée à sscanf. Exemples donnez un nombre entier : huit ** réponse incorrecte - redonnez-la : 8 -- merci pour 8 donnez un nombre entier : un ** réponse incorrecte - redonnez-la : deux ** réponse incorrecte - redonnez-la : trois ** réponse incorrecte - redonnez-la : quatre ** réponse incorrecte - redonnez-la : cinq -- nombre d'essais dépassé ANALYSE Le message à imprimer sera transmis sous forme de l'adresse d'une chaîne. La fonction affichera ce message dè s son appel. Son contenu devra donc ê tre : donnez un nombre entier : dans l'appelinitialde la fonction (réalisé dans le programme principal), et : **réponse incorrecte - redonnez -a : dans l'appelde la fonction par e le-même en cas de réponse incorrecte. En ce qui concerne le nombre maximald'appels, on le transmettra par valeur et on s'arrangera pour faire décroître sa valeur de 1 à chaque appel. La récursivité des appels cessera lorsque l'une des deux conditions suivantes sera satisfaite : - valeur lue correcte - on fournira alors 0 comme valeur de retour,

198 198 Exercices en langage C - nombre maximald'appels dépassé - on fournira alors -1 comme valeur de retour. Programme #include <stdio.h> #define LG_LIG 20 /* longueur maxi information lue au clavier */ main() { int lecture (char *, int *, int) ; /* proto fonction (récursive) de lecture */ int n ; /* entier à lire */ const nessais = 5 ; /* nombre d'essais autorisés */ if ( lecture ("donnez un nombre entier : ", &n, nessais)!= -1) printf ("-- merci pour %d", n) ; else printf ("-- nombre d'essais dépassés") ; int lecture (char * mes, int * p, int nmax) /* mes : adresse message à afficher avant lecture */ /* p : adresse de la valeur à lire */ /* nmax : nombre d'essais autorisés */ { int compte ; /* compteur du nb de valeurs OK */ char ligne [LG_LIG] ; /* pour lire une ligne au clavier par fgets */ printf ("%s", mes) ; fgets (ligne, LG_LIG, stdin) ; compte = sscanf (ligne, "%d", p) ; if (!compte) if (--nmax) return (lecture ("** réponse incorrecte - redonnez la : ", p, nmax) ) ; else return (-1) ; else return (0) ; Commentaires *Nous avons choisi ici de faire afficher le message : nombre d'essais dépassé

199 VI. Récursivité 199 dans le programme principal. Ils'agit là d'un choix arbitraire puisque nous aurions tout aussi bien pu le faire afficher par la fonction e le-même. VI-4 Puissance entiè re Enoncé Ecrire une fonction récursive permettant de calculer la valeur de x k pour x réelquelconque et k entier relatif quelconque. On exploitera les propriétés suivantes : x 0 = 1, x k = x pour k = 1, x -k = 1 / x k pour k positif, x k = (x k-1 ) x pour k positif impair, x k = (x k/2 ) x pour k positif pair. On testera cette fonction à l'aide d'un programme principalpermettant à l'utilisateur de fournir en données les valeurs de x et de k. Exemples donnez une valeur réelle : 4 donnez une puissance entière : e+000 à la puissance -2 = e-002 donnez une valeur réelle : 5.2

200 200 Exercices en langage C donnez une puissance entière : e+000 à la puissance 3 = e+002 ANALYSE L'énoncé fournit les "définitions récursives" à employer. Programme #include <stdio.h> main() { double puissance(double, int) ; /* proto fonction d'élévation à la puissance */ double x ; /* valeur dont on cherche la puissance neme */ int n ; /* puissance à laquelle on veut élever x */ printf ("donnez une valeur réelle : ") ; scanf ("%le", &x) ; printf ("donnez une puissance entière : ") ; scanf ("%d", &n) ; printf ("%le à la puissance %d = %le", x, n, puissance (x, n) ) ; double puissance (double x, int n) { double z ; if (n < 0) return (puissance (1.0/x, -n) ) ; /* puissance négative */ else if (n == 0) return (1) ; /* x puissance 0 égale 1 */ else if (n == 1) return (x) ; /* x puissance 1 égale x */ else if (n%2 == 0) { z = puissance (x, n/2) ; /* puissance paire */ return (z*z) ; else return (x * puissance (x, n-1) ) ; /* puissance impaire */

201 VI. Récursivité 201 Commentaires Ilest préférable d'écrire : z = puissance (x, n/2) ; return (z*z) ; plutôt que : return (puissance (x,n/2) * puissance (x,n/2) ) ; qui produirait deux fois plus d'appels de la fonction puissance. VI-5 Fonction d'ackermann Enoncé Ecrire une fonction récursive calculant la valeur de la fonction d'ackermann, définie pour m et n, entiers positifs ou nuls, par : A(m,n) = A(m-1, A(m,n-1) ) pour m>0 et n> 0, A(0,n) = n+ 1 pour n> 0, A(m,0) = A(m-1,1) pour m>0. Cette fonction possédera en argument les valeurs de m et de n et fournira en résultat la valeur de A correspondante. On visualisera l'empilement des appels et leur dépilement en affichant un message accompagné de la valeur des deux arguments lors de chaque entrée dans la fonction ainsi que juste avant sa sortie (dans ce dernier cas, on affichera également la valeur que la fonction s'apprê te à retourner). On testera cette fonction à l'aide d'un programme principalauquelon fournira en données les valeurs de m et de n.

202 202 Exercices en langage C Exemple valeurs de m et n? : 1 1 ** entrée Acker (1, 1) ** entrée Acker (1, 0) ** entrée Acker (0, 1) -- sortie Acker (0, 1) = 2 -- sortie Acker (1, 0) = 2 ** entrée Acker (0, 2) -- sortie Acker (0, 2) = 3 -- sortie Acker (1, 1) = 3 Acker (1, 1) = 3 Programme #include <stdio.h> main() { int m, n, a ; int acker (int, int) ; /* prototype fonction de calcul fonction d'ackermann */ printf ("valeurs de m et n? : ") ; scanf ("%d %d", &m, &n) ; a = acker (m, n) ; printf ("\n\nacker (%d, %d) = %d", m, n, a) ; /***********************************************************/ /* fonction récursive de calcul de la fonction d'ackermann */ /***********************************************************/ int acker (int m, int n) { int a ; /* valeur de la fonction */ printf ("** entrée Acker (%d, %d)\n", m, n) ;

203 VI. Récursivité 203 if (m<0 n<0) a = -1 ; /* cas arguments incorrects */ else if (m == 0) a = n+1 ; else if (n == 0) a = acker (m-1, 1) ; else a = acker (m-1, acker(m, n-1) ) ; printf ("-- sortie Acker (%d, %d) = %d\n", m, n, a) ; return (a) ; VI-6 Tours de Hanoi Enoncé Réaliser une fonction récursive proposant une solution au problème dit des tours de H anoi, lequels'énonce ainsi : On dispose de trois piquets, numérotés 1, 2 et 3 et de n disques de tai les différentes. Au départ, ces disques sont empilés par tai le décroissante sur le piquet numéro 1. Le but du jeu est de déplacer ces n disques du piquet numéro 1 sur le piquet numéro 3, en respectant les contraintes suivantes : - on ne déplace qu'un seuldisque à la fois (d'un piquet à un autre), - un disque ne doit jamais être placé au-dessus d'un disque plus petit que lui. On testera cette fonction avec un programme principalpermettant de choisir, en donnée, le nombre totalde disques à déplacer (n). Si vous n'ê tes pas familiarisé avec ce type de problème, nous vous consei lons de tenter tout d'abord de le résoudre manue lement avant de chercher à programmer la fonction demandée. Exemple combien de disques? 4 déplacer un disque de 1 en 2

204 204 Exercices en langage C déplacer un disque de 1 en 3 déplacer un disque de 2 en 3 déplacer un disque de 1 en 2 déplacer un disque de 3 en 1 déplacer un disque de 3 en 2 déplacer un disque de 1 en 2 déplacer un disque de 1 en 3 déplacer un disque de 2 en 3 déplacer un disque de 2 en 1 déplacer un disque de 3 en 1 déplacer un disque de 2 en 3 déplacer un disque de 1 en 2 déplacer un disque de 1 en 3 déplacer un disque de 2 en 3 ANALYSE Pour n=1, la solution est évidente ;ilsuffit de déplacer l'unique disque du piquet numéro 1 au piquet numéro 3. Dès que n est supérieur à 1, on remarque qu'il est nécessaire d'utiliser le piquet numéro 2 pour des stockages intermédiaires. On peut considérer que le problème consiste à déplacer n disques du piquet numéro 1 vers le piquet numéro 3, en utilisant le piquet numéro 2 comme piquet intermédiaire. On peut montrer que cette opération se décompose en trois opérations plus simples : - déplacer les n-1 disques supérieurs du piquet numéro 1 vers le piquet numéro 2 ;pendant cette phase, on peut utiliser le piquet numéro 3 comme piquet intermédiaire, - déplacer les n-1 disques du piquet numéro 2 vers le piquet numéro 3 ;là encore, on peut utiliser le piquet numéro 1 comme piquet intermédiaire (le disque initialement présent sur ce piquet étant plus grand que tous les disques à déplacer). Cela nous conduit à la réalisation d'une fonction récursive possédant comme arguments : -le nombre de disques à déplacer, -le numéro du piquet "de départ", -le numéro du piquet "d'arrivée", -le numéro du piquet "intermédiaire".

205 VI. Récursivité 205 Programme #include <stdio.h> main() { void hanoi (int, int, int, int) ; int nd ; /* nombre total de disques */ printf ("combien de disques? ") ; scanf ("%d", &nd) ; hanoi (nd, 1, 3, 2) ; /***********************************************/ /* fonction résolvant le pb des tours de hanoi */ /***********************************************/ void hanoi (int n, int depart, int but, int inter) /* n : nombre de disques à déplacer */ /* depart : tour d'où l'on part */ /* but : tour où l'on arrive */ /* inter : tour intermédiaire */ { if (n>0) { hanoi (n-1, depart, inter, but) ; printf ("déplacer un disque de %d en %d\n", depart, but) ; hanoi (n-1, inter, but, depart) ;

206

207 VII : TRAITEMENT DE FICH IERS Les exercices de ce chapitre vous fournissent des exemples classiques de traitement de fichiers correspondant à différents aspects : - traitement séquentiel, - accè s direct, - fichiers de type texte. VII-1 Création séquentie le de fich ier Enoncé Ecrire un programme de création séquentie le d'un fichier comportant, pour un certain nombre de personnes, les informations suivantes, fournies au clavier : - nom (au maximum 20 caractères), - â ge, - nombre d'enfants, - â ge de chacun des différents enfants ;on ne demandera (et donc on n'enregistrera) que l'â ge des 15 premiers enfants (mais le nombre figurant dans le champ précédent pourra ê tre supérieur à 15). L'utilisateur fournira un nom "vide" pour signaler qu'iln'a plus de personnes à enregistrer.

208 208 Exercices en langage C On ne prévoira aucun contrôle particulier au niveau de la saisie des données Exemple donnez le nom du fichier à créer : person pour terminer la saisie, donnez un nom 'vide' --- nom : dubois age : 32 nombre enfants : 1 age enfant no 1 : 7 nom : dunoyer age : 29 nombre enfants : 0 nom : dutronc age : 45 nombre enfants : 3 age enfant no 1 : 21 age enfant no 2 : 18 age enfant no 3 : 17 nom : FIN CREATION FICHIER ANALYSE La structure de chaque enregistrement du fichier découle de l'énoncé. Cependant, en ce qui concerne la maniè re de représenter le nom des personnes, nous devons décider de la présence ou de l'absence du caractère de fin de chaîne (\0). Ici, nous avons choisi, par facilité, d'introduire ce caractère, ce qui implique que la zone correspondante soit de longueur 21. Pour créer notre fichier, nous utiliserons les fonctions de niveau 2, c'est-à -dire ici fopen et fwrite. Rappelons que ce lesci travai lent avec un pointeur sur une structure de type FILE (prédéfini dans stdio.h). La valeur de ce pointeur nous est fournie par fopen ;cette fonction restitue un pointeur nulen cas d'erreur d'ouverture. La création du fichier consiste simplement à répéter les actions :

209 VII. Traitement de fichiers 209 -lecture d'informations au clavier, - écriture de ces informations dans le fichier. Cette répétition doit être interrompue à la rencontre d'un nom vide. Programme #include <stdio.h> #include <string.h> #include <stdlib.h> #define LGNOM 20 /* longueur maxi d'un nom */ #define NBENFMAX 15 /* nombre maxi d'enfants */ #define LNOMFICH 20 /* longueur maxi nom de fichier */ main() { char nomfich [LNOMFICH+1] ; /* nom du fichier à créer */ FILE * sortie ; /* descripteur fichier (niveau 2) */ struct { char nom [LGNOM+1] ; int age ; /* description d'un enregistrement */ int nbenf ; /* du fichier */ int agenf [NBENFMAX] ; bloc ; int i ; /* ouverture fichier à créer */ /* attention : mode d'ouverture w au lieu de wb dans certains cas */ printf ("donnez le nom du fichier à créer : ") ; gets (nomfich) ; if ( (sortie = fopen (nomfich, "w")) == NULL ) { printf ("***** erreur ouverture - abandon programme") ; exit(-1) ; /* création du fichier à partir d'informations */ /* fournies au clavier */ printf ("----- pour terminer la saisie, donnez un nom 'vide' ---\n") ; do { printf ("nom : ") ; /* saisie nom */ gets (bloc.nom) ; if ( strlen(bloc.nom) == 0) break ; /* sortie boucle si nom vide */ printf ("age : ") ; scanf ("%d", &bloc.age) ; /* saisie age */

210 210 Exercices en langage C printf ("nombre enfants : ") ; scanf ("%d", &bloc.nbenf) ; /* saisie nb enfants */ for (i=0 ; i < bloc.nbenf && i < NBENFMAX ; i++) { printf ("age enfant no %d : ", i+1) ; /* saisie age des */ scanf ("%d", &bloc.agenf[i]) ; /* différents enfants */ getchar() ; /* pour éliminer \n */ printf ("\n") ; fwrite (&bloc, sizeof(bloc), 1, sortie) ; /* écriture fichier */ while (1) ; /* fin création */ fclose(sortie) ; printf ("\n FIN CREATION FICHIER ") ; Commentaires *Notez le "mode d'ouverture" wb : w : ouverture en écriture ;si le fichier n'existe pas, ilest créé. S'ilexiste, son ancien contenu est perdu. b : mode dit "binaire" ou "non translaté". En fait, l'indication b ne se justifie que dans les implémentations qui distinguent les fichiers de texte des autres. Une te le distinction est motivée par le fait que le caractère de fin de ligne (\n) possède, sur certains systèmes, une représentation particuliè re obtenue par la succession de deux caractères. La présence de b évite le risque que le fichier concerné soit considéré comme un fichier de type texte, ce qui amènerait une interprétation non souhaitée des couples de caractères représentant une fin de ligne. *Ici, nous avons fait appelà la fonction exit (son prototype figure dans stdlib.h) pour interrompre le programme en cas d'erreur d'ouverture du fichier. Ils'agit là d'un choix arbitraire. Nous aurions pu demander à l'utilisateur de proposer un autre nom de fichier. *En ce qui concerne la boucle de création du fichier, nous avons choisi de la programmer sous forme d'une boucle infinie : do......

211 VII. Traitement de fichiers 211 while (1) ; que nous interrompons au moment opportun par break. Nous aurions pu également choisir d'introduire les premiè res instructions de la boucle dans l'expression conditionnant une instruction while, de cette maniè re : while (printf("nom : "), gets(bloc.nom), strlen(bloc.mot) ) *Comme prévu par l'énoncé, aucun contrôle particulier n'est effectué sur les données qui sont donc lues par scanf et gets. Là encore se pose le problème d'ignorer le \n qui subsiste aprè s une lecture par scanf, ce qui impose d'introduire artificie lement une instruction getchar (pour plus de détails sur ce problème, voyez les commentaires de l'exercice V-3). *Rappelons que la fonction d'écriture dans le fichier (fwrite) possède 4 arguments : - L'adresse de début d'un ensemble de blocs à écrire (notez bien la notation & bloc et non simplement bloc, dans la mesure où le nom d'une structure désigne sa valeur et non son adresse, comme cela est le cas pour un tableau). - La tai le d'un bloc. Notez qu'ici nous avons utilisé la fonction sizeof, ce qui assure la portabilité du programme. - Le nombre de blocs de cette tai le à écrire (ici, 1). - L'adresse de la structure décrivant le fichier (e le a été fournie par fopen). DISCUSSION *Ce programme n'examine pas le code de retour de fwrite, lequelprécise le nombre de blocs rée lement écrits dans le fichier (ce nombre étant inférieur au nombre souhaité en cas d'erreur d'écriture). Ilfaut toutefois noter, à ce propos, que, généralement, un certain nombre d'erreurs sont "récupérées" par le système qui affiche alors lui-même son propre message. *Comme le prévoyait l'énoncé, ce programme n'est pas protégé d'éventue les erreurs dans les réponses fournies par l'utilisateur. A titre indicatif, voici quelques situations que l'on peut rencontrer : - Si l'utilisateur fournit un nom de fichier de plus de 20 caractères, ily aura écrasement d'informations en mémoire. Ici, il serait toutefois assez facile de remédier à ce problème en attribuant au symbole LNOMFICH une valeur supérieure au nombre de caractères que l'on peut frapper au clavier dans l'implémentation concernée. On pourrait également lire un nombre de caractères limités en utilisant, au lieu de gets (nomfich), l'instruction : fgets (nomfich, LNOMFICH, stdin) ; Notez toutefois que, dans ce cas, les caractères supplémentaires frappés éventue lement par l'utilisateur sur la même "ligne" seraient pris en compte par une prochaine instruction de lecture sur l'entrée standard.

212 212 Exercices en langage C Dans certaines implémentations (notamment Turbo/Borland C et C/QuickC Microsoft), il est possible de régler complètement le problème en utilisant l'instruction cgets qui a le mérite de limiter, non seulement le nombre de caractères pris en compte, mais également ceux effectivement frappés au clavier. - Si l'utilisateur fournit plus de caractères que n'en attend scanf, ceux-ci seront utilisés (avec plus ou moins de bonheur) par une lecture suivante. Là encore, le problème ne peut être convenablement réglé que d'une façon dépendant de l'implémentation, par exemple avec la fonction cgets (associée, cette fois, à sscanf) citée précédemment. - Si l'utilisateur fournit des caractères non numériques là où scanf attend des chiffres, le résultat de la lecture sera arbitraire ;le programme ne s'en apercevra pas puisqu'ilne teste pas le code de retour de scanf (qui fournit le nombre de valeurs effectivement lues). De plus, là encore, les caractères non traités seront repris par une lecture ultérieure. Le premier point peut, là encore, ê tre résolu par l'emploi de sscanf, associé à fgets (..., stdin). Là encore, dans certaines implémentations, cgets (associée à sscanf) permet de régler totalement le problème. VII-2 Liste séquentie le d'un fich ier Enoncé Réaliser un programme permettant d'afficher successivement chacun des enregistrements d'un fichier analogue à ceux créés par le programme précédent. Le programme présentera un seulenregistrement à la fois, accompagné d'un numéro précisant son rang dans le fichier (on attribuera le numéro 1 au premier enregistrement) ;il attendra que l'utilisateur frappe la touche return avant de passer à l'enregistrement suivant. L'affichage des informations sera réalisé par une fonction à laque le on transmettra en argument l'enregistrement à afficher et son numéro. Le modè le même de la structure correspondante sera, quant à lui, défini à un niveau global. Le programme devra s'assurer de l'existence du fichier à lister. Exemple donnez le nom du fichier à lister : person enregistrement numéro : 1 NOM : dubois AGE : 32 NOMBRE D'ENFANTS : 1

213 VII. Traitement de fichiers 213 AGE ENFANT 1 : 7 enregistrement numéro : 2 NOM : dunoyer AGE : 29 NOMBRE D'ENFANTS : 0 enregistrement numéro : 3 NOM : dutronc AGE : 45 NOMBRE D'ENFANTS : 3 AGE ENFANT 1 : 21 AGE ENFANT 2 : 18 AGE ENFANT 3 : FIN LISTE FICHIER Programme #include <stdio.h> #include <string.h> #define LGNOM 20 /* longueur maxi d'un nom */ #define NBENFMAX 15 /* nombre maxi d'enfants */ #define LNOMFICH 20 /* longueur maxi nom de fichier */ struct enreg { char nom [LGNOM+1] ; int age ; int nbenf ; int agenf [NBENFMAX] ; ;

214 214 Exercices en langage C main() { void affiche (struct enreg *, int) ; /* fonction d'affichage */ char nomfich [LNOMFICH+1] ; /* nom du fichier à lister */ FILE * entree ; /* descripteur fichier (niveau 2) */ struct enreg bloc ; /* enregistrement fichier */ int num ; /* numéro d'enregistrement */ /* ouverture fichier à lister */ /* attention : mode d'ouverture : r au lieu de rb dans certains cas */ do { printf ("donnez le nom du fichier à lister : ") ; gets (nomfich) ; if ( (entree = fopen (nomfich, "rb")) == 0 ) printf ("fichier non trouvé\n") ; while (!entree) ; /* liste du fichier */ num = 1 ; while (fread(&bloc, sizeof(bloc), 1, entree),! feof(entree) ) { affiche (&bloc, num++) ; getchar() ; /* attente frappe "return" */ /* fin liste */ fclose(entree) ; printf ("\n\n FIN LISTE FICHIER ") ; /*************************************************/ /* fonction d'affichage d'un enregistrement */ /*************************************************/ void affiche (struct enreg * bloc, int num) { int i ; printf ("\n\nenregistrement numéro : %d\n\n", num) ; printf ("NOM : %s\n", bloc->nom) ; printf ("AGE : %d\n", bloc->age) ; printf ("NOMBRE D'ENFANTS : %d\n", bloc->nbenf) ; for (i=0 ; i < bloc->nbenf && i < NBENFMAX ; i++)

215 VII. Traitement de fichiers 215 printf ("AGE ENFANT %2d : %2d\n", i+1, bloc->agenf[i]) ; Commentaires *Notez le mode d'ouverture rb : r : ouverture en lecture. Si le fichier n'existe pas, fopen fournit un pointeur nul. b : ouverture en mode "binaire" ou "non translaté" (pour plus d'informations sur la différence entre les modes translaté et non translaté, voyez les commentaires de l'exercice VII-1). *Rappelons que la fonction de lecture fread possède 4 arguments, comparables à ceux de fwrite : -l'adresse de début d'un ensemble de blocs à lire, -la tai le d'un bloc (en octets), -le nombre de blocs de cette tai le à lire, -l'adresse de la structure décrivant le fichier (e le a été fournie par fopen). *La fonction feof prend la valeur vrai (1) lorsque la fin de fichier a été effectivement rencontrée. Autrement dit, ilne suffit pas, pour détecter la fin d'un fichier, d'avoir simplement lu son dernier octet ;ilest, de plus, nécessaire d'avoir tenté de lire au-delà. C'est ce qui justifie que cette condition soit examinée aprè s fread et non avant. *Voyez la façon dont nous avons programmé la boucle de lecture des différents enregistrements du fichier. Cela nous évite une sortie en cours de boucle par break, comme dans : do { fread (&bloc, sizeof(bloc), 1, entree) ; if (feof(entree)) break ; affiche (&bloc, num++) ; getchar() ; while (1) ; ou un test supplémentaire dans la boucle comme dans :

216 216 Exercices en langage C do { fread (&bloc, sizeof(bloc), 1, entree) ; if (!feof(entree)) { affiche (&bloc, num++) ; getchar ; while (!feof(entree)) ; DISCUSSION *Ce programme n'examine pas le code de retour de fread (celui-ci précise le nombre de blocs rée lement lus). *Notre programme n'est pas protégé contre la fourniture par l'utilisateur d'un nom de fichier de plus de 20 caractères. Voyez la discussion de l'exercice précédent. *Le passage à l'enregistrement suivant est déclenché par la frappe de return. Mais si l'utilisateur frappe un ou plusieurs caractères (validés par return), ilverra défiler plusieurs enregistrements de suite. La solution à ce problème dépend, ici encore, de l'implémentation. Par exemple, dans un environnement DOS, avec Turbo/Borland C/C+ + ou Quick C/C Microsoft, ilsuffira de "vider le tampon du système" par : avant chaque attente. while (kbhit()) getch ; VII-3 Correction de fich ier Enoncé Réaliser un programme permettant d'effectuer des corrections sur un fichier analogue à ceux créés par le programme de l'exercice VII-1.

217 VII. Traitement de fichiers 217 L'utilisateur désignera un enregistrement par son numéro d'ordre dans le fichier. Le programme s'assurera de son existence et l'affichera d'abord tel quel avant de demander les modifications à lui apporter. Ces derniè res seront effectuées champ par champ. Pour chaque champ, le programme en affichera à nouveau la valeur, puis ildemandera à l'utilisateur d'entrer une éventue le valeur de remplacement. Si aucune modification n'est souhaitée, il suffira à ce dernier de répondre directement par la frappe de return. On prévoira deux fonctions : - une pour l'affichage d'un enregistrement (on pourra reprendre la fonction affiche de l'exercice précédent), - une pour la modification d'un enregistrement. Exemple donnez le nom du fichier à modifier : person numéro enregistrement à modifier (0 pour fin) : 14 numéro enregistrement à modifier (0 pour fin) : 2 enregistrement numéro : 2 NOM : dunoyer AGE : 29 NOMBRE D'ENFANTS : 0 entrez vos nouvelles infos (return si pas de modifs) NOM : Dunoyer AGE : NOMBRE D'ENFANTS : 1 AGE ENFANT 1 : 15 numéro enregistrement à modifier (0 pour fin) : FIN MODIFICATIONS FICHIER

218 218 Exercices en langage C ANALYSE A partir du moment où l'on souhaite retrouver un enregistrement par son rang dans le fichier, ilparaît logique de réaliser un "accè s direct". Rappelons qu'en langage C celui-ci s'obtient en agissant sur la valeur d'un pointeur dans le fichier à l'aide de la fonction fseek. La lecture et l'écriture, quant à e les, restent toujours réalisées par les fonctions fread et fwrite. L'énoncé ne nous impose pas de contrôle sur l'information lue au clavier. Néanmoins, nous devons être en mesure d'accepter et de reconnaître comme te le une "réponse vide". Dans ces conditions, nous ne pouvons pas employer scanf qui risquerait de conduire à un bouclage sur le caractère \n. Une solution à un telproblème consiste à lire tout d'abord la réponse de l'utilisateur sous forme d'une chaîne, ce qui permet de déceler convenablement les réponses vides. Si l'on souhaite une solution dépendante de l'implémentation, cela peut se faire soit avec gets, soit (si l'on souhaite limiter le nombre de caractères pris en compte) avec fgets (..., stdin).ici, nous utiliserons la premiè re possibilité, en faisant appel à une zone de 128 caractères (dans bon nombre d'implémentations, on ne peut pas frapper au clavier de "lignes" plus longues!). Lorsqu'une information numérique est attendue, ilnous suffit alors de "décoder" le contenu de cette chaîne. Cela peut se faire, soit avec la fonction sscanf assortie (ici) d'un format %d, soit avec la fonction standard atoi. Par souci de diversité, nous avons choisi ici la seconde. Programme #include <stdio.h> #include <string.h> #define VRAI 1 /* pour simuler... */ #define FAUX 0 /*... des booléens */ #define LGNOM 20 /* longueur maxi d'un nom */ #define NBENFMAX 15 /* nombre maxi d'enfants */ #define LNOMFICH 20 /* longueur maxi nom de fichier */ struct enreg { char nom [LGNOM+1] ; int age ; int nbenf ; int agenf [NBENFMAX] ; ; main() {

219 VII. Traitement de fichiers 219 void affiche (struct enreg *, int) ; /* fonction d'affichage */ void modifie (struct enreg *) ; /* fonction de modif d'un enreg */ char nomfich [LNOMFICH+1] ; /* nom du fichier à lister */ FILE * fichier ; /* descripteur fichier (niveau 2) */ struct enreg bloc ; /* enregistrement fichier */ int num, /* numéro d'enregistrement */ horsfich ; /* indicateur "logique" */ long nb_enreg, /* nbre d'enregistrements du fichier */ pos ; /* position courante (octets) dans fich */ /* ouverture (en mise à jour) fichier à modifier et calcul de sa taille */ /* attention, mode d'ouverture r+ au lieu de r+b dans certains cas */ do { printf ("donnez le nom du fichier à modifier : ") ; gets (nomfich) ; if ( (fichier = fopen (nomfich, "r+b")) == 0 ) printf ("fichier non trouvé\n") ; while (! fichier) ; fseek (fichier, 0, 2) ; nb_enreg = ftell (fichier) / sizeof(bloc) ; do /* boucle de corrections d'enregistrements */ /* jusqu'à demande d'arrêt */ { do { printf ("\nnuméro enregistrement à modifier (0 pour fin) : "); scanf ("%d", &num) ; getchar() ; /* pour sauter le dernier \n" */ horsfich = num < 0 num > nb_enreg ; while (horsfich) ; if (num == 0 ) break ; /* sortie boucle si demande arrêt */ pos = (num-1) * sizeof(bloc) ; /* calcul position courante */ fseek (fichier, pos, 0) ; /* positionnement fichier */ fread (&bloc, sizeof(bloc), 1, fichier) ; /* lecture enreg */ affiche (&bloc, num) ; /* affichage enreg */ modifie (&bloc) ; /* modif enreg */ fseek (fichier, pos, 0) ; /* repositionnement fichier */ fwrite (&bloc, sizeof(bloc), 1, fichier) ; /* réécriture enreg */

220 220 Exercices en langage C while (1) ; /* fin modifications */ fclose(fichier) ; printf ("\n\n FIN MODIFICATIONS FICHIER \n") ; /*************************************************/ /* fonction d'affichage d'un enregistrement */ /*************************************************/ void affiche (struct enreg * bloc, int num) { int i ; printf ("\n\nenregistrement numéro : %d\n\n", num) ; printf ("NOM : %s\n", bloc->nom) ; printf ("AGE : %d\n", bloc->age) ; printf ("NOMBRE D'ENFANTS : %d\n", bloc->nbenf) ; for (i=0 ; i < bloc->nbenf && i < NBENFMAX ; i++) printf ("AGE ENFANT %2d : %2d\n", i+1, bloc->agenf[i]) ; /***************************************************/ /* fonction de modification d'un enregistrement */ /***************************************************/ void modifie (struct enreg * bloc) { char ligne[127] ; /* chaîne de lecture d'une ligne d'écran */ int i ; printf ("\n\n\entrez vos nouvelles infos (return si pas de modifs)\n") ; printf ("NOM : ") ; gets (ligne) ; if (strlen(ligne)) strcpy (bloc->nom, ligne) ; printf ("AGE : ") ; gets (ligne) ; if (strlen(ligne)) bloc->age = atoi(ligne) ; printf ("NOMBRE D'ENFANTS : ") ;

221 VII. Traitement de fichiers 221 gets (ligne) ; if (strlen(ligne)) bloc->nbenf = atoi(ligne) ; for (i=0 ; i < bloc->nbenf && i < NBENFMAX ; i++) { printf ("AGE ENFANT %2d : ", i+1) ; gets (ligne) ; if (strlen(ligne)) bloc->agenf[i] = atoi(ligne) ; Commentaires *Nous avons ouvert le fichier dans le mode r+ b, lequelautorise la mise à jour (lecture et écriture en un emplacement quelconque du fichier). Notez qu'il faut éviter d'écrire ici rb+, ce qui ne provoquerait généralement pas d'erreur d'ouverture, mais qui empê cherait toute écriture dans le fichier (ici, notre programme ne s'apercevrait pas de cette anomalie puisqu'ilne teste pas le code de retour de fwrite). En ce qui concerne l'indication b, rappelons que ce le-ci n'est indispensable que dans les implémentations qui distinguent les fichiers de type texte des autres. Revoyez éventue lement les commentaires de l'exercice VII.1. *Aprè s l'ouverture du fichier, nous en déterminons la tai le (dans la variable nb_enreg) à l'aide des fonctions fseek et ftell. Plus précisément : fseek (fichier, 0, 2) nous place à 0 octet de la fin (code 2) du fichier et : ftell (fichier) nous donne la position courante du pointeur associé au fichier (qui pointe ici sur la fin). Ilnous est alors facile de la transformer en un nombre d'enregistrements, en la divisant par la tai le d'un enregistrement. *N'oubliez pas qu'aprè s avoir lu un enregistrement, ilest nécessaire, avant de le réécrire, de positionner à nouveau le pointeur dans le fichier. DISCUSSION

222 222 Exercices en langage C *Comme dans les précédents programmes, nous n'avons pas introduit de protections particuliè res vis-à -vis des réponses fournies par l'utilisateur (voyez les discussions des précédents programmes). Toutefois, ici, la maniè re même dont nous avons programmé la saisie des corrections, il n'existe pas, à ce niveau, de risque de "plangage" consécutif à une mauvaise réponse puisque nous n'avons pas fait appelà scanf. VII-4 Comptage de lettres et mots d'un fich ier texte Enoncé Ecrire un programme qui, à partir d'un fichier texte, détermine : -le nombre de caractères qu'ilcontient, -le nombre de chacune des lettres de l'alphabet (on ne considérera que les minuscules), -le nombre de mots, -le nombre de lignes. Les fins de lignes ne devront pas être comptabilisées dans les caractères. On admettra que deux mots sont toujours séparés par un ou plusieurs des caractères suivants : - fin de ligne - espace - ponctuation : :., ;?! - parenthèses : ( ) - gui lemets : " - apostrophe : ' On admettra également, pour simplifier, qu'aucun mot ne peut être commencé sur une ligne et se poursuivre sur la suivante. Ilest consei lé de réaliser une fonction permettant de décider si un caractère donné, transmis en argument, est un des séparateurs mentionnés ci-dessus. E le restituera la valeur 1 lorsque le caractère est un séparateur et la valeur 0 dans le cas contraire.

223 VII. Traitement de fichiers 223 Exemple donnez le nom du fichier à examiner : b:letfic.c votre fichier contient 87 lignes, 371 mots et 3186 caractères dont : 69 fois la lettre a 6 fois la lettre b 74 fois la lettre c 36 fois la lettre d 163 fois la lettre e fois la lettre t 63 fois la lettre u 7 fois la lettre v 3 fois la lettre w 6 fois la lettre x 0 fois la lettre y 1 fois la lettre z et 1979 autres caractères ANALYSE Comme nous avons déjà eu l'occasion de le voir dans les exercices I-5 et I-6, ce type de problème peut être résolu d'au moins deux maniè res : - en effectuant une répétition du traitement d'un caractère, - en effectuant une répétition du traitement d'une ligne, lui-même constitué de la répétition du traitement de chacun des caractères qu'e le contient. Toutefois, ici, nous avons à faire à un fichier dans lequella longueur maximale d'une ligne n'est pas connue a priori, ce qui rend la seconde méthode difficile à mettre en oeuvre. Nous choisirons donc la premiè re ;chaque caractère du fichier sera donc lu par fgetc. Rappelons que certaines implémentations distinguent les fichiers de type texte des autres. Dans ce cas, une te le distinction n'est pas liée au contenu même du fichier (en fait, on peut toujours considérer qu'un fichier, quelque soit son contenu, est formé d'une suite d'octets, donc, finalement, d'une suite de caractères). E le a simplement pour objectif de

224 224 Exercices en langage C faire en sorte que, pour le programme, les "fins de ligne" apparaissent toujours matérialisées par un caractère unique, à savoir \n (alors que, précisément, certaines implémentations, DOS notamment, représentent une fin de ligne par un "coupe" de caractères). Lorsqu'une te le distinction est nécessaire, ilest prévu d'introduire l'indication t, au niveau du mode d'ouverture du fichier (de même qu'on y introduisait l'indication b pour signaler qu'ilne s'agissait pas d'un fichier de type texte). Bien entendu, ici, nous avons tout intérê t à profiter de cette possibilité, de maniè re à nous faciliter la détection des fins de ligne et, surtout, à obtenir un programme portable (à l'exception, éventue lement, de l'indication t). Les comptages à effectuer au niveau des caractères (nombre de caractères, nombre de chacune des minuscules) peuvent être réalisés de façon nature le, à condition toutefois de ne pas comptabiliser \n comme un caractère (au contraire, à sa rencontre, ilfaudra incrémenter le compteur de lignes). En ce qui concerne les comptages de mots, nous procéderons comme dans le premier programme de l'exercice I-6 en employant : - une fonction permettant de tester si un caractère est un séparateur, - un indicateur logique : mot_en_cours. Programme #include <stdio.h> #define LNOMFICH 20 /* longueur maximale d'un nom de fichier */ #define VRAI 1 /* pour "simuler" des... */ #define FAUX 0 /*... valeurs logiques */ main() { int sep (char) ; /* fonction test "caractère séparateur?" */ char nomfich [LNOMFICH+1] ; /* nom du fichier à examiner */ FILE * entree ; /* descripteur du fichier à examiner */ char c ; /* caractère courant */ int compte [26], /* pour compter les différentes lettres */ numl, /* rang lettre courante dans l'alphabet */ ntot, /* compteur nombre total de caractères */ nautres, /* compteur carac autres que minuscules */ nmots, /* compteur du nombre de mots */ nlignes, /* compteur du nombre de lignes */ mot_en_cours, /* indicateur logique : mot trouvé */ i ;

225 VII. Traitement de fichiers 225 /* entrée du nom de fichier à examiner et ouverture */ /* attention, mode r au lieu de rt, dans certains cas */ do { printf ("donnez le nom du fichier à examiner : ") ; gets (nomfich) ; if ( (entree = fopen (nomfich, "rt")) == NULL) printf ("***** fichier non trouvé\n") ; while (entree == NULL) ; /* initialisations */ for (i=0 ; i<26 ; i++) compte[i] = 0 ; ntot = 0 ; nautres = 0 ; nmots = 0 ; nlignes = 0 ; mot_en_cours = FAUX ; /* boucle d'examen de chacun des caractères du fichier */ while ( c = fgetc (entree),! feof (entree) ) { if (c == '\n') nlignes++ ; /* comptages au niveau caractères */ else { ntot++ ; numl = c -'a' ; if (numl >= 0 && numl < 26) compte[numl]++ ; else nautres++ ; if (sep(c)) /* comptages au niveau mots */ { if (mot_en_cours) { nmots++ ; mot_en_cours = FAUX ; else mot_en_cours = VRAI ; /* affichage résultats */ printf ("\nvotre fichier contient %d lignes, %d mots\n", nlignes, nmots) ;

226 226 Exercices en langage C printf ("et %d caractères dont :\n", ntot) ; for (i=0 ; i<26 ; i++) printf ("%d fois la lettre %c\n", compte[i], 'a'+i) ; printf ("\net %d autres caractères\n", nautres) ; /*********************************************************/ /* fonction de test "caractère séparateur" */ /*********************************************************/ int sep (char c) { char sep[12] = {'\n', /* fin de ligne */ ' ', /* espace */ ',', ';', ':', '.', '?', '!', /* ponctuation */ '(', ')', /* parenthèses */ '"', '\'' ; /* guillemets, apostr*/ int nsep=12, /* nbre séparateurs */ i ; i = 0 ; while ( c!=sep[i] && i<nsep ) i++ ; if (i == nsep) return (0) ; else return (1) ; Commentaires Le fichier a été ouvert en mode rt : r : ouverture en lecture. Si le fichier n'existe pas, fopen fournit un pointeur nul. t : ouverture en mode translaté (voyez à ce propos, le premier commentaire de l'exercice VII-1). Notez que le choix du mode translaté n'est jamais absolument indispensable. Toutefois, comme nous l'avons dit dans l'analyse, ilnous facilite la détection de fin de ligne et, de plus, ilrend le programme transportable (par exemple sous UNIX, où une fin de ligne est représentée par \n).

227 VII. Traitement de fichiers 227 DISCUSSION Nous avons supposé (implicitement) que notre programme traitait un véritable fichier texte, autrement dit que ce dernier se terminait par une fin de ligne. Si cela n'était pas le cas : -la derniè re ligne ne serait pas comptabilisée, -le dernier mot ne serait pas comptabilisé, à moins d'ê tre suivi d'au moins un séparateur.

228

229 VIII : ANALYSE NUMERIQUE Ce chapitre vous propose quelques applications du langage C à l'analyse numérique. Nous avons cherché à y introduire les techniques de programmation qui interviennent fréquemment dans ce domaine. Citons, par exemple : -la représentation et les manipulations de matrices, -la représentation de nombres complexes, -la réalisation de modules susceptibles de travai ler avec une fonction quelconque ou avec des tableaux de dimensions quelconques. VIII-1 Produit de matrices rée les Enoncé Ecrire une fonction calculant le produit de deux matrices rée les. On supposera que le premier indice de chaque tableau représentant une matrice correspond à une ligne. On prévoira en arguments : -les adresses des deux matrices à multiplier et ce le de la matrice produit, -le nombre de lignes et le nombre de colonnes de la premiè re matrice, -le nombre de colonnes de la seconde matrice (son nombre de lignes étant obligatoirement égal au nombre de colonnes de la premiè re). Un programme principalpermettra de tester cette fonction.

230 230 Exercices en langage C Exemple MATRICE A MATRICE B PRODUIT A x B ANALYSE Rappelons que si A est une matrice n, p (n lignes et p colonnes) et si B est une matrice p, q, la matrice produit : C = A x B est une matrice n, q définie par : c ij = a ik b kj Programme #define N 5 #define P 4 #define Q 3

231 VIII. Analyse numérique 231 main() { void prod_mat(double *, double *, double *, int, int, int) ; double a[n][p], b[p][q], c[n][q] ; int i, j ; /* initialisation matrice a */ for (i=0 ; i<n ; i++) for (j=0 ; j<p ; j++) a[i][j] = i+j ; /* initialisation matrice b */ for (i=0 ; i<p ; i++) for (j=0 ; j<q ; j++) b[i][j] = i+ j ; /* calcul produit a x b */ /* les "cast" (int *) sont facultatifs */ prod_mat ( (double *) a, (double *) b, (double *) c, N, P, Q) ; /* affichage matrice a */ printf (" MATRICE A\n") ; for (i=0 ; i<n ; i++) { for (j=0 ; j<p ; j++) printf ("%4.0f", a[i][j]) ; printf ("\n") ; printf ("\n") ; /* affichage matrice b */ printf (" MATRICE B\n") ; for (i=0 ; i<p ; i++) { for (j=0 ; j<q ; j++) printf ("%4.0f", b[i][j]) ; printf ("\n") ; printf ("\n") ; /* affichage produit */ printf (" PRODUIT A x B\n") ; for (i=0 ; i<n ; i++) { for (j=0 ; j<q ; j++) printf ("%4.0f", c[i][j]) ; printf ("\n") ;

232 232 Exercices en langage C void prod_mat ( double * a, double * b, double * c, int n, int p, int q) { int i, j, k ; double s ; double *aik, *bkj, *cij ; cij = c ; for (i=0 ; i<n ; i++) for (j=0 ; j<q ; j++) { aik = a + i*p ; bkj = b + j ; s = 0 ; for (k=0 ; k<p ; k++) { s += *aik * *bkj ; aik++ ; bkj += q ; * (cij++) = s ; Commentaires *Dans la fonction prod_mat, nous n'avons pas pu utiliser le "formalisme" des tableaux pour les matrices a, b et c car ce les-ci possèdent deux dimensions non connues lors de la compilation du programme. Rappelons qu'un telproblème ne se pose pas lorsqu'ils'agit de tableaux à une seule dimension (car une notation te le que t[i] a toujours un sens, que le que soit la tai le de t) ou lorsqu'ils'agit d'un tableau à plusieurs dimensions dont seule la premiè re est inconnue (compte tenu de la maniè re dont les éléments d'un tableau sont rangés en mémoire). Dans ces conditions, nous sommes obligé de faire appelau formalisme des pointeurs pour repérer un élément quelconque de nos matrices. Pour ce faire, nous transmettons à la fonction prodmatl'adresse de début des trois matrices concernées. Notez qu'en toute rigueur (du moins d'aprè s la norme ANSI), dans le programme main, un symbole telque a est du type (double [P]) * (c'est-à -dire qu'il représente un pointeur sur des blocs de P éléments de type double), et non pas simplement du type double*. Ildoit donc ê tre converti dans le type double *, cette conversion ne modifiant pas, en fait, l'adresse correspondante (revoyez éventue lement les commentaires de l'exercice V.5 de la premiè re partie de cet ouvrage). Cette conversion quelque peu fictive peut soit être mise en place automatiquement par le compilateur, au vu du

233 VIII. Analyse numérique 233 prototype, soit être explicitée à l'aide d'un opérateur de "cast" ;cette derniè re façon de faire a souvent le mérite d'éviter des messages d'avertissement intempestifs ("warnings"). *Notez que, dans la définition de la fonction prodmat, nous avons dû tenir compte de la maniè re dont le langage C range en mémoire les éléments d'un tableau à deux dimensions (suivant ce qu'on nomme abusivement les "lignes" du tableau, c'est-à -dire suivant l'ordre obtenu en faisant varier en premier le dernier indice). Plus précisément : - Le symbole aik représente un pointeur courant sur les éléments a ik. Pour chaque valeur de i, aik est initialisé à l'adresse du premier élément de la ligne i de la matrice a (a+i*p) et ilest incrémenté d'une colonne, en même temps que l'indice k (d'où la présence de aik++ dans la boucle en k). - De même, bkj représente un pointeur courant sur les éléments b kj. Pour chaque valeur de j, bkj est initialisé à l'adresse du premier élément de la colonne j de la matrice b (b+j) et ilest incrémenté d'une ligne en même temps que l'indice k (d'où la présence de bkj=bkj+q dans la boucle en k). - Enfin, cij représente un pointeur courant sur les éléments c ij. Ilest initialisé à l'adresse du premier élément de la matrice c. Ilprogresse de 1 à chaque tour de la boucle la plus interne en j (notez qu'iln'en aurait pas été ainsi si nous avions inversé les deux boucles en i et j). DISCUSSION *On a souvent tendance à dire qu'une fonction comme prod_mat travai le sur des matrices de dimensions variables. En fait, le terme est quelque peu ambigu. Ainsi, dans notre exemple, les matrices dont on transmet l'adresse en argument à prod_mat ont une tai le bien déterminée dans le programme principal. Iln'en reste pas moins que : - d'une part, la même fonction peut travai ler sur des matrices de tai les différentes, - d'autre part, rien n'empê cherait qu'au sein du programme principal, les matrices a, b et c voient leur tai le définie uniquement lors de l'exécution et leurs emplacements a loués dynamiquement. *Au sein d'une fonction comme prod_mat, ilest possible d'employer le formalisme des tableaux à la place de celui des pointeurs en faisant appelà un artifice. Celui-ci consiste à créer, pour chaque matrice, un tableau de pointeurs contenant l'adresse de début de chaque ligne. Ainsi, par exemple, pour la matrice a, on pourrait réserver un tableau nommé ada par : double * * ada ; Ilserait rempli de la maniè re suivante : for (i=1 ; i<n ; i++) ada[i] = a + i*p ;

234 234 Exercices en langage C Dans ces conditions, en effet, la notation ada [i] [j] correspondrait (compte tenu de l'associativité de gauche à droite de l'opérateur []) à : c'est-à -dire à : (ada [i]) [j] * (ada [i] + j) Autrement dit, cette notation ada [i] [j] désignerait simplement la valeur de l'élément situé à l'intersection de la ligne i et de la colonne j de la matrice a. On notera que pour que cet artifice soit utilisable au sein d'une fonction comme prod_mat, censée travai ler sur des matrices de tai le quelconque, ilest nécessaire que les emplacements des tableaux de pointeurs tels que ada soient a loués dynamiquement. VIII-2 Arithmétique complexe Enoncé Ecrire deux fonctions calculant la somme et le produit de deux nombres complexes. Ces derniers seront représentés par une structure comportant deux éléments de type double, correspondant à la partie rée le et à la partie imaginaire. Chacune de ces fonctions comportera trois arguments : -l'adresse des deux nombres complexes (structures) concernés, -l'adresse du résultat (structure). Un programme principalpermettra de tester ces deux fonctions avec les valeurs complexes : 0,5 + i 1 + i

235 VIII. Analyse numérique 235 Exemple i et i ont pour somme i et pour produit i ANALYSE Soit deux nombres complexes : x = a + ib y = c + id On sait que : x + y = (a+ c) + i (b+ d) et que : x y = (ac - bd) + i (ad + bc) Programme typedef struct { double reel ; double imag ; complexe ; main() { void somme (complexe *, complexe *, complexe *) ; void produit (complexe *, complexe *, complexe *) ; complexe z1, z2, s, p ; z1.reel = 0.5 ; z1.imag = 1.0 ; z2.reel = 1.0 ; z2.imag = 1.0 ; somme (&z1, &z2, &s) ; produit (&z1,&z2, &p) ; printf ("%lf + %lf i et %lf + %lf i \n",

236 236 Exercices en langage C z1.reel, z1.imag, z2.reel, z2.imag) ; printf ("ont pour somme %lf + %lf i \n", s.reel, s.imag) ; printf ("et pour produit %lf + %lf i \n", p.reel, p.imag) ; void somme (complexe * x, complexe * y, complexe * som) { som->reel = x->reel + y->reel ; som->imag = x->imag + y->imag ; void produit (complexe * x, complexe * y, complexe * prod) { prod->reel = x->reel * y->reel - x->imag * y->imag ; prod->imag = x->reel * y->imag + x->imag * y->reel ; Commentaires *Nous avons défini, à un niveau global, un modè le de structure nommé complexe. *Notez bien que, dans le programme principal, l'accè s à une structure se fait par l'opérateur "." (comme dans z1.reel) car z1 désigne ici la valeur d'une structure ;par contre, dans les fonctions, ilse fait par l'opérateur -> (comme dans x- >reel) car x désigne alors l'adresse d'une structure. On peut toutefois éviter l'emploi de cet opérateur, en remarquant que x-> reelest équivalent à (*x).reel. *En toute rigueur, d'aprè s la norme ANSI, ilest possible de transmettre, en argument d'une fonction, la valeur d'une structure. Aussi, aurions-nous pu prévoir que somme et produit reçoivent les valeurs des complexes sur lesquels porte l'opération. En revanche, le résultat devrait toujours être transmis par valeur puisque déterminé par la fonction e lemême. Par exemple, la définition de somme aurait pu ê tre : void somme (complexe x, complexe y, complexe * som) { prod->reel = x.reel + y.reel ; prod->imag = x.imag + y.imag ;

237 VIII. Analyse numérique 237 DISCUSSION Dans la pratique, les fonctions somme et produit seraient compilées séparément des fonctions les utilisant. Pour ce faire, ilest nécessaire qu'e les disposent de la description de la structure complexe. On voit qu'on risque alors d'ê tre amené à décrire une même structure à différentes reprises. Certes, ici la chose n'est pas bien grave, dans la mesure où cette définition est simple. D'une maniè re générale, toutefois, on a tout intérê t à régler ce type de problème en plaçant une fois pour toutes une te le définition dans un fichier (d'extension h, par exemple) qu'on incorpore par #include dans tous les programmes en ayant besoin. VIII-3 Produit de matrices complexes Enoncé Ecrire une fonction calculant le produit de deux matrices complexes. Chaque matrice sera définie comme un tableau à deux dimensions dans lequelchaque élément sera une structure représentant un nombre complexe ;cette structure sera constituée de deux éléments de type double correspondant à la partie rée le et à la partie imaginaire du nombre. On supposera que le premier indice du tableau représentant une matrice correspond à une ligne. On prévoira en arguments : -les adresses des deux matrices à multiplier, -l'adresse de la matrice produit, -le nombre de lignes et de colonnes de la premiè re matrice, -le nombre de colonnes de la deuxiè me matrice (son nombre de lignes étant obligatoirement égal au nombre de colonnes de la premiè re). On réalisera un programme principalpermettant de tester cette fonction. On pourra éventue lement faire appelaux fonctions somme et produit réalisées dans l'exercice VIII-2 pour calculer la somme et le produit de deux nombres complexes. Exemple MATRICE A 0+ 0i 1+ 2i 2+ 4i 3+ 6i

238 238 Exercices en langage C 1+ 1i 2+ 3i 3+ 5i 4+ 7i 2+ 2i 3+ 4i 4+ 6i 5+ 8i 3+ 3i 4+ 5i 5+ 7i 6+ 9i 4+ 4i 5+ 6i 6+ 8i 7+ 10i MATRICE B 0+ 0i 1+ 2i 2+ 4i 1+ 1i 2+ 3i 3+ 5i 2+ 2i 3+ 4i 4+ 6i 3+ 3i 4+ 5i 5+ 7i PRODUIT A x B i i i i i i i i i i i i i i i ANALYSE Les formules de définition du produit de matrices complexes restent ce les proposées dans l'analyse de l'exercice VIII-1 pour les matrices rée les ;ilsuffit d'y remplacer les opérations + et x portant sur des réels par les opérations somme et produit de deux complexes (les rè gles de ces deux opérations ont été exposées dans l'analyse de l'exercice VIII-2). Programme #define N 5 #define P 4 #define Q 3 typedef struct { double reel ; double imag ; complexe ;

239 main() { void prod_mat (complexe *, complexe *, complexe *, int, int, int) ; complexe a[n][p], b[p][q], c[n][q] ; int i, j ; VIII. Analyse numérique 239 /* initialisation matrice a */ for (i=0 ; i<n ; i++) for (j=0 ; j<p ; j++) { a[i][j].reel = i+j ; a[i][j].imag = i+2*j ; /* initialisation matrice b */ for (i=0 ; i<p ; i++) for (j=0 ; j<q ; j++) { b[i][j].reel = i+j ; b[i][j].imag = i+2*j ; /* calcul produit a x b */ /* les "cast" (complexe *) sont facultatifs */ prod_mat ((complexe *) &a, (complexe *) &b, (complexe *) &c, N, P, Q) ; /* affichage matrice a */ printf (" MATRICE A\n") ; for (i=0 ; i<n ; i++) { for (j=0 ; j<p ; j++) printf ("%4.0lf+%4.0lfi ", a[i][j].reel, a[i][j].imag) ; printf ("\n") ; printf ("\n") ; /* affichage matrice b */ printf (" MATRICE B\n") ; for (i=0 ; i<p ; i++) { for (j=0 ; j<q ; j++) printf ("%4.0lf+%4.0lfi ", b[i][j].reel, b[i][j].imag) ; printf ("\n") ; printf ("\n") ;

240 240 Exercices en langage C /* affichage produit */ printf (" PRODUIT A x B\n") ; for (i=0 ; i<n ; i++) { for (j=0 ; j<q ; j++) printf ("%4.0lf+%4.0lfi ", c[i][j].reel, c[i][j].imag) ; printf ("\n") ; /*********************************************************/ /* fonction de calcul de produit de 2 matrices complexes */ /*********************************************************/ void prod_mat ( complexe * a, complexe * b, complexe * c, int n, int p, int q) { void produit() ; int i, j, k ; complexe s, pr ; complexe *aik, *bkj, *cij ; cij = c ; for (i=0 ; i<n ; i++) for (j=0 ; j<q ; j++) { aik = a + i*p ; bkj = b + j ; s.reel = 0 ; s.imag = 0 ; for (k=0 ; k<p ; k++) { produit (aik, bkj, &pr) ; s.reel += pr.reel ; s.imag += pr.imag ; aik++ ; bkj += q ; * (cij++) = s ; void produit (x, y, prod) complexe *x, *y, *prod ; { prod->reel = x->reel * y->reel - x->imag * y->imag ; prod->imag = x->reel * y->imag + x->imag * y->reel ;

241 VIII. Analyse numérique 241 Commentaires La fonction prod_mat peut être considérée comme une adaptation de la fonction prod_mat de l'exercice VIII-1. Cette fois, les symboles aik, bkj et cij désignent, non plus des pointeurs sur des réels, mais des pointeurs sur une structure représentant un nombre complexe. La souplesse du langage C en matiè re d'opérations arithmétiques sur les pointeurs fait que les instructions d'incrémentation de ces quantités restent les mêmes (l'unité étant ici la structure complexe - soit 2 éléments de type double, au lieu d'une valeur de type double). DISCUSSION Les remarques faites dans l'exercice VIII-2, à propos de la description de la structure complexe restent nature lement valables ici. VIII-4 Rech erch e de zéro d'une fonction par dich otomie Enoncé Ecrire une fonction déterminant, par dichotomie, le zéro d'une fonction quelconque (rée le d'une variable rée le et continue). On supposera connu un interva le [a,b] sur lequella fonction change de signe, c'est-à -dire telque f(a).f(b) soit négatif. On prévoira en arguments : -les valeurs des bornes a et b (de type double) de l'interva le de départ, -l'adresse d'une fonction permettant de calculer la valeur de f pour une valeur quelconque de la variable. On supposera que l'en-tête de cette fonction est de la forme : double fct (x) double x ;

242 242 Exercices en langage C -l'adresse d'une variable de type double destinée à recuei lir la valeur approchée du zéro de f, -la valeur de la précision (absolue) souhaitée (de type double). Le code de retour de la fonction sera de -1 lorsque l'interva le fourni en argument ne convient pas, c'est-à -dire : - soit lorsque la condition a<b n'est pas satisfaite, - soit lorsque la condition f(a).f(b)<0 n'est pas satisfaite. Dans le cas contraire, le code de retour sera égalà 0. Un programme principalpermettra de tester cette fonction. Exemple zéro de la fonction sin entre -1 et 1 à 1e-2 près = e+000 zéro de la fonction sin entre -1 et 2 à 1e-2 près = e-003 zéro de la fonction sin entre -1 et 2 à 1e-12 près = e-013 ANALYSE La démarche consiste donc, aprè s avoir vérifié que l'interva le reçu en argument était convenable, à répéter le traitement suivant : - prendre le milieu m de [a,b] : m = (a+ b)/2 - calculer f(m), - si f(m) = 0, le zéro est en m, - si f(a).f(m)<0, ilexiste un zéro sur [a,m] ;on remplace donc l'interva le [a,b] par [a,m] en faisant : b = m - si f(a).f(m)>0, ilexiste un zéro sur [b,m] ;on remplace donc l'interva le [a,b] par [b,m], en faisant : a = m Le traitement est interrompu soit lorsque l'interva le [a,b] aura été suffisamment réduit, c'est-à -dire lorsque b-a est inférieur à la précision souhaitée, soit lorsque le zéro a été localisé exactement (f(m)=0).

243 VIII. Analyse numérique 243 Programme #include <stdio.h> #include <math.h> /* pour la fonction sin */ main() { /* fonction de recherche d'un zéro par dichotomie */ int dichoto ( double (*(double)(), double, double, double *, double) ; double z, /* zéro recherché */ a, b, /* bornes de l'intervalle de recherche */ eps ; /* précision souhaitée */ dichoto (sin, -1.0, 1.0, &z, 1.0e-2) ; printf ("zéro de la fonction sin entre -1 et 1 à 1e-2 près = %le\n",z); dichoto (sin, -1.0, 2.0, &z, 1.0e-2) ; printf ("zéro de la fonction sin entre -1 et 2 à 1e-2 près = %le\n",z); dichoto (sin, -1.0, 2.0, &z, 1.0e-12) ; printf ("zéro de la fonction sin entre -1 et 2 à 1e-12 près = %le\n",z); /*************************************************************/ /* fonction de recherhce dichotomique du zéro d'une fonction */ /*************************************************************/ int dichoto ( double (* f)(double), double a, double b, double * zero, double eps) { /* f : fonction dont on cherche le zéro */ /* a, b : bornes de l'intervalle de recherche */ /* zero : zéro estimé */ /* eps : précision souhaitée) */ double m, /* milieu de l'intervalle courant */ fm, /* valeur de f(m) */ fa, fb ; /* valeurs de f(a) et de f(b) */ fa = (*f)(a) ; fb = (*f)(b) ; if (fa*fb >= 0 a >= b) return (-1) ; /* intervalle incorrect */ while (b-a > eps) { m = (b+a) / 2.0 ;

244 244 Exercices en langage C fm = (*f)(m) ; if (fm == 0) break ; /* zéro atteint */ if (fa*fm < 0) { b = m ; fb = fm ; else { a = m ; fa = fm ; * zero = m ; return (0) ; Commentaires *Notez, dans la fonction dichoto : -la déclaration de l'argument correspondant à l'adresse de la fonction dont on cherche le zéro : double (*f)(double) Ce le-ci s'interprè te comme suit : (*f) est une fonction recevant un argument de type double et fournissant un résultat de type double, *f est donc une fonction recevant un argument de type double et fournissant un résultat de type double, f est donc un pointeur sur une fonction recevant un argument de type double et fournissant un résultat de type double. -l'utilisation du symbole f ;ainsi (*f)(a) représente la valeur de la fonction (*f) (fonction d'adresse f), à laque le on fournit l'argument a. Les mêmes réflexions s'appliquent au prototype servant à déclarer dichoto. *La fonction dichoto recevant en argument les valeurs des arguments a et b (et non des adresses), nous pouvons nous permettre de les modifier au sein de la fonction, sans que cela ait d'incidence sur les valeurs effectives des bornes définies dans le programme principal. *Voyez comment, dans le programme principal, un symbole comme sin est interprété par le compilateur comme l'adresse d'une fonction prédéfinie ;ilest toutefois nécessaire d'avoir incorporé son prototype (situé dans math.h) ;en

245 VIII. Analyse numérique 245 l'absence de l'instruction #include correspondante, le compilateur détecterait un erreur puisque alors le symbole sin ne serait pas défini. DISCUSSION En théorie, la méthode de dichotomie conduit toujours à une solution, avec une précision aussi grande qu'on le désire, à partir du moment où la fonction change effectivement de signe sur l'interva le de départ. En pratique, toutefois, les choses ne sont pas toujours aussi idy liques, compte tenu de la limitation de la précision des calculs. Tout d'abord, si on impose une précision trop faible par rapport à la précision de l'ordinateur, on peut aboutir à ce que : m = (a+b)/2 soit égalà l'une des deux bornes a ou b. Ilest alors facile de montrer que l'algorithme peut boucler indéfiniment. D'autre part, les valeurs de f(a) et de f(b) sont nécessairement évaluées de maniè re approchée. Dans le cas de formules quelque peu complexes, on peut trè s bien aboutir à une situation dans laque le f(a).f(b) est positif. La premiè re situation est assez facile à éviter : ilsuffit de choisir une précision relative (attention, ici, notre fonction travai le avec une précision absolue) inférieure à ce le de l'ordinateur. Iln'en va pas de même pour la seconde dans la mesure où iln'est pas toujours possible de maîtriser la précision des calculs des valeurs de f.

Claude Delannoy. 3 e édition C++

Claude Delannoy. 3 e édition C++ Claude Delannoy 3 e édition Exercices Exercices C++ en en langage langage delc++ titre 4/07/07 15:19 Page 2 Exercices en langage C++ AUX EDITIONS EYROLLES Du même auteur C. Delannoy. Apprendre le C++.

Plus en détail

I. Introduction aux fonctions : les fonctions standards

I. Introduction aux fonctions : les fonctions standards Chapitre 3 : Les fonctions en C++ I. Introduction aux fonctions : les fonctions standards A. Notion de Fonction Imaginons que dans un programme, vous ayez besoin de calculer une racine carrée. Rappelons

Plus en détail

Initiation à la programmation en Python

Initiation à la programmation en Python I-Conventions Initiation à la programmation en Python Nom : Prénom : Une commande Python sera écrite en caractère gras. Exemples : print 'Bonjour' max=input("nombre maximum autorisé :") Le résultat de

Plus en détail

1. Structure d un programme C. 2. Commentaire: /*..texte */ On utilise aussi le commentaire du C++ qui est valable pour C: 3.

1. Structure d un programme C. 2. Commentaire: /*..texte */ On utilise aussi le commentaire du C++ qui est valable pour C: 3. 1. Structure d un programme C Un programme est un ensemble de fonctions. La fonction "main" constitue le point d entrée pour l exécution. Un exemple simple : #include int main() { printf ( this

Plus en détail

Algorithmique et Programmation, IMA

Algorithmique et Programmation, IMA Algorithmique et Programmation, IMA Cours 2 : C Premier Niveau / Algorithmique Université Lille 1 - Polytech Lille Notations, identificateurs Variables et Types de base Expressions Constantes Instructions

Plus en détail

Chapitre 1 : La gestion dynamique de la mémoire

Chapitre 1 : La gestion dynamique de la mémoire Chapitre 1 : La gestion dynamique de la mémoire En langage C un programme comporte trois types de données : Statiques; Automatiques ; Dynamiques. Les données statiques occupent un emplacement parfaitement

Plus en détail

INITIATION AU LANGAGE C SUR PIC DE MICROSHIP

INITIATION AU LANGAGE C SUR PIC DE MICROSHIP COURS PROGRAMMATION INITIATION AU LANGAGE C SUR MICROCONTROLEUR PIC page 1 / 7 INITIATION AU LANGAGE C SUR PIC DE MICROSHIP I. Historique du langage C 1972 : naissance du C dans les laboratoires BELL par

Plus en détail

Rappels Entrées -Sorties

Rappels Entrées -Sorties Fonctions printf et scanf Syntaxe: écriture, organisation Comportement Données hétérogènes? Gestion des erreurs des utilisateurs 17/11/2013 Cours du Langage C [email protected] ibrahimguelzim.atspace.co.uk

Plus en détail

Info0101 Intro. à l'algorithmique et à la programmation. Cours 3. Le langage Java

Info0101 Intro. à l'algorithmique et à la programmation. Cours 3. Le langage Java Info0101 Intro. à l'algorithmique et à la programmation Cours 3 Le langage Java Pierre Delisle, Cyril Rabat et Christophe Jaillet Université de Reims Champagne-Ardenne Département de Mathématiques et Informatique

Plus en détail

Cours d Algorithmique-Programmation 2 e partie (IAP2): programmation 24 octobre 2007impérative 1 / 44 et. structures de données simples

Cours d Algorithmique-Programmation 2 e partie (IAP2): programmation 24 octobre 2007impérative 1 / 44 et. structures de données simples Cours d Algorithmique-Programmation 2 e partie (IAP2): programmation impérative et structures de données simples Introduction au langage C Sandrine Blazy - 1ère année 24 octobre 2007 Cours d Algorithmique-Programmation

Plus en détail

1/24. I passer d un problème exprimé en français à la réalisation d un. I expressions arithmétiques. I structures de contrôle (tests, boucles)

1/24. I passer d un problème exprimé en français à la réalisation d un. I expressions arithmétiques. I structures de contrôle (tests, boucles) 1/4 Objectif de ce cours /4 Objectifs de ce cours Introduction au langage C - Cours Girardot/Roelens Septembre 013 Du problème au programme I passer d un problème exprimé en français à la réalisation d

Plus en détail

Recherche dans un tableau

Recherche dans un tableau Chapitre 3 Recherche dans un tableau 3.1 Introduction 3.1.1 Tranche On appelle tranche de tableau, la donnée d'un tableau t et de deux indices a et b. On note cette tranche t.(a..b). Exemple 3.1 : 3 6

Plus en détail

TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile

TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile Dans ce TP, vous apprendrez à définir le type abstrait Pile, à le programmer en Java à l aide d une interface

Plus en détail

Chapitre 2 Devine mon nombre!

Chapitre 2 Devine mon nombre! Python 3 : objectif jeux Chapitre 2 Chapitre 2 Devine mon nombre! 2.1. Thèmes abordés dans ce chapitre commentaires modules externes, import variables boucle while condition : if... elif... else la fonction

Plus en détail

Introduction au langage C

Introduction au langage C Introduction au langage C Cours 1: Opérations de base et premier programme Alexis Lechervy Alexis Lechervy (UNICAEN) Introduction au langage C 1 / 23 Les premiers pas Sommaire 1 Les premiers pas 2 Les

Plus en détail

Licence ST Université Claude Bernard Lyon I LIF1 : Algorithmique et Programmation C Bases du langage C 1 Conclusion de la dernière fois Introduction de l algorithmique générale pour permettre de traiter

Plus en détail

Pour signifier qu'une classe fille hérite d'une classe mère, on utilise le mot clé extends class fille extends mère

Pour signifier qu'une classe fille hérite d'une classe mère, on utilise le mot clé extends class fille extends mère L'héritage et le polymorphisme en Java Pour signifier qu'une classe fille hérite d'une classe mère, on utilise le mot clé extends class fille extends mère En java, toutes les classes sont dérivée de la

Plus en détail

IN 102 - Cours 1. 1 Informatique, calculateurs. 2 Un premier programme en C

IN 102 - Cours 1. 1 Informatique, calculateurs. 2 Un premier programme en C IN 102 - Cours 1 Qu on le veuille ou non, les systèmes informatisés sont désormais omniprésents. Même si ne vous destinez pas à l informatique, vous avez de très grandes chances d y être confrontés en

Plus en détail

Le Langage C Version 1.2 c 2002 Florence HENRY Observatoire de Paris Université de Versailles [email protected]

Le Langage C Version 1.2 c 2002 Florence HENRY Observatoire de Paris Université de Versailles florence.henry@obspm.fr Le Langage C Version 1.2 c 2002 Florence HENRY Observatoire de Paris Université de Versailles [email protected] Table des matières 1 Les bases 3 2 Variables et constantes 5 3 Quelques fonctions indispensables

Plus en détail

Algorithmique & Langage C IUT GEII S1. Notes de cours (première partie) cours_algo_lgc1.17.odp. Licence

Algorithmique & Langage C IUT GEII S1. Notes de cours (première partie) cours_algo_lgc1.17.odp. Licence Licence Algorithmique & Langage C Paternité - Pas d'utilisation Commerciale Partage des Conditions Initiales à l'identique 2.0 France Vous êtes libres : * de reproduire, distribuer et communiquer cette

Plus en détail

Programmation C++ (débutant)/instructions for, while et do...while

Programmation C++ (débutant)/instructions for, while et do...while Programmation C++ (débutant)/instructions for, while et do...while 1 Programmation C++ (débutant)/instructions for, while et do...while Le cours du chapitre 4 : le for, while et do...while La notion de

Plus en détail

V- Manipulations de nombres en binaire

V- Manipulations de nombres en binaire 1 V- Manipulations de nombres en binaire L ordinateur est constitué de milliards de transistors qui travaillent comme des interrupteurs électriques, soit ouverts soit fermés. Soit la ligne est activée,

Plus en détail

Chap III : Les tableaux

Chap III : Les tableaux Chap III : Les tableaux Dans cette partie, on va étudier quelques structures de données de base tels que : Les tableaux (vecteur et matrice) Les chaînes de caractères LA STRUCTURE DE TABLEAU Introduction

Plus en détail

INTRODUCTION A JAVA. Fichier en langage machine Exécutable

INTRODUCTION A JAVA. Fichier en langage machine Exécutable INTRODUCTION A JAVA JAVA est un langage orienté-objet pur. Il ressemble beaucoup à C++ au niveau de la syntaxe. En revanche, ces deux langages sont très différents dans leur structure (organisation du

Plus en détail

UEO11 COURS/TD 1. nombres entiers et réels codés en mémoire centrale. Caractères alphabétiques et caractères spéciaux.

UEO11 COURS/TD 1. nombres entiers et réels codés en mémoire centrale. Caractères alphabétiques et caractères spéciaux. UEO11 COURS/TD 1 Contenu du semestre Cours et TDs sont intégrés L objectif de ce cours équivalent a 6h de cours, 10h de TD et 8h de TP est le suivant : - initiation à l algorithmique - notions de bases

Plus en détail

Bases de programmation. Cours 5. Structurer les données

Bases de programmation. Cours 5. Structurer les données Bases de programmation. Cours 5. Structurer les données Pierre Boudes 1 er décembre 2014 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License. Types char et

Plus en détail

Java Licence Professionnelle CISII, 2009-10

Java Licence Professionnelle CISII, 2009-10 Java Licence Professionnelle CISII, 2009-10 Cours 4 : Programmation structurée (c) http://www.loria.fr/~tabbone/cours.html 1 Principe - Les méthodes sont structurées en blocs par les structures de la programmation

Plus en détail

Cours d initiation à la programmation en C++ Johann Cuenin

Cours d initiation à la programmation en C++ Johann Cuenin Cours d initiation à la programmation en C++ Johann Cuenin 11 octobre 2014 2 Table des matières 1 Introduction 5 2 Bases de la programmation en C++ 7 3 Les types composés 9 3.1 Les tableaux.............................

Plus en détail

SUPPORT DE COURS. Langage C

SUPPORT DE COURS. Langage C Dpt Informatique 2010-2011 SUPPORT DE COURS Langage C Semestre 1 par : «CaDePe» Marie-Françoise Canut Marianne de Michiel André Péninou Table des Matières 1 Généralités...8 1.1 Introduction aux langages

Plus en détail

INITIATION A LA PROGRAMMATION

INITIATION A LA PROGRAMMATION 2004-2005 Université Paris Dauphine IUP Génie Mathématique et Informatique INITIATION A LA PROGRAMMATION PROCEDURALE, A L'ALGORITHMIQUE ET AUX STRUCTURES DE DONNEES PAR LE LANGAGE C Maude Manouvrier La

Plus en détail

Programmation en langage C

Programmation en langage C Programmation en langage C Anne CANTEAUT INRIA - projet CODES B.P. 105 78153 Le Chesnay Cedex [email protected] http://www-rocq.inria.fr/codes/anne.canteaut/cours C 2 Table des matières 3 Table des

Plus en détail

Programmation système I Les entrées/sorties

Programmation système I Les entrées/sorties Programmation système I Les entrées/sorties DUT 1 re année Université de Marne La vallée Les entrées-sorties : E/O Entrées/Sorties : Opérations d échanges d informations dans un système informatique. Les

Plus en détail

Centre CPGE TSI - Safi 2010/2011. Algorithmique et programmation :

Centre CPGE TSI - Safi 2010/2011. Algorithmique et programmation : Algorithmique et programmation : STRUCTURES DE DONNÉES A. Structure et enregistrement 1) Définition et rôle des structures de données en programmation 1.1) Définition : En informatique, une structure de

Plus en détail

DE L ALGORITHME AU PROGRAMME INTRO AU LANGAGE C 51

DE L ALGORITHME AU PROGRAMME INTRO AU LANGAGE C 51 DE L ALGORITHME AU PROGRAMME INTRO AU LANGAGE C 51 PLAN DU COURS Introduction au langage C Notions de compilation Variables, types, constantes, tableaux, opérateurs Entrées sorties de base Structures de

Plus en détail

Les chaînes de caractères

Les chaînes de caractères Les chaînes de caractères Dans un programme informatique, les chaînes de caractères servent à stocker les informations non numériques comme par exemple une liste de nom de personne ou des adresses. Il

Plus en détail

Seance 2: En respectant la méthode de programmation par contrat, implémentez les autres fonctions de jeu.

Seance 2: En respectant la méthode de programmation par contrat, implémentez les autres fonctions de jeu. Seance 2: Complétion du code de jeu. (durée max: 2h) Mot clé const et pointeurs: En respectant la méthode de programmation par contrat, implémentez les autres fonctions de jeu. Implémentez jeu_recupere_piece

Plus en détail

Cours Informatique Master STEP

Cours Informatique Master STEP Cours Informatique Master STEP Bases de la programmation: Compilateurs/logiciels Algorithmique et structure d'un programme Programmation en langage structuré (Fortran 90) Variables, expressions, instructions

Plus en détail

Programmation Classique en langage C

Programmation Classique en langage C DI GALLO Frédéric Programmation Classique en langage C Cours du Cycle d Approfondissement CNAM ANGOULEME 2000-2001 DI GALLO Frédéric Page 1 01/04/01 PROGRAMMATION CLASSIQUE : LANGAGE C DI GALLO Frédéric

Plus en détail

Cours 1 : Introduction. Langages objets. but du module. contrôle des connaissances. Pourquoi Java? présentation du module. Présentation de Java

Cours 1 : Introduction. Langages objets. but du module. contrôle des connaissances. Pourquoi Java? présentation du module. Présentation de Java Langages objets Introduction M2 Pro CCI, Informatique Emmanuel Waller, LRI, Orsay présentation du module logistique 12 blocs de 4h + 1 bloc 2h = 50h 1h15 cours, 45mn exercices table, 2h TD machine page

Plus en détail

Les structures. Chapitre 3

Les structures. Chapitre 3 Chapitre 3 Les structures Nous continuons notre étude des structures de données qui sont prédéfinies dans la plupart des langages informatiques. La structure de tableau permet de regrouper un certain nombre

Plus en détail

Algorithmes et Programmes. Introduction à l informatiquel. Cycle de vie d'un programme (d'un logiciel) Cycle de vie d'un programme (d'un logiciel)

Algorithmes et Programmes. Introduction à l informatiquel. Cycle de vie d'un programme (d'un logiciel) Cycle de vie d'un programme (d'un logiciel) Algorithmes et Programmes Introduction à l informatiquel! Vie d'un programme! Algorithme! Programmation : le langage! Exécution et test des programmes Chapitre : Algorithmes et Programmes 2 Cycle de vie

Plus en détail

Atelier C TIA Portal CTIA04 : Programmation des automates S7-300 Opérations numériques

Atelier C TIA Portal CTIA04 : Programmation des automates S7-300 Opérations numériques Atelier C TIA Portal CTIA04 : Programmation des automates S7-300 Opérations numériques CTIA04 Page 1 1. Les types de données sous S7 300 Il existe plusieurs types de données utilisées pour la programmation

Plus en détail

Algorithmique avec Algobox

Algorithmique avec Algobox Algorithmique avec Algobox 1. Algorithme: Un algorithme est une suite d instructions qui, une fois exécutée correctement, conduit à un résultat donné Un algorithme doit contenir uniquement des instructions

Plus en détail

Introduction à la programmation orientée objet, illustrée par le langage C++ Patrick Cégielski [email protected]

Introduction à la programmation orientée objet, illustrée par le langage C++ Patrick Cégielski cegielski@u-pec.fr Introduction à la programmation orientée objet, illustrée par le langage C++ Patrick Cégielski [email protected] Mars 2002 Pour Irène et Marie Legal Notice Copyright c 2002 Patrick Cégielski Université

Plus en détail

Le chiffre est le signe, le nombre est la valeur.

Le chiffre est le signe, le nombre est la valeur. Extrait de cours de maths de 6e Chapitre 1 : Les nombres et les opérations I) Chiffre et nombre 1.1 La numération décimale En mathématique, un chiffre est un signe utilisé pour l'écriture des nombres.

Plus en détail

Définitions. Numéro à préciser. (Durée : )

Définitions. Numéro à préciser. (Durée : ) Numéro à préciser (Durée : ) On étudie dans ce problème l ordre lexicographique pour les mots sur un alphabet fini et plusieurs constructions des cycles de De Bruijn. Les trois parties sont largement indépendantes.

Plus en détail

Travaux pratiques. Compression en codage de Huffman. 1.3. Organisation d un projet de programmation

Travaux pratiques. Compression en codage de Huffman. 1.3. Organisation d un projet de programmation Université de Savoie Module ETRS711 Travaux pratiques Compression en codage de Huffman 1. Organisation du projet 1.1. Objectifs Le but de ce projet est d'écrire un programme permettant de compresser des

Plus en détail

Cours 1 : Introduction Ordinateurs - Langages de haut niveau - Application

Cours 1 : Introduction Ordinateurs - Langages de haut niveau - Application Université de Provence Licence Math-Info Première Année V. Phan Luong Algorithmique et Programmation en Python Cours 1 : Introduction Ordinateurs - Langages de haut niveau - Application 1 Ordinateur Un

Plus en détail

Cours de C++ François Laroussinie. 2 novembre 2005. Dept. d Informatique, ENS de Cachan

Cours de C++ François Laroussinie. 2 novembre 2005. Dept. d Informatique, ENS de Cachan Cours de C++ François Laroussinie Dept. d Informatique, ENS de Cachan 2 novembre 2005 Première partie I Introduction Introduction Introduction Algorithme et programmation Algorithme: méthode pour résoudre

Plus en détail

COMPARAISONDESLANGAGESC, C++, JAVA ET

COMPARAISONDESLANGAGESC, C++, JAVA ET REPUBLIQUE DU BENIN *******@******* MINISTERE DE L ENSEIGNEMENT SUPERIEUR ET DE LA RECHERCHE SCIENTIFIQUE(MESRS) *******@******* UNIVERSITE D ABOMEY CALAVI(UAC) *******@******* ECOLE POLYTECHNIQUE D ABPOMEY

Plus en détail

Le langage C. Séance n 4

Le langage C. Séance n 4 Université Paris-Sud 11 Institut de Formation des Ingénieurs Remise à niveau INFORMATIQUE Année 2007-2008 Travaux pratiques d informatique Le langage C Séance n 4 But : Vous devez maîtriser à la fin de

Plus en détail

Introduction au Langage de Programmation C

Introduction au Langage de Programmation C Faculté Polytechnique de Mons Service d'informatique Introduction au Langage de Programmation C Mohammed Benjelloun 1 ère Candidature Année académique 2003-2004 Avant-propos Ces notes permettent de se

Plus en détail

PROBLEMES D'ORDONNANCEMENT AVEC RESSOURCES

PROBLEMES D'ORDONNANCEMENT AVEC RESSOURCES Leçon 11 PROBLEMES D'ORDONNANCEMENT AVEC RESSOURCES Dans cette leçon, nous retrouvons le problème d ordonnancement déjà vu mais en ajoutant la prise en compte de contraintes portant sur les ressources.

Plus en détail

Chapitre 2. Classes et objets

Chapitre 2. Classes et objets Chapitre 2: Classes et Objets 1/10 Chapitre 2 Classes et objets Chapitre 2: Classes et Objets 2/10 Approche Orientée Objet Idée de base de A.O.O. repose sur l'observation de la façon dont nous procédons

Plus en détail

UE C avancé cours 1: introduction et révisions

UE C avancé cours 1: introduction et révisions Introduction Types Structures de contrôle Exemple UE C avancé cours 1: introduction et révisions Jean-Lou Desbarbieux et Stéphane Doncieux UMPC 2004/2005 Introduction Types Structures de contrôle Exemple

Plus en détail

Les différents types de données et leurs opérations de base

Les différents types de données et leurs opérations de base Séquence 2 Les différents types de données et leurs opérations de base Contenu Présentation générale... 21 Partie 1 Les 3 familles de données et l'opération d'affectation... 22 1. Les trois familles de

Plus en détail

1.6- Génération de nombres aléatoires

1.6- Génération de nombres aléatoires 1.6- Génération de nombres aléatoires 1- Le générateur aléatoire disponible en C++ 2 Création d'un générateur aléatoire uniforme sur un intervalle 3- Génération de valeurs aléatoires selon une loi normale

Plus en détail

Généralités sur le Langage Java et éléments syntaxiques.

Généralités sur le Langage Java et éléments syntaxiques. Généralités sur le Langage Java et éléments syntaxiques. Généralités sur le Langage Java et éléments syntaxiques....1 Introduction...1 Genéralité sur le langage Java....1 Syntaxe de base du Langage...

Plus en détail

Algorithme. Table des matières

Algorithme. Table des matières 1 Algorithme Table des matières 1 Codage 2 1.1 Système binaire.............................. 2 1.2 La numérotation de position en base décimale............ 2 1.3 La numérotation de position en base binaire..............

Plus en détail

LE PROBLEME DU PLUS COURT CHEMIN

LE PROBLEME DU PLUS COURT CHEMIN LE PROBLEME DU PLUS COURT CHEMIN Dans cette leçon nous définissons le modèle de plus court chemin, présentons des exemples d'application et proposons un algorithme de résolution dans le cas où les longueurs

Plus en détail

Les structures de données. Rajae El Ouazzani

Les structures de données. Rajae El Ouazzani Les structures de données Rajae El Ouazzani Les arbres 2 1- Définition de l arborescence Une arborescence est une collection de nœuds reliés entre eux par des arcs. La collection peut être vide, cad l

Plus en détail

Langage Éric Guérin 5 octobre 2010

Langage Éric Guérin 5 octobre 2010 Langage Éric Guérin 5 octobre 2010 Langage C TABLE DES MATIÈRES Table des matières 1 Introduction 7 1.1 Historique........................................... 7 1.2 Architecture matérielle....................................

Plus en détail

Cours d introduction à l informatique. Partie 2 : Comment écrire un algorithme? Qu est-ce qu une variable? Expressions et instructions

Cours d introduction à l informatique. Partie 2 : Comment écrire un algorithme? Qu est-ce qu une variable? Expressions et instructions Cours d introduction à l informatique Partie 2 : Comment écrire un algorithme? Qu est-ce qu une variable? Expressions et instructions Qu est-ce qu un Une recette de cuisine algorithme? Protocole expérimental

Plus en détail

INF2015 Développement de logiciels dans un environnement Agile. Examen intra 20 février 2014 17:30 à 20:30

INF2015 Développement de logiciels dans un environnement Agile. Examen intra 20 février 2014 17:30 à 20:30 Examen intra 20 février 2014 17:30 à 20:30 Nom, prénom : Code permanent : Répondez directement sur le questionnaire. Question #1 5% Quelle influence peut avoir le typage dynamique sur la maintenabilité

Plus en détail

MISE A NIVEAU INFORMATIQUE LANGAGE C - EXEMPLES DE PROGRAMMES. Université Paris Dauphine IUP Génie Mathématique et Informatique 2 ème année

MISE A NIVEAU INFORMATIQUE LANGAGE C - EXEMPLES DE PROGRAMMES. Université Paris Dauphine IUP Génie Mathématique et Informatique 2 ème année 2003-2004 Université Paris Dauphine IUP Génie Mathématique et Informatique 2 ème année MISE A NIVEAU INFORMATIQUE LANGAGE C - EXEMPLES DE PROGRAMMES Maude Manouvrier La reproduction de ce document par

Plus en détail

TP, première séquence d exercices.

TP, première séquence d exercices. TP, première séquence d exercices. Benoît Valiron [email protected] 7 novembre 2010 Introduction Vous écrirez les réponses aux questions courtes sur une feuille à rendre à la fin de la

Plus en détail

1. Structure d'un programme FORTRAN 95

1. Structure d'un programme FORTRAN 95 FORTRAN se caractérise par la nécessité de compiler les scripts, c'est à dire transformer du texte en binaire.(transforme un fichier de texte en.f95 en un executable (non lisible par un éditeur) en.exe.)

Plus en détail

STAGE IREM 0- Premiers pas en Python

STAGE IREM 0- Premiers pas en Python Université de Bordeaux 16-18 Février 2014/2015 STAGE IREM 0- Premiers pas en Python IREM de Bordeaux Affectation et expressions Le langage python permet tout d abord de faire des calculs. On peut évaluer

Plus en détail

SHERLOCK 7. Version 1.2.0 du 01/09/09 JAVASCRIPT 1.5

SHERLOCK 7. Version 1.2.0 du 01/09/09 JAVASCRIPT 1.5 SHERLOCK 7 Version 1.2.0 du 01/09/09 JAVASCRIPT 1.5 Cette note montre comment intégrer un script Java dans une investigation Sherlock et les différents aspects de Java script. S T E M M E R I M A G I N

Plus en détail

Représentation d un entier en base b

Représentation d un entier en base b Représentation d un entier en base b 13 octobre 2012 1 Prérequis Les bases de la programmation en langage sont supposées avoir été travaillées L écriture en base b d un entier est ainsi défini à partir

Plus en détail

Le prototype de la fonction main()

Le prototype de la fonction main() Le prototype de la fonction main() 1. Introduction...1 2. Paramètres et type de retour de la fonction main()...1 3. Exemple 1...2 4. La fonction exit() du C...2 5. Détecter le code de retour d un programme

Plus en détail

Chapitre 2. Eléments pour comprendre un énoncé

Chapitre 2. Eléments pour comprendre un énoncé Chapitre 2 Eléments pour comprendre un énoncé Ce chapitre est consacré à la compréhension d un énoncé. Pour démontrer un énoncé donné, il faut se reporter au chapitre suivant. Les tables de vérité données

Plus en détail

Utilisation d objets : String et ArrayList

Utilisation d objets : String et ArrayList Chapitre 6 Utilisation d objets : String et ArrayList Dans ce chapitre, nous allons aborder l utilisation d objets de deux classes prédéfinies de Java d usage très courant. La première, nous l utilisons

Plus en détail

C++ Programmer. en langage. 8 e édition. Avec une intro aux design patterns et une annexe sur la norme C++11. Claude Delannoy

C++ Programmer. en langage. 8 e édition. Avec une intro aux design patterns et une annexe sur la norme C++11. Claude Delannoy Claude Delannoy Programmer en langage C++ 8 e édition Avec une intro aux design patterns et une annexe sur la norme C++11 Groupe Eyrolles, 1993-2011. Groupe Eyrolles, 2014, pour la nouvelle présentation,

Plus en détail

Cours d Informatique

Cours d Informatique Cours d Informatique 1ère année SM/SMI 2007/2008, Info 2 Département de Mathématiques et d Informatique, Université Mohammed V [email protected] [email protected] 2007/2008 Info2, 1ère année SM/SMI 1

Plus en détail

TD3: tableaux avancées, première classe et chaînes

TD3: tableaux avancées, première classe et chaînes TD3: tableaux avancées, première classe et chaînes de caractères 1 Lestableaux 1.1 Élémentsthéoriques Déclaration des tableaux Pour la déclaration des tableaux, deux notations sont possibles. La première

Plus en détail

Licence Sciences et Technologies Examen janvier 2010

Licence Sciences et Technologies Examen janvier 2010 Université de Provence Introduction à l Informatique Licence Sciences et Technologies Examen janvier 2010 Année 2009-10 Aucun document n est autorisé Les exercices peuvent être traités dans le désordre.

Plus en détail

Feuille TD n 1 Exercices d algorithmique éléments de correction

Feuille TD n 1 Exercices d algorithmique éléments de correction Master Sciences, Technologies, Santé Mention Mathématiques, spécialité Enseignement des mathématiques Algorithmique et graphes, thèmes du second degré Feuille TD n 1 Exercices d algorithmique éléments

Plus en détail

Manuel d utilisation 26 juin 2011. 1 Tâche à effectuer : écrire un algorithme 2

Manuel d utilisation 26 juin 2011. 1 Tâche à effectuer : écrire un algorithme 2 éducalgo Manuel d utilisation 26 juin 2011 Table des matières 1 Tâche à effectuer : écrire un algorithme 2 2 Comment écrire un algorithme? 3 2.1 Avec quoi écrit-on? Avec les boutons d écriture........

Plus en détail

Par combien de zéros se termine N!?

Par combien de zéros se termine N!? La recherche à l'école page 79 Par combien de zéros se termine N!? par d es co llèg es An dré Do ucet de Nanterre et Victor Hugo de Noisy le Grand en seignants : Danielle Buteau, Martine Brunstein, Marie-Christine

Plus en détail

Anis ASSÈS Mejdi BLAGHGI Mohamed Hédi ElHajjej Mohamed Salah Karouia

Anis ASSÈS Mejdi BLAGHGI Mohamed Hédi ElHajjej Mohamed Salah Karouia Ministère de l Enseignement Supérieur, de la Recherche Scientifique et de la Technologie Direction Générale des Etudes Technologiques Institut Supérieur des Etudes Technologiques de Djerba SUPPORT DE COURS

Plus en détail

ALGORITHMIQUE ET PROGRAMMATION En C

ALGORITHMIQUE ET PROGRAMMATION En C Objectifs ALGORITHMIQUE ET PROGRAMMATION Une façon de raisonner Automatiser la résolution de problèmes Maîtriser les concepts de l algorithmique Pas faire des spécialistes d un langage Pierre TELLIER 2

Plus en détail

Cours intensif Java. 1er cours: de C à Java. Enrica DUCHI LIAFA, Paris 7. Septembre 2009. [email protected]

Cours intensif Java. 1er cours: de C à Java. Enrica DUCHI LIAFA, Paris 7. Septembre 2009. Enrica.Duchi@liafa.jussieu.fr . Cours intensif Java 1er cours: de C à Java Septembre 2009 Enrica DUCHI LIAFA, Paris 7 [email protected] LANGAGES DE PROGRAMMATION Pour exécuter un algorithme sur un ordinateur il faut le

Plus en détail

avec des nombres entiers

avec des nombres entiers Calculer avec des nombres entiers Effectuez les calculs suivants.. + 9 + 9. Calculez. 9 9 Calculez le quotient et le rest. : : : : 0 :. : : 9 : : 9 0 : 0. 9 9 0 9. Calculez. 9 0 9. : : 0 : 9 : :. : : 0

Plus en détail

Le Langage C++ Patrick TRAU - ULP IPST version du 02/10/05

Le Langage C++ Patrick TRAU - ULP IPST version du 02/10/05 Le Langage C++ Patrick TRAU - ULP IPST version du 02/10/05 1) Introduction 1.1) Organisation - fonctionnement de l'ordinateur Je suppose que vous savez utiliser un ordinateur, et comprenez comment il fonctionne.

Plus en détail

Présentation du langage et premières fonctions

Présentation du langage et premières fonctions 1 Présentation de l interface logicielle Si les langages de haut niveau sont nombreux, nous allons travaillé cette année avec le langage Python, un langage de programmation très en vue sur internet en

Plus en détail

Logiciel de Base. I. Représentation des nombres

Logiciel de Base. I. Représentation des nombres Logiciel de Base (A1-06/07) Léon Mugwaneza ESIL/Dépt. Informatique (bureau A118) [email protected] I. Représentation des nombres Codage et représentation de l'information Information externe formats

Plus en détail

Initiation. àl algorithmique et à la programmation. en C

Initiation. àl algorithmique et à la programmation. en C Initiation àl algorithmique et à la programmation en C Initiation àl algorithmique et à la programmation en C Cours avec 129 exercices corrigés Illustration de couverture : alwyncooper - istock.com Dunod,

Plus en détail

Programmer en JAVA. par Tama ([email protected]( [email protected])

Programmer en JAVA. par Tama (tama@via.ecp.fr( tama@via.ecp.fr) Programmer en JAVA par Tama ([email protected]( [email protected]) Plan 1. Présentation de Java 2. Les bases du langage 3. Concepts avancés 4. Documentation 5. Index des mots-clés 6. Les erreurs fréquentes

Plus en détail

Solutions du chapitre 4

Solutions du chapitre 4 Solutions du chapitre 4 Structures de contrôle: première partie 4.9 Identifiez et corrigez les erreurs (il peut y en avoir plus d une par segment de code) de chacune des proposition suivantes: a) if (

Plus en détail

UE Programmation Impérative Licence 2ème Année 2014 2015

UE Programmation Impérative Licence 2ème Année 2014 2015 UE Programmation Impérative Licence 2 ème Année 2014 2015 Informations pratiques Équipe Pédagogique Florence Cloppet Neilze Dorta Nicolas Loménie [email protected] 2 Programmation Impérative

Plus en détail

Cours Programmation Système

Cours Programmation Système Cours Programmation Système Filière SMI Semestre S6 El Mostafa DAOUDI Département de Mathématiques et d Informatique, Faculté des Sciences Université Mohammed Premier Oujda [email protected] Février

Plus en détail

Représentation des Nombres

Représentation des Nombres Chapitre 5 Représentation des Nombres 5. Representation des entiers 5.. Principe des représentations en base b Base L entier écrit 344 correspond a 3 mille + 4 cent + dix + 4. Plus généralement a n a n...

Plus en détail

Introduction. I Étude rapide du réseau - Apprentissage. II Application à la reconnaissance des notes.

Introduction. I Étude rapide du réseau - Apprentissage. II Application à la reconnaissance des notes. Introduction L'objectif de mon TIPE est la reconnaissance de sons ou de notes de musique à l'aide d'un réseau de neurones. Ce réseau doit être capable d'apprendre à distinguer les exemples présentés puis

Plus en détail

Stockage du fichier dans une table mysql:

Stockage du fichier dans une table mysql: Stockage de fichiers dans des tables MYSQL avec PHP Rédacteur: Alain Messin CNRS UMS 2202 Admin06 30/06/2006 Le but de ce document est de donner les principes de manipulation de fichiers dans une table

Plus en détail

3IS - Système d'exploitation linux - Programmation système

3IS - Système d'exploitation linux - Programmation système 3IS - Système d'exploitation linux - Programmation système 2010 David Picard Contributions de : Arnaud Revel, Mickaël Maillard [email protected] Environnement Les programmes peuvent être exécutés dans des

Plus en détail

Série TD 3. Exercice 4.1. Exercice 4.2 Cet algorithme est destiné à prédire l'avenir, et il doit être infaillible! Exercice 4.3. Exercice 4.

Série TD 3. Exercice 4.1. Exercice 4.2 Cet algorithme est destiné à prédire l'avenir, et il doit être infaillible! Exercice 4.3. Exercice 4. Série TD 3 Exercice 4.1 Formulez un algorithme équivalent à l algorithme suivant : Si Tutu > Toto + 4 OU Tata = OK Alors Tutu Tutu + 1 Tutu Tutu 1 ; Exercice 4.2 Cet algorithme est destiné à prédire l'avenir,

Plus en détail

MICROINFORMATIQUE NOTE D APPLICATION 1 (REV. 2011) ARITHMETIQUE EN ASSEMBLEUR ET EN C

MICROINFORMATIQUE NOTE D APPLICATION 1 (REV. 2011) ARITHMETIQUE EN ASSEMBLEUR ET EN C Haute Ecole d Ingénierie et de Gestion Du Canton du Vaud MICROINFORMATIQUE NOTE D APPLICATION 1 (REV. 2011) ARITHMETIQUE EN ASSEMBLEUR ET EN C Programmation en mode simulation 1. DOCUMENTS DE RÉFÉRENCE...

Plus en détail

INF111. Initiation à la programmation impérative en C http://ama.liglab.fr/ amini/cours/l1/inf111/ Massih-Reza Amini

INF111. Initiation à la programmation impérative en C http://ama.liglab.fr/ amini/cours/l1/inf111/ Massih-Reza Amini Initiation à la programmation impérative en C http://ama.liglab.fr/ amini/cours/l1// Massih-Reza Amini Université Joseph Fourier Laboratoire d Informatique de Grenoble 2/52 Introduction Structures de contrôle

Plus en détail