Вы находитесь на странице: 1из 245

Exercices en langage C

C. Delannoy
2 Exe rcice s e n langage C

PREM IERE PARTIE :

EXERCICES D 'APPLICATIO N

Ce tte pre m iè re partie vous propose des exercices, à ré s oudre , de préfé re nce , pe ndant la ph ase d'étude du langage C lui-
m ê m e . Elle é pous e la structure d'un cours "classique"1, sous la form e de 7 ch apitre s : types de bas e , opé rate urs e t
e xpre s s ions ;e ntré e s -sortie s conve rsationne lles ;instructions de contrôle ;les fonctions ;les tableaux e t les pointe urs ;
les ch aî nes de caractè re s ;les s tructure s .

Ch aq ue ch apitre com porte :

- des exe rcices d'application im m édiate destiné s à facilite r l'assim ilation du cours corre s pondant,
- des exe rcice s , sans grande difficulté algorith m iq ue m e ttant e n oe uvre les diffé re nte s notions acq uis e s au cours des
pré cédents ch apitre s .
Note z q ue l'utilisation de s fich ie rs, ainsi que la ge s tion dynam iq ue ne s ont pas abordés dans ce tte pre m iè re partie ;ce s
deux points fe ront ch acun l'obje t d'un ch apitre approprié dans la s e conde partie de l'ouvrage .

1 Un telcours vous e s t proposé, par exem pl


e, dans "Apprendre à program m er en Turbo C" ou dans "C norm e ANSI - Guide com plet de
program m ation" du m ê m e auteur, égalem ent aux éditions Eyrolles.
I : TYPES D E BASE,
O PERA TEURS
ET EXPRESSIO NS

Exe rcice I.1

___________________________________________________________________________

Enoncé

Elim ine r les pare nth è s e s s upe rflues dans les e xpre s s ions suivante s :

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 */

___________________________________________________________________________

Sol
ution

a = x+5 /* expression 1 */

L'opé rate ur + e s t prioritaire s ur l'opé rate ur d'affe ctation =.

a = (x=y) + 2 /* expression 2 */

Ici, l'opé rate ur + é tant prioritaire s ur =, les pare nth è s e s s ont indispensables .

a = x==y /* expression 3 */
4 Exe rcice s e n langage C

L'opé rate ur == e s t prioritaire s ur =.

a<b && c<d /* expression 4 */

L'opé rate ur & & e s t prioritaire s ur l'opé rate ur < .

i++ * (n+p) /* expression 5 */

L'opé rate ur + + e s t prioritaire s ur *;e n re vanch e , *e s t prioritaire s ur + ;de sorte q u'on ne pe ut é lim ine r les derniè re s
pare nth è s e s .

Exe rcice I.2

___________________________________________________________________________

Enoncé

Soie nt les déclarations :

char c = '\x01' ;
short int p = 10 ;

Quels sont le type e t la valeur de ch acune d e s e xpre s s ions suivante s :

p + 3 /* 1 */
c + 1 /* 2 */
p + c /* 3 */
3 * p + 5 * c /* 4 */

___________________________________________________________________________

Sol
ution

1) p e s t d'abord soum is à la conve rsion "systé m atiq ue " sh ort -> int, avant d'ê tre ajouté à la valeur 3 (int). Le ré s ultat 13
e s t de type int.

2) c e s t d'abord soum is à la conve rsion "systé m atiq ue " ch ar -> int (ce q ui aboutit à la valeur 1), avant d'ê tre ajouté à la
valeur 1 (int). Le ré s ultat 2 e s t de type int.
I. Types de base, opérate urs e t e xpre s s ions 5
3) p e s t d'abord soum is à la conve rsion systé m atiq ue sh ort -> int, tandis q ue c e s t soum is à la conve rsion systé m atiq ue
ch ar -> int ;les ré s ultats sont alors additionné s pour aboutir à la valeur 11 de type int.

4) p e t c sont d'abord aux m ê m e s conve rsions systé m atiq ue s q ue ci-de s s us ;le ré s ultat 35 e s t de type int.

Exe rcice I.3

___________________________________________________________________________

Enoncé

Soie nt les déclarations :

char c = '\x05' ;
int n = 5 ;
long p = 1000 ;
float x = 1.25 ;
double z = 5.5 ;

Quels sont le type e t la valeur de ch acune d e s e xpre s s ions suivante s :

n + c + p /* 1 */
2 * x + c /* 2 */
(char) n + c /* 3 */
(float) z + n / 2 /* 4 */

___________________________________________________________________________

Sol
ution

1) c e s t tout d'abord conve rti e n int, avant d'ê tre ajouté à n. Le ré s ultat (10), de type int, e s t alors conve rti e n long, avant
d'ê tre ajouté à p. O n obtie nt finalem e nt la valeur 1010, de type long.

2) O n é value d'abord la valeur de 2*x, e n conve rtissant 2 (int) e n float, ce q ui fournit la valeur 2.5 (de type float). Par
ailleurs, c e s t conve rti e n int (conve rsion systé m atiq ue ). O n é value e nsuite la valeur de 2*x, e n conve rtissant 2 (int) e n
float, ce q ui fournit la valeur 2.5 (de type float). Pour e ffe ctue r l'addition, on conve rtit alors la valeur e ntiè re 5 (c) e n
float, avant de l'ajoute r au ré s ultat pré cédent. O n obtie nt finalem e nt la valeur 7.75, de type float.
6 Exe rcice s e n langage C
3) n e s t tout d'abord conve rti e n ch ar (à cause de l'opé rate ur de "cast"), tandis q ue c e s t conve rti (conve rsion
systé m atiq ue ) e n int. Puis, pour procéder à l'addition, ile s t né ce s s aire de re conve rtir la valeur de (ch ar) n e n int.
Finalem e nt, on obtie nt la valeur 10, de type int.

4) z e s t d'abord conve rti e n float, ce q ui fournit la valeur 5.5 (approxim ative , car, e n fait, on obtie nt une valeur un pe u
m oins précise que ne le s e rait 5.5 e xprim é e n double). Par ailleurs, on procè de à la division e ntiè re de n par 2, ce q ui
fournit la valeur e ntiè re 2. Ce tte derniè re e s t e nsuite conve rtie e n float, avant d'ê tre ajouté e à 5.5, ce q ui fournit le
ré s ultat 7.5, de type float.

R e m arque :

D ans la pre m iè re définition de K e rnigh an e t R itch ie , les valeurs de type float é taie nt, e lles aussi, soum ises à une
conve rsion systé m atiq ue e n double. Dans ce cas, les e xpre s s ions 3 et 4 é taie nt alors de type double.

Exe rcice I.4

___________________________________________________________________________

Enoncé

Soie nt les déclarations suivante s :

int n = 5, p = 9 ;
int q ;
float x ;

Quelle e s t la valeur affe cté e aux diffé re nte s variables conce rné e s par ch acune des instructions suivante s :

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 *:

___________________________________________________________________________
I. Types de base, opérate urs e t e xpre s s ions 7

Sol
ution

1) 1

2) 0

3) 5 (p%n vaut 4, tandis q ue p> n vaut 1)

4) 1 (p/n e s t d'abord é valué e n int, ce q ui fournit 1 ;puis le ré s ultat e s t conve rti e n float, avant d'ê tre affe cté à x).

5) 1.8 (p e s t conve rti e n float, avant d'ê tre divisé par le ré s ultat de la conve rsion de n en float).

6) 1.9 (p e s t conve rti e n float, avant d'ê tre ajouté à 0.5 ;le ré s ultat e s t divisé par le ré s ultat de la conve rsion de n en
float).

7) 1 (p e s t conve rti e n float, avant d'ê tre ajouté à 0.5 ;le ré s ultat (5.5) e s t alors conve rti e n int avant d'ê tre divisé par n).

8) 25

9 ) 45

Exe rcice I.5

___________________________________________________________________________

Enoncé

Quels ré s ultats fournit le program m e s uivant :

#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 Exe rcice s e n 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) ;

}
___________________________________________________________________________

Sol
ution

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

Exe rcice I.6

___________________________________________________________________________

Enoncé

Quels ré s ultats fournira ce program m e :

#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) ;
I. Types de base, opérate urs e t e xpre s s ions 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) ;
}
___________________________________________________________________________

Sol
ution

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

Exe rcice I.7

___________________________________________________________________________

Enoncé

Quels ré s ultats fournira ce program m e :

#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 Exe rcice s e n 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) ;
}
___________________________________________________________________________

Sol
ution

Ilne faut pas oublie r q ue les opé rate urs & & e t || n'é value nt leur de uxiè m e opé rande q ue lors q ue ce la e s t né ce s s aire .
Ainsi, ici, iln'e s t pas é valué dans les cas 1 et 3. Voici les ré s ultats fournis par le program m e :

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
II : LES ENTREES-SO RTIES
CO NVERSA TIO NNELLES

Exe rcice II.1

___________________________________________________________________________

Enoncé

Quels s e ront les ré s ultats fournis par ce program m e :

#include <stdio.h>
main ()
{ int n = 543 ;
int p = 5 ;
float x = 34.5678;
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) ;
}
_______________________________________________________________

Sol
ution

A : 543 34.567799
B : 543 34.567799
12 Exe rcice s e n langage C
C : 543 34.567799
D : 34.568 3.457e+01
E : 543 34.567799
F : 543
G : 34.56780
H : 21f : 21f :
I : 1037 : 1037 :

Exe rcice II.2

___________________________________________________________________________

Enoncé

Quels s e ront les ré s ultats fournis par ce program m e :

#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) ;
}
_______________________________________________________________

Sol
ution

A : S
B : S
C : 83 83
D : 53 53
II. Le s e ntré e s -sortie s conve rsationne lles 13
Exe rcice II.3

___________________________________________________________________________

Enoncé

Quelles s e ront les valeurs lues dans les variables n e t p (de type int), par l'instruction suivante :

scanf ("%d %d", &n, &p) ;

lors q u'on lui fournit les données suivante s (le sym bole ^ re pré s e nte un e s pace e t le sym bole @ re pré s e nte une fin de
ligne , c'e s t-à -dire une "validation") :

a)
253^45@
b)
^253^@
^^ 4 ^ 5 @
_______________________________________________________________

Sol
ution

a) n = 243, p = 45

b) n = 253, p = 4 (les dernie rs caractè res de la deuxiè m e ligne pourront é ve ntue llem e nt ê tre utilisés par une instruction
de lecture ulté rie ure ).

Exe rcice II.4

___________________________________________________________________________

Enoncé

Quelles s e ront les valeurs lues dans les variables n e t p (de type int), par l'instruction suivante :

scanf ("%4d %2d", &n, &p) ;

lors q u'on lui fournit les données suivante s (le sym bole ^ re pré s e nte un e s pace e t le sym bole @ re pré s e nte une fin de
ligne , c'e s t-à -dire une "validation") :
14 Exe rcice s e n langage C
a)
12^45@
b)
123456@
c)
123456^7@
d)
1^458@
e)
^^^4567^^8912@

_______________________________________________________________

Sol
ution

R appe lons q ue lors q u'une indication de longue ur e s t pré s e nte dans le code form at fourni à scanf (com m e , par e xe m ple, le
4 de %4d), scanf inte rrom pt son exploration si le nom bre corre s pondant de caractè re s a é té e xploré , sans q u'un
s é parate ur (ou "e s pace blanc") n'ait é té trouvé . Note z bie n, ce pe ndant, q ue les é ve ntue ls caractè re s s é parate urs "sauté s "
auparavant ne s ont pas considérés dans ce com pte . Voici les ré s ultats obte nus :

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 obtie ndrait e xacte m e nt les m ê m e s ré s ultats sans indication de longue ur (c'e s t-à -dire ave c %d %d). En b, e n
re vanch e , sans l'indication de longue ur 4, les ré s ultats s e raie nt diffé re nts (n vaudrait 123456, tandis q u'ilm anq ue rait des
inform ations pour p). En c, les inform ations ^ et 7 ne s ont pas prises en com pte par scanf (e lles le s e ront é ve ntue llem e nt
par une proch aine lecture !) ;sans la pre m iè re indication de longue ur, les ré s ultats s e raie nt diffé re nts : 123456 pour n (e n
supposant q ue ce la ne conduis e pas à une valeur non re pré s e ntable dans le type int) e t 7 pour p. En d, ce tte fois, c'e s t
l'indication de longue ur 2 q ui a de l'im portance ;e n son abscence, n vaudrait e ffe ctive m e nt 1, m ais p vaudrait 458.
Enfin, e n e , les deux indications de longue ur sont im portante s ;note z bie n q ue les trois e s pace s placé s avant les
caractè re s pris e n com pte pour n, ainsi que les 2 e s pace s placé s avant les caractè re s pris e n com pte pour p ne s ont pas
com ptabilisés dans la longue ur im pos é e .

Exe rcice II.5

___________________________________________________________________________
II. Le s e ntré e s -sortie s conve rsationne lles 15
Enoncé

Soit le program m e s uivant :

#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é s ultats fournira-t-il, e n supposant q u'on lui e ntre les données suivante s (atte ntion, on supposera q ue les données
sont frappé e s au clavie r e t les ré s ultats affich é s à l'é cran, ce q ui signifie q u'ily aura "m ixage " e ntre ces deux sorte s
d'inform ations) :

1 2
3
4
123456
78901234 5
6 7 8 9 10
0
0
12
_______________________________________________________________

Sol
ution

Ici, on re trouve le m é canism e lié à l'indication d'une longue ur m axim ale dans le code form at, com m e dans l'e xe rcice
pré cédent. De plus, on e xploite le fait q ue les inform ations d'une ligne q ui n'ont pas é té pris e s e n com pte lors d'une
lecture re s te nt disponibles pour la lecture s uivante . Enfin, rappe lons q ue , tant q ue scanf n'a pas re çu suffisam m e nt
d'inform ation, com pte te nu des diffé re nts code s form at spécifié s (e t non pas des variables indiq ué e s ), e lle e n atte nd de
nouve lles . Voici finalem e nt les ré s ultats obte nus :

donnez 2 entiers (0 pour finir)


1 2
merci pour : 1 2
16 Exe rcice s e n langage C
donnez 2 entiers (0 pour finir)
3
4
merci pour : 3 4
donnez 2 entiers (0 pour finir)
123456
merci pour : 1234 56
donnez 2 entiers (0 pour finir)
78901234 5
merci pour : 7890 12
donnez 2 entiers (0 pour finir)
merci pour : 34 5
donnez 2 entiers (0 pour finir)
6 7 8 9 10
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
III : LES INSTRUCTIO NS
D E CO NTRO LE

Exe rcice III.1

___________________________________________________________________________

Enoncé

Quelles e rre urs ont é té com m ises dans ch acun de s 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 Exe rcice s e n langage C
...
switch (n)
{ case LIMITE-1 : printf ("un peu moins") ;
case LIMITE : printf ("juste") ;
case LIMITE+1 : printf ("un peu plus") ;
}

_______________________________________________________________

Sol
ution

1) Ilm anq ue un point-virgule à la fin du pre m ie r printf :

if (a<b) printf ("ascendant") ;


else printf ("non ascendant") ;

2) Le s valeurs suivant le m ot cas e doive nt obligatoire m e nt ê tre des "e xpre s s ions constante s ", c'e s t-à -dire d e s e xpre s s ions
calculables par le com pilate ur lui-m ê m e . Ce n'e s t pas le cas de n.

3) Aucune e rre ur, les e xpre s s ions te lles q ue LIM ITE-1 é tant bien des expressions constante s .

4) Ici, les e xpre s s ions suivant le m ot cas e ne s ont plus des expre s s ions constante s , car le sym bole LIM ITE a é té défini
sous form e d'une "constante sym boliq ue " (e n C+ + , ce pe ndant, ce s instructions s e ront corre cte s ).

Exe rcice III.2

___________________________________________________________________________

Enoncé

Soit le program m e s uivant :

#include <stdio.h>
main()
{ int n ;
scanf ("%d", &n) ;
switch (n)
{ case 0 : printf ("Nul\n") ;
case 1 :
III. Le s instructions de contrôle 19
case 2 : printf ("Petit\n") ;
break ;
case 3 :
case 4 :
case 5 : printf ("Moyen\n") ;
default : printf ("Grand\n") ;
}
}

Quels ré s ultats affich e -t-illors q u'on lui fournit e n donné e :

a) 0
b) 1
c) 4
d) 10
e) -5
___________________________________________________________________________

Sol
ution

a)
Nul
Petit
b)
Petit
c)
Moyen
Grand
d)
Grand
e)
Grand

Exe rcice III.3

___________________________________________________________________________
20 Exe rcice s e n langage C
Enoncé

Quelles e rre urs ont é té com m ises dans ch acune des instructions suivante s :

a)
do c = getchar() while (c != '\n') ;
b)
do while ( (c = getchar()) != '\n') ;
c)
do {} while (1) ;

___________________________________________________________________________

Sol
ution

a) Ilm anq ue un point-virgule :

do c = getchar() ; while (c != '\n') ;

b) Ilm anq ue une instruction (é ve ntue llem e nt "vide") aprè s le m ot do. O n pourrait é crire , par e xe m ple :

do {} while ( (c = getchar()) != '\n') ;

ou :

do ; while ( (c = getchar()) != '\n') ;

c) Iln'y aura pas d'erreur de com pilation ;toute fois, ils'agit d'une "boucle infinie ".

Exe rcice III.4

___________________________________________________________________________

Enoncé

Ecrire plus lisiblem e nt :

do {} while (printf("donnez un nombre >0 "), scanf ("%d", &n), n<=0) ;


___________________________________________________________________________
III. Le s instructions de contrôle 21

Sol
ution

Plusieurs possibilité s e xiste nt, puis q u'il "suffit" de re porte r, dans le corps de la boucle, des instructions figurant
"artificie llem e nt" sous form e d'expressions dans la condition de poursuite :

do
printf("donnez un nombre >0 ") ;
while (scanf ("%d", &n), n<=0) ;

ou, m ie ux :

do
{ printf("donnez un nombre >0 ") ;
scanf ("%d", &n) ;
}
while (n<=0) ;

Exe rcice III.5

___________________________________________________________________________

Enoncé

Soit le pe tit program m e s uivant :

#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 program m e ré alisant e xacte m e nt la m ê m e ch os e , e n e m ployant, à la place de l'instruction for :


22 Exe rcice s e n langage C
a) une instruction w h ile,

b) une instruction do ... w h ile.

___________________________________________________________________________

Sol
ution

a)

#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) ;
}

b)
#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) ;
}
III. Le s instructions de contrôle 23
Exe rcice III.6

___________________________________________________________________________

Enoncé

Quels ré s ultats fournit le program m e s uivant :

#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) ;
}
___________________________________________________________________________

Sol
ution

0 est pair
3 est multiple de 3
9 est multiple de 3
15 est multiple de 3
20 est multiple de 5
24 Exe rcice s e n langage C
Exe rcice III.7

___________________________________________________________________________

Enoncé

Quels ré s ultats fournit le program m e s uivant :

#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) ;
}

___________________________________________________________________________

Sol
ution

A : n = 6
B : n = 10
C : n = 10
D : n = 15
III. Le s instructions de contrôle 25
D : n = 21

Exe rcice III.8

___________________________________________________________________________

Enoncé

Quels ré s ultats fournit le program m e s uivant :

#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) ;
}

___________________________________________________________________________

Sol
ution

A : n = 6, p = 1
B : n = 6, p = 3

Exe rcice III.9

___________________________________________________________________________
26 Exe rcice s e n langage C
Enoncé

Quels ré s ultats fournit le program m e s uivant :

#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) ;
}

___________________________________________________________________________

Sol
ution

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
III. Le s instructions de contrôle 27
Exe rcice III.10

___________________________________________________________________________

Enoncé

Ecrire un program m e q ui calcule les racine s carré e s d e nom bre s fournis e n donné e . Ils'arrê te ra lorq u'on lui fournira la
valeur 0. Ilre fus e ra les valeurs négative s . Son e xé cution s e pré s e nte ra ainsi :

donnez un nombre positif : 2


sa racine carrée est : 1.414214e+00
donnez un nombre positif : -1
svp positif
donnez un nombre positif : 5
sa racine carrée est : 2.236068e+00
donnez un nombre positif : 0

R appe lons q ue la fonction s q rt fournit la racine carré e (double) de la valeur (double) q u'on lui fournit e n argum e nt.

___________________________________________________________________________

Sol
ution

Ile xiste beaucoup de "rédactions possibles " ;e n 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 Exe rcice s e n 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) ;
}

R e m arque :

Ilne faut surtout pas oublie r #include < m ath .h > car, sinon, le com pilate ur considè re (e n l'absce nce du prototype )
q ue s q rt fournit un ré s ultat de type int.
III. Le s instructions de contrôle 29
Exe rcice III.11

___________________________________________________________________________

Enoncé

Calculer la som m e des n pre m ie rs te rm es de la "s é rie h arm oniq ue ", c'e s t-à -dire la som m e :

1 + 1/2 + 1/3 + 1/4 + ..... + 1/n

La valeur de n s e ra lue e n donné e .

___________________________________________________________________________

Sol
ution

#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) ;
}

R e m arques :

1) R appe lons q ue dans :


som += (float)1/i

l'e xpre s s ion de droite e s t é valué e e n conve rtissant d'abord 1 e t i e n float.


30 Exe rcice s e n langage C
Ilfaut é vite r d'é crire :
som += 1/i

auq ue lcas, les valeurs de 1/i seraient toujours nulles (sauf pour i=1) puiq ue l'opé rate ur /, lors q u'ilporte s ur de s
e ntie rs, corre s pond à la division e ntiè re .
D e m ê m e , e n é crivant :
som += (float) (1/i)

le ré s ultat ne s e rait pas plus satisfaisant puis q ue la conve rsion en flottant n'aurait lie u q u'aprè s la division (e n e ntie r).
En re vanch e , on pourrait é crire :
som += 1.0/i ;

2) Si l'on ch e rch ait à e xé cute r ce program m e pour de s valeurs é levé e s d e n (e n pré voyant alors une variable de type
float ou double), on constate rait q ue la valeur de la som m e s e m ble "conve rge r" ve rs une lim ite (bie n q u'e n th é orie la
s é rie h arm oniq ue "dive rge "). Ce la provie nt tout sim plem e nt de ce q ue , dè s q ue la valeur de 1/i e s t "pe tite " devant
som , le ré s ultat de l'addition de 1/i e t de som e s t exactem ent som . O n pourrait toute fois am é liore r le ré s ultat e n
e ffe ctuant la som m e "à l'e nve rs" (e n e ffe t, dans ce cas, le rapport e ntre la valeur à ajoute r e t la som m e courante s e rait
plus faible q ue pré cédem m e nt)..

Exe rcice III.12

___________________________________________________________________________

Enoncé

Affich e r un triangle isocè le form é d'étoiles . La h aute ur du triangle (c'e s t-à -dire le nom bre de ligne s ) s e ra fourni e n
donné e , com m e dans l'e xe m ple ci-de s s ous. O n s'arrange ra pour q ue la derniè re ligne du triangle s 'affich e s ur le bord
gauch e de l'é cran.

combien de lignes ? 10
*
***
*****
*******
*********
***********
*************
***************
III. Le s instructions de contrôle 31
*****************
*******************

___________________________________________________________________________

Sol
ution

#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') ;
}
}

Exe rcice III.13

___________________________________________________________________________

Enoncé

Affich e r toute s les m aniè re s possibles d'obte nir un franc ave c des piè ces de 2 ce ntim e s , 5 ce ntim e s e t 10 ce ntim e s . Dire
com bien de possibilité s ont é té ainsi trouvé e s . Le s ré s ultats s e ront affich é s com m e s uit :

1 F = 50 X 2c
32 Exe rcice s e n 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

___________________________________________________________________________

Sol
ution

#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++)
III. Le s instructions de contrôle 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") ;
}

printf ("\nEn tout, il y a %d façons de faire 1 F\n", nbf) ;


}

Exe rcice III.14

___________________________________________________________________________

Enoncé

Ecrire un program m e q ui dé te rm ine la nie m e valeur un (n é tant fourni e n donné e ) de la "suite de Fibonacci" dé finie
com m e s uit :

u1 = 1
u2 = 1
un = un-1 + un-2 pour n> 2

_______________________________________________________________

Sol
ution

#include <stdio.h>

main()
{
int u1, u2, u3 ; /* pour "parcourir" la suite */
int n ; /* rang du terme demandé */
int i ; /* compteur */
34 Exe rcice s e n 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) ;


}

Note z q ue , com m e à l'accoutum é e e n C, be aucoup de form ulations sont possibles . Nous e n avons d'ailleurs placé une
s e conde e n com m e ntaire de notre program m e .

Exe rcice III.15

___________________________________________________________________________

Enoncé

Ecrire un program m e q ui trouve la plus grande e t la plus petite valeur d'une s ucce s s ion de note s (nom bre s e ntie rs e ntre 0
e t 20) fournie s e n donné e s , ainsi que le nom bre de fois où ce m axim um e t ce m inim um ont é té attribué s . O n supposera
q ue les note s , e n nom bre non connu à l'avance , s e ront te rm iné e s par une valeur né gative . O n s'astre indra à ne pas utiliser
de "tableau". L'e xé cution du program m e pourra s e pré s e nte r 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
III. Le s instructions de contrôle 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

note maximale : 13 attribuée 1 fois


note minimale : 7 attribuée 2 fois

_______________________________________________________________

Sol
ution

#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 Exe rcice s e n langage C
printf ("note minimale : %d attribuée %d fois\n", min, nmin) ;
}
}

Exe rcice III.16

___________________________________________________________________________

Enoncé

Ecrire un program m e q ui affich e la "table de m ultiplication" de s nom bres de 1 à 10, sous la form e s uivante :

I 1 2 3 4 5 6 7 8 9 10
-----------------------------------------------
1 I 1 2 3 4 5 6 7 8 9 10
2 I 2 4 6 8 10 12 14 16 18 20
3 I 3 6 9 12 15 18 21 24 27 30
4 I 4 8 12 16 20 24 28 32 36 40
5 I 5 10 15 20 25 30 35 40 45 50
6 I 6 12 18 24 30 36 42 48 54 60
7 I 7 14 21 28 35 42 49 56 63 70
8 I 8 16 24 32 40 48 56 64 72 80
9 I 9 18 27 36 45 54 63 72 81 90
10 I 10 20 30 40 50 60 70 80 90 100

_______________________________________________________________

Sol
ution

#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) ;
III. Le s instructions de contrôle 37
printf ("\n") ;
printf ("-------") ;
for (j=1 ; j<=NMAX ; j++) printf ("----") ;
printf ("\n") ;

/* 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") ;
}
IV : LES FO NCTIO NS

N.B. Ici, on ne trouve ra aucun e xe rcice faisant inte rve nir de s pointe urs, e t par cons é q ue nt aucun e xe rcice m e ttant e n
oe uvre une transm ission d'argum e nts par adre s s e . De te ls e xe rcice s apparaî
tront dans le ch apitre s uivant.

Exe rcice IV.1

___________________________________________________________________________

Enoncé

a) Que fournit le program m e s uivant :

#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) Ajoute r une déclaration conve nable de la fonction fct :

- sous la form e la plus brè ve possible (suivant la norm e ANSI),


40 Exe rcice s e n langage C
- sous form e d'un "prototype ".
_______________________________________________________________

Sol
ution

a) Bie n q u'ilne possè de pas de déclaration de la fonction fct, le program m e m ain e s t corre ct. En e ffe t, la norm e ANSI
autoris e q u'une fonction ne s oit pas déclaré e , auq ue lcas e lle e s t considérée com m e fournissant un ré s ultat de type int.
Ce tte facilité e s t toute fois forte m e nt décons e illée (e t e lle ne s e ra plus acce pté e d e C+ + ). Voici les ré s ultats fournis par
le program m e :

p = 5, n = 10

b) La déclaration la plus brè ve s e ra :

int fct () ;

La déclaration (vive m e nt cons e illée ), sous form e de prototype s e ra :

int fct (int) ;

ou, é ve ntue llem e nt, sous form e d'un prototype "com plet" :

int fct (int r) ;

D ans ce dernie r cas, le nom r n'a aucune s ignification : on utilise souve nt le m ê m e nom (lors q u'on le connaî
t!) q ue dans
l'e n-tê te de la fonction, m ais ilpourrait s'agir de n'im porte q ue lautre nom de variable).

Exe rcice IV.2

___________________________________________________________________________

Enoncé

Ecrire :
IV. Le s fonctions 41
- une fonction, nom m é e f1, s e conte ntant d'affich e r "bonjour" (e lle ne possédera aucun argum e nt, ni valeur de
re tour),
- une fonction, nom m é e f2, q ui affich e "bonjour" un nom bre de fois é galà la valeur re çue e n argum e nt (int) e t q ui ne
re nvoie aucune valeur,
- une fonction, nom m é e f3, q ui fait la m ê m e ch os e q ue f2, m ais q ui, de plus, re nvoie la valeur (int) 0.
Ecrire un pe tit program m e appe lant succe s s ive m e nt ch acune de ce s 3 fonctions, aprè s les avoir conve nablem e nt déclaré e s
sous form e d'un prototype .

_______________________________________________________________

Sol
ution

#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 Exe rcice s e n langage C
}

Exe rcice IV.3

___________________________________________________________________________

Enoncé

Quels ré s ultats fournira ce program m e :

#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) ;
}

_______________________________________________________________
IV. Le s fonctions 43

Sol
ution

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

Exe rcice IV.4

___________________________________________________________________________

Enoncé

Ecrire une fonction q ui re çoit e n argum e nts 2 nom bre s flottants e t un caractè re e t q ui fournit un ré s ultat corre s pondant à
l'une des 4 opé rations appliq ué e s à ses deux pre m ie rs argum e nts, e n fonction de la valeur du de rnie r, à savoir : addition
pour le caractè re + , soustraction pour -, m ultiplication pour *e t division pour / (tout autre caractè re q ue l'un de s 4 cité s
s e ra inte rpré té com m e une addition). O n ne tie ndra pas com pte des ris q ues de division par zé ro.

Ecrire un pe tit program m e (m ain) utilisant ce tte fonction pour e ffe ctue r les 4 opé rations sur de ux nom bre s fournis e n
donné e .

_______________________________________________________________

Sol
ution

#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 Exe rcice s e n 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, '/') ) ;
}

Exe rcice IV.5

___________________________________________________________________________

Enoncé

Transform e r le program m e (fonction + m ain) é crit dans l'e xe rcice pré cédent de m aniè re à ce q ue la fonction ne dispose
plus q ue de 2 argum e nts, le caractè re indiq uant la nature de l'opé ration à e ffe ctue r é tant pré cis é , ce tte fois, à l'aide d'une
variable globale.

_______________________________________________________________

Sol
ution

#include <stdio.h>
IV. Le s 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) ) ;
}

R e m arque :

Ils'agissait ici d'un e xe rcice d'"é cole" destiné à force r l'utilisation d'une variable globale. Dans la pratiq ue , on
é vite ra le plus possible ce ge nre de program m ation q ui favoris e trop large m e nt les ris q ues d'"e ffe ts de bord".
46 Exe rcice s e n langage C
Exe rcice IV.6

___________________________________________________________________________

Enoncé

Ecrire une fonction, sans argum e nt ni valeur de re tour, q ui s e conte nte d'affich e r, à ch aq ue appe l, le nom bre totalde fois
où e lle a é té appe lée s ous la form e :

appel numéro 3

_______________________________________________________________

Sol
ution

La m e illeure s olution consiste à pré voir, au s e in de la fonction e n q ue s tion, une variable de clas s e s tatiq ue . Elle s e ra
initialisée une seule fois à zé ro (ou à toute autre valeur é ve ntue llem e nt e xplicité e ) au début de l'e xé cution du program m e .
Ici, nous avons, de plus, pré vu un pe tit program m e 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à e ncore , la dém arch e consistant à utiliser com m e com pte ur d'appe ls une variable globale (q ui de vrait alors ê tre connue
du program m e utilisate ur) e s t à proscrire .
IV. Le s fonctions 47
Exe rcice IV.7

___________________________________________________________________________

Enoncé

Ecrire 2 fonctions à un argum e nt e ntie r e t une valeur de re tour e ntiè re pe rm e ttant de préciser si l'argum e nt re çu e s t
m ultiple de 2 (pour la pre m iè re fonction) ou m ultiple de 3 (pour la s e conde fonction).

Utiliser ces deux fonctions dans un pe tit program m e q ui lit un nom bre e ntie r e t q ui pré cis e s 'ile s t pair, m ultiple de 3
e t/ou m ultiple de 6, com m e dans ce t e xe m ple (ily a de ux e xé 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
_______________________________________________________________

Sol
ution

#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 Exe rcice s e n 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") ;
}
V : TA BLEAUX ET
PO INTEURS

Exe rcice V.1

___________________________________________________________________________

Enoncé

Quels ré s ultats fournira ce program m e :

#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 Exe rcice s e n langage C
printf ("\n") ;
}

_______________________________________________________________

Sol
ution

/*1*/ re m plit le tableau ave c les valeurs 0 (0+ 0), 2 (1+ 1) e t 4 (2+ 2) ;on obtie ndrait plus sim plem e nt le m ê m e ré s ultat
ave c l'e xpre s s ion 2*i.

/*2 */ affich e "classiquem e nt" les valeurs du tableau t, dans l'ordre "nature l".

/* 3 */ fait la m ê m e ch os e , e n utilisant le form alism e pointe ur au lie u du form alism e tableau. Ainsi, *(t+ i) e s t
parfaite m e nt é q uivalent à t[i].

/*4 */ fait la m ê m e ch os e , e n utilisant la "lvalue " adt (à laq ue lle on a affe cté initialem e nt l'adre s s e t du tableau) e t e n
"l'incré m e ntant" pour parcourir les diffé re nte s adresses des 4 é lém e nts du tableau.

/*5 */ affich e les valeurs de t, à l'e nve rs, e n utilisant le m ê m e form alism e pointe ur q ue dans 4. O n aurait pu é crire , de
façon é q uivalente :

for (i=2 ; i>=0 ; i--) printf ("%d ", t[i]) ;

Voici les ré s ultats fournis par ce program m e :

0 2 4
0 2 4
0 2 4
4 2 0

Exe rcice V.2

___________________________________________________________________________
V. Tableaux e t pointe urs 51
Enoncé

Ecrire , de deux façons diffé re nte s , un program m e q ui lit 10 nom bre s e ntie rs dans un tableau avant d'en rech e rch e r le plus
grand e t le plus petit :

a) e n utilisant uniq ue m e nt le "form alism e tableau",


b) e n utilisant le "form alism e pointe ur", à ch aq ue fois q ue ce la e s t possible
_______________________________________________________________

Sol
ution

a) La program m ation e s t, ici, "classique". Nous avons sim plem e nt défini un sym bole NVALdestiné à conte nir le nom bre
de valeurs du tableau. Note z bie n q ue la déclaration int t[NVAL] e s t acce pté e puis q ue NVAL e s t une "e xpre s s ion
constante ". En re vanch e , e lle ne l'aurait pas é té s i nous avions défini ce sym bole NVAL par une "constante sym boliq ue "
(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) O n pe ut re m place r systé m atiq ue m e nt, t[i] par *(t+ i)./ D e plus, dans scanf, on pe ut re m place r & t[i] par t+ i. Voici
finalem e nt le program m e obte nu :

#include <stdio.h>
#define NVAL 10 /* nombre de valeurs du tableau */
main()
{ int i, min, max ;
52 Exe rcice s e n 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) ;
}

Exe rcice V.3

___________________________________________________________________________

Enoncé

Soie nt deux tableaux t1 e t t2 dé claré s ainsi :

float t1[10], t2[10] ;

Ecrire les instructions perm e ttant de re copie r, dans t1, tous les é lém e nts positifs de t2, e n com plétant é ve ntue llem e nt t1
par de s zé ros. Ici, on ne ch e rch e ra pas à fournir un program m e com plet e t on utilisera systé m atiq ue m e nt le form alism e
tableau.

_______________________________________________________________

Sol
ution

O n pe ut com m e nce r par re m plir t1 de zé ros, avant d'y re copie r les é lém e nts 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++)
V. Tableaux e t pointe urs 53
if (t2[j] > 0) t1[i++] = t2[j] ;

M ais, on pe ut re copie r d'abord dans t1 les é lém e nts positifs de t2, avant de com pléte r é ve ntue llem e nt par de s zé ros.
Ce tte deuxiè m e form ulation, m oins sim ple q ue la pré cédente , s e ré vé lerait toute fois plus e fficace s ur 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 ;

Exe rcice V.4

___________________________________________________________________________

Enoncé

Quels ré s ultats fournira ce program m e :

#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 */
}

_______________________________________________________________

Sol
ution

Le tableau ad e s t un tableau de 4 é lém e nts ;ch acun de ce s é lém e nts e s t un pointe ur sur un int. L'instruction /* 1 */
re m plit le tableau ad ave c les adresses des 4 é lém e nts du tableau t. L'instruction /*2 */ affich e finalem e nt les 4 é lém e nts
du tableau t ;e n e ffe t, *ad[i] re pré s e nte la valeur situé e à l'adre s s e ad[i]. /*2 */ e s t é q uivalente ici à :

for (i=0 ; i<4 ; i++) printf ("%d", t[i]) ;


54 Exe rcice s e n langage C
Enfin, dans l'instruction /*3 */, *(ad[1] + 1) re pré s e nte la valeur situé e à l'e ntie r suivant ce lui d'adre s s e ad[1] ;ils'agit
donc de t[2]. En re vanch e , *ad[1] + 1 re pré s e nte la valeur situé e à l'adre s s e ad[1] augm e nté e d e 1, autre m e nt dit t[1] +
1.

Voici, e n dé finitive , les ré s ultats fournis par ce program m e :

10 20 30 40
30 21

Exe rcice V.5

___________________________________________________________________________

Enoncé

Soit le tableau t déclaré ainsi :

float t[3] [4] ;

Ecrire les (s e ules ) instructions perm e ttant de calculer, dans une variable nom m é e som , la som m e d e s é lém e nts de t :

a) e n utilisant le "form alism e usueldes tableaux à deux indice s ",


b) e n utilisant le "form alism e pointe ur".
_______________________________________________________________

Sol
ution

a) La pre m iè re s olution ne pos e aucun problèm e particulie r :

int i, j ;
som = 0 ;
for (i=0 ; i<3 ; i++)
for (j=0 ; j<4 ; j++)
som += t[i] [j] ;

b) Le form alism e pointe ur e s t ici m oins facile à appliq ue r q ue dans le cas des tableaux à un indice . En e ffe t, ave c, par
e xe m ple, float t[4], t e s t de type int * e t ilcorre s pond à un pointe ur sur le pre m ie r é lém e nt du tableau. Ilsuffit donc
d'incré m e nte r conve nablem e nt t pour parcourir tous les é lém e nts du tableau.
V. Tableaux e t pointe urs 55
En re vanch e , ave c notre tableau float t [3] [4], t e s t du type pointe ur sur des tabl eaux de 4 fl ottants(type : float[4] *). La
notation *(t+ i) e s t gé né ralem e nt inutilisable s ous ce tte form e puis q ue , d'une part, e lle corre s pond à des valeurs de
tableaux de 4 flottants e t q ue , d'autre part, l'incré m e nt i porte , non plus sur de s flottants, m ais sur des blocs de 4
flottants ;par e xe m ple, t+ 2 re pré s e nte l'adresse du h uitiè m e flottant, com pté à partir de ce lui d'adre s s e t.

Une s olution consiste à "conve rtir" la valeur de t e n un pointe ur de type float *. O n pourrait s e conte nte r de procéder
ainsi :

float * adt ;
.....
adt = t ;

En e ffe t, dans ce cas, l'affe ctation e ntraî ne une conve rsion forcé e d e t e n float *, ce q ui ne ch ange pas l'adre s s e
corre s pondante 1 (s e ule la nature du pointe ur a ch angé ).

Gé né ralem e nt, on y gagne ra e n lisibilité e n e xplicitant la conve rsion m ise en oeuvre à l'aide de l'opé rate ur de "cast".
Note z q ue , d'une part, ce la pe ut é vite r ce rtains m e s s ages d'ave rtissem e nt ("w arnings") de la part du com pilate ur.

Voici finalem e nt ce q ue pourraie nt ê tre les instructions dem andé e s :

int i ;
int * adt ;
som = 0 ;
adt = (float *) t ;
for (i=0 ; i<12 ; i++)
som += * (adt+i);

Exe rcice V.6

___________________________________________________________________________

Enoncé

Ecrire une fonction q ui fournit e n valeur de re tour la som m e d e s é lém e nts d'un tableau de flottants transm is, ainsi q ue s a
dim e nsion, e n argum e nt.

Ecrire un pe tit program m e d'essai.

1 Attention, cel
a n'e s t vrai q ue parce que l'on passe de pointeurs sur des groupes d'élém ents à un pointeur sur ces élém ents. Autrem ent dit, aucune
"contrainte d'alignem ent" ne risque de nuire ici. Iln'en irait pas de m ê m e, par exem ple, pour des conversions de ch ar *e n int *.
56 Exe rcice s e n langage C
_______________________________________________________________

Sol
ution

En ce q ui conce rne le tableau de flottants re çu e n argum e nt, ilne pe ut ê tre transm is que par adresse. Quant au nom bre
d'élém e nt (de type int), nous le transm e ttrons classiquem e nt par valeur. L'e n-tê te de notre fonction pourra s e pré s e nte r
sous l'une des form e s s uivante s :

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 e ffe t, la dim e nsion ré e lle de t n'a aucune incide nce s ur les instructions de la fonction e lle-m ê m e (e lle n'inte rvie nt pas
dans le calculde l'adresse d'un é lém e nt du tableau2 e t e lle ne s e rt pas à "alloue r" un e m place m e nt puis q ue le tableau e n
q ue s tion aura é té alloué dans la fonction appe lant som m e ).

Voici ce q ue pourrait ê tre la fonction de m andé 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 q ui e s t du program m e d'utilisation de la fonction som m e , on pe ut, là e ncore , é crire le "prototype " sous
diffé re nte s form e s :

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 e xe m ple d'un te lprogram m e :

#include <stdio.h>
main()

2Iln'en irait pas de m ê m e pour des tabl


eaux à plusieurs indices.
V. Tableaux e t pointe urs 57
{
float somme (float *, int) ;
float t[4] = {3, 2.5, 5.1, 3.5} ;
printf ("somme de t : %f\n", somme (t, 4) ) ;
}

Exe rcice V.7

___________________________________________________________________________

Enoncé

Ecrire une fonction q ui ne re nvoie aucune valeur e t q ui dé te rm ine la valeur m axim ale e t la valeur m inim ale d'un tableau
d'entie rs (à un indice ) de taille q ue lconq ue . Ilfaudra donc pré voir 4 argum e nts : le tableau, sa dim e nsion, le m axim um e t
le m inim um .

Ecrire un pe tit program m e d'essai.

_______________________________________________________________

Sol
ution

En langage C, un tableau ne pe ut ê tre transm is que par adresse (en toute rigue ur, C n'autoris e q ue la transm ission par
valeur m ais, dans le cas d'un tableau, on transm e t une valeur de type pointe ur q ui n'e s t rie n d'autre q ue l'adresse du
tableau!). En ce q ui conce rne s on nom bre d'élém e nts, on pe ut indiffé re m m e nt e n transm e ttre l'adre s s e (sous form e d'un
pointe ur de type int*), ou la valeur ;ici, la s e conde s olution e s t la plus norm ale.

En re vanch e , e n ce q ui conce rne le m axim um e t le m inim um , ils ne peuve nt pas ê tre transm is par valeur, puis q u'ils
doive nt pré cis é m e nt ê tre déte rm iné s par la fonction. Ilfaut donc obligatoire m e nt pré voir de pas s e r de s pointe urs sur de s
float. L'e n-tê te de notre fonction pourra donc s e pré s e nte r ainsi (nous ne donnons plus toute s les é criture s possibles ) :

void maxmin (int t[], int n, int * admax, int * admin)

L'algorith m e de re ch e rch e de m axim um e t de m inim um pe ut ê tre calqué s ur ce lui de l'e xe rcice V.2, e n re m plaçant m ax
par *adm ax e t m in par *adm in. Ce la nous conduit à la fonction suivante :

void maxmin (int t[], int n, int * admax, int * admin)


58 Exe rcice s e n 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 souh aite é vite r les "indire ctions" q ui apparais s e nt systé m atiq ue m e nt dans les instructions de com paraison, on pe ut
"travailler" te m poraire m e nt sur des variables locales à la fonction (nom m é e s ici m ax e t m in). Ce la nous conduit à une
fonction de la form e s uivante :

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 pe tit e xe m ple de program m e 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) ;
}
V. Tableaux e t pointe urs 59
Exe rcice V.8

___________________________________________________________________________

Enoncé

Ecrire une fonction q ui fournit e n re tour la som m e des valeurs d'un tableau de flottants à deux indices dont les
dim e nsions sont fournie s e n argum e nt.

_______________________________________________________________

Sol
ution

Par analogie ave c ce q ue nous avions fait dans l'e xe rcice V.6, nous pourrions songe r à déclare r le tableau conce rné dans
l'e n-tê te de la fonction sous la form e t[][]. M ais, ce la n'e s t plus possible car, ce tte fois, pour dé te rm ine r l'adresse d'un
é lém e nt t[i][j] d'un te ltableau, le com pilate ur doit e n connaître la deuxiè m e dim e nsion.

Une s olution consiste à considérer qu'on reçoit un pointe ur (de type float*) sur le début du tableau e t d'en parcourir tous
les é lém e nts (au nom bre de n*p si n et p dé s igne nt les dim e nsions du tableau) com m e s i l'on avait affaire à un tableau à
une dim e nsion.

Ce la nous conduit à ce tte 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 lle fonction, la s e ule difficulté consiste à lui transm e ttre e ffe ctive m e nt l'adresse de début du tableau,
sous la form e d'un pointe ur de type int *. O r, ave c, par e xe m ple t[3][4], t, s'ilcorrre s pond bie n à la bonne adre s s e , e s t
du type "pointe ur sur des tableaux de 4 flottants". A priori, toute fois, com pte te nu de la pré s e nce du prototype , la
conve rsion voulue s e ra m ise en oeuvre autom atiq ue m e nt par le com pilate ur. Toute fois, com m e nous l'avons déjà dit dans
l'e xe rcice V.5, on y gagne ra e n lisibilité (e t e n é ve ntue ls m e s s ages d'ave rtissem e nt!) e n faisant appe là l'opé rate ur de
"cast".

Voici finalem e nt un e xe m ple d'un te lprogram m e d'utilisation de notre fonction :

#include <stdio.h>
main()
{
60 Exe rcice s e n 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) ) ;
}
VI: LES CH A INES D E
CARACTERES

Exe rcice VI.1

___________________________________________________________________________

Enoncé

Quels ré s ultats fournira ce program m e :

#include <stdio.h>
main()
{
char * ad1 ;
ad1 = "bonjour" ;
printf ("%s\n", ad1) ;
ad1 = "monsieur" ;
printf ("%s\n", ad1) ;
}

_______________________________________________________________

Sol
ution

L'instruction ad1 = "bonjour" place dans la variable ad1 l'adresse de la ch aî ne constante "bonjour". L'instruction printf
("%s\n", ad1) s e conte nte d'affich e r la valeur de la ch aî
ne dont l'adre s s e figure dans ad1, c'e s t-à -dire , e n l'occurre nce
"bonjour". D e m aniè re com parable, l'instruction ad1 = "m onsie ur" place l'adresse de la ch aî ne constante "m onsieur"
62 Exe rcice s e n langage C
dans ad1 ;l'instruction printf ("%s\n", ad1) affich e la valeur de la ch aî
ne ayant m ainte nant l'adre s s e conte nue dans ad1,
c'e s t-à -dire m ainte nant "m onsieur".

Finalem e nt, ce program m e affich e tout sim plem e nt :

bonjour
monsieur

O n aurait obte nu plus sim plem e nt le m ê m e ré s ultat e n é crivant :

printf ("bonjour\nmonsieur\n") ;

Exe rcice VI.2

___________________________________________________________________________

Enoncé

Quels ré s ultats fournira ce program m e :

#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 */
}

_______________________________________________________________

Sol
ution

La déclaration /*1 */ place dans la variable adr, l'adresse de la ch aî ne constante bonjour. L'instruction /*2 */ affich e
les caractè re s adr[0], adr[1] e t adr[2], c'e s t-à -dire les 3 pre m ie rs caractè res de ce tte ch aî
ne . L'instruction /*3 */ affich e
tous les caractè re s à partir de ce lui d'adre s s e adr, tant q ue l'on a pas affaire à un caractè re nul;com m e notre ch aî ne
VI. Le s ch aîne s d e caractè re s 63
"bonjour" e s t pré cis é m e nt te rm iné e par un te lcaractè re nul, ce tte instruction affich e finalem e nt, un par un, tous les
caractè res de "bonjour".

En dé finitive , le program m e fournit sim plem e nt les ré s ultats suivants :

bon
bonjour

Exe rcice VI.3

___________________________________________________________________________

Enoncé

Ecrire le program m e pré cédent (Exe rcice VI.2), sans util


iser l
e "form al
ism e tabl
eau" (ile xiste plusieurs solutions).

_______________________________________________________________

Sol
ution

Voici de ux solutions possibles :

a) O n pe ut re m place r systé m atiq ue m e nt la notation adr[i] par *(adr+ i), ce q ui conduit à ce program m e :

#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) O n pe ut é galem e nt parcourir notre ch aî


ne , non plus à l'aide d'un "indice " i, m ais e n incré m e ntant un pointe ur de type
ch ar *: ilpourrait s'agir tout sim plem e nt de adr, m ais généralem e nt, on pré fé re ra ne pas détruire ce tte inform ation e t e n
e m ploye r une copie :
64 Exe rcice s e n 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++)) ;
}

Note z bie n q ue s i nous incré m e ntions directe m e nt adr dans la pre m iè re instruction d'affich age , nous ne disposerions plus
de la "bonne adre s s e " pour la deuxiè m e instruction d'affich age .

Exe rcice VI.4

___________________________________________________________________________

Enoncé

Ecrire un program m e q ui de m ande à l'utilisate ur de lui fournir un nom bre e ntie r e ntre 1 e t 7 e t q ui affich e le nom du jour
de la s e m aine ayant le num é ro indiq ué (lundi pour 1, m ardi pour 2, ... dim anch e pour 7).

_______________________________________________________________

Sol
ution

Une dém arch e consiste à cré e r un "tableau de 7 pointe urs sur de s ch aî ne s ", corre s pondant ch acune au nom d'un jour de
la s e m aine . Com m e ce s ch aî
ne s s ont ici constante s , ile s t possible de cré e r un te ltableau par une déclaration com portant
une intialisation de la form e :

char * jour [7] = { "lundi", "mardi", ...

N'oublie z pas alors q ue jour[0] contie ndra l'adresse de la pre m iè re ch aî


ne , c'e s t-à -dire l'adresse de la ch aî
ne constante
"lundi" ;jour[1] contie ndra l'adresse de "m ardi", ...

Pour affich e r la valeur de la ch aî


ne de rang i, ilsuffit de re m arq ue r q ue s on adre s s e e s t sim plem e nt jour[i-1].

D 'où le program m e dem andé :


VI. Le s ch aî
ne s d e caractè re s 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]) ;
}

Exe rcice VI.5

___________________________________________________________________________

Enoncé

Ecrire un program m e q ui lit deux nom bre s e ntie rs fournis obligatoire m e nt sur une m ê m e ligne . Le program m e ne devra
pas "s e plante r" e n cas de ré pons e incorre cte (caractè re s invalides) com m e le fe rait scanf ("%d %d", ...) m ais
sim plem e nt affich e r un m e s s age e t redem ande r une autre ré pons e . Ildevra e n aller de m ê m e lors q ue la ré pons e fournie
ne com porte pas as s e z d'inform ations. En re vanch e , lors q ue la ré pons e com porte ra trop d'inform ations, les derniè re s
devront ê tre ignoré e s .

Le traite m e nt (dem ande de 2 nom bre s e t affich age ) devra s e poursuivre jus q u'à ce q ue le pre m ie r nom bre fourni soit 0.

Voici un e xe m ple d'exécution d'un te lprogram m e :

--- donnez deux entiers : é


réponse erronée - redonnez-la : 2 15
merci pour 2 15
--- donnez deux entiers : 5
réponse erronée - redonnez-la : 4 12
merci pour 4 12
--- donnez deux entiers : 4 8 6 9
merci pour 4 8
--- donnez deux entiers : 5 é3
66 Exe rcice s e n langage C
réponse erronée - redonnez-la : 5 23
merci pour 5 23
--- donnez deux entiers : 0 0
merci pour 0 0

R e m arque : on pe ut utiliser les fonctions ge ts e t sscanf.

_______________________________________________________________

Sol
ution

Com m e le s uggè re la re m arq ue de l'é noncé , on pe ut ré s oudre les problèm e s pos é s e n e ffe ctuant e n de ux te m ps la lecture
d'un couple d'entie rs :

- lecture d'une ch aî ne de caractè re s (c'e s t-à -dire une s uite de caractè re s absol
um ent quel
conques, validée par
"re turn") ave c la fonction ge ts,
- "décodage " de ce tte ch aî ne ave c sscanf, suivant un "form at", d'une m aniè re com parable à ce q ue fe rait scanf, à
partir de s on "tam pon d'e ntré e ".
R appe lons q ue sscanf, tout com m e scanf, fournit e n re tour le nom bre d'inform ations corre cte m e nt lue s , de sorte q u'il
suffit de ré pé te r les deux opé rations précédente s jus q u'à ce q ue la valeur fournie par sscanf soit é gale à 2.

L'é noncé ne fait aucune h ypoth è s e s ur le nom bre m axim alde caractè re s q ue l'utilisate ur pourra ê tre am e né à fournir. Ici,
nous avons suppos é q u'au plus 128 caractè re s s e raie nt fournis ;ils'agit là d'une h ypoth è s e q ui, dans la pratiq ue , s'avè re
ré aliste , dans la m e s ure où on ris q ue rare m e nt de frappe r de s ligne s plus longue s ;de surcroî t, ils'agit m ê m e d'une
lim itation "nature lle" de ce rtains e nvironne m e nts (DOS, e n particulie r).

Voici le program m e dem andé :

#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) */

/* boucle de lecture des différents couples de valeurs */


do
{ /* boucle de lecture d'un couple de valeur jusqu'à OK */
printf ("--- donnez deux entiers : ") ;
do
{ gets (ligne) ;
compte = sscanf (ligne, "%d %d", &n1, &n2) ;
VI. Le s ch aî
ne s d e caractè re s 67
if (compte<2) printf ("réponse erronée - redonnez-la : ") ;
}
while (compte < 2) ;
printf ("merci pour %d %d\n", n1, n2) ;
}
while (n1) ;
}

R e m arques

1) Si l'utilisate ur fournit plus de caractè re s q u'iln'e n faut pour form e r 2 nom bre s e ntie rs, ce s caractè re s (lus dans
ligne ) ne s e ront pas utilisés par sscanf ;m algré tout, ils ne seront pas e xploité s ulté rie ure m e nt puis q ue , lors q ue l'on
redem ande ra 2 nouve aux e ntie rs, on re lira une nouve lle ch aî
ne par ge ts.
2) Si l'on souh aite absolum e nt pouvoir lim ite r la longue ur de la ch aî ne lue au clavie r, e n utilisant des instructions
1
"portables ", ilfaut s e tourne r ve rs la fonction fge ts destiné e à lire une ch aî ne dans un fich ie r, e t l'appliq ue r à stdin.
O n re m place ra l'instruction ge ts (ligne ) par fge ts (ligne , LG, stdin) q ui lim ite ra à LG le nom bre de caractè re s pris e n
com pte . Note z toute fois q ue , dans ce cas, les caractè re s e xcédentaire s (e t donc non "vus" par fge ts) re s te ront
disponibles pour une proch aine lecture (ce q ui n'e s t pas pire q ue dans la situation actue lle où ce s caractè re s
vie ndraie nt é cras e r de s e m place m e nts m é m oire s itué s au-de là du tableau ligne !).
D ans ce rtaine s im plém e ntations (Turbo/Borland C/C+ + e t Quick C/C M icrosoft), il e xiste une fonction (non
portable, puis q ue non pré vue par la norm e ANSI) nom m é e cge ts q ui, utilisée à la place de ge ts (ou fge ts) pe rm e t de
ré gler le problèm e é voq ué . En e ffe t, cge ts pe rm e t de lire une ch aî ne , e n lim itant le nom bre de caractè re s
e ffe ctive m e nt fournis au clavie r : iln'e s t pas possible à l'utilisate ur d'e n frappe r plus q ue pré vu, de s orte q ue le ris q ue
de caractè re s e xcédentaire s n'e xiste plus!

Exe rcice VI.6

___________________________________________________________________________

1 M ais, si vous réal


isez ces exercices en accom pagnem ent d'un cours de langage C, ile s t probable q ue vous n'aurez pas e ncore étudié la fonction
fge ts (en général, elle e s t introduite dans le ch apitre relatif au traitem ent des fich iers). Certains e xercices de la s e conde partie de cet ouvrage feront
appelà fgets, et/ou à s s canf.
68 Exe rcice s e n langage C
Enoncé

Ecrire un program m e déte rm inant le nom bre de lettre s e (m inuscule) conte nues dans un te xte fourni e n donné e s ous
form e d'une seule ligne ne dépassant pas 128 caractè re s . O n ch e rch e ra, ici, à n'utiliser aucune des fonctions de
traite m e nt de ch aî
ne .

_______________________________________________________________

Sol
ution

Com pte te nu de s contrainte s im pos é e s par l'é noncé , nous ne pouvons pas faire appe là la fonction strlen. Pour "e xplore r"
notre ch aî
ne , nous utiliserons le fait q u'e lle e s t te rm iné e par un caractè re nul(\0]. D'où le program m e 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) ;


}

Exe rcice VI.7

___________________________________________________________________________

Enoncé

Ecrire un program m e q ui lit, e n donné e , un ve rbe du prem ie r groupe e t q ui e n affich e la conjugaison au pré s e nt de
l'indicatif, sous la form e :
VI. Le s ch aî
ne s d e caractè re s 69
je chante
tu chantes
il chante
nous chantons
vous chantez
ils chantent

O n s'assure ra q ue le m ot fourni s e te rm ine bien par "er". O n supposera q u'ils'agit d'un ve rbe ré gulie r ;autre m e nt dit,
on adm e ttra q ue l'utilisate ur ne fournira pas un ve rbe te lq ue m ange r (le program m e affich e rait alors : nous m angons!).

_______________________________________________________________

Sol
ution

O n lira "classiquem e nt" un m ot, sous form e d'une ch aî ne à l'aide de la fonction ge ts. Pour vé rifie r sa te rm inaison par
"e r", on com pare ra ave c la ch aî
ne constante "e r", la ch aî
ne ayant com m e adre s s e l'adresse de fin du m ot, dim inué e d e 2.
L'adresse de fin se déduira de l'adresse de début e t de la longue ur de la ch aî ne (obte nue par la fonction strlen).

Quant à la com paraison voulue , e lle s e fe ra à l'aide de la fonction strcm p ;rappe lons q ue ce tte derniè re re çoit e n
argum e nt 2 pointe urs sur de s ch aî ne s e t q u'e lle fournit e n re tour une valeur nulle lors q ue les deux ch aî
ne s
corre s pondante s s ont é gales e t une valeur non nulle dans tous les autre s cas.

Les diffé re nte s pe rsonnes du ve rbe s 'obtie nne nt e n re m plaçant, dans la ch aîne e n q ue s tion, la te rm inaison "e r" par une
te rm inaison approprié e . O n pe ut, pour ce la, utiliser la fonction strcpy q ui re copie une ch aî ne donnée (ici la te rm inaison)
à une adresse donné e (ici, ce lle déjà utilisée dans strcm p pour vé rifie r q ue le ve rbe s e te rm ine bien par "er").

Les diffé re nte s te rm inaisons possibles s e ront rangées dans un tableau de ch aî ne s constante s (plus précisém e nt, dans un
tableau de pointe urs sur de s ch aî ne s constante s ). Nous fe rons de m ê m e pour les diffé re nts suje ts (je , tu...) ;e n re vanch e ,
ici, nous ne ch e rch e rons pas à les "concaté ne r" au ve rbe conjugué ;nous nous conte ntons de les é crire , au m om e nt
opportun.

Voici finalem e nt le program m e dem andé :

#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 Exe rcice s e n 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) ;
}
}

R e m arque : rappe lons q ue strcpy re copie (sans aucun contrôle) la ch aî ne dont l'adre s s e e s t fournie e n pre m ie r argum e nt
(c'e s t-à -dire , e n fait, tous les caractè re s à partir de ce tte adre s s e , jus q u'à ce q ue l'on re ncontre un \0) à l'adre s s e fournie
e n s e cond argum e nt ;de plus, e lle com plète bien le tout ave c un caractè re nulde fin de ch aî ne .

Exe rcice VI.8

___________________________________________________________________________

Enoncé

Ecrire un program m e q ui supprim e toute s les lettre s e (m inuscule) d'un te xte de m oins d'une ligne (ne dépassant pas 128
caractè re s ) fourni e n donné e . O n s'arrange ra pour q ue le te xte ainsi m odifié s oit cré é e n m é m oire , à l
a pl
ace de
l'ancien.

N.B. on pourra utiliser la fonction strch r.

_______________________________________________________________

Sol
ution

La fonction strch r pe rm e t de trouve r un caractè re donné dans une ch aî ne . Elle e s t donc tout à fait approprié e pour
localiser les 'e ' ;ilfaut toute fois note r q ue , pour localiser tous les 'e ', ile s t né ce s s aire de ré pé te r l'appe lde ce tte
VI. Le s ch aî ne s d e caractè re s 71
fonction, e n m odifiant à ch aq ue fois l'adresse de début de la ch aî
ne conce rné e (ilfaut é vite r de boucler sur la re ch e rch e
du m ê m e caractè re 'e ').

La fonction strch r fournit l'adre s s e à laq ue lle on a trouvé le pre m ie r caractè re indiq ué (ou la valeur 0 si ce caractè re
n'e xiste pas). La suppre s s ion du 'e ' trouvé pe ut s e faire e n re copiant le "re s te " de la ch aîne à l'adre s s e où l'on a
trouvé le 'e '.
Voici une s olution 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) ;
}
VII : LES STRUCTURES

Exe rcice VII.1

___________________________________________________________________________

Enoncé

Soit le m odè le (type ) de structure s uivant :

struct s_point
{ char c ;
int x, y ;
} ;

Ecrire une fonction q ui re çoit e n argum e nt une s tructure de type s_point e t q ui e n affich e le conte nu sous la form e :

point B de coordonnées 10 12

a) En transm e ttant e n argum e nt la val


eur de la structure conce rné e ,

b) En transm e ttant e n argum e nt l'adresse de la structure conce rné e .

D ans les deux cas, on é crira un pe tit program m e d'essai de la fonction ainsi réalisée.

_______________________________________________________________

Sol
ution

a) Voici la fonction de m andé e :

#include <stdio.h>
74 Exe rcice s e n langage C

void affiche (struct s_point p)


{ printf ("point %c de coordonnées %d %d\n", p.c, p.x, p.y) ;
}

Note z q ue s a com pilation né ce s s ite obligatoire m e nt la déclaration du type s_point, c'e s t-à -dire les instructions :

struct s_point
{ char c ;
int x, y ;
} ;

Voici un pe tit program m e q ui affe cte les valeurs 'A', 10 e t 12 aux diffé re nts ch am ps d'une structure nom m é e s , avant
d'en affich e r 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 llem e nt, la re m arq ue pré cédente s 'appliq ue é galem e nt ici. En pratiq ue , la déclaration de la structure s_point
figure ra dans un fich ie r d'e xte nsion h q ue l'on s e conte nte ra d'incorpore r par #include au m om e nt de la com pilation. D e
m ê m e , ile s t né ce s s aire d'inclure stdio.h .

b) Voici la nouve lle fonction de m andé 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) ;
}

Note z q ue l'on doit, ce tte fois, faire appe là l'opé rate ur -> , à la place de l'opé rate ur point (.), puis q ue l'on "travaille"
sur un pointe ur sur une s tructure , e t non plus sur la valeur de la structure e lle-m ê m e . Toute fois l'usage de -> n'e s t pas
totalem e nt indispensable, dans la m e s ure où, par e xe m ple, adp-> x e s t é q uivalent à (*adp).x.

Voici l'adaptation du program m e d'essai pré cédent :

main()
{
VII. Le s s tructure s 75
void affiche (struct s_point *) ;
struct s_point s ;
s.c = 'A' ;
s.x = 10 ;
s.y = 12 ;
affiche (&s) ;
}

R e m arque :

Au lie u d'affe cte r de s valeurs aux ch am ps c, x e t y de notre s tructure s (dans les deux program m es d'essai), nous
pourrions (ici) utiliser les possibilité s d'initial
isation offe rte s par le langage C, e n é crivant :
struct s_point s = {'A', 10, 12} ;

Exe rcice VII.2

___________________________________________________________________________

Enoncé

Ecrire une fonction q ui "m e t à zé ro" les diffé re nts ch am ps d'une structure du type s_point (défini dans l'e xe rcice
pré cédent) q ui lui e s t transm ise en argum e nt. La fonction ne com porte ra pas de valeur de re tour.

_______________________________________________________________

Sol
ution

Ici, bie n q ue l'é noncé ne le pré cis e pas, ile s t né ce s s aire de transm e ttre à la fonction conce rné e , non pas la valeur, m ais
l'adresse de la structure à "re m e ttre à zé ro". Voici la fonction de m andé e (ici, nous avons re produit la déclaration de
s_point) :

#include <stdio.h>
struct s_point
{ char c ;
int x, y ;
} ;

void raz (struct s_point * adr)


76 Exe rcice s e n langage C
{ adr->c = 0 ;
adr->x = 0 ;
adr->y = 0 ;
}

Voici, à titre indicatif, un pe tit program m e d'essai (sa com pilation né ce s s ite la déclaration de s_point, ainsi que le fich ie r
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) ;
}

Exe rcice VII.3

___________________________________________________________________________

Enoncé

Ecrire une fonction q ui re çoit e n argum e nt l'adresse d'une s tructure du type s_point (défini dans l'e xe rcice VII.1) e t q ui
re nvoie e n ré s ultat une s tructure de m ê m e type corre s pondant à un point de m ê m e nom (c) e t de coordonné e s oppos é e s .

Ecrire un pe tit program m e d'essai.

_______________________________________________________________

Sol
ution

Bie n q ue l'é noncé ne pré cis e rie n, le ré s ultat de notre fonction ne pe ut ê tre transm is que par valeur. En e ffe t, ce ré s ultat
doit ê tre cré é au s e in de la fonction e lle-m ê m e ;ce la signifie q u'ils e ra dé truit dè s la sortie de la fonction ;e n transm e ttre
l'adre s s e re vie ndrait à re nvoye r l'adre s s e d e q ue lque ch ose destiné à disparaî
tre ...

Voici ce q ue pourrait ê tre notre fonction (ici, e ncore , nous avons re produit la déclaration de s_point) :

#include <stdio.h>
struct s_point
{ char c ;
VII. Le s s tructure s 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 ;
}

Note z la "dissym é trie " d'instructions te lles q ue re s .c = adr-> c ;on y fait appe là l'opé rate ur . à gauch e e t à l'opé rate ur
-> à droite (on pourrait ce pe ndant é crire re s .c = (*adr).c.

Voici un e xe m ple d'essai de notre fonction (ici, nous avons utilisé les possibilités d'initialisation d'une s tructure pour
donne r de s 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) ;
}

Exe rcice VII.4

___________________________________________________________________________

Enoncé

Soit la structure s uivante , re pré s e ntant un point d'un plan :

struct s_point
{ char c ;
int x, y ;
} ;

1) Ecrire la déclaration d'un tableau (nom m é courbe ) de NP points (NP supposé défini par une instruction #de fine )

2) Ecrire une fonction (nom m é e affich e ) q ui affich e les valeurs des diffé re nts "points" du tableau courbe , transm is en
argum e nt, sous la form e :
78 Exe rcice s e n langage C
point D de coordonnées 10 2

3) Ecrire un program m e q ui :

- lit e n données des valeurs pour le tableau courbe ;on utilisera de préfé re nce les fonctions ge ts e t sscanf, de
pré fé re nce à scanf (voir é ve ntue llem e nt l'e xe rcice VI.5) ;on supposera q u'une ligne de donnée ne peut pas dépas s e r
128 caractè re s ,
- fait appe là la fonction pré cédente pour les affich e r.
_______________________________________________________________

Sol
ution

1) Ilsuffit de déclare r un tableau de s tructure s :

struct s_point courbe [NP] ;

2) Com m e courbe e s t un tableau, on ne pe ut q u'e n transm e ttre l'adre s s e e n argum e nt de affich e . Ile s t pré fé rable de
pré voir é galem e nt e n argum e nt le nom bre de points. Voici ce q ue 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) ;
}

Com m e pour n'im porte q ue ltableau à une dim e nsion transm is en argum e nt, ile s t possible de ne pas e n m e ntionne r la
dim e nsion dans l'e n-tê te de la fonction. Bie n e nte ndu, com m e , e n fait, l'ide ntificate ur courbe n'e s t q u'un pointe ur de
type s_point*(pointe ur sur la pre m iè re s tructure du tableau), nous aurions pu égalem e nt é crire s_point*courbe .

Note z q ue , com m e à l'accoutum é e , le "form alism e tableau" e t le "form alism e pointe ur" pe uve nt ê tre indiffé re m m e nt
utilisés (voire com biné s ). Par e xe m ple, notre fonction aurait pu é galem e nt s'é crire :

void affiche (struct s_point * courbe, int np)


{
struct s_point * adp ;
int i ;
VII. Le s s tructure s 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) Com m e nous avons appris à le faire dans l'e xe rcice VI.5, nous lirons les inform ations re lative s aux diffé re nts points à
l'aide des deux fonctions :

- ge ts, pour lire , sous form e d'une ch aî


ne , une ligne d'inform ation,
- sscanf, pour dé code r suivant un form at le conte nu de la ch aî
ne ainsi lue .
Voici ce q ue pourrait le program m e dem andé (ici, nous avons re produit, à la fois la déclaration de s_point e t la fonction
affich e 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 Exe rcice s e n langage C

Exe rcice VII.5

___________________________________________________________________________

Enoncé

Ecrire le program m e de la q ue s tion 3 de l'e xe rcice pré cédent, sans util


iser de structures. O n pré voira toujours une
fonction pour lire les inform ations re lative s à un point.

_______________________________________________________________

Sol
ution

Ici, ilnous faut obligatoire m e nt pré voir 3 tableaux diffé re nts de m ê m e taille : un pour les nom s de points, un pour leurs
abscis s e s e t un pour leurs ordonné e s . Le program m e ne pré s e nte pas de difficulté s particuliè re s (son principalinté rê t e s t
d'ê tre com paré 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) ;
}
VII. Le s s tructure s 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]) ;
}

Exe rcice VII.6

___________________________________________________________________________

Enoncé

Soie nt les deux m odè les de structure date e t pe rsonne 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 q ui re çoit e n argum e nt une s tructure de type pe rsonne e t q ui e n re m plit les diffé re nts ch am ps ave c un
dialogue s e pré s e ntant sous l'une des 2 form e s s uivante s :

nom : DUPONT
date embauche (jj mm aa) : 16 1 75
date poste = date embauche ? (O/N) : O

nom : DUPONT
date embauche (jj mm aa) : 10 3 81
date poste = date embauche ? (O/N) : N
date poste (jj mm aa) : 23 8 91
82 Exe rcice s e n langage C
_______________________________________________________________

Sol
ution

Notre fonction doit m odifie r le conte nu d'une s tructure de type pe rsonne ;ile s t donc né ce s s aire q u'e lle e n re çoive
l'adre s s e e n argum e nt. Ici, l'é noncé n'im posant aucune prote ction particuliè re conce rnant les lecture s au clavie r, nous
lirons "classiquem e nt" le nom par ge ts e t les trois autre s inform ations num é riq ue s par scanf. Voici ce q ue pourrait ê tre la
fonction de m andé 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) ;
}
}

Note z q ue , com m e à l'accoutum é e , dè s lors q u'une lecture de valeurs num é riq ue s (ici par scanf) e s t suivie d'une lecture
d'un caractè re (ici par ge tch ar, m ais le m ê m e problèm e s e pos e rait ave c scanf e t le code %c), ile s t né ce s s aire de
"saute r" artificie llem e nt le caractè re ayant s e rvi à la validation de la derniè re inform ation num é riq ue ;e n e ffe t, dans le
cas contraire , c'e s t pré cis é m e nt ce caractè re (\n) q ui e s t pris e n com pte .

En toute rigue ur, la dém arch e ainsi utilisée n'est pas infaillible : si l'utilisate ur fournit des inform ations supplém e ntaire s
aprè s la derniè re valeur num é riq ue (ne s e rait-ce q u'un sim ple e s pace ), le caractè re lu ulté rie ure m e nt ne s e ra pas ce lui
atte ndu. Toute fois, ils'agit alors des "problèm e s h abitue ls" lié s à la fourniture d'inform ations e xcédentaire s . Ils peuve nt
ê tre ré s olus par diffé re nte s te ch niq ues dont nous avons parlé, notam m e nt, dans l'e xe rcice VI.5.
VII. Le s s tructure s 83
Voici, à titre indicatif, un pe tit program m e d'essai de notre fonction (sa com pilation né ce s s ite les déclarations des
structure s date e t pe rsonne ) :

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 ) ;
}
D EUXIEM E PARTIE :

EXERCICES TH EM A TIQUES
INTRO D UCTIO N
A LA D EUXIEM E PARTIE

Ce ch apitre vous fournit q ue lque s e xplications conce rnant la m aniè re dont sont conçus les problèm e s proposés dans ce tte
deuxiè m e partie de l'ouvrage e t les q ue lque s rè gles q ue nous nous som m e s fixé e s pour la rédaction de s program m e s
corre s pondants.

1 - Cane vas com m un à ch aq ue e xe rcice

Pour ch aq ue e xe rcice , nous avons adopté le m ê m e cane vas.

a)L'e xpos é du probl


è me

Ile s t constitué d'un énoncé accom pagné d'un exem ple. Ce t e ns e m ble constitue ce q u'ile s t indispensable de lire avant de
te nte r de ré s oudre le problèm e . Ce rte s , l'e xe m ple pe rm e t d'illustre r e t de concré tiser l'é noncé m ais, de plus, ille
pré cis e , e n particulie r e n e xplicitant la m aniè re dont le program m e dialogue ave c l'utilisate ur. O n note ra q ue ce t e xe m ple
corre s pond e xacte m e nt à une im age d'écran obte nue ave c le program m e propos é e n solution.

b)L'anal
yse

Elle s pé cifie (ou pré cis e ) les algorith m e s à m e ttre e n oe uvre pour aboutir à une solution. Elle garde un caractè re gé né ral;
notam m e nt, e lle é vite de s'inté re s s e r à ce rtains détails de program m ation dont le ch oix e s t re je té au m om e nt de l'é criture
du program m e . A priori, e lle fait déjà partie de la solution ;toute fois, si vous s é ch e z sur l'é noncé lui-m ê m e , rie n ne vous
e m pê ch e , aprè s la lecture de ce tte analyse, de te nte r d'é crire le program m e corre s pondant. En e ffe t, un te le xe rcice , bie n
86 Exe rcice s e n langage C
q ue lim ité à la sim ple traduction d'un algorith m e dans un langage , n'e n possè de pas m oins un inté rê t propre e n ce q ui
conce rne l'appre ntissage du langage lui-m ê m e .

c)Le program m e

Bie n q u'ilsuive e xacte m e nt l'analyse proposée, iln'e n re s te pas m oins q u'ilfaille le considérer com m e une rédaction
possible parm i beaucoup d'autre s . N'oublie z pas q u'à ce nive au ile s t bien difficile de porte r un juge m e nt de valeur sur
les q ualité s ou les défauts de te lle ou te lle rédaction, tant q ue l'on n'a pas précisé les critè re s re te nus (vitesse d'e xé cution,
taille m é m oire , clarté de la rédaction, re s pe ct de ce rtaine s rè gles de style, ...) ;ce la e s t d'autant plus vrai q ue ce rtains de
ce s critè re s pe uve nt s'avé re r incom patibles e ntre e ux. Ce s re m arq ue s s 'appliq ue nt d'ailleurs déjà aux e xe rcice s propos é s
pré cédem m e nt dans la pre m iè re partie de ce t ouvrage m ais ave c m oins d'accuité .

d)Le s com m e ntaire s

Ils fournis s e nt ce rtaine s e xplications q ue nous avons jugé e s utiles à la com pré h e nsion du program m e lui-m ê m e . Ilpe ut,
par e xe m ple, s'agir :

- de rappe ls conce rnant une instruction ou une fonction pe u usuelle,


- de justifications de ce rtains ch oix ré alisés uniquem e nt au m om e nt de la rédaction du program m e ,
- de m ise en évidence de certaine s particularité s ou originalités du langage ,
- e tc.

e )La dis cus s ion

Elle constitue une s orte d'ouve rture fondé e s ur une ré flexion de caractè re gé né ralq ui pe ut porte r sur :

- les insuffisance s é ve ntue lles du program m e propos é , notam m e nt e n ce q ui conce rne s on com porte m e nt face à des
e rre urs de la part de l'utilisate ur,
- les am é liorations q u'ile s t possible de lui apporte r,
- une gé né ralisation du problèm e pos é ,
- e tc.
Introduction à la de uxiè m e partie 87
2 - Prote ction de s program m e s par rapport aux donné e s

Com m e beaucoup d'autre s langage s , les instructions usuelles de lecture au clavie r du langage C ne s ont pas totalem e nt
proté gées d'é ve ntue lles ré pons e s incorre ctes de la part de l'utilisate ur. Ce lles -ci pe uve nt e ntraî
ne r un com porte m e nt
anorm aldu program m e .

D 'une m aniè re gé né rale, ce problèm e de contrôle des données peut ê tre ré s olu par l'e m ploi de te ch niq ue s approprié e s
te lles q ue ce lles q ue nous avons re ncontrées dans l'e xe rcice VI.5 de la pre m iè re partie . Toute fois, ce lles -ci pré s e nte nt
l'inconvé nie nt d'alourdir le te xte du program m e . C'e s t pourq uoi nous avons é vité d'introduire systé m atiq ue m e nt de te lles
prote ctions dans tous nos exem ples , ce q ui aurait m anife s te m e nt m as q ué l'obje ctif e s s e ntie lde l'e xe rcice (bie n e nte ndu,
ce s prote ctions pourraie nt deve nir indispe nsables dans un program m e ré e l). Note z toute fois q ue ce rtains e xe rcice s , de par
leur nature m ê m e , re q uiè re nt une te lle prote ction ;ce lle-ci s e ra alors claire m e nt dem andée dans l'é noncé lui-m ê m e .

3 - A propos d e s s tructure s de boucl


e

En principe , lors q ue l'analyse d'un problèm e fait inte rve nir une ré pé tition, ilfaudrait, pour ê tre com plet, e n pré cis e r le
type :

- ré pé tition définie (ou ave c com pte ur) : e lle e s t ré alisée en C ave c l'instruction for,
- ré pé tition tant q u e , dans laq ue lle le te s t de poursuite a lie u e n début de boucle : e lle e s t ré alisée en C ave c
l'instruction w h ile,
- ré pé tition jusqu'à dans laq ue lle le te s t d'arrê t a lie u e n fin de boucle : e lle e s t ré alisée en C ave c l'instruction do ...
w h ile.
En fait, ile xiste plusieurs raisons de ne pas toujours spé cifie r le ch oix du type d'une répétition au nive au de l'analyse et
de le re porte r au nive au de l'é criture du program m e :

- d'une part, le ch oix d'un type de boucle n'e s t pas toujours dicté im pé rative m e nt par le problèm e : par e xe m ple, un
algorith m e utilisant une ré pé tition de type jusqu'à pe ut toujours ê tre transform é e n un algorith m e utilisant une
ré pé tition de type tant q u e ,
- d'autre part, com m e nous l'avons déjà e ntre vu dans le ch apitre III de la pre m iè re partie , le langage C autorise des
form es de ré pé tition plus varié e s q ue les trois q ue nous ve nons d'évoq ue r (e t q ui sont ce lles propos é e s classiquem e nt
par la "program m ation structuré e ") : ainsi, par e xe m ple :
*grâ ce à la notion d'opé rate ur s é q ue ntie l, on pe ut ré aliser, à l'aide de l'instruction w h ile, des boucles dans
les q ue lles le te s t de poursuite a lie u, non plus e n début, m ais e n cours de boucle,
*l'instruction bre ak autorise des boucles à sortie s m ultiples .
88 Exe rcice s e n langage C
Ce rte s , on pe ut obje cte r q ue ce s ont là des possibilité s q ui sont contraire s à l'e s prit de la program m ation structuré e .
Ce pe ndant, utilisées à bon e s cie nt, e lles pe uve nt am é liore r la concision et le te m ps d'exécution de s program m e s . Com pte
te nu de l'orie ntation du langage C, ilne nous a pas paru opportun de nous prive r totalem e nt de ce s facilité s .

En dé finitive , ilnous arrive ra souve nt, au cours de l'analyse, de nous conte nte r de pré cis e r la (ou les ) condition(s) d'arrê t
d'une ité ration e t de re porte r au nive au de la program m ation m ê m e le ch oix de s instructions à utiliser. On note ra q u'e n
procédant ainsi un effort de ré flexion logiq ue pe ut re s te r né ce s s aire au m om e nt de la rédaction du program m e , laq ue lle,
dans ce cas, s e trouve ê tre plus q u'une s im ple traduction litté rale!

4 - A propos d e s fonctions

a) Com m e nous l'avons déjà re m arq ué dans l'avant-propos, la norm e ANSI acce pte deux form es de définition de
fonctions. Voici, par e xe m ple, deux façons d'écrire l'e n-tê te d'une fonction fct re ce vant deux argum e nts de type int e t
ch are t re nvoyant une valeur de type double :

double fct (int x, char * p)

double fct (x, p)


int x ;
char * p ;

Ilne s 'agit là q ue de sim ples diffé re nces de rédaction, sans aucune incide nce s ur le plan fonctionne l. Ici, nous avons
systé m atiq ue m e nt e m ployé la pre m iè re form e (on la nom m e parfois form e "m ode rne "), dans la m e s ure où e lle a te ndance
à s e gé né raliser et où, de plus, ils'agit de la s e ule form e acce pté e par le C+ + .

b) Le s fonctions ont toujours é té déclarées dans les fonctions les utilisant bien q u'a priori :

- ce la ne s oit pas obligatoire pour les fonctions fournissant un ré s ultat de type int,
- ce la ne s oit pas obligatoire lors q u'une fonction a é té définie , dans le m ê m e s ource , avant d'ê tre utilisée.

c) D ans les déclarations des fonctions, nous avons utilisé la form e prototype autoris é e par le s tandard ANSI. Ce lle-ci s e
ré vè le s urtout fort pré cie us e lors q ue l'on e xploite les possibilités de com pilation s é paré e e t q ue l'on a donc affaire à
plusieurs fich ie rs source diffé re nts. Ce rte s , ce n'e s t pas le cas ici, m ais, com pte te nu de ce q u'e lle e s t pratiq ue m e nt
acce pté e d e tous les com pilate urs actue ls e t q ue , de plus, e lle e s t e s t obligatoire e n C+ + , ilnous a paru judicie ux d'e n
faire une h abitude .
I : VARIA TIO NS A LGO RITH M IQUES
SUR LES INSTRUCTIO NS
D E BASE

Ce ch apitre vous propose des problèm e s ne faisant appe lq u'aux notions de base du langage C, à savoir :

- e ntré e s -sortie s conve rsationne lles (ge tch ar, scanf, ge ts, putch ar, printf),
- instructions de contrôle,
- tableaux,
- ch aî
ne s ,
- fonctions.

I-1 Triangl
e de Pas cal

______________________________________________________________________________
Enoncé

Affich e r un "triangle de Pascal" dont le nom bre de ligne s e s t fourni e n donné e . Nous vous rappe lons q ue les "cas e s " d'un
te ltriangle contie nne nt les valeurs des coe fficie nts du binom e C (ou nom bre de com binaisons de n élém e nts pris p à p).
n,p
Ce tte valeur e s t placée dans la cas e corre s pondant à l'inte rs e ction de la ligne de rang n e t la colonne de rang p (la
num é rotation com m e nçant à 0).

O n é vite ra de calculer ch aq ue te rm e s é paré m e nt ;au contraire , on ch e rch e ra à e xploite r la re lation de ré curre nce :
90 Exe rcice s e n langage C
C = C + C
i,j i-1, j i-1,j-1

O n lim ite ra à 15 le nom bre de lignes dem andé e s par l'utilisate ur e t on re s pe cte ra la pré s e ntation proposée dans l'e xe m ple
ci-de s s ous.

Exe m pl
e
combien de lignes voulez vous ? 12

p 0 1 2 3 4 5 6 7 8 9 10 11
n
-----------------------------------------------------------------
0 -- 1
1 -- 1 1
2 -- 1 2 1
3 -- 1 3 3 1
4 -- 1 4 6 4 1
5 -- 1 5 10 10 5 1
6 -- 1 6 15 20 15 6 1
7 -- 1 7 21 35 35 21 7 1
8 -- 1 8 28 56 70 56 28 8 1
9 -- 1 9 36 84 126 126 84 36 9 1
10 -- 1 10 45 120 210 252 210 120 45 10 1
11 -- 1 11 55 165 330 462 462 330 165 55 11 1
______________________________________________________________________________

ANALYSE

A priori, nous pourrions utiliser un tableau t à deux dim e nsions com portant 15x15 é lém e nts e t décide r (arbitraire m e nt)
q ue le pre m ie r indice corre s pond au rang d'une ligne du triangle, le s e cond à ce lui d'une colonne . Nous re m plirions
alors partie llem e nt ce tableau ave c les valeurs C voulue s (i varie rait de 0 à n-1 si n re pré s e nte le nom bre de ligne s
i,j
dem andé e s e t, pour ch aq ue valeur de i, j varie rait de 0 à i).

Pour e xploite r la ré curre nce propos é e , ilnous suffirait alors de procéder com m e s uit :

- place r la valeur 1 e n t(0,0) (ce q ui constitue la pre m iè re ligne ),


- pour ch aq ue ligne de rang i, à partir de i=1, procéder ainsi :
*place r la valeur 1 e n t(i,0) e t t(i,i) (e xtré m ité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)
I. Variations algorith m iques sur les instructions de base 91
En fait, ile s t possible de n'utiliser qu'un tableau à une s e ule dim e nsion, dans leq ue lon vie nt calculer succe s s ive m e nt
ch acune des lignes du triangle (ilfaut alors, bie n sûr, affich e r ch aq ue ligne dè s q u'e lle a é té déte rm iné e ).

Supposons, en effe t, q u'à un instant donné , nous disposions dans ce tableau t des i+1 valeurs de la ligne de rang i e t
voyons com m e nt déte rm ine r ce lles de la ligne de rang i+1. Nous constatons q ue la ré curre nce propos é e pe rm e t de définir
la nouve lle valeur d'un é lém e nt de t e n fonction de s on ancie nne valeur e t de l'ancie nne valeur de l'é lém e nt pré cédent.

Ce rte s , si nous répétions une affe ctation de la form e :

t(j) = t(j) + t(j-1)


e n faisant varie r j de 1 à i-1, nous n'aboutirions pas au résultat e s com pté puis q u'alors la valeur de t(j) dépendrait de la
nouve lle valeur pré alablem e nt attribué e à t(j-1).

M ais, ile s t facile de m ontre r q u'e n e xplorant la ligne de droite à gauch e , c'e s t-à -dire e n ré pé tant l'affe ctation ci-de s s us
e n faisant décroî tre j de i-1 à 0, le problèm e ne s e pos e plus.

Voici finalem e nt l'algorith m e q ue nous utiliserons :

Faire varie r i de 0 à n-1. Pour ch aq ue valeur de i :


- ré pé te r, e n faisant décroî
tre j de i-1 à 1 :
t(j) = t(j) + t(j-1)
- place r la valeur 1 dans t(i).

R e m arques :

1) Te lq ue l'algorith m e vie nt d'ê tre é noncé , nous constatons q ue pour i=0, j doit décroî tre de -1 à 1! Nous adm e ttrons
q ue ce la signifie e n fait q u'aucun traite m e nt n'e s t à ré aliser dans ce cas (ce qui est norm alpuis q ue alors notre ligne
e s t réduite à la s e ule valeur 1, laq ue lle s e ra placé e par l'affe ctation t(i)=1). Ile n va de m ê m e pour i=1, j devant alors
décroî tre de 0 à 1. O n note ra q u'e n langage C la boucle for pe rm e t de te nir com pte de ce s cas particulie rs (le te s t de
poursuite de boucle é tant ré alisé en début). Ce n'e s t toute fois pas là une rè gle gé né ralisable à tous les langage s .
2) Ave c les pré cautions q ue nous ve nons d'évoq ue r, l'algorith m e "s'initialise" de lui-m ê m e .

Program m e

#include <stdio.h>
#define NMAX 15 /* nombre maximal de lignes */

main()
{ int t [NMAX], /* tableau représentant une ligne du triangle */
92 Exe rcice s e n 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") ;
}
}

Com m e ntaire s

*En langage C, les indices d'un tableau com m e nce nt à 0. Ici, ce tte particularité s 'avè re inté re s s ante puis q ue nos
num é ros de ligne s ou de colonnes doive nt aussi com m e nce r à 0.

*Plutôt q ue d'utiliser directe m e nt la constante 15 dans notre program m e , nous avons préfé ré faire appe là l'instruction
#de fine du pré proce s s e ur pour dé finir un sym bole NMAX possédant ce tte valeur. Ile s t ainsi beaucoup plus facile, le cas
é ch é ant, de m odifie r ce tte valeur (puis q u'ilsuffit alors d'inte rve nir e n un s e ule ndroit du program m e ). Note z q ue nous
n'aurions pas pu utiliser la déclaration de constante sym boliq ue (const int NM AX = 15), car, dans ce cas, NM AX n'aurait
pas é té une "e xpre s s ion constante ", e t nous n'aurions pas pu l'utiliser com m e dim e nsion d'un tableau.

*Ne pas oublie r q ue t[NM A X] ré s e rve NMAX élém e nts (c'e s t-à -dire 15), dont les indice s varie nt de 0 à 14.
I. Variations algorith m iques sur les instructions de base 93

*Si l'utilisate ur de m ande un nom bre de ligne s s upé rie ur à NMAX, le program m e s e conte nte de lim ite r ce tte dem ande à
la valeur NM A X.

D ISCUSSIO N

*Nous aurions pu te nir com pte de la sym é trie de ch aq ue ligne par rapport à son ce ntre ;q ue lque s instructions
supplém e ntaire s nous auraie nt alors perm is une légè re réduction du te m ps de calcul.

*L'é noncé lim itait à 15 le nom bre de lignes de notre triangle. En e ffe t, au-de là, iln'e s t gé né ralem e nt plus possible
d'affich e r toute s les valeurs sur une s e ule ligne d'écran.

*Notre program m e n'e s t pas proté gé dans le cas où l'utilisate ur fournit une ré pons e non num é riq ue à la q ue s tion pos é e .
D ans ce cas, toute fois, la situation n'e s t pas trè s grave ;e n e ffe t, la valeur de nle s t, ce rte s , aléatoire m ais, de toute
façon, e lle s e ra lim ité e à 15 par le program m e .

Si vous souh aitie z q uand m ê m e traite r ce type d'anom alie , ilvous suffirait d'exam ine r le code de re tour de la fonction
scanf (ilfournit le nom bre de valeurs conve nablem e nt lue s ) e t de vé rifie r q u'ile s t bien égalà 1.

I-2 Cribl
e d'Eratos th è ne

________________________________________________________________________________________

Ile xiste une m é th ode de déte rm ination de nom bre s pre m ie rs connue s ous le nom de "crible d'Erastoth è ne ". Elle pe rm e t
d'obte nir tous les nom bre s pre m ie rs infé rie urs à une valeur donné e n.

La m é th ode (m anue lle) consiste à dre s s e r une liste des nom bre s considérés (de 1 à n) e t à y raye r tous les nom bre s
m ultiples d'autre s e ntie rs (de te ls nom bre s s ont né ce s s aire m e nt non pre m ie rs). Plus précisém e nt, on procè de ainsi :

1 - on raye le 1 (q ui, par dé finition, n'e s t pas un nom bre pre m ie r).

2 - on re ch e rch e , à partir du de rnie r nom bre pre m ie r considéré (la pre m iè re fois, on convie nt q u'ils'agit du 1), le
pre m ie r nom bre non rayé (on pe ut m ontre r q u'ile s t pre m ie r). Ildevie nt, à son tour, le dernie r nom bre pre m ie r considéré
e t on raye tous s e s m ultiples .

3 - on ré pè te le point 2 jus q u'à ce q ue le nom bre pre m ie r considéré soit supérieur à la racine carré e d e n. O n pe ut alors
m ontre r q ue tous les nom bre s non pre m ie rs ont é té rayés de la liste .
94 Exe rcice s e n langage C
Enoncé

Ecrire un program m e bas é s ur ce tte m é th ode re ch e rch ant tous les nom bre s pre m ie rs com pris e ntre 1 e t n (la valeur de n
é tant fixée dans le program m e )

Exe m pl
e

entre 1 et 1000, les nombres premiers sont :


2 3 5 7 11 13 17 19 23 29
31 37 41 43 47 53 59 61 67 71
73 79 83 89 97 101 103 107 109 113
127 131 137 139 149 151 157 163 167 173
179 181 191 193 197 199 211 223 227 229
233 239 241 251 257 263 269 271 277 281
283 293 307 311 313 317 331 337 347 349
353 359 367 373 379 383 389 397 401 409
419 421 431 433 439 443 449 457 461 463
467 479 487 491 499 503 509 521 523 541
547 557 563 569 571 577 587 593 599 601
607 613 617 619 631 641 643 647 653 659
661 673 677 683 691 701 709 719 727 733
739 743 751 757 761 769 773 787 797 809
811 821 823 827 829 839 853 857 859 863
877 881 883 887 907 911 919 929 937 941
947 953 967 971 977 983 991 997

________________________________________________________________________________________

ANALYSE

La m é th ode m anue lle s uggè re d'utiliser un tableau. Toute fois, devons-nous, par analogie , y range r les nom bre s e ntie rs de
1 à n?En fait, ce la ne s e rait guè re utile puis q ue alors ch aq ue nom bre s e rait é galà son rang dans le tableau (du m oins, à
une unité prè s, suivant les conve ntions q ue l'on adopte rait pour l'indice du prem ie r é lém e nt).

En ré alité , le bon déroulem e nt de l'algorith m e nous im pos e s e ulem e nt d'ê tre e n m e s ure de faire corre s pondre à ch aq ue
e ntie r e ntre 1 e t n, une inform ation pré cisant, à ch aq ue instant, s'ile s t rayé ou non (ce tte inform ation pouvant é volue r au
fildu déroulem e nt du program m e ). Ils'agit là tout nature llem e nt d'une inform ation de type "logiq ue " (vrai ou faux).
Com m e ce type n'e xiste pas e n tant q ue te le n langage C, nous le s im ulerons à l'aide de deux constante s e ntiè re s : VR A I
de valeur 1, FAUX de valeur 0. Note z q ue le ch oix de la valeur 0 pour FAUX est im pos é par la m aniè re dont le langage
I. Variations algorith m iques sur les instructions de base 95
C considè re une e xpre s s ion num é riq ue apparaissant dans une condition ;la valeur 1, par contre , pourrait ê tre , sans
inconvé nie nt, re m placé e par n'im porte q ue lle valeur non nulle.

Notons raye un te ltableau e t supposons q ue raye [i] corre s pond à l'e ntie r i (ce q ui, com pte te nu de s conve ntions du
langage C, signifie q ue raye [0] e s t inutilisé). Notre algorith m e nous im pose de garde r la trace du dernier nom bre pre m ie r
considéré. Nous le nom m e rons pre m . La dém arch e m anue lle s e transpose alors com m e s uit :

*Initialisation :
- m e ttre à FAUX tous les é lém e nts du tableau raye ,
- m e ttre à FAUX le pre m ie r é lém e nt de raye , e t faire :
pre m = 1
*Ité ration :
- re ch e rch e r, à partir de pre m , le pre m ie r nom bre non e ncore rayé , c'e s t-à -dire incré m e nte r la valeur de pre m
jus q u'à ce q ue t[pre m ] soit FAUX (en toute rigue ur, ilfaut se dem ande r s'ile xiste e ncore un te lnom bre dans
notre tableau, e t donc lim ite r l'incré m e ntation de pre m à N).
- raye r tous les m ultiples de pre m , dans le cas où un te lnom bre a é té trouvé .
*L'ité ration propos é e pe ut ê tre ré pé té e , indiffé re m m e nt (les deux form ulations é tant é q uivalentes dè s q ue N est
supérieur ou égalà 1) :
- jusqu'à ce q ue la valeur de pre m soit supérieure à la racine carrée de N,
- ou tant q u e la valeur de pre m e s t infé rie ure ou é gale à la racine carrée de N.

Program m e

#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 Exe rcice s e n 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 */
}
}

Com m e ntaire s

*La re ch e rch e du prem ie r nom bre non e ncore rayé est ré alisée par la s e ule instruction :

while (raye[++prem] && prem<N) {}

Note z bie n la pré -incré m e ntation de pre m ;une post-incré m e ntation :

while (t[prem++] && prem<N) {}

aurait conduit à une boucle infinie s ur le pre m ie r nom bre pre m ie r trouvé , c'e s t-à -dire 2 (du m oins si N est supérieur ou
é galà 2). Ilsuffirait toute fois d'incré m e nte r pre m une fois avant d'entre r dans la boucle pour q ue ce la fonctionne .

*Nous avons cons e rvé le garde -fou :

prem < N
I. Variations algorith m iques sur les instructions de base 97
O n pourrait toute fois dém ontre r q ue , dè s q ue N est supérieur ou égalà 2, on e s t toujours assuré de trouve r au m oins un
nom bre non rayé avant la fin du tableau (com pte te nu de ce q ue l'on com m e nce l'e xploration ave c un nom bre infé rie ur
ou é galà la racine carrée de N).

*Nous avons prévu d'affich e r nos nom bre s pre m ie rs, à raison de 10 par ligne , ch aq ue nom bre occupant 7 caractè re s .
Pour ce faire , nous utilisons une variable nom m é e na nous perm e ttant de com ptabiliser le nom bre de nom bre s affich é s . A
ch aq ue fois q ue na e s t m ultiple de 10, nous provoq uons un saut de ligne .

D ISCUSSIO N

*Te lq u'ile s t propos é ici, le program m e traite le cas n=1000. Pour le faire fonctionne r ave c d'autre s valeurs, ile s t
né ce s s aire d'inte rve nir au nive au du program m e lui-m ê m e e t de le re com piler. Si vous souh aite z q ue la valeur de n puis s e
ê tre fournie e n donné e , ilfaut lui fixe r une valeur m axim ale, afin de pré voir la ré s e rvation du tableau corre s pondant.

Note z toute fois q ue les possibilités de ge s tion dynam iq ue du langage C offre nt une s olution plus agré able à ce problèm e
de dim e nsions variables . Vous e n trouve re z ce rtains e xe m ples dans le ch apitre consacré à la ge s tion dynam iq ue .

*Le tableau raye , ainsi que les variables pre m e t i, ont é té déclarés de type int, ce q ui, dans ce rtaine s im plém e ntations,
pe ut lim ite r à 32767 les valeurs q u'ile s t ainsi possible d'exam ine r. O n pe ut toujours faire m ie ux, e n utilisant le type
unsigne d int, ou m ie ux le type long ou unsigne d long. Toute fois, dans ce cas, on s'assure ra q ue l'on n'e s t pas soum is à
des contrainte s s ur la taille des diffé re nts m odules obje ts, sur la taille de la pile ou, e ncore , tout sim plem e nt, sur la taille
des diffé re nts obje ts q u'ile s t possible de m anipuler. Iln'e s t pas rare , e n e ffe t, q ue l'on re ncontre des lim itations à 64 KO
(c'e s t le cas, actue llem e nt, des com pilate urs Borland/Turbo C/C+ + utilisés dans l'e nvironne m e nt D O S).

I-3 Le ttre s com m une s à de ux m ots (1)

________________________________________________________________________________________

Enoncé

R é aliser un program m e q ui affich e les lettre s com m une s à deux m ots fournis au clavie r. O n pré voira d'affich e r plusieurs
fois une lettre q ui apparaît à plusieurs reprises dans ch acun des deux m ots.
98 Exe rcice s e n langage C
O n supposera q ue ce s m ots ne peuve nt pas com porte r plus de 26 caractè re s e t on les lira à l'aide de la fonctions ge ts.

Exe m pl
es

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 im pose d'utiliser ge ts, donc de re pré s e nte r nos m ots sous form e de ch aî nes de caractè re s (suites de
caractè re s te rm iné e s par le caractè re nul, noté e n C : \0). Nous utiliserons à ce t e ffe t des tableaux de caractè res de
dim e nsion 27 (pour 26 lettre s m axim um e t un caractè re de fin).

La re ch e rch e des lettre s com m une s aux de ux m ots peut s e faire e n com parant ch acun de s caractè res de la pre m iè re ch aî
ne
à ch acun de s caractè res de la s e conde . Ce la nous conduit nature llem e nt à l'utilisation de deux boucles ave c com pte ur
(instructions for) im briq ué e s .

Toute fois, nous devons te nir com pte de ce q u'une m ê m e lettre pe ut figure r plusieurs fois dans un m ê m e m ot. Dans ces
conditions, ilfaut é vite r :

*q u'une m ê m e lettre du prem ie r m ot ne puis s e ê tre trouvé e e n de ux e ndroits diffé re nts du second. Par e xe m ple,
ave c :
m onsieur
et
bonjour
I. Variations algorith m iques sur les instructions de base 99
aprè s avoir trouvé q ue le o de m onsie ur figurait e n position 2 de bonjour, ilfaut é vite r de s ignaler une nouve lle
coïncide nce e ntre ce m ê m e o de m onsie ur e t le s e cond o de bonjour.
Ile s t donc né ce s s aire d'inte rrom pre la com paraison entre une lettre du prem ie r m ot ave c toute s ce lles du second m ot,
dè s q u'une coïncide nce a é té déte cté e .
*q u'une m ê m e lettre du second m ot ne puis s e coïncide r ave c deux lettres diffé re ntes du second. Par e xe m ple, ave c
(atte ntion à l'ordre des m ots) :
bonjour
et
m onsieur
ilfaut é vite r de trouve r une coïncide nce e ntre le pre m ie r o de bonjour e t l'uniq ue o de m onsie ur e t une autre
coïncide nce e ntre le s e cond o de bonjour e t le m ê m e o de m onsie ur.
Pour ce faire , une dém arch e (parm i d'autre s ) consiste à é lim ine r dans le s e cond m ot la lettre ayant fait l'obje t d'une
coïncide nce . Plus précisém e nt, ilsuffit de re m place r une te lle lettre par un caractè re dont on e s t sûr q u'iln'apparaîtra
pas dans un m ot. Ici, nous avons ch oisi l'e s pace puis q ue nous som m e s ce ns é s travailler ave c des m ots.

Program m e

#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 Exe rcice s e n langage C
{ printf ("la lettre %c est commune aux deux mots\n", mot1[i]) ;
mot2[j] = ' ' ;
break ;
}
}

Com m e ntaire s

*Nous avons utilisé le sym bole LM A X pour re pré s e nte r la longue ur m axim ale d'un m ot. Note z bie n q ue les tableaux
m ot1 e t m ot2 ont dû ê tre pré vus de dim e nsion LM A X+1, afin de te nir com pte de la pré s e nce du caractè re de fin de
ch aî
ne .

*Nous aurions pu utiliser, à la place de la s e conde boucle ave c com pte ur (e n j), une boucle tant q u e (w h ile). Ce rte s , la
program m ation e û t é té plus structuré e m ais, né anm oins, m oins concis e .

D ISCUSSIO N

Ce program m e n'e s t pas proté gé contre des ré ponses de plus de 26 caractè re s . Dans ce cas, en effe t, les caractè re s
superflus iront é cras e r les données se trouvant au-de là de l'un de s tableaux m ot1 ou m ot2. Le s cons é q ue nce s pe uve nt ê tre
as s e z varié e s (vous pouve z e xpé rim e nte r le pré s e nt program m e dans dive rs e s s ituations e t te nte r d'e xpliq ue r les
com porte m e nts observé s ).

Ile xiste diffé re nte s façons d'évite r ce ris q ue . Citons, par e xe m ple :

- lire (toujours par ge ts), une ch aî ne com portant un nom bre de caractè re s s uffisam m e nt é levé pour q ue l'utilisate ur ne
ris q ue pas (trop!) d'en fournir plus. O n pourrait ch oisir, par e xe m ple 80 ou 128 caractè re s (dans ce rtaine s
im plém e ntations, iln'e s t jam ais possible de tape r de s lignes de plus de 128 caractè re s ).
- lim ite r autom atiq ue m e nt la longue ur de la ch aî
ne lue , e n utilisant la fonction fge ts ;par e xe m ple, ave c fge ts (m ot1,
LM AX, stdin), on lim ite à LM AX le nom bre de caractè re s lus sur stdin.
- utiliser, dans certaine s im plém e ntations (Turbo/Borland C/C+ + , C/Quick C M icrosoft), une fonction (non
portable!) nom m é e cge ts.
I. Variations algorith m iques sur les instructions de base 101
I-4 Le ttre s com m une s à de ux m ots (2)

________________________________________________________________________________________

Enoncé

R é aliser un program m e q ui affich e les lettre s com m une s à deux m ots fournis e n donné e . Ce tte fois, on n'im pos e ra pas de
lim ite à la taille des m ots fournis par l'utilisate ur, m ais on ne pre ndra e n com pte q ue les 26 pre m ie rs caractè re s . Que lq ue
soit le nom bre de caractè re s e ffe ctive m e nt frappé s , l'utilisate ur de vra toujours valider sa réponse par la frappe de la
touch e re turn.

Là e ncore , on pré voira d'affich e r plusieurs fois une lettre q ui apparaî


t à plusieurs reprises dans ch acun de s m ots.

O n s'astre indra à n'utiliser pour la lecture au clavie r q ue l


a seul e fonction getch ar. De plus, on ré alisera une fonction
destiné e à lire un m ot dans un tableau q u'on lui transm e ttra e n argum e nt ;e lle fournira, e n re tour, la longue ur e ffe ctive
du m ot ainsi lu.

Exe m pl
es

Voir ce ux de l'e xe rcice pré cédent

________________________________________________________________________________________

ANALYSE

L'é noncé nous im pos e l'e m ploi de ge tch ar, ce q ui signifie q ue ch acun des deux m ots devra ê tre lu caractè re par
caractè re . Dans ces conditions, nous pouvons ch oisir de représente r nos m ots :

- soit sous form e d'une ch aî ne de caractè re s . Ilnous faudra alors introduire nous-m ê m e s le caractè re de fin de ch aî
ne
(\0), ce q ue faisait autom atiq ue m e nt ge ts.
- soit sous form e d'une sim ple s uite de caractè re s (c'e s t-à -dire s ans ce caractè re de fin). Dans ce cas, ilnous faudra
alors prévoir d'e n dé te rm ine r la "longue ur".
Com m e l'é noncé nous im pos e q ue la fonction de lecture d'un m ot e n re s titue la longue ur, nous ch oisirons la s e conde
solution.

La lecture d'un m ot consiste donc à lire des caractè re s au clavie r jus q u'à ce q ue l'on re ncontre une validation (\n) ou q ue
l'on ait obte nu 26 caractè re s ;de plus, dans le cas où l'on a obte nu 26 caractè re s , ilfaut poursuivre la lecture de
caractè re s au clavie r, sans les pre ndre e n com pte , jus q u'à ce q ue l'on re ncontre une validation.
102 Exe rcice s e n langage C

Program m e

#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')
I. Variations algorith m iques sur les instructions de base 103
while (getchar() != '\n') {} /* recherche '\n' */
return(i) ;
}

Com m e ntaire s

*Là e ncore , nous avons utilisé le sym bole LM A X pour re pré s e nte r la taille m axim ale d'un m ot. Par contre , ce tte fois, la
dim e nsion des tableaux m ot1 e t m ot2 e s t é gale à LM A X (e t non plus LM A X+ 1), puis q ue nous n'avons pas à y introduire
le caractè re s upplém e ntaire de fin de ch aî ne .

*En ce q ui conce rne la fonction (nom m é e lire ) de lecture d'un m ot au clavie r, vous constate z q ue nous l'avons déclaré e
dans le program m e principal(m ain), bie n q ue ce la soit facultatif, dans la m e s ure où e lle fournit un ré s ultat de type int (e n
e ffe t, toute fonction q ui n'e s t pas e xplicite m e nt déclaré e e s t supposée produire un résultat de type int).

D 'autre part, com m e nous l'avons e xpliq ué dans l'introduction de ce tte s e conde partie , nous avons utilisé, dans ce tte
déclaration, la form e "prototype " autoris é e par la norm e ANSI (ce prototype assure les contrôles de types d'argum e nts e t
m e t e n place d'éve ntue lles conve rsions).

Par ailleurs, l'e n-tê te de notre fonction lire a é té é crit suivant la form e "m ode rne ". La norm e ANSI aurait autoris é le
re m place m e nt de note e n-tê te par :

int lire (mot)


char mot [LMAX] ;

*Le tableau de caractè re s re pré s e ntant l'uniq ue argum e nt de lire doit obligatoire m e nt ê tre transm is par adre s s e puis q ue
ce tte fonction doit ê tre e n m e s ure d'en m odifie r le conte nu. N'oublie z pas ce pe ndant q u'e n langage C un nom de tableau
e s t inte rpré té (par le com pilate ur) com m e un pointe ur (constant) sur son pre m ie r é lém e nt. C'e s t ce q ui justifie la
pré s e nce , dans les appe ls à la fonction lire , de m ot1 ou m ot2, e t non de & m ot1 ou & m ot2.

*La déclaration de l'argum e nt de lire , dans son e n-tê te :

char mot [LMAX]

aurait pu é galem e nt s'é crire :

char mot []

ou m ê m e :

char * mot
104 Exe rcice s e n langage C
D ans le pre m ie r cas, on continue de spé cifie r (au lecte ur du program m e plus q u'au com pilate ur) q ue m ot e s t un tableau
de caractè re s m ais q ue s a dim e nsion n'a pas besoin d'ê tre connue au s e in de lire . Dans le s e cond cas, on e xprim e plus
claire m e nt q ue , finalem e nt, l'argum e nt re çu par lire n'e s t rie n d'autre q u'un pointe ur sur des caractè re s . Ce s
form ulations sont totalem e nt é q uivalente s pour le com pilate ur e t, dans tous les cas (m ê m e le dernie r), ilre s te possible de
faire appe lau "form alism e " tableau au s e in de lire , e n utilisant une notation te lle q ue :

mot [i++]

D 'ailleurs, pour le com pilate ur, ce tte derniè re e s t é q uivalente à :

* (mot + i++)

Le s m ê m e re flexions s'appliq ue nt à l'é criture du prototype de lire .

D ISCUSSIO N

*Le sym bole LM A X e s t défini pour l'e ns e m ble du source conte nant, ici, le program m e principale t la fonction lire .
M ais, si ce tte fonction de vait ê tre com pilée s é paré m e nt du re s te , ils e rait alors néce s s aire de faire figure r la définition
(#de fine ) dans les deux source s , ce q ui com porte un ris q ue d'erreur. Dans une situation "ré e lle", on pourrait avoir inté rê t
à faire appe là l'une des dém arch e s s uivante s :

- transm e ttre la valeur de LM A X e n argum e nt de la fonction lire .


- re groupe r les définitions de sym boles com m uns à plusieurs sources dans un fich ie r s é paré q ue l'on appe lle par
#include dans ch acun de s s ource s conce rné s .

*Contraire m e nt au program m e de l'e xe rcice pré cédent, ce lui-ci s e trouve proté gé de ré pons e s trop longues de la part de
l'utilisate ur.

I-5 Com ptage de l


e ttre s

________________________________________________________________________________________
I. Variations algorith m iques sur les instructions de base 105
Enoncé

R é aliser un program m e q ui com pte le nom bre de ch acune des lettres de l'alph abet d'un te xte e ntré au clavie r. Pour
sim plifie r, on ne tie ndra com pte q ue des m inuscules , m ais on com pte ra le nom bre des caractè re s non re connus com m e
te ls (q ue ls q u'ils soie nt : m ajuscules , ponctuation, ch iffre s ,...).

Le program m e devra acce pte r un nom bre q ue lconq ue de ligne s . L'utilisate ur tape ra une "ligne vide" pour signaler q u'ila
te rm iné la frappe de son te xte (ce q ui re vie nt à dire q u'ilfrappe ra donc de ux fois de suite la touch e re turn, aprè s la
frappe de sa derniè re ligne ).

O n supposera q ue les ligne s frappé e s au clavie r ne pe uve nt jam ais dépas s e r 127 caractè re s . Par ailleurs, on fe ra
l'h ypoth è s e (pe u re s trictive e n pratiq ue ) q ue les "code s " des lettre s m inuscules a à z sont cons é cutifs (ce q ui e s t le cas,
notam m e nt, ave c le code A SCII).

Exe m pl
e

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
......
......
7 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 Exe rcice s e n langage C
ANALYSE

Ilnous faut donc utiliser un tableau de 26 e ntie rs perm e ttant de com ptabiliser le nom bre de fois où l'on a re ncontré
ch acune des 26 lettre s (m inuscules ) de l'alph abet. Nous le nom m e rons com pte . Nous utiliserons égalem e nt un com pte ur
nom m é ntot pour le nom bre totalde caractè re s e t un autre nom m é nautre s pour les caractè res diffé re nts d'une lettre
m inuscule.

En ce q ui conce rne le com ptage propre m e nt dit, ilnous faut e xam ine r ch acune des lettres du te xte . Pour ce faire , ile xiste
(au m oins) deux dém arch e s possibles :

- e ffe ctue r une ré pé tition du traite m e nt d'un caractè re ,


- e ffe ctue r une ré pé tition du traite m e nt d'une ligne , lui-m ê m e constitué de la ré pé tition du traite m e nt de ch acun de s
caractè re s q u'e lle contie nt.
a) La pre m iè re dém arch e aboutit à une s im ple boucle ave c com pte ur. Elle ne dem ande d'accéder q u'à un s e ulcaractè re à
la fois (par e xe m ple, par ge tch ar). Elle né ce s s ite , par contre , l'é lim ination de s caractè res de fin de ligne \n (q ui sont
transm is com m e les autre s par ge tch ar), puis q u'ils ne font pas vraim e nt partie du te xte .

D e s urcroî t, la déte ction de la fin du te xte oblige à cons e rve r e n pe rm ane nce le "caractè re pré cédent". Lors q ue ce
caractè re , ainsi que le caractè re courant, sont é gaux à \n, c'e s t q ue l'on a atte int la fin du te xte . Ilsuffit d'initialiser
artificie llem e nt ce caractè re pré cédent à une valeur q ue lconq ue (autre q ue \n) pour é vite r de devoir e ffe ctue r un
traite m e nt particulie r pour le pre m ie r caractè re .

b) La s e conde dém arch e aboutit à deux boucles im briq ué e s . Elle pe rm e t de lire directe m e nt ch aq ue ligne par ge ts. Elle
rè gle de m aniè re nature lle les problèm es de fin de ligne e t de fin de te xte .

Nous vous proposons ici de ux program m e s , corre s pondant à ch acune de ces deux dé m arch e s .

Program m e bas é s ur l
a ré pé tition du traite m e nt 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 */
I. Variations algorith m iques 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) ;
}

Com m e ntaire s

*L'e xpre s s ion :

c - 'a'
pe rm e t d'obte nir le "rang" dans l'alph abet du caractè re conte nu dans c. N'oublie z pas q ue le langage C considè re le type
ch ar com m e num é riq ue . Plus précisém e nt, dans le cas présent, les valeurs de c e t de 'a' sont conve rtie s e n int (ce q ui
fournit la valeur num é riq ue de leur code ) avant q ue ne s oit é valué e l'e xpre s s ion c-'a'. Com m e nous avons suppos é q ue
les codes des m inuscules s ont cons é cutifs, nous obte nons bien le ré s ultat e s com pté .

*Le s instructions :

if (c != '\n')
{ numl = c - 'a' ;
if (numl >=0 && numl < 26) compte[numl]++ ;
else nautres++ ;
ntot++ ;
108 Exe rcice s e n langage C
}
cprec = c;

pourraie nt s e conde ns e r e n :

if ( (cprec=c) != '\n')
{ numl = c - 'a' ;
if (numl >=0 && numl < 26) compte[numl]++ ;
else nautres++ ;
ntot++ ;
}

Program m e bas é s ur l
a ré pé tition du traite m e nt d'une l
igne

#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 */
I. Variations algorith m iques 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) ;
}

D ISCUSSIO N

*Aucun des deux program m e s propos é s ne pose de problèm e de prote ction vis-à -vis des réponses fournie s par
l'utilisate ur.

I-6 Com ptage de m ots

________________________________________________________________________________________

Enoncé

Ecrire un program m e pe rm e ttant de com pte r le nom bre de m ots conte nus dans un te xte fourni au clavie r. Le te xte pourra
com porte r plusieurs ligne s e t l'utilisate ur tape ra une ligne "vide" pour signaler q u'ile n a te rm iné la frappe (ce q ui re vie nt
à dire q u'ilfrappe ra de ux fois de suite la touch e re turn aprè s avoir fourni la derniè re ligne ).

O n adm e ttra q ue deux m ots sont toujours s é paré s par un ou plusieurs des caractè re s s uivants :

- fin de ligne
- e s pace
- ponctuation : : . , ;?!
- pare nth è s e s : ( )
- guillem e ts : "
- apostroph e : '
O n adm e ttra é galem e nt, pour sim plifie r, q u'aucun m ot ne pe ut ê tre com m e ncé s ur une ligne e t s e poursuivre s ur la
suivante .

O n pré voira une fonction pe rm e ttant de décide r si un caractè re donné transm is en argum e nt e s t un de s s é parate urs
m e ntionné s ci-de s s us. Elle fournira la valeur 1 lors q ue le caractè re e s t un s é parate ur e t la valeur 0 dans le cas contraire .
110 Exe rcice s e n langage C
Exe m pl
e

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

Com m e dans l'e xe rcice pré cédent, ile xiste (au m oins) deux dém arch e s possibles :

- e ffe ctue r une ré pé tition du traite m e nt d'un caractè re ,


- e ffe ctue r une ré pé tition du traite m e nt d'une ligne , lui-m ê m e constitué de la ré pé tition du traite m e nt de ch acun de s
caractè re s q u'e lle contie nt.

La pre m iè re dém arch e aboutit à une s im ple boucle ave c com pte ur. Elle dem ande s im plem e nt d'accéder à un s e ul
caractè re (par e xe m ple par ge tch ar).

La s e conde dém arch e aboutit à deux boucles im briq ué e s . Elle dem ande d'effe ctue r une lecture ligne par ligne (par
e xe m ple par ge ts).

Là e ncore , nous e xam ine rons les deux dé m arch e s e t nous proposerons un program m e corre s pondant à ch acune d'entre
e lles .

D ans les deux dé m arch e s , tous les caractè re s s é parate urs joue nt le m ê m e rôle, à condition d'y inclure \n (si l'on travaille
ave c ge tch ar) ou \0 (si l'on travaille ave c ge ts). O n pe ut alors dire que l'on a progressé d'un m ot dans le te xte , ch aq ue
fois q ue l'on a ré alisé la s é q ue nce s uivante :

- re ch e rch e du prem ie r caractè re diffé re nt d'un séparate ur,


- re ch e rch e du prem ie r caractè re é galà un s é parate ur.
I. Variations algorith m iques sur les instructions de base 111
O n pourrait ré pé te r succe s s ive m e nt ces deux opé rations, tant q u e q ue l'on n'e s t pas arrivé à la fin du te xte . En fait, ile s t
plus sim ple d'utiliser un "indicate ur logiq ue " (nous l'appe llerons m ot_e n_cours) e t d'effe ctue r pour ch aque caractè re le
traite m e nt suivant :

- si le caractè re e s t un s é parate ur e t si m ot_e n_cours e s t vrai, augm e nte r de un le com pte ur de m ots e t re m e ttre
m ot_e n_cours à faux.
- si le caractè re n'e s t pas un séparate ur e t si m ot_e n_cours e s t faux, m e ttre m ot_e n_cours à vrai.
Quant à la condition d'arrê t, e lle s 'e xprim e diffé re m m e nt suivant la dém arch e adopté e :

- deux caractè re s cons é cutifs é gaux à \n pour la pre m iè re , ce q ui im pose de cons e rve r e n pe rm ane nce la valeur du
"caractè re pré cédent" ;ce dernie r s e ra initialisé à une valeur q ue lconq ue diffé re nte de \n pour é vite r un traite m e nt
particulie r du pre m ie r caractè re du te xte .
- ligne vide pour la s e conde .

Program m e bas é s ur l
a ré pé tition du traite m e nt 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 Exe rcice s e n 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) ;
}

Com m e ntaire s

*Nous avons introduit une variable "logiq ue " nom m é e fin_te xte q ui nous facilite la déte ction de la fin du te xte . Nous
aurions pu nous en passer en introduisant une instruction bre ak au s e in d'une boucle do ... w h ile {1}(boucle infinie ).

*D ans le traite m e nt de ch aq ue caractè re , nous n'avons pas respecté "à la lettre " l'algorith m e propos é lors de l'analyse.
En e ffe t, nous e xé cutons l'instruction :

mot_en_cours = VRAI

m ê m e s i l'indicate ur m ot_e n_cours a déjà la valeur VR A I ;ce la nous é vite un te s t supplém e ntaire , sans m odifie r le
com porte m e nt du program m e (puis q ue la m odification ainsi apporté e consiste à m e ttre à VR A I l'indicate ur alors q u'ily
e s t déjà ).
I. Variations algorith m iques sur les instructions de base 113

*D ans la fonction s e p, la s e ule instruction :

while ( c!=sep[i] && i++<nsep-1 ) ;

pe rm e t de savoir si le caractè re c e s t un s é parate ur. En e ffe t, ilne faut pas oublie r q ue l'opé rate ur & & n'é value s on
s e cond opé rande q ue lors q ue ce la e s t né ce s s aire . Autre m e nt dit, si la pre m iè re condition e s t faus s e (c e s t donc é galà un
s é parate ur), l'e xpre s s ion i++<nsep-1 n'e s t pas é valué e e t i n'e s t donc pas incré m e nté e . Si, par contre , ce tte pre m iè re
condition e s t vé rifié e alors q u'on a e xploré la totalité des séparate urs (i=11), la s e conde condition e s t é valué e e t e lle e s t
trouvé e faus s e , m ais e n m ê m e te m ps, i s e trouve incré m e nté e (à 12).

En dé finitive , on voit q u'à la fin de ce tte instruction, lors q ue i vaut 12, ce la signifie q ue c ne figure pas dans la liste des
s é parate urs.

Program m e bas é s ur l
a ré pé tition du traite m e nt d'une l
igne

#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 Exe rcice s e n 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) ;
}

Com m e ntaire s

Nous avons dû :

- d'une part, au s e in de la fonction s e p, re m place r le s é parate ur \n par \0,


- d'autre part, dans la boucle de traite m e nt des caractè res d'une ligne , traite r com m e les autre s ce caractè re de fin de
ligne (c'e s t-à -dire faire varie r i de 0 à strlen(ligne ) e t non strlen(ligne )-1), afin d'é vite r de com pte r pour un s e ulm ot
le dernie r m ot d'une ligne (non suivi d'un séparate ur) e t le pre m ie r de la suivante .

D is cus s ion

*En ce q ui conce rne la fonction d'e xam e n d'un caractè re (nom m é e s e p), vous constate z (dans les deux ve rsions
propos é e s ) q ue nous l'avons déclarée dans le program m e principal(m ain), bie n q ue ce la soit facultatif, dans la m e s ure où
e lle fournit un ré s ultat de type int.

*Aucun des deux program m e s propos é s ne pose de problèm e de prote ction vis-à -vis des réponses fournie s par
l'utilisate ur.
II : UTILISATIO N
D E STRUCTURES

Le ch apitre I vous a proposé des exe rcice s faisant appe laux instructions de base du langage C. Le s e xe rcices de ce
ch apitre font inte rve nir, e n plus, la notion de structure sous des form es dive rs e s (e n particulie r les tableaux de s tructure s
e t leur initialisation).

II-1 Signe du zodiaq ue

________________________________________________________________________________________

Enoncé

Affich e r le s igne du zodiaq ue corre s pondant à une date de naissance fournie e n donné e , sous la form e :

jour m ois
Les deux inform ations s e ront s é paré e s par au m oins un espace. La pre m iè re s e ra fournie s ous form e num é riq ue , tandis
q ue la s e conde le s e ra sous form e d'une ch aî
ne de caractè re s .

Nous vous rappe lons q ue les pé riode s corre s pondant à ch aq ue s igne s ont les s uivante s :

Capricorne 23 décembre - 19 janvier


Verseau 20 janvier - 19 février
Poisson 20 février - 20 mars
116 Exe rcice s e n 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

Exe m pl
es

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 program m e doit ê tre e n m e s ure d'établir une corre s pondance e ntre le nom d'un signe e t les deux date s lim ite s
corre s pondante s . O n pe ut déjà note r q ue la date de fin d'un signe e s t la ve ille de ce lle de début du suivant. Nous nous
conte nte rons donc de ne cons e rve r q u'une s e ule de ces deux inform ations, par e xe m ple la date de fin.

La corre s pondance s ouh aité e pe ut ê tre ré alisée :

- par plusieurs tableaux (jour, m ois, signe) re lié s par une valeur com m une d'indice .
- par un s e ultableau dans leq ue lch aq ue é lém e nt e s t une structure com portant un num é ro de jour, un nom de m ois e t
un nom de signe.

Nous ch oisirons la s e conde s olution car e lle pe rm e t de m ie ux m e ttre e n é vidence la corre s pondance e ntre les
inform ations, au m om e nt de l'initialisation au s e in du program m e .

La re ch e rch e du signe correspondant à une date donnée se fait alors de la m aniè re s uivante :

- O n ch e rch e tout d'abord l'é lém e nt (nous le nom m e rons x) apparte nant à notre tableau de s tructure s , dont le nom de
m ois corre s pond à ce lui propos é e n donné e . S'iln'e xiste pas, on le s ignale par un m e s s age approprié .
II. Utilisation de s tructure s 117
- O n re garde e nsuite s i le num é ro du jour propos é e s t infé rie ur ou é galà ce lui de l'é lém e nt x.
D ans l'affirm ative , on pe ut e n conclure q ue la date propos é e e s t anté rie ure à la date de fin du signe figurant dans
l'é lém e nt x, ce q ui fournit la ré pons e voulue .
D ans le cas contraire , on e n conclut q ue la date propos é e e s t posté rie ure à la date de début du signe figurant dans
l'é lém e nt x ;ilsuffit donc d'e xam ine r l'é lém e nt suivant pour obte nir la ré pons e voulue . Toute fois, si x est le
dernie r é lém e nt de notre tableau, ilfaudra considérer que son suivant e s t e n fait le pre m ie r é lém e nt du tableau.

O n re m arq ue ra q ue l'algorith m e propos é fonctionne e ffe ctive m e nt parce q ue ch acun de s 12 m ois de l'anné e ne com porte
q u'un s e ulch ange m e nt de signe. Si ce la n'avait pas é té le cas, ilaurait fallu "e ncadre r" la date propos é e par de ux date s
d'élém e nts cons é cutifs de notre tableau.

Program m e

#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 Exe rcice s e n 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 ***") ;
}

Com m e ntaire s

*Nous avons défini ici un m odè le de structure nom m é s_date , dans leq ue lnous trouvons un num é ro de jour, un nom de
m ois e t le s igne corre s pondant. Nous avons prévu 10 caractè re s pour le nom de m ois, ce q ui autorise des ch aî nes de
longue ur infé rie ure ou é gale à 9 (com pte te nu du \0 de fin) ;de m ê m e , nous avons prévu 11 caractè re s pour le s igne .

Le tableau nom m é date e s t un tableau de 12 é lém e nts ayant ch acun le type s_date . Nous l'avons initialisé dans sa
déclaration, ce q ui pe rm e t de m e ttre facilem e nt e n parallèle ch aq ue s igne e t sa date de fin.

*En ce q ui conce rne la lecture de la date au clavie r, nous n'avons pas prévu, ici, de prote ction vis-à -vis d'éve ntue lles
e rre urs de frappe de l'utilisate ur (ce la n'é tait pas dem andé par l'é noncé ).

*R appe lons q ue la fonction stricm p com pare , sans te nir com pte de la distinction m ajuscules /m inuscules , les deux ch aî ne s
dont on lui fournit l'adre s s e e n argum e nt. Elle re s titue une valeur non nulle (q u'on pe ut inte rpré te r com m e vrai) lors q ue
les deux ch aî
ne s s ont diffé re nte s e t une valeur nulle (faux) lors q u'e lles s ont é gales .

*La re ch e rch e du nom de m ois e s t ré alisée par la s e ule instruction :

while ( stricmp(date[i].mois, mois_n) && i++<11 ) {}

Ce lle-ci possè de un double avantage ;tout d'abord, ce lui de la concision ;e nsuite , ce lui de nous perm e ttre de savoir
dire cte m e nt si la re ch e rch e a é té fructue us e ou non.
II. Utilisation de s tructure s 119
En e ffe t, ilne faut pas oublie r q ue l'opé rate ur & & n'é value s on s e cond opé rande q ue lors q ue ce la e s t né ce s s aire .
Autre m e nt dit, si la pre m iè re condition e s t faus s e (ily a donc é galité des deux ch aî ne s ), l'e xpre s s ion i++<11 n'e s t pas
é valué e e t i n'e s t donc pas incré m e nté e . La valeur de i désigne alors l'é lém e nt voulu.

Si, par contre , ce tte pre m iè re condition e s t vé rifié e (iln'y a donc pas é galité des deux ch aî
ne s ) alors q u'on e s t arrivé e n
fin de table (i=11), la s e conde condition e s t é valué e e t e lle e s t trouvé e faus s e , m ais e n m ê m e te m ps i se trouve
incré m e nté e (à 12).

En dé finitive , on voit q ue , à la fin de ce tte instruction, lors q ue i vaut 12, ce la signifie q ue l'é lém e nt ch e rch é ne figure pas
dans la table. Dans le cas contraire (i< 12), i dé s igne l'é lém e nt ch e rch é .

Bie n e nte ndu, ce tte "re ch e rch e e n table" pouvait s e program m e r de beaucoup d'autre s m aniè re s . Par e xe m ple, nous
aurions pu écrire :

while ( stricmp(date[i].mois, mois_n) && i<11 ) i++ ;

Toute fois, ce tte instruction n'e s t pas é q uivalente à la pré cédente . En e ffe t, lors q ue i vaut 11, ce la pe ut signifie r :

- soit q ue l'é lém e nt ch e rch é e s t e n position 11 (pre m ie r te s t satisfait),


- soit q ue l'é lém e nt ch e rch é ne figure pas dans la table (s e cond te s t satisfait).
Pour tranch e r, ile s t donc né ce s s aire , dans ce cas, d'e ffe ctue r une com paraison supplém e ntaire .

Note z q ue , par contre , une instruction te lle q ue :

while ( stricmp(date[i].mois, mois_n) && i++ <= 11) {}

s e rait q ue lque pe u e rroné e . En e ffe t, dans le cas où l'é lém e nt ch e rch é ne figure rait pas dans le tableau, on s e rait am e né à
é value r l'e xpre s s ion :

date[i].mois

ave c une valeur i é gale à 12, c'e s t-à -dire désignant un é lém e nt situé e n de h ors du tableau. Ce rte s , e n gé né ral, ce la ne
s e rait guè re visible dans le com porte m e nt du program m e , dans la m e s ure où ile s t bien peu probable q ue ce tte valeur soit
é gale au nom de m ois voulu...

*Note z l'e m ploi de l'opé rate ur arith m é tiq ue % q ui pe rm e t de ré gler le problèm e du signe suivant le dernie r signe du
tableau.

D ISCUSSIO N

*Te lq u'ila é té pré vu, notre program m e acce pte des nom s de m ois é crits e n m inuscules ou e n m ajuscules m ais sans
acce nt. Dans un program m e ré e l, ils e rait souh aitable de faire pre uve de plus de tolérance .
120 Exe rcice s e n langage C

*Notre re ch e rch e du nom de m ois a é té ré alisée ici par un algorith m e dit de rech erch e séquentiel le en table (algorith m e
q ui, com m e nous l'avons vu, pe ut s e program m e r e n C à l'aide d'une seule instruction). D'autre s algorith m e s plus
rapide s e xiste nt, e n particulie r ce lui dit de rech erch e dich otom ique. L'e xe rcice IV-5 vous en proposera un exem ple.

II-2 Codage m ors e


________________________________________________________________________________________

Enoncé

Ecrire un program m e affich ant le codage e n m orse d'un te xte fourni au clavie r e t ne dépassant pas une "ligne " de 127
caractè re s . Le s caractè re s s usce ptibles d'ê tre codé s e n m ors e s ont :

- les 26 lettres de l'alph abet (supposées tapé e s e n m ajuscul


es),
- les 10 ch iffres de 0 à 9 ,
- le point,
Si le te xte contie nt d'autre s caractè re s q ue ce ux-ci, le program m e affich e ra sim plem e nt des points d'inte rrogation à la
place du code m ors e .

Tabl
e au de s code s m ors e s

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 ----.

Exe m pl
e
donnez votre message (1 ligne maxi) :
LE LANGAGE C, CONCU EN 1972, EST L'OEUVRE DE DENIS RITCHIE.

voici la traduction de votre message


II. Utilisation de s tructure s 121
.-.. . ?????? .-.. .- -. --. .- --. .
?????? -.-. ?????? ?????? -.-. --- -. -.-. ..- ??????
. -. ?????? .---- ----. --... ..--- ?????? ?????? .
... - ?????? .-.. ?????? --- . ..- ...- .-.
. ?????? -.. . ?????? -.. . -. .. ...
?????? .-. .. - -.-. .... .. . .-.-.-
________________________________________________________________________________________

ANALYSE

Le program m e doit donc ê tre e n m e s ure d'établir une corre s pondance e ntre un caractè re e t son code m ors e . Là e ncore ,
nous pourrions utiliser deux tableaux re lié s par une valeur com m une d'un indice . M ais l'e m ploi d'un tableau de
structure s pe rm e t de m ie ux m e ttre e n é vidence la corre s pondance e ntre les inform ations, lors de l'initialisation. Ch aq ue
é lém e nt (structure ) du tableau contie ndra :

- un caractè re ,
- le code m ors e corre s pondant, e xprim é s ous form e d'une ch aî
ne .
Le codage d'un caractè re s e fe ra alors sim plem e nt par sa localisation dans le tableau.

Program m e

#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 Exe rcice s e n 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 */
}
}

Com m e ntaire s

*Nous avons défini un m odè le de structure , nom m é code , dans leq ue lnous trouvons :

- un caractè re ,
- un pointeur sur une ch aî
ne de caractè res destiné e à conte nir le code m ors e corre s pondant.
Note z q ue , contraire m e nt à ce q ue nous avions fait dans le program m e de l'e xe rcice pré cédent, nous avons prévu ici un
pointe ur sur une ch aî ne e t non un tableau de caractè re s .

D ans ce s conditions, le tableau table occupe ra s e ulem e nt 37 (valeur de NL) e m place m e nts dont la taille s e ra
gé né ralem e nt de 3 ou 5 octe ts (1 pour le caractè re e t 2 ou 4 pour le pointe ur). L'e m place m e nt m ê m e des ch aî ne s
corre s pondante s s e trouve ce pe ndant ré s e rvé à la com pilation, de par le fait q ue nous avons initialisé ce tableau lors de sa
déclaration. Ilne faut pas oublie r, e n e ffe t, q u'une notation te lle q ue :

".-.-."
II. Utilisation de s tructure s 123
e s t inte rpré té e par le com pilate ur com m e re pré s e ntant l'adresse de la ch aî
ne fournie , m ais q u'e n m ê m e te m ps illui
ré s e rve un e m place m e nt.

Ce tte façon de procéder pe ut s e ré vè ler plus é conom iq ue e n place m é m oire q ue la pré cédente , dans la m e s ure où ch aq ue
ch aîne n'occupe q ue l'e s pace q ui lui e s t né ce s s aire (ilfaut toute fois ajoute r, pour ch aq ue ch aî
ne , l'e s pace né ce s s aire à un
pointe ur).

R e m arque :

En toute rigue ur, le tableau table e s t de clas s e autom atiq u e (puis q u'ilapparaî t au s e in d'une fonction - ici le
program m e principal). Son e m place m e nt e s t donc alloué au m om e nt de l'e xé cution du program m e (c'e s t-à -dire , ici,
dè s le début). Le s constante s ch aî
ne s , par contre , voie nt leurs e m place m e nts définis dè s la com pilation.
Si notre tableau table avait é té déclaré de m aniè re globale, ilaurait é té de clas s e statiq u e . Son e m place m e nt aurait
alors é té ré s e rvé dè s la com pilation.
Une te lle distinction e s t toute fois re lative m e nt form e lle e t e lle n'a guè re d'incide nce e n pratiq ue . Ile s t, e n e ffe t,
gé né ralem e nt, as s e z te ntant de considérer les variables déclarées dans le program m e principal com m e "q uasi
statiq ue s ", dans la m e s ure où, bie n q ue non ré s e rvé e s à la com pilation, e lles n'e n n'occupe nt pas m oins de l'e s pace
pe ndant toute la duré e d e l'e xé cution du program m e .

*La re ch e rch e du caractè re dans notre tableau table e s t ré alisée par la s e ule instruction :

while (ligne[i] != table[j].lettre && j++<NL-1) ;

D ISCUSSIO N

D ans un program m e "ré e l", ilfaudrait pré voir d'acce pte r un m e s s age de plus d'une ligne , ce q ui pos e rait le problèm e de
sa m é m orisation. O n pourrait ê tre am e né , soit à lui im pos e r une taille m axim ale, soit à s e tourne r ve rs des m é th odes de
"ge s tion dynam iq ue ".

II-3 D é codage m ors e

________________________________________________________________________________________
124 Exe rcice s e n langage C
Enoncé

Ecrire un program m e pe rm e ttant de décode r un m e s s age e n m ors e fourni au clavie r sous form e d'une suite de caractè re s .
Ce lle-ci pourra com porte r :

- des points e t des tire ts re pré s e ntant les code s propre m e nt dits,
- un ou plusieurs espaces pour séparer les diffé re nts code s (on n'im pos e ra donc pas à l'utilisate ur d'e m ploye r un
"gabarit" fixe pour ch aq ue code ).
O n supposera q ue le m e s s age fourni ne dépas s e pas une ligne de 127 caractè re s . Le s code s ine xistants s e ront traduits par
le caractè re "?".

O n utilisera le tableau de s code s m ors e s fourni dans l'e xe rcice pré cédent (II-2).

Exe m pl
e

donnez votre message (1 ligne maxi) :


-... --- -. .--- --- ..- .-. .-.-.- .-.-.

voici la traduction de votre message


B O N J O U R . ?

________________________________________________________________________________________

ANALYSE

Ce program m e doit donc é tablir une corre s pondance e ntre un code m ors e e t un caractè re . Nous pouvons, pour ce faire ,
utiliser la m ê m e s tructure q ue dans l'e xe rcice pré cédent. Le décodage d'un caractè re s e fe ra alors e n e xplorant, non plus
la partie caractè re , m ais la partie ch aî ne du tableau de s tructure . L'algorith m e de re ch e rch e s e ra donc sim ilaire , la
com paraison de caractè re s é tant re m placé e par une com paraison de ch aî ne s .

En ce q ui conce rne le m e s s age e n m ors e , nous pouvons le lire par ge ts dans un tableau de 128 caractè re s , nom m é ligne .
Le principalproblèm e q ui s e pos e alors à nous e s t ce lui de l'accè s à ch acun de s code s m ors e s conte nus dans ligne ;e n
e ffe t, ce ux-ci sont é crits ave c un gabarit variable e t s é paré s par un nom bre variable d'espace s .

Nous proposons de ré pé te r le traite m e nt suivant, fondé s ur l'e m ploi d'un pointe ur de caractè re s (indice ) dans le tableau
ligne :

- Avance r le pointe ur, tant q u'ildésigne un espace.


II. Utilisation de s tructure s 125
- Extraire , à partir de la position indiq ué e par le pointe ur, à l'aide de sscanf, une ch aî ne de longue ur m axim ale 7
(puis q ue aucun code m ors e ne dépas s e ce tte longue ur). Pour ce la, nous fe rons appe lau code form at %7s, leq ue l
inte rrom pt l'e xploration, soit q uand un s é parate ur e s t re ncontré , soit lors q ue la longue ur indiq ué e (7) e s t atte inte .
- Incré m e nte r le pointe ur de la longue ur de la ch aî
ne ainsi lue (car, bie n sûr, iln'aura pas é té m odifié par sscanf).

Program m e

#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 Exe rcice s e n 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é */
}
}
}

Com m e ntaire s

*D ans la boucle de saut des espace s é ve ntue ls, on ne ris q ue pas d'aller au-de là de la fin de la ch aî
ne conte nue dans
ligne , car le caractè re de fin (\0), diffé re nt d'un e s pace , s e rvira de "s e ntine lle".

*Par contre , avant d'extraire un nouve au code par sscanf, ile s t né ce s s aire de s'assure r q ue l'on n'e s t pas parve nu e n fin
de ligne . En e ffe t, dans ce cas, sscanf fournirait une s uite de caractè re s constituée du caractè re \0 (q ui n'e s t pas considéré
par ce tte fonction com m e un s é parate ur) e t des caractè re s s uivants (pré levé s e n de h ors du tableau ligne ). Note z q ue , e n
l'abs e nce d'un te lte s t, le m alne s e rait pas trè s grave puis q u'ilre vie ndrait sim plem e nt à place r au plus 7 caractè res dans
code , com m e nçant par \0.

*La re ch e rch e du code m orse dans le tableau table e s t ré alisée par la s e ule instruction :

while ( stricmp (code, table[j].morse) && j++<NL-1) ;

Le s re m arq ue s faites dans le q uatriè m e com m e ntaire de l'e xe rcice II-1, à propos de la re ch e rch e s é q ue ntie lle e n table,
s'appliq ue nt é galem e nt ici.

D ISCUSSIO N
II. Utilisation de s tructure s 127
Notre program m e ne déte cte pas le cas où l'utilisate ur fournit un code m orse de plus de 6 caractè re s . Dans ce cas, en
e ffe t, ils e conte nte de le "découpe r" e n tranch es de 6 caractè re s (la derniè re tranch e pouvant avoir une longue ur
infé rie ure ).

Si l'on souh aitait déte cte r ce ge nre d'anom alie , il faudrait, aprè s ch aq ue e xam e n d'un code , s'assure r q u'il e s t
e ffe ctive m e nt suivi d'un e s pace ou d'une fin de ch aî
ne .

II-4 Facturation par code

________________________________________________________________________________________

Enoncé

R é aliser un program m e é tablissant une facture pouvant porte r sur plusieurs articles . Pour ch aq ue article à facture r,
l'utilisate ur ne fournira q ue la quantité e t un num é ro de code à partir duq ue lle program m e devra re trouve r à la fois le
libellé e t le prix unitaire .

Le program m e devra re fus e r les code s ine xistants. A la fin, ilaffich e ra un ré capitulatif te nant lie u de facture .

Le s inform ations re lative s aux diffé re nts articles s e ront définies dans le s ource m ê m e du program m e (e t non dans un
fich ie r de données). Elle s e ront toute fois placé e s à un nive au global, de m aniè re à pouvoir, le cas é ch é ant, faire l'obje t
d'un source s é paré , appe lable par #include .

O n pré voira de ux fonctions :

- une pour re ch e rch e r les inform ations re lative s à un article, à partir de s on num é ro de code ,
- une pour affich e r la facture ré capitulative .

Exe m pl
e

combien d'articles à facturer ? 3


code article ? 25
quantité de Centrifugeuse au prix unitaire de 370.00 ? 33
code article ? 7
** article inexistant - redonnez le code : 16
128 Exe rcice s e n langage C
quantité de Grille-pain au prix unitaire de 199.50 ? 12
code article ? 26
quantité de Four à raclette 6P au prix unitaire de 295.25 ? 6

FACTURE

ARTICLE NBRE P-UNIT MONTANT

Centrifugeuse 33 370.00 12210.00


Grille-pain 12 199.50 2394.00
Four raclette 6P 6 295.25 1771.50

TOTAL 16375.50

________________________________________________________________________________________

ANALYSE

L'é noncé nous précise que les codes d'articles s ont num é riq ue s , m ais ilne dit pas q u'ils sont cons é cutifs. Dans ces
conditions, ile s t né ce s s aire de m é m oris e r les diffé re nte s valeurs possibles de ce s code s . Com m e nous devons pouvoir
associe r à ch aq ue code un libellé (ch aî ne ) e t un prix (ré e l), nous pouvons songe r à utiliser un tableau de s tructure s , dans
leq ue lch aq ue é lém e nt contie nt les inform ations re lative s à un article (code , libellé, prix unitaire ). Ce tableau s e ra,
com m e dem andé par l'é noncé , déclaré à un nive au globale t initialisé dans sa déclaration.

Le travailde la fonction de re ch e rch e (nous la nom m e rons re ch e rch e ) consiste ra à vé rifie r la pré s e nce du code d'article
dans le tableau de s tructure ainsi défini. En cas de succè s, e lle e n re s titue ra le rang (ce q ui s e ra suffisant au program m e
principalpour affich e r les inform ations corre s pondante s ). Dans le cas contraire , e lle re s titue ra la valeur -1. Note z q ue le
code d'article s e ra le s e ulargum e nt de ce tte fonction.

Nous voyons donc dé jà com m e nt, pour ch aq ue code (corre ct) fourni par l'utilisate ur, affich e r les inform ations
corre s pondante s avant d'en dem ande r la q uantité . M ais, com pte te nu de ce q ue l'édition de la facture doit ê tre faite aprè s
les s aisies re lative s à tous les articles , nous devons obligatoire m e nt, pour ch aq ue article à facture r, cons e rve r :

- la q uantité ,
- une inform ation pe rm e ttant d'en retrouve r le libellé e t le prix unitaire . Nous pourrions, ce rte s , arch ive r ce s
inform ations dans un tableau. M ais, e n fait, ce la n'e s t pas néce s s aire puis q u'ile s t possible de les re trouve r à partir du
rang de l'article dans la structure (le code article convie ndrait é galem e nt, m ais ilnous faudrait alors e xplore r à
nouve au notre tableau de s tructure s lors de l'édition de la facture ).
Ces deux inform ations s e ront cons e rvées dans deux tableaux (nom m é s q te e t rangart) com portant autant d'élém e nts q ue
d'articles à facture r (on e n pré voira un nom bre m axim al).
II. Utilisation de s tructure s 129
La fonction d'édition de la facture (nom m é e facture ) s e conte nte ra alors d'explore r s é q ue ntie llem e nt ces deux tableaux
pour re trouve r toute s les inform ations néce s s aire s . Elle re ce vra, e n argum e nt, les adresses des deux tableaux (q te e t
rangart), ainsi que le nom bre d'articles à facture r.

Program m e

#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", 199.50,
19, "Balance de ménage", 278.0,
25, "Centrifugeuse", 370.0,
26, "Four raclette 6P", 295.25
} ;
/* ----------------------------------------------------------------------*/

#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 Exe rcice s e n 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 ;
II. Utilisation de s tructure s 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) ;
}

Com m e ntaire s

*Nous avons ch oisi ici d'utilise r type de f pour dé finir sous le nom t_article la structure corre s pondant à un article. Vous
constate z q ue le libellé y apparaît sous la form e d'un pointe ur sur une ch aî ne e t non d'une ch aî ne ou d'un tableau de
caractè re s . Dans ces conditions, le tableau article, déclaré de ce type , n'occupe ra q ue 6 e m place m e nts de petite taille
(gé né ralem e nt 6 ou 8 octe ts)

*Là e ncore , une s e ule instruction pe rm e t d'effe ctue r la re ch e rch e d'un code article dans le tableau article. Voye z, à ce
propos, les re m arq ue s faites dans le q uatriè m e com m e ntaire de l'e xe rcice II-1.

*Le code form at %-20s, utilisé à deux reprises dans la fonction facture , pe rm e t de "cadre r" une ch aî
ne à gauch e .

D ISCUSSIO N

*Notre program m e n'e s t pas proté gé contre des ré pons e s incorre ctes de la part de l'utilisate ur. En particulie r, une
ré pons e non num é riq ue pe ut e ntraî
ne r un com porte m e nt as s e z désagré able. Dans un program m e ré e l, ils e rait né ce s s aire
de ré gler conve nablem e nt ce type de problèm e , par e xe m ple e n utilisant fge ts (..., stdin) e t sscanf.

*D e m ê m e , dans un program m e ré e l, ilpourrait ê tre judicie ux de dem ande r à l'utilisate ur de confirm e r q ue le produit
ch e rch é e s t bien celui dont on vie nt de lui affich e r les inform ations.
132 Exe rcice s e n langage C

*La pré cision offe rte par le type float (6 ch iffre s s ignificatifs) pe ut s e ré vé ler insuffisante .
III : H ASARD ET
RECREA TIO NS

Ce ch apitre vous propose un certain nom bre d'exercices correspondant à la ré alisation de program m e s ré cré atifs, bas é s
sur l'utilisation du h asard.

Les deux pre m ie rs e xe rcice s s ont e s s e ntie llem e nt destiné s à vous m ontre r com m e nt gé né re r de s nom bre s aléatoire s e n
langage C.

III-1 Tirage al
é atoire

________________________________________________________________________________________

Enoncé

Ecrire une fonction fournissant un nom bre e ntie r tiré au h asard e ntre 0 (inclus) e t une valeur n (inclus e ) fournie e n
argum e nt.

R é aliser un program m e principalutilisant ce tte fonction pour e xam ine r la "distribution" de s valeurs ainsi obte nues dans
l'inte rvalle [0, 10]. Le nom bre de tirage s à ré aliser sera lu e n donné e e t le program m e affich e ra le nom bre de fois où
ch acune de ce s valeurs aura é té obte nue .

Exe m pl
e

combien de tirages : 1100


nombre de tirages obtenus
0 : 106
1 : 95
134 Exe rcice s e n langage C
2 : 115
3 : 95
4 : 91
5 : 103
6 : 103
7 : 101
8 : 92
9 : 94
10 : 105

Indication : ile s t né ce s s aire de faire appe là la fonction rand de la "biblioth è q ue s tandard".

________________________________________________________________________________________

ANALYSE

Ilfaut faire appe là la fonction rand. Ce lle-ci fournit un nom bre e ntie r, tiré de façon "ps e udo-aléatoire " dans l'inte rvalle
[0, R A ND_M A X] , ch aq ue nom bre de ce t inte rvalle ayant q uasim e nt la m ê m e probabilité d'ê tre tiré . Note z q ue la valeur
de RAND_M AX e s t définie dans stdlib.h ;d'aprè s la norm e , e lle n'e s t jam ais infé rie ure à la capacité m inim ale d'un int,
c'e s t-à -dire 32767.

Pour aboutir au ré s ultat voulu, une dém arch e consiste à transform e r un te lnom bre e n un nom bre ré e lapparte nant à
l'inte rvalle [0,1[. Ilsuffit e nsuite de m ultiplie r ce ré e lpar n+1 e t d'en prendre la partie e ntiè re pour obte nir le ré s ultat
e s com pté . O n pe ut alors m ontre r q ue les valeurs 0, 1, ... n-1, n sont q uasi équiprobables .

Pour obte nir un te lnom bre aléatoire , nous pouvons diviser le nom bre fourni par rand par la valeur RAND_M AX+ 1 (il
faut é vite r de diviser par RAND_M AX, car la valeur 1 ris q ue rait alors d'ê tre obte nue - e n m oye nne une fois sur
RAND_M AX!). Là e ncore , on pe ut, de m aniè re form e lle, m ontre r q ue s i la loi de probabilité e s t uniform e s ur [0,1[, ile n
va de m ê m e de ce lle du nom bre ainsi fabriq ué dans l'inte rvalle d'entie rs [0,n].

Program m e

#include <stdio.h>
#include <stdlib.h> /* pour la fonction rand */

#define N 10 /* les tirages se feront entre 0 et N */

main()
{
III. H asard e t 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) ;
}

Com m e ntaire s

*D ans la fonction aleat, la division par RAND_M AX+ 1 doit bien sûr s'effe ctue r sur des valeurs ré e lles . M ais, de plus, il
faut pre ndre garde à ne pas é crire le diviseur sous la form e RAND_M AX + 1. En e ffe t, ce lui-ci s e rait é valué dans le type
int e t, dans le cas (fré q ue nt) où la valeur de RAND_M AX e s t e xacte m e nt la valeur m axim ale du type int, l'addition de 1 à
RAND_M AX conduirait à la valeur ... -1 (le dépas s e m e nt de capacité n'é tant jam ais déte cté e n cas d'opérations sur de s
e ntie rs).

D ISCUSSIO N
136 Exe rcice s e n langage C
En gé né ral, la fonction rand fournit toujours la m ê m e s uite de valeurs, d'une e xé cution à une autre . L'e xe rcice s uivant
vous m ontre com m e nt é vite r ce ph é nom è ne .

III-2 Tirage al
é atoire variabl
e

________________________________________________________________________________________

Enonce

Ecrire une fonction fournissant un nom bre e ntie r tiré au h asard e ntre 0 e t une valeur n fournie e n argum e nt. La suite des
valeurs re s titué e s par ce tte fonction (lors q u'on l'appe lle à dive rs e s re pris e s ) devra ê tre diffé re nte d'une exécution à une
autre e t ne pas dépendre d'une quelconq ue inform ation fournie par l'utilisate ur.

Com m e dans l'e xe rcice pré cédent, on ré alisera un program m e principal utilisant ce tte fonction pour e xam ine r la
"distribution" de s valeurs ainsi obte nues dans l'inte rvalle [0,10]. Pour ce faire , on lira e n donné e s le nom bre de tirage s à
ré aliser et le program m e affich e ra le nom bre de fois où ch acune des valeurs aura é té obte nue .

Suggestion : ilfaut "initialiser" conve nablem e nt le "gé né rate ur de nom bre s aléatoire ", e n utilisant la fonction srand. La
"graîne " né ce s s aire pe ut ê tre fabriq ué e à l'aide de la fonction tim e , de façon à avoir un caractè re s uffisam m e nt
im pré visible.

Exe m pl
es

(ils'agit là des ré s ultats corre s pondant à deux exécutions diffé re ntes du m ê m e program m e )

combien de tirages : 1100


nombre de tirages obtenus
0 : 124
1 : 104
2 : 97
3 : 97
4 : 89
5 : 93
6 : 105
III. H asard e t ré cré ations 137
7 : 109
8 : 110
9 : 89
10 : 83
___________________

combien de tirages : 1100


nombre de tirages obtenus
0 : 104
1 : 98
2 : 98
3 : 106
4 : 98
5 : 97
6 : 99
7 : 109
8 : 99
9 : 96
10 : 96

________________________________________________________________________________________

ANALYSE

En langage C, la fonction srand pe rm e t d'initialiser le gé né rate ur de nom bre s aléatoire s . Ilfaut ce pe ndant lui fournir une
"graî ne ", c'e s t-à -dire un nom bre e ntie r (de type unsigne d int) q ui dé te rm ine ra le pre m ie r nom bre tiré par rand. Ce tte
m é th ode pe rm e t ainsi, si on le s ouh aite , d'obte nir à volonté une m ê m e s uite de nom bre s aléatoire s ;ilfaut d'ailleurs
note r q ue , par dé faut, tout s e pas s e com m e s i srand é tait appe lé, e n début de l'e xé cution d'un program m e , ave c
l'argum e nt 1.

Ici, par contre , nous souh aitons obte nir une s uite diffé re nte d'une exécution à une autre . Une s olution à ce problèm e
consiste à ch oisir une "graîne " aléatoire . Bie n sûr, iln'e s t pas q ue s tion de faire appe là rand dans ce cas. Par contre , la
fonction tim e fournit une date , e xprim é e e n s e conde s . Ce lle-ci possè de un caractè re s uffisam m e nt im pré visible pour ê tre
utilisée com m e graîne .

Ce tte initialisation du gé né rate ur de nom bre s aléatoires doit toute fois n'ê tre ré alisée qu'une seule fois pour une e xé cution
donné e . Dans le cas contraire , on ris q ue rait, e n e ffe t, d'obte nir plusieurs fois de suite les m ê m e s nom bre s . Si l'on
souh aite q ue ce problèm e s oit pris e n ch arge par la fonction de tirage d'un nom bre e lle-m ê m e , ile s t né ce s s aire q ue ce tte
derniè re s oit capable de le faire lors de son prem ie r appe l(e t uniq ue m e nt à ce m om e nt-là). Ce m é canism e pas s e par
l'e m ploi d'une variable de clas s e statiq u e .
138 Exe rcice s e n langage C
Program m e

#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 ;
}
III. H asard e t ré cré ations 139

/* génération nombre */
i = rand() / (RAND_MAX + 1.) * (n+1) ;
return (i) ;
}

Com m e ntaire s

*Le m é canism e du traite m e nt particulie r à e ffe ctue r au pre m ie r appe le s t ré alisé grâ ce à la variable pre m , déclaré e d e
clas s e s tatiq ue . Ce tte variable e s t initialisée à un, lors de la com pilation. D è s le pre m ie r appe l, e lle e s t m ise à zé ro e t e lle
garde ra e nsuite ce tte valeur jus q u'à la fin de l'e xé cution du program m e . Ainsi, la fonction srand n'e s t e ffe ctive m e nt
appe lée q u'une s e ule fois, lors du prem ie r appe lde notre fonction aleat.

*La fonction tim e fournit e n re tour le te m ps (e xprim é e n s e conde s ) é coulé depuis une certaine "origine " dépendant de
l'im plém e ntation. Le type de ce tte valeur dé pe nd, lui aussi, de l'im plém e ntation ;toute fois, la norm e pré voit q u'ile xiste ,
dans tim e .h , un sym bole tim e _t (défini par type de f) pré cisant le type e ffe ctive m e nt e m ployé . Ici, lors q ue nous
transm e ttons ce tte valeur à srand, ile s t possible q u'apparais s e une conve rsion du type tim e _t dans le type unsigne d int ;
ici, ce la n'a guè re d'im portance , dans la m e s ure où, m ê m e s i ce tte conve rsion est "dégradante ", la valeur obte nue re s te ra
im pré visible pour l'utilisate ur.

D 'autre part, la fonction tim e ne s e conte nte pas de fournir une "h e ure " e n re tour ;e lle range é galem e nt ce tte m ê m e
inform ation à l'adre s s e q u'on lui fournit (obligatoire m e nt) e n argum e nt ;c'e s t ce q ui justifie l'e xiste nce de la variable
date (q ui n'e s t pas utilisée par ailleurs) e t q ui doit, ici, absolum e nt ê tre déclarée dans le "bon type ", sous peine de risquer
d'aboutir à un é cras e m e nt inte m pe s tif de données (dans le cas où on aurait déclaré date d'un type "plus petit" q ue le type
e ffe ctif).

III-3 A l
é a d'é toil
es

________________________________________________________________________________________
140 Exe rcice s e n langage C
Enoncé

Affich e r au h asard un ce rtain nom bre d'étoiles (caractè re "*") à l'inté rie ur d'un re ctangle. Le nom bre d'étoiles
souh aité e s , ainsi que le nom bre de ligne s e t de colonnes du rectangle s e ront fournis e n donné e s .

Le program m e vé rifie ra q ue la zone e s t as s e z grande pour re ce voir le nom bre d'étoiles re q uis. O n é vite ra q ue plusieurs
é toiles ne s oie nt superposées.

Exe m pl
e

combien de lignes : 10
combien de colonnes : 45
combien de points : 200
** * **** ** *** * ** *** * *** **
* * * ** * ** * * ****** * ** **
* * ** * * * ***** *** ** * *** * *
* *** * * * * * ** * * **
* * ** ** ** **** ** ** ** ** * * * *
* * ** *** * * * ** * * * * **
*** ** ** * ** * * * * **
* * * * * ***** ** ** * *
* * ***** ** *** * ** * *****
**** * * *** * ** **** * *****

________________________________________________________________________________________

ANALYSE

Nous utiliserons un tableau de caractè re s à deux dim e nsions, dans leq ue lch aq ue é lém e nt re pré s e nte ra une case de notre
re ctangle. Nous convie ndrons q ue le pre m ie r indice re pré s e nte le rang de la ligne e t q ue le s e cond indice re pré s e nte le
rang de la colonne . Com m e l'utilisate ur doit pouvoir ch oisir les dim e nsions du rectangle conce rné , nous prévoirons de
donne r à notre tableau une taille s uffisante pour couvrir tous les cas possibles (nous avons ch oisi, ici, 25 lignes de 80
caractè re s ) ;ce la signifie q ue , la plupart du te m ps, le program m e n'utilisera qu'une partie de ce tableau.

Au départ, nous initialiserons tous les é lém e nts de la "partie utile" de ce tableau ave c le caractè re e s pace . Nous
ch oisirons ensuite au h asard les é lém e nts dans les q ue ls nous devrons place r un caractè re "*". Pour ce faire , ilnous suffira
de tire r au h asard un num é ro de ligne e t un num é ro de colonne jusqu'à ce q ue l'e m place m e nt corre s pondant soit
disponible (caractè re e s pace ). L'algorith m e de tirage au h asard d'un nom bre e ntie r apparte nant à un inte rvalle donné a
é té e xposé dans l'analyse de l'e xe rcice III-1.
III. H asard e t ré cré ations 141
Ilne nous re s te ra plus q u'à affich e r, par e xe m ple ave c la fonction putch ar, les diffé re nts é lém e nts de notre tableau, e n
pré voyant un "ch ange m e nt de ligne " aux m om e nts opportuns.

Program m e

#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 Exe rcice s e n 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) ;
}

Com m e ntaire s
III. H asard e t ré cré ations 143
*L'initialisation de la partie utile du tableau ave c le caractè re e s pace aurait pu s e program m e r ainsi :

for (i=0 ; i<nx ; i++)


for (j=0 ; j<ny ; j++)
ecran [i][j] = ' ' ;

Ici, nous avons préfé ré faire appe là la fonction m e m s e t, d'e xé cution plus rapide . Toute fois, ce lle-ci re m plit d'un
caractè re donné une suite d'octe ts cons é cutifs ;ce ci e xclut donc de lim ite r l'initialisation à la partie utile du tableau. Ilne
faut pas oublie r, e n e ffe t, q ue ce lle-ci n'e s t pas form é e d e nx*ny octe ts cons é cutifs (q uoiq ue , e n toute rigue ur, e n te nant
com pte de la m aniè re dont sont rangé s e n m é m oire les diffé re nts é lém e nts d'un tableau, ilsoit possible de lim ite r
l'initialisation à nx*NY élém e nts cons é cutifs).

*Nous avons re pris la fonction aleat de l'e xe rcice pré cédent. Ce lle-ci tire une valeur e ntiè re au h asard e ntre 0 e t une
lim ite q u'on lui fournit e n argum e nt ;de plus, lors de son prem ie r appe l, e lle e ffe ctue une initialisation du gé né rate ur de
nom bre s aléatoire s .

III-4 Es tim ation de pi

________________________________________________________________________________________

Enoncé

Calculer une valeur approch é e d e pi, par la m é th ode s uivante :

- on tire un ce rtain nom bre de points au h asard dans un carré donné.


- on dé te rm ine le rapport e ntre le nom bre de ce s points apparte nant au ce rcle inscrit dans le carré e t le nom bre total
de points tiré s . Ce rapport fournit une e s tim ation de la valeur de pi/4.
Le nom bre totalde points à tire r s e ra fourni e n donné e .

Exe m pl
e

combien de points ? 10000


estimation de pi avec 10000 points : 3.164800e+000
144 Exe rcice s e n langage C
________________________________________________________________________________________

ANALYSE

Nous ch oisirons un carré de côté unité . Nous convie ndrons de prendre son coin bas gauch e com m e origine d'un repè re
carté s ie n.

Nous tire rons alors au h asard le nom bre de points voulus, à l'inté rie ur de ce carré . Plus précisém e nt, pour ch aq ue point,
nous déte rm ine rons au h asard ses deux coordonné e s , e n tirant deux nom bre s ré e ls apparte nant à l'inte rvalle [0,1]. A ce t
e ffe t, nous fe rons appe là la m é th ode e xposée dans l'analyse de l'e xe rcice III-1.

Pour ch aq ue point, nous calculerons sa distance au ce ntre du carré (de coordonné e s : 0.5, 0.5) e t nous considérerons qu'il
appartie nt au ce rcle inscrit si ce tte distance e s t infé rie ure à 0.5 (note z q ue , par souci de s im plicité , nous travaillerons e n
fait ave c le carré de ce tte distance ).

Program m e

#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 ;
III. H asard e t ré cré ations 145
printf ("estimation de pi avec %d points : %e", np, pi) ;
}

float caleat (void) /* fonction fournissant une valeur aléatoire */


/* appartenant à l'intervalle [0-1] */
{
return ( (float) rand() / (RAND_MAX + 1.0) ) ;
}

D ISCUSSIO N

Notre fonction de tirage aléatoire d'un entie r fournit toujours la m ê m e s uite de valeurs. Ce q ui signifie q ue , pour un
nom bre donné de points, nous obtie ndrons toujours la m ê m e e s tim ation de pi. Vous pouve z é vite r ce ph é nom è ne e n
utilisant la fonction ré alisée dans l'e xe rcice III-2.

III-5 Je u du de vin

________________________________________________________________________________________

Enoncé

Ecrire un program m e q ui ch oisit un nom bre e ntie r au h asard e ntre 0 e t 1000 e t q ui de m ande à l'utilisate ur de le
"devine r". A ch aq ue proposition faite par le joue ur, le program m e ré pondra e n situant le nom bre propos é par rapport à
ce lui à devine r (plus grand, plus petit ou gagné ).

Lors q ue le joue ur aura de viné le nom bre ch oisi, ou lors q u'un nom bre m axim alde coups (10) aura é té dépas s é , le
program m e affich e ra la ré capitulation des diffé re nte s propositions.

Exe m pl
e

Devinez le nombre que j'ai choisi (entre 1 et 1000)


votre proposition : 500
----------- trop grand
146 Exe rcice s e n langage C
votre proposition : 250
----------- trop grand
votre proposition : 125
----------- trop grand
votre proposition : 64
----------- trop grand
votre proposition : 32
----------- trop grand
votre proposition : 16
----------- trop grand
votre proposition : 8
----------- trop petit
votre proposition : 12
----------- trop grand
votre proposition : 10

++++ vous avez gagné en 9 coups

---- Récapitulation des coups joués ----

500 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 program m e com m e nce ra par tire r un nom bre e ntie r au h asard, suivant la dém arch e e xposée dans l'analyse de
l'e xe rcice III-1.

Ildevra e nsuite ré pé te r l'action :

faire joue r le joue ur


jusqu'à ce q ue le joue ur ait gagné ou q u'ilait dépas s é le nom bre m axim alde coups perm is.

L'action e n q ue s tion consiste s im plem e nt à :


III. H asard e t ré cré ations 147
- D e m ande r au joue ur de propos e r un nom bre .
- Cons e rve r ce nom bre dans un tableau (pour pouvoir é tablir la ré capitulation finale). Note z q ue , com pte te nu de ce
q u'un nom bre de coups m axim ale s t im pos é , ce dernie r fournira le nom bre m axim ald'élém e nts de notre tableau.
- Com pare r le nom bre fourni ave c la valeur ch oisie par le program m e e t affich e r le m e s s age corre s pondant.

Program m e

#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 Exe rcice s e n 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 ;
}

D ISCUSSIO N

Notre fonction de tirage aléatoire d'un nom bre e ntie r fournit toujours la m ê m e valeur, ce q ui gâ ch e q ue lque pe u l'inté rê t
du je u. D ans la pratiq ue , ils e rait né ce s s aire de re m place r la fonction aleat de ce program m e par ce lle proposée dans
l'e xe rcice III-2, laq ue lle pe rm e t d'obte nir un nom bre diffé re nt d'une e xé cution à une autre .

III-6 M as te rm ind

________________________________________________________________________________________
III. H asard e t ré cré ations 149
Enoncé

R é aliser un program m e q ui ch oisit au h asard une com binaison de 5 ch iffre s (com pris e ntre 1 e t 8) e t q ui de m ande à
l'utilisate ur de la devine r. A ch aq ue proposition, le program m e pré cis e ra :

- le nom bre de ch iffre s exacts propos é s à la bonne pl


ace,
- le nom bre de ch iffre s exacts m ais proposés à la m auvaise pl
ace.
Les diffé re nte s propositions du joue ur s e ront fournie s s ous la form e de 5 ch iffre s cons é cutifs (sans aucun s é parate ur),
validés par re turn.

Le program m e devra traite r conve nablem e nt le cas des ré pons e s incorre cte s : lettre à la place d'un ch iffre , ré pons e trop
courte ou trop longue , ch iffre incorre ct (nulou supérieur à 8).

O n pré voira un nom bre lim ite d'essais, au-de là duq ue l le program m e s 'inte rrom pra e n indiq uant q ue lle é tait la
com binaison à devine r.

Exe m pl
e

proposition ? : 12345
2 P 0 C
proposition ? : 23456
0 P 1 C
proposition ? : 34567
0 P 1 C
proposition ? : 45678
0 P 0 C
proposition ? : 56789
** incorrect **
proposition ? : 1133é
** incorrect **
proposition ? : 11332
3 P 1 C
proposition ? : 11333
4 P 0 C
proposition ? : 11313
5 P 0 C
vous avez trouvé en 7 coups

________________________________________________________________________________________
150 Exe rcice s e n langage C
ANALYSE

Ilparaî t as s e z nature ld'utiliser un tableau à 5 é lém e nts pour y range r la com binaison tiré e au h asard. Note z q ue nous
pourrions é galem e nt tire r au h asard un nom bre de 5 ch iffre s , m ais ilfaudrait, de toute façon, e n e xtraire ch acun de s
ch iffre s ;de plus, la m é th ode s e rait difficilem e nt gé né ralisable à un nom bre q ue lconq ue de positions.

La principale difficulté ré s ide dans l'analyse de la proposition du joue ur. D ans la com paraison des deux tableaux
(com binaison tiré e par le program m e e t com binaison proposé e par le joue ur), ilfaudra te nir com pte des re m arq ue s
suivante s :

- Un ch iffre com pté "à sa place " ne doit pas pouvoir ê tre é galem e nt com pté com m e "e xact, m ais m alplacé ".
- Lors q u'un tirage com porte plusieurs ch iffre s identiq ue s , ilne faut pas q u'un m ê m e ch iffre de la proposition du
joue ur puis s e ê tre com pté plusieurs fois com m e e xact.
- Lors q u'une proposition com porte plusieurs ch iffre s identiq ue s , il ne faut pas les considérer tous com m e
corre s pondant à un m ê m e ch iffre du tirage .
Nous vous proposons la m é th ode s uivante :

1 - Nous re ch e rch ons tout d'abord les ch iffre s e xacts placé s e n bonne position. A ch aq ue fois q u'une coïncide nce e s t
re levé e , nous supprim ons le ch iffre , à la fois dans la proposition du joue ur e t dans le tirage (e n le re m plaçant, par
e xe m ple, par la valeur 0).
2 - Nous re pre nons e nsuite , un à un, ch acun de s ch iffres du tirage q ui n'ont pas é té s upprim é s (c'e s t-à -dire q ui sont
diffé re nts de 0). Nous les com parons à ch acun de s ch iffres de la proposition. Là e ncore , si une coïncide nce e s t
re levé e , nous supprim ons les ch iffre s corre s pondants, à la fois dans la proposition e t dans le tirage . Note z bie n q u'il
faut absolum e nt é vite r de considérer les ch iffres déjà supprim és du tirage : ils ris q ue raie nt d'ê tre trouvé s é gaux à
d'autre s ch iffre s s upprim és de la proposition.
Ce tte m é th ode q ui dé truit le tirage nous oblige né ce s s aire m e nt à e n faire une copie avant d'entam e r l'analyse de la
proposition.

Nous avons ch oisi de réaliser trois fonctions :

- tirage : tirage au h asard de la com binaison (tableau de 5 e ntie rs) à devine r.


- e ntre e : e ntré e d e la proposition du joue ur. Ilparaî t logiq ue q ue ce tte fonction fournis s e ce tte proposition dans un
tableau d'e ntie rs. Toute fois, afin de traite r conve nablem e nt les cas de ré pons e s incorre cte s , la proposition du joue ur
s e ra tout d'abord lue dans une ch aî ne à l'aide de la fonction cge ts (son m é canism e e s t décrit dans l'e xe rcice II-4).
- analy s e : analyse de la proposition du joue ur, suivant l'algorith m e décrit pré cédem m e nt.

Program m e
III. H asard e t ré cré ations 151
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#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 Exe rcice s e n 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] ;
III. H asard e t 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 ;
}
}

Com m e ntaire s

*Le nom bre de positions (NPO S) e t le nom bre de ch iffre s (NCH IF) ont é té définis par #de fine , ce q ui e n facilite
l'é ve ntue lle m odification.

*La fonction tirage fait appe là l'algorith m e de tirage au h asard d'un e ntie r, te lq ue nous l'avons e xposé dans l'e xe rcice
III-1. Toute fois, ici, le nom bre tiré doit apparte nir à l'inte rvalle [1,NCH IF] e t non à l'inte rvalle [0,NCH IF]. C'e s t ce q ui
e xpliq ue q ue le nom bre ré e ltiré dans l'inte rvalle [0,1[ soit m ultiplié par NCH IF e t q ue l'on ajoute 1 au ré s ultat.

*La fonction e ntre e lit, com m e pré vu, la proposition du joue ur sous form e d'une ch aî ne . Elle e n e ffe ctue les contrôles
re q uis e n re s tituant la valeur 0 lors q ue la ré pons e e s t valide et la ré pons e -1 dans le cas contraire . Note z q ue la décision
de dem ande r, e n cas d'erreur, une nouve lle proposition au joue ur e s t prise dans le program m e principale t non dans la
fonction e lle-m ê m e .

*Le s argum e nts de la fonction analy s e sont transm is par leur adre s s e , afin q ue leur valeur puis s e ê tre m odifié e . C'e s t ce
q ui justifie leur dé claration sous form e de pointe urs sur de s e ntie rs. N'oublie z pas q ue les nom s de tableaux
corre s ponde nt à leur adre s s e ;c'e s t ce q ui justifie q ue dans l'appe lde analy s e , on trouve e ffe ctive m e nt les sym boles prop
e t tir, alors q ue , par ailleurs, on y trouve & bpos e t & bch if.

*D ans la boucle s uivante (du program m e principal) :


154 Exe rcice s e n langage C
while (printf ("proposition ? : "), entree(&prop) )
printf ("\n** incorrect **\n") ;

l'e xpre s s ion figurant dans w h ile utilise un "opérate ur s é q ue ntie l", ce q ui pe rm e t ainsi de sim plifie r q ue lque pe u l'é criture .
A titre indicatif, voici de ux constructions é q uivalente s , l'une parfaite m e nt structuré e , l'autre bas é e s ur l'utilisation de
bre ak (les valeurs des sym boles VR A I e t FAUX étant re s pe ctive m e nt 1 e t 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) ;

D ISCUSSIO N

*Ici, la saisie de la proposition du joue ur e s t parfaite m e nt satisfaisante , m ê m e pour un program m e "ré e l". En particulie r,
e lle autoris e les corre ctions, m ê m e aprè s q ue l'utilisate ur a frappé le dernie r ch iffre .

*Par contre , te lq u'ile s t propos é ici, ce program m e ch oisit toujours la m ê m e com binaison, ce q ui e nlève q ue lque inté rê t
à la pratiq ue ré guliè re du je u (m ais q ui pe ut facilite r la m ise au point du program m e ). Pour ré m édier à ce tte lacune , il
suffit d'introduire , dans la fonction tirage , une initialisation du gé né rate ur de nom bre s aléatoire s , lors de son prem ie r
appe l, com m e nous l'avons fait dans l'e xe rcice III-2.

*Le program m e s upporte , sans aucune m odification, de s valeurs q ue lconq ues de NPO S e t des valeurs de NCH IF
infé rie ure s à 10. Ile s t facile d'aller au-de là, e n m odifiant sim plem e nt la fonction e ntre e .
IV : TRIS, FUSIO NS
ET RECH ERCH E EN TA BLE

Nous vous proposons ici de s e xe rcices de program m ation d'algorith m e s classiques ayant trait aux tris e t fusions de
tableaux, ainsi qu'à la re ch e rch e e n table.

IV-1 Tri par e xtraction s im pl


e

________________________________________________________________________________________

Enoncé

R é aliser un program m e de tri par valeurs décroissantes d'un tableau d'e ntie rs, e n utilisant l'algorith m e dit "par e xtraction
sim ple" q ui se définit de la m aniè re s uivante :

- O n re ch e rch e le plus grand de s n é lém e nts du tableau.


- O n é ch ange ce t é lém e nt ave c le pre m ie r é lém e nt du tableau.
- Le plus petit é lém e nt s e trouve alors e n pre m iè re position. O n pe ut alors appliq ue r les deux opé rations précédente s
aux n-1 é lém e nts re s tants, puis aux n-2, ... e t ce la jus q u'à ce q u'ilne re s te plus q u'un s e ulé lém e nt (le dernie r - q ui
e s t alors le plus petit).
Le program m e affich e ra tous les "ré s ultats inte rm édiaire s ", c'e s t-à -dire les valeurs du tableau, aprè s ch aq ue é ch ange de
deux élém e nts.

Exe m pl
e

combien de valeurs à trier : 8


donnez vos valeurs à trier
156 Exe rcice s e n langage C
3 9 2 7 11 6 2 8

---- valeurs à trier ----


3 9 2 7 11 6 2 8

11 9 2 7 3 6 2 8
11 9 2 7 3 6 2 8
11 9 8 7 3 6 2 2
11 9 8 7 3 6 2 2
11 9 8 7 6 3 2 2
11 9 8 7 6 3 2 2
11 9 8 7 6 3 2 2

---- valeurs triées ----


11 9 8 7 6 3 2 2

________________________________________________________________________________________

ANALYSE

L'algorith m e propos é par l'é noncé pe ut s e form aliser com m e s uit, e n te nant com pte des conve ntions d'indice s propre s au
langage C :

R é pé te r, pour i variant de 0 à n-2 :


- re ch e rch e r k te lq ue t(k ) soit le plus grand de s t(k ), pour k allant de i à n-1,
m m

- é ch ange r les valeurs de t(k ) e t de t(i).


m

Program m e

#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 */
IV. Tris, fusions e t re ch e rch e e n 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") ;
}

Com m e ntaire s

Ce program m e fonctionne pour toute s les valeurs de NMAX, en particulie r :

- pour NM A X infé rie ur ou é galà 0, ilne fait rie n,


- pour NM A X = 1, illit une valeur q u'ilaffich e te lle q ue lle.
158 Exe rcice s e n langage C

IV-2 Tri par pe rm utation s im pl


e

________________________________________________________________________________________

Enoncé

Ecrire une fonction ré alisant le tri par valeurs croissantes d'un tableau d'e ntie rs, e n utilisant l'algorith m e de tri par
pe rm utation sim ple (dit de "la bulle"), q ui se définit ainsi (n re pré s e ntant le nom bre d'élém e nts du tableau) :

O n parcourt l'e ns e m ble du tableau, de puis sa fin jus q u'à son début, e n com parant deux élém e nts cons é cutifs, e n les
inve rsant s'ils sont m alclas s é s . O n s e re trouve ainsi ave c le plus petit é lém e nt placé e n tê te du tableau.
O n re nouve lle une te lle opé ration (dite "pas s e ") ave c les n-1 é lém e nts re s tants, puis ave c les n-2 é lém e nts re s tants, e t
ainsi de suite ... jus q u'à ce q ue :
- soit l'avant-dernier élém e nt ait é té clas s é (le dernie r é tant alors obligatoire m e nt à sa place ),
- soit q u'aucune pe rm utation n'ait e u lie u pe ndant la derniè re pas s e (ce q ui prouve alors q ue l'e ns e m ble du tableau
e s t conve nablem e nt ordonné ).
O n pré voira e n argum e nts :

- l'adresse du tableau à trie r,


- son nom bre d'élém e nts,
- un indicate ur pré cisant si l'on souh aite q ue la fonction affich e les valeurs du tableau aprè s ch aq ue pe rm utation (0
pour non, 1 pour oui).

Exe m pl
e

combien de valeurs à trier : 6


donnez vos valeurs à trier
2 8 4 7 0 8

---- valeurs à trier ----


2 8 4 7 0 8

2 8 4 0 7 8
2 8 0 4 7 8
IV. Tris, fusions e t re ch e rch e e n table 159

2 0 8 4 7 8
0 2 8 4 7 8
0 2 4 8 7 8
0 2 4 7 8 8

---- valeurs triées ----


0 2 4 7 8 8

________________________________________________________________________________________

ANALYSE

L'algorith m e nous e s t indiq ué par l'é noncé . Nous utiliserons cependant une ré pé tition de type tant q u e (instruction w h ile)
q ui pe rm e t de prendre conve nablem e nt e n com pte le cas où l'on appe lle la fonction de tri e n lui fournissant e n argum e nt
un nom bre de valeurs infé rie ur ou é galà 1.

D ans la m ise en oeuvre de ce t algorith m e , nous fe rons appe là un e ntie r i spécifiant le rang à partir duq ue lle tableau
n'e s t pas e ncore trié . Initialem e nt, ilfaudra pré voir q u'aucun é lém e nt n'e s t e ncore à sa place , ce q ui conduira à
l'initialisation artificie lle de i à -1 (puis q ue e n C, le pre m ie r é lém e nt d'un tableau porte le num é ro 0). D'autre part, un
indicate ur logiq ue nom m é pe rm ut nous s e rvira à pré cis e r si au m oins une perm utation a e u lie u au cours de la derniè re
pas s e .

Si nous notons nvalle nom bre de valeurs de notre tableau, l'algorith m e de tri pe ut alors s'é nonce r com m e s uit :

Tant q ue i ne désigne pas le dernie r é lém e nt du tableau (c'e s t-à -dire i < nval-1) e t q ue pe rm ut e s t VR A I, nous
e ffe ctuons une passe. Cette derniè re consiste e n une s ucce s s ion de com paraisons des élém e nts de rang j e t j+1, j
décrivant tous les é lém e nts depuis l'avant-dernier jus q u'à ce lui de rang i+1 (autre m e nt dit, j décroissant de nval-2 à
i+1). A ch aq ue pe rm utation, nous donnons à pe rm ut la valeur VR A I ;nous aurons, bie n sûr, pris soin d'initialiser
pe rm ut à FAUX au début de ch aq ue pas s e .
Note z q ue l'utilisation d'une ré pé tition de type tant q u e (dans laq ue lle la condition de poursuite fait inte rve nir l'indicate ur
pe rm ut) nous oblige à initialiser artificie llem e nt pe rm ut à VR A I, e n tout début de travail.

Program m e

#include <stdio.h>
#define VRAI 1 /* pour "simuler" des ... */
#define FAUX 0 /* ... valeurs logiques */
#define NMAX 100 /* nombre maximal de valeurs à trier */
main()
{
160 Exe rcice s e n 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)
IV. Tris, fusions e t re ch e rch e e n 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++ ;
}
}

Com m e ntaire s

D ans la fonction bullle, la déclaration :

int * t ;

e s t é q uivalente à :

int t[] ;

D ISCUSSIO N

Les deux algorith m e s proposés dans l'e xe rcice pré cédent e t dans ce lui-ci corre s ponde nt à ce q ue l'on appe lle des
"m é th odes directe s ". D 'une m aniè re gé né rale, ce s ont des algorith m e s s im ples à program m e r, m ais q ui né ce s s ite nt un
2
nom bre de com paraisons de l'ordre de n (note z q u'ile xiste une troisiè m e m é th ode directe dite "tri par ins e rtion").

En fait, ile xiste des m é th odes dite s "é volué e s " q ui conduis e nt à un nom bre de com paraisons de l'ordre de n *log n.
Ce lles -ci débouch e nt sur des program m e s plus com plexe s e t les opé rations q u'e lles font inte rve nir sont e lles -m ê m e s plus
gourm ande s e n te m ps q ue ce lles des m é th odes directe s . Aussi, les m é th ode s é volué e s ne pre nne nt vé ritablem e nt d'inté rê t
q ue pour de s valeurs é levé e s d e n.
162 Exe rcice s e n langage C
A titre indicatif, nous vous fournissons ici l'algorith m e re latif à la m é th ode é volué e la plus perform ante , nom m é e "Tri
rapide " (Quick sort), inve nté e par C. A. R . H oare . Ce t algorith m e , délicat à program m e r, e s t bas é s ur l'opé ration de
"s e gm e ntation" d'un tableau ;ce lle-ci consiste à partage r un tableau e n de ux partie s , nom m é e s s e gm e nts, te lles q ue tout
é lém e nt de l'une s oit infé rie ur ou é gal à tout é lém e nt de l'autre . Une te lle s e gm e ntation pe ut ê tre ré alisée par
l'algorith m e s uivant :

- Pre ndre un é lém e nt au h asard (on pe ut pre ndre l'é lém e nt du m ilie u). Soit m sa valeur.
- R e ch e rch e r, de puis le début du tableau, le pre m ie r é lém e nt t(i) te lq ue t(i)> m .
- R e ch e rch e r, de puis la fin du tableau, le pre m ie r é lém e nt t(j) te lq ue t(j)< m .
- Pe rm ute r t(i) e t t(j).
- Poursuivre ce "parcours" du tableau jus q u'à ce q ue i e t j s e re ncontre nt.
Le tri propre m e nt dit s'e ffe ctue e n appliq uant à nouve au l'opé ration de s e gm e ntation à ch aq ue s e gm e nt obte nu, puis aux
s e gm e nts obte nus par segm e ntation de ce s s e gm e nts,... e t ainsi de suite jus q u'à ce q ue ch aq ue s e gm e nt ne contie nne plus
q u'un s e ulé lém e nt.

Note z q u'une te lle m é th ode s e prê te particuliè re m e nt bien à une program m ation ré cursive .

IV-3 Tri d'un tabl


e au de ch aî
ne s

________________________________________________________________________________________

Enoncé

Ecrire une fonction utilisant la m é th ode de tri par e xtraction sim ple (décrite dans l'e xe rcice IV-1) pour trie r un tableau de
ch aî
ne s , par ordre alph abétiq ue (sans distinction e ntre m ajuscules e t m inuscules ).

Ce tte fonction re ce vra, e n argum e nt :

- l'adresse d'un tableau de pointe urs sur les ch aî


ne s conce rné e s ,
- le nom bre de ch aî
ne s à trie r.
Le tri propre m e nt dit porte ra, non sur les valeurs des ch aî
ne s e lles -m ê m e s , m ais uniq ue m e nt sur le tableau de pointe urs.

O n te s te ra ce tte fonction à l'aide d'un program m e principalcré ant un sim ple tableau de ch aî
ne s (ayant donc ch acune une
longue ur m axim ale donnée).
IV. Tris, fusions e t re ch e rch e e n table 163

Exe m pl
e

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 é th ode de tri a é té décrite dans l'e xe rcice IV-1. Il e s t ce pe ndant né ce s s aire de procéder à plusieurs sorte s
d'adaptations :

- ilfaut e n faire une fonction,


- la re lation d'ordre q ui s e rt au tri ne porte plus sur de s e ntie rs, m ais sur de s ch aî
nes de caractè re s ;ce la im pliq ue de
re courir à la fonction stricm p (e t non strcm p, puis q ue l'on souh aite ne pas distingue r les m ajuscules des m inuscules ),
- les é lém e nts à pe rm ute r s e ront des pointe urs e t non plus des entie rs.
164 Exe rcice s e n langage C
Program m e

#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,
IV. Tris, fusions e t re ch e rch e e n 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 ;
}
}

Com m e ntaire s

*Ici, les ch aîne s à trie r ont é té placé e s (par le program m e principal) dans un tableau de caractè re s (nom m é ch aine s ) à
deux dim e nsions. Note z bie n q u'ilne s e rait pas possible d'en inve rs e r l'ordre des dim e nsions ;ile s t e n e ffe t né ce s s aire
q ue tous les caractè res d'une m ê m e ch aî ne s oie nt cons é cutifs.

*Bie n q ue ce la n'ait pas é té e xplicite m e nt dem andé par l'é noncé , nous avons prévu un contrôle s ur la longue ur de s
ch aî
ne s fournie s au clavie r ;pour ce faire , nous avons fait appe là la fonction fge ts, e n l'appliq uant au fich ie r stdin.
L'instruction :

fgets (chaines[i], LGMAX+1, stdin) ;

lit au m axim um LGM AX caractè re s s ur stdin e t les range à l'adre s s e ch aine [i], e n com plétant le tout par un zé ro de fin de
ch aîne . Ainsi, on é vite les ris q ues de déborde m e nt m é m oire q ue pré s e nte ge ts.

Toute fois un lége r inonvé nie nt apparaî t. En e ffe t, tant q ue le nom bre de caractè re s m axim al(LGM AX) n'e s t pas atte int,
le caractè re \n q ui a s e rvi à délim ite r la ch aî
ne lue e s t rangé e n m é m oire , au m ê m e titre q ue les autre s . En re vanch e ,
lors q ue le nom bre m axim alde caractè re s a é té atte int, alors précisém e nt q ue ce caractè re \n n'a pas é té re ncontré , on ne
trouve plus ce caractè re e n m é m oire (le caractè re nulde fin de ch aî ne , q uant à lui, e s t bien toujours présent).

Ce t inconvé nie nt e s t surtout s e nsible lors q ue l'on affich e à nouve au les ch aî ne s par printf aprè s leur tri : les ch aînes de
longue ur m axim ale ne s e ront pas suivies d'un ch ange m e nt de ligne . Note z bie n q u'e n e m ployant puts on obtie ndrait, e n
re vanch e , 1 caractè re de ch ange m e nt de ligne pour les ch aî nes de longue ur m axim ale (transm is par la fonction puts
m ê m e ) e t 2 caractè res de ch ange m e nt de ligne pour les autre s ch aî ne s (ce lui figurant dans la ch aî ne e t ce lui transm is par
puts).

D ans un "program m e opé rationne l", ilfaudrait gé re r conve nablem e nt ce tte s ituation, ce q ue nous n'avons pas fait ici.
166 Exe rcice s e n langage C
*R appe lons q ue , aprè s la lecture par scanf du nom bre de ch aî ne s à traite r, le pointe ur re s te (com m e à l'accoutum é e )
positionné s ur le dernie r caractè re non e ncore utilisé ;dans le m e illeur de s cas, ils'agit de \n (m ais ilpe ut y avoir
d'autre s caractè re s avant si l'utilisate ur a é té distrait). Dans ces conditions, la lecture ulté rie ure d'une ch aî ne par ge ts
conduira à lire ... une ch aî ne vide.

Pour é vite r ce problèm e , nous avons placé une instruction ge tch ar q ui absorbe ce caractè re \n. En toute rigue ur, si l'on
souh aitait traite r corre cte m e nt le cas où l'utilisate ur a fourni trop d'inform ation pour le scanf pré cédent, il s e rait
né ce s s aire d'opérer une lecture d'une ch aîne par ge ts (ilfaudrait pré voir un e m place m e nt à ce t e ffe t!).

*D ans la fonction trich aine s , le pre m ie r argum e nt adr a é té déclaré par :

char * * adr

Ils'agit d'un pointe ur sur le tableau de pointe urs sur les diffé re nte s ch aî
ne s . Nous aurions pu é galem e nt le déclare r par :

char * adr[]

Note z d'ailleurs q ue nous avons utilisé le "form alism e " tableau au s e in de la fonction e lle-m ê m e . Ainsi :

adr[i] = adr[j]

aurait pu s e form uler :

* (adr+i) = * (adr+j)

*Nous vous rappe lons q ue la fonction stricm p com pare les deux ch aî
nes dont on lui fournit les adre s s e s e t e lle fournit
une valeur e ntiè re définie com m e é tant :

- positive s i la pre m iè re ch aî
ne arrive aprè s la s e conde , au s e ns de l'ordre défini par le code des caractè re s (sans te nir
com pte de la diffé re nce e ntre m ajuscules e t m inuscules pour les 26 lettres de l'alph abet),
- nulle s i les deux ch aî
ne s s ont é gales ,
- né gative s i la pre m iè re ch aî
ne arrive avant la s e conde .

D ISCUSSIO N

D 'une m aniè re gé né rale, iln'e s t pas néce s s aire q ue les ch aî


ne s à trie r soient, com m e ici, im planté e s e n m é m oire de
m aniè re cons é cutive .

D e m ê m e , la fonction trich aine s propos é e pourrait tout aussi bien opérer sur des ch aî
nes dont les e m place m e nts auraie nt
é té alloué s "dynam iq ue m e nt" (le ch apitre V vous propose d'ailleurs un exercice dans ce sens).
IV. Tris, fusions e t re ch e rch e e n table 167

IV-4 Fus ion de de ux tabl


e aux ordonné s

La fusion consiste à ras s e m bler e n un s e ultableau ordonné les é lém e nts de deux tableaux, e ux-m ê m e s ordonné s .

________________________________________________________________________________________

Enoncé

R é aliser une fonction q ui fusionne deux tableaux d'e ntie rs ordonné s par valeurs croissante s .

O n pré voira e n argum e nts :

- les adresses des trois tableaux conce rné s ,


- le nom bre de valeurs de ch acun des deux tableaux à fusionne r.
Pour te s te r ce tte fonction, on é crira un program m e principalq ui lit au clavie r de ux e ns e m bles de valeurs q ue l'on trie ra
au pré alable à l'aide de la fonction bulle ré alisée dans l'e xe rcice IV-2.

Exe m pl
e

combien de valeurs pour le premier tableau ? 5


donnez vos valeurs
3 9 2 8 11
combien de valeurs pour le second tableau ? 7
donnez vos valeurs
12 4 6 3 1 9 6

premier tableau à fusionner


2 3 8 9 11
second tableau à fusionner
1 3 4 6 6 9 12

résultat de la fusion des deux tableaux


1 2 3 3 4 6 6 8 9 9 11 12
________________________________________________________________________________________
168 Exe rcice s e n langage C
ANALYSE

La dém arch e , as s e z sim ple, s'inspire de ce lle q ue l'on adopte rait pour ré s oudre "à la m ain" un te lproblèm e . Ilsuffit, e n
e ffe t, d'avance r e n parallèle dans ch acun des deux tableaux à fusionne r (t1 e t t2), e n pré levant, à ch aq ue fois, le plus
pe tit des deux élém e nts e t e n l'introduisant dans le tableau ré s ultant t. Plus précisém e nt, nous som m e s am e né s à utiliser
trois indice s :

- i1 : pre m ie r é lém e nt de t1 non e ncore pris e n com pte ,


- i2 : pre m ie r é lém e nt de t2, non e ncore pris e n com pte ,
- i : e m place m e nt du proch ain é lém e nt à introduire dans t.
Nous initialisons ces trois indice s à zé ro (com pte te nu de s conve ntions du C). Nous ré pé tons alors le traite m e nt suivant :

Ch oisir le plus petit des élém e nts t1(i1) e t t2(i2) e t le place r e n t(i). Incré m e nte r de 1 la valeur de l'indice
corre s pondant à l'é lém e nt e xtrait (i1 ou i2), ainsi que celle de i.
Nous poursuivons ainsi jus q u'à ce q ue l'un des deux tableaux soit é puis é . Ilne re s te plus alors q u'à re copie r la fin de
l'autre tableau.

Program m e

#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) ;
IV. Tris, fusions e t re ch e rch e e n 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 Exe rcice s e n 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++ ;
}
}

Com m e ntaire s

*Pour e ffe ctue r le tri pré alable des deux tableaux fournis e n donné e , nous avons re pris la fonction bulle ré alisée dans
l'e xe rcice IV-2. Nous en avons toute fois supprim é les instructions perm e ttant d'affich e r, sur dem ande , les im pre s s ions
inte rm édiaire s .
IV. Tris, fusions e t re ch e rch e e n table 171

IV-5 Re ch e rch e dich otom iq ue

L'e xe rcice II-4 de facturation par code faisait inte rve nir un algorith m e s é q ue ntie lde re ch e rch e e n table. Nous vous
proposons ici de réaliser un algorith m e plus perform ant de re ch e rch e par "dich otom ie ".

________________________________________________________________________________________

Enoncé

Ecrire un program m e q ui re ch e rch e , à partir d'un code d'article (num é riq ue ), l'inform ation q ui lui e s t associé e , à savoir
un libellé (ch aî
ne ) e t un prix unitaire (ré e l).

Com m e dans l'e xe rcice II-4, le program m e utilisera un tableau de s tructure s , déclaré à un nive au global, pour cons e rve r
les inform ations re q uis e s . Ce tte fois, par contre , ces derniè re s s e ront rangé e s par ordre croissant du num é ro de code .

La localisation d'un num é ro de code donné se fe ra par une re ch e rch e dich otom iq ue . Ce lle-ci consiste à profite r de l'ordre
du tableau pour accé lére r la re ch e rch e e n procédant com m e s uit :

- O n considè re l'é lém e nt figurant au "m ilie u" du tableau. Si le code ch e rch é lui e s t é gal, la re ch e rch e e s t te rm iné e .
S'illui e s t infé rie ur, on e n conclut q ue le code re ch e rch é ne pe ut s e trouve r q ue dans la pre m iè re m oitié du tableau ;
dans le cas contraire , on e n conclut q u'ils e trouve dans la s e conde m oitié .
- O n re com m e nce alors l'opé ration sur la "m oitié " conce rné e , puis sur la m oitié de ce tte m oitié , e t ainsi de suite ...
jus q u'à ce q ue l'une des conditions suivante s s oit satisfaite :
*on a trouvé l'é lém e nt ch e rch é ,
*on e s t sûr q u'ilne figure pas dans le tableau.

Exe m pl
es

code article recherché : 24


le code 24 n'existe pas

________________

code article recherché : 19


article de code 19
libellé : Balance de ménage
prix : 278.00

________________________________________________________________________________________
172 Exe rcice s e n langage C

ANALYSE

L'algorith m e propos é par l'é noncé s uggè re d'utiliser trois variables pe rm e ttant de spécifie r, à un instant donné , la partie
du tableau dans laq ue lle s 'e ffe ctue la re ch e rch e :

gauch e : début de la partie re s tant à e xplore r,


droite : fin de la partie re s tant à e xplore r,
m ilie u : position ch oisie pour le "m ilie u" de ce tte partie re s tant à e xplore r.
Note z déjà q ue ce tte notion de m ilie u e s t q ue lque pe u am biguë. Nous convie ndrons q u'e lle corre s pond à la partie e ntiè re
de la m oye nne des indice s gauch e e t droite .

L'algorith m e de re ch e rch e par dich otom ie pe ut alors s'é nonce r ainsi (t désignant le tableau, n le nom bre de code s e t x
l'é lém e nt ch e rch é ) :

- Initialiser gauch e e t droite de façon q u'ils désignent l'e ns e m ble du tableau.


- R é pé te r le traite m e nt suivant :
*D é te rm ine r le m ilie u de la partie à e xplore r :
m ilie u = (gauch e + droite ) / 2
*Com pare r l'é lém e nt ch e rch é x ave c t(m ilie u) :
+ S'ils sont é gaux, l'é lém e nt ch e rch é e s t localisé en position m ilie u,
+ Si x e s t supérieur à t(m ilie u), l'é lém e nt ch e rch é ne pe ut s e s itue r q ue dans la partie droite ;on ré alise
l'affe ctation :
debut = m ilie u + 1
+ dans le cas contraire , l'é lém e nt ch e rch é ne pe ut s e s itue r q ue dans la partie gauch e ;on ré alise l'affe ctation :
fin = m ilie u - 1
Ilnous re s te à spécifie r la condition d'arrê t (ou de poursuite ) de ce tte ré pé tition. O n pe ut déjà note r q ue , à ch aq ue
parcours de la boucle, soit la valeur de gauch e augm e nte , soit ce lle de droite dim inue . Ainsi, on e s t sûr q u'au bout d'un
nom bre fini de tours on aboutira à l'une des situations suivante s :

- l'é lém e nt a é té localisé.


- la valeur de gauch e e s t supérieure à ce lle de droite .
Elles nous fournis s e nt donc tout nature llem e nt la condition de fin de notre boucle.
IV. Tris, fusions e t re ch e rch e e n table 173

Note z q ue , dans un pre m ie r te m ps, la valeur de gauch e devie nt é gale à ce lle de droite ;m ais, dans ce cas, nous ne
savons pas encore si le s e ulé lém e nt re s tant à e xam ine r e s t ou non é galà x ;aussi est-ilné ce s s aire de faire un tour
supplém e ntaire pour s'e n assure r.

Program m e

#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", 199.50,
19, "Balance de ménage", 278.0,
25, "Centrifugeuse", 370.0,
26, "Four raclette 6P", 295.25
} ;
/* ----------------------------------------------------------------------*/

#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 Exe rcice s e n 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) ;
}

Com m e ntaire s

*Note z bie n la condition ré gissant la boucle while :

gauche <= droite && !trouve

- D 'une part, com m e nous l'avons dit dans notre analyse, nous poursuivons notre e xploration, m ê m e q uand les
valeurs de gauch e e t droite sont é gales , de m aniè re à savoir si le s e ulé lém e nt re s tant à e xam ine r convie nt ou non.
- D 'autre part, nous y faisons inte rve nir un indicate ur logiq ue (trouve ). Nous aurions pu nous e n pas s e r, à condition
de place r un bre ak dans la boucle. Toute fois, dans ce cas, il aurait fallu pré voir, e n fin de boucle, un te s t
supplém e ntaire pe rm e ttant de savoir si la re ch e rch e avait é té fructue us e ou non.

D ISCUSSIO N

Ilfaut pre ndre garde , dans le déroulem e nt de l'algorith m e , à ne pas s e conte nte r de pre ndre com m e nouve lle borne de la
partie de tableau à e xplore r la valeur de m ilie u, e n é crivant :

debut = m ilie u
ou :

fin = m ilie u
En e ffe t, dans ce cas, on ne pe ut plus prouve r q ue la boucle s 'ach è ve e n un nom bre fini de tours. Ce rtaine s s ituations
conduis e nt d'ailleurs à une boucle infinie .
V : GESTIO N D Y NA M IQUE

Les données d'un program m e s e ré partissent e n trois caté gorie s : statiq ue s , autom atiq ue s e t dynam iq ue s . Les données
statiq ue s s ont définies dè s la com pilation ;la ge s tion des données autom atiq ue s re s te transpare nte au program m e ur e t
s e ules les données dynam iq ue s s ont vé ritablem e nt cré é e s (dans le tas) sur son initiative .

D 'une m aniè re gé né rale, l'utilisation de données dynam iq ue s fournit des solutions à des problèm e s te ls q ue :

- ge s tion de données dont l'am pleur n'e s t pas connue lors de la ré alisation du program m e ,
- m ise en oeuvre de structures dites dynam iq ue s , te lles q ue les liste s ch aî
né e s ou les arbres binaire s .
Ce ch apitre vous e n propos e q ue lque s e xe m ples .

V-1 Cribl
e dynam iq ue

________________________________________________________________________________________

Enoncé

R é aliser un program m e q ui dé te rm ine les pre m ie rs nom bre s pre m ie rs par la m é th ode du crible d'Eratosth è ne , e xpos é e
dans l'e xe rcice I-2.

Ce tte fois, par contre , le nom bre d'entie rs à considérer ne sera pas fixé par le program m e , m ais fourni e n donné e . Le
program m e alloue ra dynam iq ue m e nt l'e m place m e nt m é m oire né ce s s aire au dé roulem e nt de l'algorith m e . En cas de
m é m oire insuffisante , ildem ande ra à l'utilisate ur de form uler une dem ande m oins im portante .

O n s'astre indra ici à utiliser la fonction m al


loc.

Exe m pl
e

combien d'entiers voulez-vous examiner : 200


176 Exe rcice s e n langage C
entre 1 et 200 les nombres premiers sont :
2 3 5 7 11 13 17 19 23 29
31 37 41 43 47 53 59 61 67 71
73 79 83 89 97 101 103 107 109 113
127 131 137 139 149 151 157 163 167 173
179 181 191 193 197 199

________________________________________________________________________________________

ANALYSE

L'algorith m e lui-m ê m e a déjà é té e xposé dans l'e xe rcice I-2. La nouve auté ré s ide ici dans l'allocation dynam iq ue de
l'e s pace im parti au tableau d'e ntie rs. Pour ce faire , la dém arch e la plus classique consiste à faire appe là la fonction
m alloc, com m e nous le pré conis e l'é noncé .

Program m e

#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) ;
V.Ge s tion dynam iq u e 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 */
}
}

Com m e ntaire s

*L'allocation de l'e s pace m é m oire né ce s s aire au tableau d'e ntie rs e s t ré alisée par l'instruction :

raye = (unsigned *) malloc ( (n+1)*sizeof(unsigned) ) ;

dans laq ue lle raye e s t un pointe ur sur des entie rs non signé s .

O r, le prototype de m alloc e s t pré cis é m e nt :

void * malloc (size_t) ;

Le ré s ultat fourni par m alloc e s t un "pointe ur gé né riq ue " q ui pe ut ê tre conve rti im plicite m e nt e n un pointe ur de n'im porte
q ue ltype . Aussi, l'opé rate ur de "cast" (unsigne d *) n'e s t pas indispensable ici. Notre instruction d'allocation m é m oire
aurait pu s'é crire :

raye = malloc ( (n+1) * sizeof(unsigned) ) ;


178 Exe rcice s e n langage C

En ce q ui conce rne l'argum e nt de m alloc, ce lui-ci e s t a priori d'un type size _t défini (par type de f) dans stdlib.h . Le type
e xact corre s pondant dépend de l'im plém e ntation (m ais ile s t toujours non signé - e n gé né ral, ils'agit de unsigne d int).
Note z q ue le ré s ultat fourni par size of e s t du m ê m e type size _t.

R appe lons q ue m alloc fournit e n ré s ultat un pointe ur sur le début de la zone conce rné e lors q ue l'allocation a ré ussi et un
pointe ur nuldans le cas contraire (note z q ue le sym bole NULLe s t défini dans stdlib.h ).

*En ce q ui conce rne l'algorith m e de passage au crible, vous re m arq ue z q ue nous avons e m ployé e xacte m e nt les m ê m e s
instructions q ue dans le program m e de l'e xe rcice I-2. Pourtant, dans ce dernie r, le sym bole raye désignait un tableau
d'entie rs, tandis q u'ici ildésigne un pointe ur sur des entie rs. Ce la e s t possible parce q u'e n langage C, un nom de tableau
e s t un pointe ur (constant).

D ISCUSSIO N

*Le ch oix du type unsigne d pour n e s t q ue lque pe u arbitraire ;ile s t guidé par le fait q ue m alloc adm e t gé né ralem e nt un
argum e nt de ce type . En supposant q ue te le s t le cas, on constate q u'alors l'e xpre s s ion :

(n+1) * sizeof (unsigned)

conduit à des valeurs e rronées dè s q ue la valeur de n*size of(int) dépas s e la capacité du type int (n'oublie z pas q u'iln'y a
pas de déte ction de dépas s e m e nt de capacité pour les opé rations portant sur des entie rs). Le ré s ultat pe ut alors ê tre
catastroph iq ue car le nom bre d'octe ts dem andé s à m alloc s e trouve ê tre infé rie ur à ce lui ré e llem e nt utilisé.

Le problèm e s e com pliq ue e ncore un pe u si l'on tie nt com pte de ce q ue , dans ce rtaine s im plém e ntations, le type size _t
pe u corre s pondre à autre ch os e q ue unsigne d int.

En toute rigue ur, ilfaudrait donc s'assure r q ue le nom bre de valeurs dem andé e s par l'utilisate ur e s t e ffe ctive m e nt
infé rie ur à une ce rtaine lim ite à fixe r e n fonction de l'im plém e ntation conce rné e .

V-2 Cré ation dynam iq ue de ch aî


ne s

Lors q u'un program m e doit traite r un grand nom bre de ch aî nes de longue ur variable e t q ue ce nom bre n'e s t pas connu a
priori, ilpe ut s'avé re r inté re s s ant de faire alloue r dynam iq ue m e nt (par le program m e ) l'e s pace m é m oire né ce s s aire au
stock age des ch aî ne s . C'e s t ce q ue vous propose cet e xe rcice q ui pe ut ê tre considéré com m e pré alable à un traite m e nt
ulté rie ur de ce s ch aîne s (par e xe m ple un tri com m e vous le propos e ra l'e xe rcice V-3).
V.Ge s tion dynam iq u e 179

________________________________________________________________________________________

Enoncé

Ecrire un program m e q ui lit un nom bre q ue lconq ue de ch aî ne s au clavie r e t q ui les range e n m é m oire dans des
e m place m e nts alloués dynam iq ue m e nt au fur e t à m e s ure des besoins. Le s adresses de ch acune des ch aî ne s s e ront
cons e rvées dans un tableau de pointe urs. Ce dernie r s e ra ré s e rvé dans le program m e (e n clas s e autom atiq ue ) e t sa taille
(fixe ) im pos e ra donc une valeur m axim ale au nom bre de ch aî ne s q u'ils e ra ainsi possible de traite r.

L'utilisate ur signalera q u'ila fourni sa derniè re ch aî


ne e n la faisant suivre d'une ch aî
ne "vide".

Le program m e affich e ra e nsuite les ch aî


ne s lue s , à titre de sim ple contrôle.

R e m arque : on utilisera la fonction m alloc e t on supposera q ue les ligne s lue s au clavie r ne pe uve nt jam ais dépas s e r 127
caractè re s .

Exe m pl
e

----- 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 Exe rcice s e n langage C
Basic
------- chaîne numéro 4
Pascal
------- chaîne numéro 5
Turbo Pascal

________________________________________________________________________________________

ANALYSE

L'é noncé nous im pose donc de définir, au s e in du program m e , un tableau de pointe urs destiné à conte nir les adresses des
ch aîne s à cré e r.

Ch aq ue ch aî ne s e ra d'abord lue dans une zone inte rm édiaire (non dynam iq ue ). O n lui alloue ra e nsuite , dynam iq ue m e nt, à
l'aide de la fonction m alloc, un e m place m e nt dont la taille corre s pond e xacte m e nt à sa longue ur ;l'adre s s e ainsi obte nue
s e ra m é m orisée dans le tableau de pointe urs.

Le traite m e nt s e ra inte rrom pu :

- soit q uand le tableau de pointe urs e s t plein,


- soit q uand l'utilisate ur fournit une ch aî
ne vide.
D e plus, à ch aq ue allocation ré alisée par m alloc, on s'assure ra q ue l'e s pace m é m oire né ce s s aire a pu ê tre obte nu. D ans le
cas contraire , on pré voira d'inte rrom pre le program m e .

Program m e

#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 */


V.Ge s tion dynam iq u e 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]) ;
}

Com m e ntaire s

*Ici, com pte te nu de ce q ue nous précisait l'é noncé , nous avons ch oisi de lire nos ch aî
nes dans un tableau de 128
caractè re s , à l'aide de la fonction ge ts.

*Nous avons re m is à "zé ro" le tableau de pointe urs sur nos ch aî ne s . Ils'agit là d'une opé ration superflue m ais q ui pe ut
s'avé re r utile pe ndant la ph ase de m ise au point du program m e . Note z l'usage du sym bole NULL;prédéfini dans le
fich ie r stdlib.h , ilcorre s pond à la constante pointe ur nulle.

*La cré ation de s ch aî


ne s e s t ré alisée par une boucle tant q u e (instruction w h ile), dans laq ue lle nous avons prévu de ux
autre s s ortie s :
182 Exe rcice s e n langage C
- une s ortie par bre ak , dans le cas où l'utilisate ur a fourni une ch aî
ne vide,
- un arrê t e xce ptionne ldu program m e par e xit, dans le cas où l'allocation dynam iq ue a é ch oué . Ce tte fonction (dont
le prototype figure dans stdlib.h ) re q uie rt un argum e nt ;sa valeur e s t transm ise au systè m e e t e lle pourrait
é ve ntue llem e nt ê tre ré cupé ré e par d'autre s program m e s . Note z q ue , e n l'abs e nce de l'instruction #include re lative à
stdlib.h , le com pilate ur acce pte un appe lde e xit sans argum e nt (ile s t incapable de déte cte r l'e rre ur - laq ue lle n'a
d'ailleurs aucune incide nce s ur l'e xé cution du program m e lui-m ê m e ).
Nature llem e nt, beaucoup d'autre s form ulations s e raie nt possibles .

D ISCUSSIO N

*Le fait de ré s e rve r le tableau dans le program m e (e n clas s e autom atiq ue ) im pos e une lim ite au nom bre de ch aî
ne s q u'il
e s t ainsi possible de traite r ;ce tte lim ite e s t indé pe ndante de la m é m oire ré e llem e nt disponible. O n pe ut am é liore r
q ue lque pe u la situation e n faisant é galem e nt allouer dynam iquem ent l 'espace nécessaire à ce tabl eau de pointeurs. Il
faut toute fois e n connaî tre la taille (ou du m oins une valeur m axim ale) lors de l'e xé cution du program m e . Ce la pe ut faire
l'obje t d'une donnée fournie par l'utilisate ur com m e dans l'e xe rcice s uivant.

V-3 Tri dynam iq ue de ch aî


ne s

________________________________________________________________________________________

Enoncé

Ecrire un program m e pe rm e ttant de trie r par ordre alph abétiq ue des ch aî


ne s fournie s e n donné e . Com m e dans l'e xe rcice
pré cédent, on alloue ra dynam iq ue m e nt des em place m e nts m é m oire aux ch aî ne s , au fur e t à m e s ure de leur lecture , e t
leurs adre s s e s s e ront cons e rvées dans un tableau de pointe urs. Par contre , ici, ce dernie r ve rra, lui aussi, son
e m place m e nt alloué dynam iq ue m e nt e n début de program m e ;pour ce faire , on de m ande ra à l'utilisate ur de fournir une
valeur m axim ale du nom bre de ch aî ne s q u'ils e ra am e né à fournir.

O n utilisera l'algorith m e de "tri par e xtraction sim ple" e xposé dans l'e xe rcice V-1 e t on fe ra appe là la fonction m alloc.

Exe m pl
e
V.Ge s tion dynam iq u e 183

nombre maximal de chaînes ? 100


------- 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 e n fait d'adapte r le program m e de l'e xe rcice pré cédent, e n lui adjoignant :

- la ré s e rvation dynam iq ue du tableau de pointe urs,


- le tri du tableau de ch aî
ne s ainsi cré é , par ré organisation de s pointe urs. Nous utiliserons pour cela l'algorith m e de tri
par e xtraction sim ple Ce lui-ci a é té e xposé dans l'é noncé de l'e xe rcice V-1 e t son adaptation au tri de ch aî ne s a é té
e xpliq uée dans l'analyse de l'e xe rcice V-2.
184 Exe rcice s e n langage C

Program m e

#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 */
}
V.Ge s tion dynam iq u e 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] ) ;
}

Com m e ntaire s

*D ans le program m e de l'e xe rcice V-2, le sym bole adr désignait un tableau de pointe urs. Ici, ce m ê m e sym bole désigne
un pointe ur sur un tableau de pointe urs. O r, m algré ce tte diffé re nce appare nte , vous constate z q ue nous e m ployons
toujours la notation :

adr[i]
ave c la m ê m e signification dans les deux cas.

En fait, dans le pré cédent program m e , adr é tait une constante pointeur dont la valeur é tait ce lle de l'adresse de début du
tableau de pointe urs. Dans le pré s e nt program m e , adr e s t une variabl
e pointeur dont la valeur e s t é galem e nt ce lle de
début du tableau de pointe urs. Ainsi, dans les deux cas :

adr[i]
e s t é q uivalent à :

*(adr + i)
Note z ce pe ndant q ue l'é q uivalence e ntre les deux program m e s n'e s t pas totale. En e ffe t, dans le pre m ie r cas, adr n'e s t
pas une lvalue (m ot anglais dont une traduction approch é e pourrait ê tre : valeur à gauch e ) ;par e xe m ple, l'e xpre s s ion
adr++ s e rait incorre cte . Dans le s e cond cas, par contre , adr e s t bien une lvalue .
186 Exe rcice s e n langage C
*Nous n'avons pris aucune pré caution particuliè re e n ce q ui conce rne les lecture s au clavie r q ui sont ré alisées ici par
ge ts e t scanf. Indé pe ndam m e nt des anom alie s h abitue lles e ncourue s e n cas de données incorrecte s (ch aî ne trop longue
pour ge ts, donné e non num é riq ue pour scanf), un problèm e s upplém e ntaire apparaî t, lié au fait q u'aprè s une lecture par
scanf, le pointe ur re s te positionné s ur le dernie r caractè re non e ncore utilisé, à savoir ici le \n (du m oins si l'utilisate ur a
validé norm alem e nt, sans fournir d'inform ations supplém e ntaire s ). Si la lecture s uivante e s t, à son tour, e ffe ctué e par
scanf, aucun problèm e particulie r ne s e pos e , le caractè re \n é tant sim plem e nt ignoré . Iln'e n va plus de m ê m e lors q ue la
lecture s uivante e s t e ffe ctué e par ge ts ;dans ce cas, e n e ffe t, ce caractè re e s t inte rpré té com m e un caractè re de "fin" e t
ge ts fournit... une ch aî ne vide. C'est pour é vite r ce ph é nom è ne q ue nous avons dû introduire une instruction ge tch ar pour
absorber le \n.

D ISCUSSIO N

Pour pouvoir alloue r conve nablem e nt l'e m place m e nt du tableau de pointe urs, notre program m e a besoin q ue l'utilisate ur
lui fournis s e une valeur m axim ale du nom bre de ch aî ne s . Si nous souh aitions q u'ile n soit autre m e nt, ils e rait né ce s s aire
de pouvoir alloue r provisoire m e nt un e m place m e nt à ce tableau, q uitte à l'é te ndre e nsuite au fur e t à m e s ure des besoins
à l'aide de la fonction re alloc. Une te lle e xte nsion pourrait ê tre ré alisée, soit à ch aq ue nouve lle ch aî ne e ntré e , soit par
blocs de taille fixe (par e xe m ple toute s les 100 ch aî
ne s ).

V-4 Cré ation d'une l


is te ch aî
né e

O n appe lle liste ch aî


né e ou liste lié e une s uite ordonnée d'é lém e nts dans laq ue lle ch aq ue é lém e nt, sauf le dernie r,
com porte un pointe ur sur l'é lém e nt suivant.

________________________________________________________________________________________

Enoncé

Ecrire un program m e q ui cré e une liste ch aî


née d'é lém e nts com portant ch acun :

- un nom (ch aî
ne ) d'au m axim um 10 caractè re s ,
- un â ge .
Le s inform ations corre s pondante s s e ront lue s au clavie r e t l'utilisate ur frappe ra un nom "vide" aprè s les données relative s
au de rnie r é lém e nt.
V.Ge s tion dynam iq u e 187

Le program m e affich e ra e nsuite les inform ations conte nues dans la liste ainsi cré é e , dans l'ordre inve rse de ce lui dans
leq ue le lles auront é té fournie s .

O n pré voira de ux fonctions : l'une pour la cré ation, l'autre pour la liste . Elles posséderont com m e uniq ue argum e nt
l'adresse de début de la liste (pointe ur sur le pre m ie r é lém e nt).

Exe m pl
e

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

Ch aq ue é lém e nt de notre liste s e ra re pré s e nté par une s tructure . Nous voyons q ue ce lle-ci doit conte nir un pointe ur sur un
é lém e nt de m ê m e type . Ce la fait inte rve nir une ce rtaine "ré cursivité " dans la déclaration corre s pondante , ce q ui e s t
acce pté e n C.

En ce q ui conce rne l'algorith m e de cré ation de la liste , deux possibilité s s 'offre nt à nous :

- Ajoute r ch aq ue nouve lé lém e nt à la fin de la liste . Le parcours ulté rie ur de la liste s e fe ra alors dans le m ê m e ordre
q ue ce lui dans leq ue lles données corre s pondante s ont é té introduite s .
- Ajoute r ch aq ue nouve lé lém e nt e n début de liste . Le parcours ulté rie ur de la liste s e fe ra alors dans l'ordre inve rs e
de ce lui dans leq ue lles données corre s pondante s ont é té introduite s .
188 Exe rcice s e n langage C
Com pte te nu de ce q ue l'é noncé nous dem ande d'affich e r la liste à l'e nve rs, aprè s sa cré ation, ilparaî
t plus apportun de
ch oisir la s e conde m é th ode .

Com m e dem andé , la cré ation de la liste s e ra ré alisée par une fonction. Le program m e principals e conte nte ra de ré s e rve r
un pointe ur (nom m é de but) destiné à désigner le pre m ie r é lém e nt de la liste . Sa valeur e ffe ctive s e ra fournie par la
fonction de cré ation.

L'algorith m e de cré ation, q uant à lui, consiste ra à ré pé te r le traite m e nt d'insertion d'un nouve lé lém e nt e n début de liste ,
à savoir :

- cré e r dynam iq ue m e nt un e m place m e nt pour un nouve lé lém e nt e t y range r les inform ations fournie s au clavie r,
- affe cte r au pointe ur conte nu dans ce nouve lé lém e nt l'ancie nne valeur de de but,
- affe cte r à de but l'adresse de ce nouve lé lém e nt.
Nous convie ndrons, de plus, q ue le dernie r é lém e nt de la liste possè de un pointe ur nul, ce q ui nous facilite ra
l'initialisation de l'algorith m e ;e n e ffe t, ce lle-ci s e ram è ne alors à l'affe ctation à de but d'une valeur nulle.

Program m e

#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) ;
}
V.Ge s tion dynam iq u e 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 ;
}
}
19 0 Exe rcice s e n langage C

Com m e ntaire s

*Nous avons ici ch oisi de déclare r notre s tructure à un nive au globale t de faire appe là type de f. Ce tte déclaration à un
nive au globalé vite de devoir dé crire la m ê m e s tructure e n diffé re nts e ndroits, ce q ui s e rait, non s e ulem e nt laborie ux
m ais, de surcroî t, source d'erreurs. Par contre , le re cours à type de f n'apporte q u'une s im plification des déclarations des
é lém e nts de ce type (dans le cas contraire , ilsuffirait de re m place r t_e le m e nt par struct e le m e nt).

Note z bie n, par contre , q u'iln'e s t pas possible de re m place r, au s e in de la définition de notre s tructure , l'é criture :

struct element * suivant

par :

t_element * suivant

*La fonction de cré ation re çoit e n argum e nt l 'adresse du pointe ur de but, car e lle doit pouvoir lui attribue r une valeur.
La fonction de liste , q uant à e lle, s e conte nte de la val eur de ce m ê m e pointe ur. Ce tte diffé re nce s e ré pe rcute
nature llem e nt sur la m aniè re d'utiliser cet argum e nt dans ch acune des deux fonctions.

Note z d'ailleurs q ue nous avons pu nous pe rm e ttre , dans la fonction de liste , de m odifie r la valeur ainsi reçue (le pointe ur
de but y décrit succe s s ive m e nt les diffé re nts é lém e nts de la liste ).

*Là e ncore , les lecture s au clavie r ont é té ré alisées par scanf e t ge ts, donc sans prote ctions particuliè re s . Com m e nous
l'avons déjà signalé dans le pré cédent e xe rcice , l'utilisation conjointe de ces deux fonctions pose un problèm e lié au fait
q ue , aprè s une lecture par scanf, le pointe ur re s te positionné s ur le dernie r caractè re non e ncore utilisé, à savoir
(gé né ralem e nt) \n. C'e s t ce q ui justifie l'introduction d'une instruction ge tch ar pour absorber ce caractè re inte m pe s tif.
VI : RECURSIVITE

La ré cursivité e s t une notion dé licate m ais q ui a l'avantage de conduire s ouve nt à des program m e s s im ples .

Le s trois prem ie rs e xe rcices de ce ch apitre s ont plutôt des "e xe rcices d'école" destiné s à vous faire e xplore r diffé re nte s
situations e n vous forçant à é crire une fonction ré cursive , là où, e n pratiq ue , on ne s e rait pas am e né à le faire .

VI-1 l
e cture ré curs ive (1)

________________________________________________________________________________________

Enoncé

Ecrire une fonction ré cursive de lecture d'une valeur e ntiè re au clavie r. La fonction de vra s'appe ler e lle-m ê m e dans le
cas où l'inform ation fournie e s t incorre cte (non num é riq ue ).

O n pré voira une fonction à un argum ent (l'adresse de la variable pour laq ue lle on ve ut lire une valeur) e t sans val
eur de
retour.

O n pourra faire appe là fge ts e t sscanf pour dé te cte r conve nablem e nt les ré pons e s incorre cte s .

Re m arq ue

Nous vous cons e illons de com pare r ce t e xe rcice au suivant dans leq ue lle m ê m e problèm e e s t ré s olu par l'e m ploi d'une
fonction ré cursive s ans argum e nt e t ave c valeur de re tour.

Exe m pl
e
19 2 Exe rcice s e n langage C
donnez un nombre entier : un
** réponse incorrecte - redonnez-la : 'à
** réponse incorrecte - redonnez-la : 40
-- merci pour 40

________________________________________________________________________________________

ANALYSE

Au sein de la fonction (q ue nous nom m e rons lecture ), nous lirons la valeur atte ndue à l'aide de fge ts (..., stdin), associé à
sscanf, com m e nous l'avons déjà fait dans ce rtains des exe rcice s pré cédents.

Nous considé re rons la ré ponse de l'utilisate ur com m e corre cte lors q ue le code de re tour de sscanf s e ra é galà 1. Si te l
n'e s t pas le cas, nous fe rons à nouve au appe là la m ê m e fonction lecture .

Program m e

#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) ;
VI. R é cursivité 19 3

}
}

Com m e ntaire s

*Note z bie n q u'au s e in de la fonction lecture , au nive au de l'appe lde sscanf, nous voyons apparaî
tre p e t non & p,
puis q ue ici p e s t déjà un pointe ur sur la variable dont on ve ut lire la valeur.

*Si nous avions utilisé sim plem e nt ge ts (com m e dans l'e xe rcice VI.5 de la pre m iè re partie ) au lie u de fge ts (..., stdin),
nous aurions pu égalem e nt nous proté ge r de m auvais e s ré ponses de l'utilisate ur, m ais nous aurions dû définir une taille
m axim ale pour la ch aî ne lue au clavie r ;nous aurions couru le ris q ue de "débordem e nt m é m oire ", dans le cas où
l'utilisate ur aurait fourni une ré pons e trop longue .

D ISCUSSIO N

Ch aq ue nouve lappe lde lecture e ntraî


ne l'allocation autom atiq ue , sur la pile, d'e m place m e nts pour :

- l'argum e nt p,
- les obje ts locaux : com pte e t ligne .
O r, e n fait, ne s ont né ce s s aire s q ue les valeurs corre s pondant au de rnie r appe lde lecture (ce lui où la lecture s 'e s t
conve nablem e nt déroulée ) ;dans ce s conditions, l'e m pilem e nt des diffé re nts e m place m e nts alloué s au tableau ligne e s t
superflu. Si l'on souh aite faire q ue lque s é conom ies d'espace m é m oire à ce nive au, on pe ut s'arrange r pour q ue ce t
e m place m e nt ne s oit ré s e rvé q u'une s e ule fois :

- soit dans le program m e appe lant (ici le program m e principal) ;dans ce cas, ilfaudra e n transm e ttre l'adre s s e e n
argum e nt, ce q ui e ntraî
ne l'e m pilem e nt d'une variable s upplém e ntaire .
- soit e n clas s e globale ;dans ce cas, on pe ut é galem e nt traite r de la sorte com pte e t p (c'e s t-à -dire , e n fait, n), ce q ui
supprim e du m ê m e coup tous les argum e nts e t les obje ts locaux de lecture . Note z q u'ilre s te ra q uand m ê m e , à ch aq ue
appe l, une allocation autom atiq ue d'espace pour l'adre s s e d e re tour.
- soit e n clas s e s tatiq ue (static) au s e in de la fonction. Là e ncore , nous pouvons traite r de la m ê m e m aniè re la variable
com pte , la variable p, q uant à e lle, re s tant soum ise aux e m pilem e nts.
19 4 Exe rcice s e n langage C
VI-2 Le cture ré curs ive (2)

________________________________________________________________________________________

Enoncé

Ecrire une fonction ré cursive de lecture d'une valeur e ntiè re au clavie r. La fonction de vra s'appe ler e lle-m ê m e dans le
cas où l'inform ation fournie e s t incorre cte (non num é riq ue ).

O n pré voira ce tte fois une fonction dans laq ue lle la valeur de re tour e s t la valeur lue (iln'y aura donc pas d'argum e nts).

Là e ncore , on pourra faire appe là fge ts (..., stdin) e t sscanf pour dé te cte r conve nablem e nt les ré pons e s incorre cte s .

Re m arq ue

Ce t e xe rcice e s t surtout destiné à ê tre com paré au pré cédent dans leq ue lle m ê m e problèm e e s t ré s olu par l'e m ploi d'une
fonction ave c argum e nt e t sans valeur de re tour.

Exe m pl
e

donnez un nombre entier : un


** réponse incorrecte - redonnez la : 'à
** réponse incorrecte - redonnez la : 40
-- merci pour 40

________________________________________________________________________________________

ANALYSE

Com m e pré cédem m e nt, au s e in de notre fonction (nom m é e lecture ), nous lirons la valeur atte ndue à l'aide de fge ts
associé à sscanf. Nous considé re rons la ré ponse de l'utilisate ur com m e corre cte lors q ue le code de re tour de sscanf s e ra
é galà 1. Si ce la n'e s t pas le cas, nous fe rons de nouve au appe là la m ê m e fonction lecture .
VI. R é cursivité 19 5

Program m e

#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) ;
}

Com m e ntaire s

*Ce tte fois, on note ra q ue p dé s igne une variable locale de type int, dont l'e m place m e nt e s t alloué autom atiq ue m e nt à
ch aq ue appe lde la fonction lecture , de la m ê m e m aniè re q ue pour les autre s obje ts locaux com pte e t ligne . Par ailleurs,
si aucun e m place m e nt n'e s t alloué ici pour un q ue lconq ue argum e nt, ilfaut e n pré voir un pour la valeur de re tour. O n
re m arq ue d'ailleurs q u'ici ce tte valeur s e trouve "propagé e " de proch e e n proch e , lors du "dépilem e nt" des appe ls.

*Pre ne z garde à ne pas é crire :


19 6 Exe rcice s e n langage C
if (!compte)
{ printf ("** réponse incorrecte - redonnez-la : ") ;
p = lecture() ;
}
else return (p) ;

car la fonction ne re nve rrait une valeur q ue lors q ue la lecture s e s e rait déroulée conve nablem e nt. Note z d'ailleurs q ue
dans ce cas, bon nom bre de com pilate urs vous prévie ndrait par un m e s s age d'ave rtissem e nt ("w arning").

Par contre , ils e rait tout à fait corre ct (e t é q uivalent) d'écrire :

if (!compte)
{ printf ("** réponse incorrecte - redonnez la : ") ;
return (lecture()) ;
}
else return (p) ;

D ISCUSSIO N

Le s re m arq ue s faites dans le pré cédent e xe rcice à propos des em pilem e nts de ligne (e t é ve ntue llem e nt com pte )
s'appliq ue nt e ncore ici.

VI-3 Le cture ré curs ive (3)

________________________________________________________________________________________

Enoncé

Ecrire une fonction ré cursive de lecture d'un entie r au clavie r. La fonction de vra s'appe ler e lle-m ê m e dans le cas où
l'inform ation fournie e s t incorre cte .

Ce tte fois, la fonction possédera 3 argum e nts :

- le m e s s age q u'e lle doit im prim e r avant de lire une valeur (le m e s s age "donne z un nom bre e ntie r :" ne s e ra donc
plus affich é par le program m e principal),
- l'adresse de la variable dans laq ue lle on doit lire une valeur,
- le nom bre m axim ald'essais autoris é s .
VI. R é cursivité 19 7

Elle fournira un code de re tour é galà 0 si la lecture a fini par aboutir e t à -1 lors q ue la lecture n'a pas pu aboutir dans le
nom bre d'essais im partis.

Com m e dans les deux pré cédents e xe rcice s , on fe ra appe là fge ts associé e à sscanf.

Exe m pl
es

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 m e s s age à im prim e r s e ra transm is sous form e de l'adresse d'une ch aî


ne . La fonction affich e ra ce m e s s age dè s son
appe l. Son conte nu de vra donc ê tre :

donne z un nom bre e ntie r :


dans l'appe linitialde la fonction (ré alisé dans le program m e principal), e t :

**ré pons e incorre cte - re donne z -a :


dans l'appe lde la fonction par e lle-m ê m e e n cas de ré pons e incorre cte .

En ce q ui conce rne le nom bre m axim ald'appe ls, on le transm e ttra par valeur e t on s'arrange ra pour faire décroî
tre s a
valeur de 1 à ch aq ue appe l.

La ré cursivité des appe ls ce s s e ra lors q ue l'une des deux conditions suivante s s e ra satisfaite :

- valeur lue corre cte - on fournira alors 0 com m e valeur de re tour,


19 8 Exe rcice s e n langage C
- nom bre m axim ald'appe ls dépas s é - on fournira alors -1 com m e valeur de re tour.

Program m e

#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) ;
}

Com m e ntaire s

*Nous avons ch oisi ici de faire affich e r le m e s s age :

nom bre d'e s s ais dépassé


VI. R é cursivité 19 9

dans le program m e principal. Ils'agit là d'un ch oix arbitraire puis q ue nous aurions tout aussi bien pu le faire affich e r par
la fonction e lle-m ê m e .

VI-4 Puis s ance e ntiè re

________________________________________________________________________________________

Enoncé
k
Ecrire une fonction ré cursive pe rm e ttant de calculer la valeur de x pour x ré e lq ue lconq ue e t k e ntie r re latif q ue lconq ue .
O n e xploite ra les proprié té s s uivante s :
0
x = 1,
k
x =x pour k = 1,
-k k
x = 1 /x pour k positif,
k k -1
x = (x )x pour k positif im pair,
k k /2
x = (x )x pour k positif pair.
O n te s te ra ce tte fonction à l'aide d'un program m e principalpe rm e ttant à l'utilisate ur de fournir e n donné e s les valeurs de
x e t de k .

Exe m pl
es

donnez une valeur réelle : 4


donnez une puissance entière : -2
4.000000e+000 à la puissance -2 = 6.250000e-002

_______________________

donnez une valeur réelle : 5.2


200 Exe rcice s e n langage C
donnez une puissance entière : 3
5.200000e+000 à la puissance 3 = 1.406080e+002

________________________________________________________________________________________

ANALYSE

L'é noncé fournit les "définitions ré cursive s " à e m ploye r.

Program m e

#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 */
VI. R é cursivité 201

Com m e ntaire s

Ile s t pré fé rable d'écrire :

z = puissance (x, n/2) ;


return (z*z) ;

plutôt q ue :

return (puissance (x,n/2) * puissance (x,n/2) ) ;

q ui produirait deux fois plus d'appe ls de la fonction puissance .

VI-5 Fonction d'A ck e rm ann

________________________________________________________________________________________

Enoncé

Ecrire une fonction ré cursive calculant la valeur de la fonction d'Ack e rm ann, dé finie pour m e t n, e ntie rs positifs ou
nuls, par :

A(m ,n) = A(m -1, A(m ,n-1) ) pour m > 0 e t n> 0,


A(0,n) = n+ 1 pour n> 0,
A(m ,0) = A(m -1,1) pour m > 0.
Ce tte fonction possédera e n argum e nt les valeurs de m e t de n et fournira e n ré s ultat la valeur de A corre s pondante .

O n visualisera l'e m pilem e nt des appe ls e t leur dé pilem e nt e n affich ant un m e s s age accom pagné de la valeur des deux
argum e nts lors de ch aq ue e ntrée dans la fonction ainsi que juste avant sa sortie (dans ce dernie r cas, on affich e ra
é galem e nt la valeur q ue la fonction s'apprê te à re tourne r).

O n te s te ra ce tte fonction à l'aide d'un program m e principalauq ue lon fournira e n donné e s les valeurs de m e t de n.
202 Exe rcice s e n langage C
Exe m pl
e

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

________________________________________________________________________________________

Program m e

#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) ;


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 d e H anoi

________________________________________________________________________________________

Enoncé

R é aliser une fonction ré cursive proposant une s olution au problèm e dit des tours de H anoi, leq ue ls'é nonce ainsi :

O n dispose de trois piq ue ts, num é roté s 1, 2 e t 3 e t de n disques de tailles diffé re nte s . Au départ, ces disques sont
e m pilés par taille décroissante s ur le piq ue t num é ro 1. Le but du je u e s t de déplace r ce s n disq ues du piq ue t num é ro 1
sur le piq ue t num é ro 3, e n re s pe ctant les contrainte s s uivante s :
- on ne déplace q u'un s e uldisque à la fois (d'un piq ue t à un autre ),
- un disq ue ne doit jam ais ê tre placé au-de s s us d'un disq ue plus petit q ue lui.
O n te s te ra ce tte fonction ave c un program m e principalpe rm e ttant de ch oisir, e n donné e , le nom bre totalde disques à
déplace r (n).

Si vous n'ê te s pas fam iliaris é ave c ce type de problèm e , nous vous cons e illons de te nte r tout d'abord de le ré s oudre
m anue llem e nt avant de ch e rch e r à program m e r la fonction de m andé e .

Exe m pl
e

combien de disques ? 4
déplacer un disque de 1 en 2
204 Exe rcice s e n 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 e s t é vidente ;ilsuffit de déplace r l'uniq ue disque du piquet num é ro 1 au piq ue t num é ro 3.

D è s q ue n e s t supérieur à 1, on re m arq ue q u'il e s t né ce s s aire d'utiliser le piq ue t num é ro 2 pour de s s tock age s
inte rm édiaire s . O n pe ut considérer que le problèm e consiste à déplace r n disques du piquet num é ro 1 ve rs le piq u e t
num é ro 3, e n utilisant le piq u e t num é ro 2 com m e piq u e t inte rm é diaire . O n pe ut m ontre r q ue ce tte opé ration s e
décom pos e e n trois opérations plus sim ples :

- déplace r les n-1 disq ue s s upé rie urs du piq ue t num é ro 1 ve rs le piq ue t num é ro 2 ;pe ndant ce tte ph as e , on pe ut
utiliser le piq ue t num é ro 3 com m e piq ue t inte rm édiaire ,
- déplace r les n-1 disq ues du piq ue t num é ro 2 ve rs le piq ue t num é ro 3 ;là e ncore , on pe ut utiliser le piq ue t num é ro 1
com m e piq ue t inte rm édiaire (le disque initialem e nt pré s e nt sur ce piq ue t é tant plus grand q ue tous les disques à
déplace r).
Ce la nous conduit à la ré alisation d'une fonction ré cursive possédant com m e argum e nts :

- le nom bre de disques à déplace r,


- le num é ro du piq ue t "de départ",
- le num é ro du piq ue t "d'arrivé e ",
- le num é ro du piq ue t "inte rm édiaire ".
VI. R é cursivité 205

Program m e

#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) ;
}
}
VII : TRA ITEM ENT D E
FICH IERS

Le s e xe rcices de ce ch apitre vous fournis s e nt des exe m ples classiques de traite m e nt de fich ie rs corre s pondant à diffé re nts
aspects :

- traite m e nt s é q ue ntie l,
- accè s direct,
- fich ie rs de type te xte .

VII-1 Cré ation s é q ue ntie l


le de fich ie r

________________________________________________________________________________________

Enoncé

Ecrire un program m e de création séquentiel l


e d'un fich ie r com portant, pour un ce rtain nom bre de pe rsonne s , les
inform ations suivante s , fournie s au clavie r :

- nom (au m axim um 20 caractè re s ),


- â ge ,
- nom bre d'enfants,
- â ge de ch acun des diffé re nts e nfants ;on ne dem ande ra (e t donc on n'e nre gistre ra) q ue l'â ge des 15 pre m ie rs e nfants
(m ais le nom bre figurant dans le ch am p pré cédent pourra ê tre s upé rie ur à 15).
L'utilisate ur fournira un nom "vide" pour signaler q u'iln'a plus de personne s à e nre gistre r.
208 Exe rcice s e n langage C
O n ne pré voira aucun contrôle particulie r au nive au de la saisie des données

Exe m pl
e

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 ch aq ue e nre gistre m e nt du fich ie r dé coule de l'é noncé . Ce pe ndant, e n ce q ui conce rne la m aniè re de
re pré s e nte r le nom des personne s , nous devons décide r de la pré s e nce ou de l'abs e nce du caractè re de fin de ch aî ne (\0).
Ici, nous avons ch oisi, par facilité , d'introduire ce caractè re , ce q ui im pliq ue q ue la zone corre s pondante s oit de longue ur
21.

Pour cré e r notre fich ie r, nous utiliserons les fonctions de nive au 2, c'e s t-à -dire ici fope n e t fw rite . R appe lons q ue ce lles -
ci travaillent ave c un pointe ur sur une s tructure de type FILE (prédéfini dans stdio.h ). La valeur de ce pointe ur nous e s t
fournie par fope n ;ce tte fonction re s titue un pointe ur nule n cas d'erreur d'ouve rture .

La cré ation du fich ie r consiste s im plem e nt à ré pé te r les actions :


VII. Traite m e nt de fich ie rs 209

- lecture d'inform ations au clavie r,


- é criture de ce s inform ations dans le fich ie r.
Ce tte ré pé tition doit ê tre inte rrom pue à la re ncontre d'un nom vide.

Program m e

#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 Exe rcice s e n 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 ----------") ;
}

Com m e ntaire s

*Note z le "m ode d'ouve rture " w b :

w : ouve rture e n é criture ;si le fich ie r n'e xiste pas, ile s t cré é . S'ile xiste , son ancie n conte nu e s t pe rdu.
b : m ode dit "binaire " ou "non translaté ".
En fait, l'indication b ne s e justifie q ue dans les im plém e ntations q ui distingue nt les fich ie rs de te xte des autre s . Une te lle
distinction e s t m otivé e par le fait q ue le caractè re de fin de ligne (\n) possè de, sur certains systè m e s , une re pré s e ntation
particuliè re obte nue par la succe s s ion de deux caractè re s . La pré s e nce de b é vite le ris q ue q ue le fich ie r conce rné s oit
considéré com m e un fich ie r de type te xte , ce q ui am è ne rait une inte rpré tation non souh aité e d e s couples de caractè re s
re pré s e ntant une fin de ligne .

*Ici, nous avons fait appe là la fonction e xit (son prototype figure dans stdlib.h ) pour inte rrom pre le program m e e n cas
d'erreur d'ouve rture du fich ie r. Ils'agit là d'un ch oix arbitraire . Nous aurions pu de m ande r à l'utilisate ur de propos e r un
autre nom de fich ie r.

*En ce q ui conce rne la boucle de cré ation du fich ie r, nous avons ch oisi de la program m e r sous form e d'une boucle
infinie :

do
.......
.......
VII. Traite m e nt de fich ie rs 211

while (1) ;

q ue nous inte rrom pons au m om e nt opportun par bre ak . Nous aurions pu é galem e nt ch oisir d'introduire les pre m iè re s
instructions de la boucle dans l'e xpre s s ion conditionnant une instruction w h ile, de ce tte m aniè re :

while (printf("nom : "), gets(bloc.nom), strlen(bloc.mot) )

*Com m e pré vu par l'é noncé , aucun contrôle particulie r n'e s t e ffe ctué s ur les données qui sont donc lue s par scanf e t
ge ts. Là e ncore s e pos e le problèm e d'ignore r le \n q ui subsiste aprè s une lecture par scanf, ce q ui im pose d'introduire
artificie llem e nt une instruction ge tch ar (pour plus de détails sur ce problèm e , voye z les com m e ntaires de l'e xe rcice V-3).

*R appe lons q ue la fonction d'é criture dans le fich ie r (fw rite ) possè de 4 argum e nts :

- L'adresse de début d'un e ns e m ble de blocs à é crire (note z bie n la notation & bloc e t non sim plem e nt bloc, dans la
m e s ure où le nom d'une s tructure désigne sa valeur e t non son adre s s e , com m e ce la e s t le cas pour un tableau).
- La taille d'un bloc. Note z q u'ici nous avons utilisé la fonction size of, ce q ui assure la portabilité du program m e .
- Le nom bre de blocs de ce tte taille à é crire (ici, 1).
- L'adresse de la structure décrivant le fich ie r (e lle a é té fournie par fope n).

D ISCUSSIO N

*Ce program m e n'e xam ine pas le code de re tour de fw rite , leq ue lpré cis e le nom bre de blocs ré e llem e nt é crits dans le
fich ie r (ce nom bre é tant infé rie ur au nom bre s ouh aité e n cas d'erreur d'écriture ). Ilfaut toute fois note r, à ce propos, q ue ,
gé né ralem e nt, un ce rtain nom bre d'erreurs sont "ré cupé ré e s " par le systè m e q ui affich e alors lui-m ê m e s on propre
m e s s age .

*Com m e le pré voyait l'é noncé , ce program m e n'e s t pas proté gé d'éve ntue lles e rre urs dans les ré pons e s fournie s par
l'utilisate ur. A titre indicatif, voici q ue lque s s ituations q ue l'on pe ut re ncontre r :

- Si l'utilisate ur fournit un nom de fich ie r de plus de 20 caractè re s , ily aura é cras e m e nt d'inform ations e n m é m oire .
Ici, ils e rait toute fois as s e z facile de re m édier à ce problèm e e n attribuant au sym bole LNO M FICH une valeur
supérieure au nom bre de caractè re s q ue l'on pe ut frappe r au clavie r dans l'im plém e ntation conce rné e . O n pourrait
é galem e nt lire un nom bre de caractè re s lim ité s e n utilisant, au lie u de ge ts (nom fich ), l'instruction :
fgets (nomfich, LNOMFICH, stdin) ;

Note z toute fois q ue , dans ce cas, les caractè re s s upplém e ntaire s frappé s é ve ntue llem e nt par l'utilisate ur sur la m ê m e
"ligne " s e raie nt pris e n com pte par une proch aine instruction de lecture s ur l'e ntré e s tandard.
212 Exe rcice s e n langage C
D ans ce rtaine s im plém e ntations (notam m e nt Turbo/Borland C e t C/Quick C M icrosoft), ile s t possible de ré gler
com plète m e nt le problèm e e n utilisant l'instruction cge ts q ui a le m é rite de lim ite r, non s e ulem e nt le nom bre de
caractè re s pris e n com pte , m ais é galem e nt ce ux e ffe ctive m e nt frappé s au clavie r.
- Si l'utilisate ur fournit plus de caractè re s q ue n'e n atte nd scanf, ce ux-ci s e ront utilisés (ave c plus ou m oins de
bonh e ur) par une lecture s uivante . Là e ncore , le problèm e ne pe ut ê tre conve nablem e nt ré glé q ue d'une façon
dépendant de l'im plém e ntation, par e xe m ple ave c la fonction cge ts (associé e , ce tte fois, à sscanf) cité e
pré cédem m e nt.
- Si l'utilisate ur fournit des caractè re s non num é riq ue s là où scanf atte nd des ch iffre s , le ré s ultat de la lecture s e ra
arbitraire ;le program m e ne s 'e n ape rce vra pas puisq u'ilne te s te pas le code de re tour de scanf (q ui fournit le nom bre
de valeurs e ffe ctive m e nt lue s ). De plus, là e ncore , les caractè re s non traité s s e ront re pris par une lecture ulté rie ure .
Le pre m ie r point pe ut, là e ncore , ê tre ré s olu par l'e m ploi de sscanf, associé à fge ts (..., stdin). Là e ncore , dans
ce rtaine s im plém e ntations, cge ts (associé e à sscanf) pe rm e t de ré gler totalem e nt le problèm e .

VII-2 Lis te s é q ue ntie l


le d'un fich ie r

________________________________________________________________________________________

Enoncé

R é aliser un program m e pe rm e ttant d'affich e r succe s s ive m e nt ch acun de s e nre gistre m e nts d'un fich ie r analogue à ce ux
cré é s par le program m e pré cédent. Le program m e pré s e nte ra un s e ule nre gistre m e nt à la fois, accom pagné d'un num é ro
pré cisant son rang dans le fich ie r (on attribue ra le num é ro 1 au pre m ie r e nre gistre m e nt) ;ilatte ndra q ue l'utilisate ur
frappe la touch e re turn avant de pas s e r à l'e nre gistre m e nt suivant.

L'affich age des inform ations s e ra ré alisé par une fonction à laq ue lle on transm e ttra e n argum e nt l'e nre gistre m e nt à
affich e r e t son num é ro. Le m odè le m ê m e de la structure corre s pondante s e ra, q uant à lui, dé fini à un nive au global.

Le program m e devra s'assure r de l'e xiste nce du fich ie r à liste r.

Exe m pl
e

donnez le nom du fichier à lister : person

enregistrement numéro : 1

NOM : dubois
AGE : 32
NOMBRE D'ENFANTS : 1
VII. Traite m e nt de fich ie rs 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 : 17

-------- FIN LISTE FICHIER ----------

________________________________________________________________________________________

Program m e

#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 Exe rcice s e n 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++)
VII. Traite m e nt de fich ie rs 215

printf ("AGE ENFANT %2d : %2d\n", i+1, bloc->agenf[i]) ;


}

Com m e ntaire s

*Note z le m ode d'ouve rture rb :

r : ouve rture e n lecture . Si le fich ie r n'e xiste pas, fope n fournit un pointe ur nul.
b : ouve rture e n m ode "binaire " ou "non translaté " (pour plus d'inform ations sur la diffé re nce e ntre les m ode s
translaté e t non translaté , voye z les com m e ntaires de l'e xe rcice VII-1).

*R appe lons q ue la fonction de lecture fre ad possè de 4 argum e nts, com parables à ce ux de fw rite :

- l'adresse de début d'un e ns e m ble de blocs à lire ,


- la taille d'un bloc (e n octe ts),
- le nom bre de blocs de ce tte taille à lire ,
- l'adresse de la structure décrivant le fich ie r (e lle a é té fournie par fope n).

*La fonction fe of pre nd la valeur vrai (1) lors q ue la fin de fich ie r a é té e ffe ctive m e nt re ncontré e . Autre m e nt dit, ilne
suffit pas, pour dé te cte r la fin d'un fich ie r, d'avoir sim plem e nt lu son dernier octe t ;ile s t, de plus, né ce s s aire d'avoir
te nté de lire au-de là. C'e s t ce q ui justifie q ue ce tte condition soit e xam iné e aprè s fre ad e t non avant.

*Voye z la façon dont nous avons program m é la boucle de lecture des diffé re nts e nre gistre m e nts du fich ie r. Ce la nous
é vite une s ortie e n cours de boucle par bre ak , com m e dans :

do
{ fread (&bloc, sizeof(bloc), 1, entree) ;
if (feof(entree)) break ;
affiche (&bloc, num++) ;
getchar() ;
}
while (1) ;

ou un te s t supplém e ntaire dans la boucle com m e dans :


216 Exe rcice s e n langage C

do
{ fread (&bloc, sizeof(bloc), 1, entree) ;
if (!feof(entree))
{ affiche (&bloc, num++) ;
getchar ;
}
}
while (!feof(entree)) ;

D ISCUSSIO N

*Ce program m e n'e xam ine pas le code de re tour de fre ad (ce lui-ci pré cis e le nom bre de blocs ré e llem e nt lus).

*Notre program m e n'e s t pas proté gé contre la fourniture par l'utilisate ur d'un nom de fich ie r de plus de 20 caractè re s .
Voye z la discussion de l'e xe rcice pré cédent.

*Le passage à l'e nre gistre m e nt suivant e s t déclench é par la frappe de re turn. M ais si l'utilisate ur frappe un ou plusieurs
caractè re s (validés par re turn), ilve rra dé filer plusieurs enregistre m e nts de suite . La solution à ce problèm e dépe nd, ici
e ncore , de l'im plém e ntation. Par e xe m ple, dans un environne m e nt D O S, ave c Turbo/Borland C/C+ + ou Quick C/C
M icrosoft, ilsuffira de "vider le tam pon du systè m e " par :

while (kbhit()) getch ;

avant ch aq ue atte nte .

VII-3 Corre ction de fich ie r

________________________________________________________________________________________

Enoncé

R é aliser un program m e pe rm e ttant d'effe ctue r de s corre ctions sur un fich ie r analogue à ce ux cré é s par le program m e de
l'e xe rcice VII-1.
VII. Traite m e nt de fich ie rs 217

L'utilisate ur dé s igne ra un e nre gistre m e nt par son num é ro d'ordre dans le fich ie r. Le program m e s 'assure ra de s on
e xiste nce e t l'affich e ra d'abord te lq ue lavant de dem ande r les m odifications à lui apporte r. Ces derniè re s s e ront
e ffe ctué e s ch am p par ch am p. Pour ch aq ue ch am p, le program m e e n affich e ra à nouve au la valeur, puis ildem ande ra à
l'utilisate ur d'e ntre r une é ve ntue lle valeur de re m place m e nt. Si aucune m odification n'e s t souh aité e , ilsuffira à ce
dernie r de ré pondre directe m e nt par la frappe de re turn.

O n pré voira de ux fonctions :

- une pour l'affich age d'un enregistre m e nt (on pourra re pre ndre la fonction affich e de l'e xe rcice pré cédent),
- une pour la m odification d'un e nre gistre m e nt.

Exe m pl
e

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) : 0

-------- FIN MODIFICATIONS FICHIER ----------

________________________________________________________________________________________
218 Exe rcice s e n langage C
ANALYSE

A partir du m om e nt où l'on souh aite re trouve r un e nre gistre m e nt par son rang dans le fich ie r, ilparaî t logiq ue de ré aliser
un "accè s direct". R appe lons q u'e n langage C ce lui-ci s'obtie nt e n agissant sur la valeur d'un pointe ur dans le fich ie r à
l'aide de la fonction fs e e k . La lecture e t l'é criture , q uant à e lles , re s te nt toujours ré alisées par les fonctions fre ad e t
fw rite .

L'é noncé ne nous im pos e pas de contrôle s ur l'inform ation lue au clavie r. Né anm oins, nous devons ê tre e n m e s ure
d'acce pte r e t de re connaî tre com m e te lle une "ré pons e vide". D ans ce s conditions, nous ne pouvons pas em ploye r scanf
q ui ris q ue rait de conduire à un bouclage s ur le caractè re \n.

Une s olution à un te lproblèm e consiste à lire tout d'abord la ré ponse de l'utilisate ur sous form e d'une ch aî ne , ce q ui
pe rm e t de déce ler conve nablem e nt les ré pons e s vides. Si l'on souh aite une s olution dé pe ndante de l'im plém e ntation, ce la
pe ut s e faire s oit ave c ge ts, soit (si l'on souh aite lim ite r le nom bre de caractè re s pris e n com pte ) ave c fge ts (..., stdin).Ici,
nous utiliserons la pre m iè re possibilité , e n faisant appe l à une zone de 128 caractè re s (dans bon nom bre
d'im plém e ntations, on ne pe ut pas frappe r au clavie r de "ligne s " plus longue s !).

Lors q u'une inform ation num é riq ue e s t atte ndue , ilnous suffit alors de "décode r" le conte nu de ce tte ch aî
ne . Ce la pe ut s e
faire , soit ave c la fonction sscanf assortie (ici) d'un form at %d, soit ave c la fonction standard atoi. Par souci de dive rsité ,
nous avons ch oisi ici la s e conde .

Program m e

#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()
{
VII. Traite m e nt de fich ie rs 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) ;

/* boucle de corrections d'enregistrements */


/* jusqu'à demande d'arrêt */
do
{ 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 Exe rcice s e n 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 : ") ;


VII. Traite m e nt de fich ie rs 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) ;
}
}

Com m e ntaire s

*Nous avons ouve rt le fich ie r dans le m ode r+ b, leq ue lautoris e la m ise à jour (lecture e t é criture e n un e m place m e nt
q ue lconq ue du fich ie r). Note z q u'ilfaut é vite r d'é crire ici rb+ , ce q ui ne provoq ue rait gé né ralem e nt pas d'erreur
d'ouve rture , m ais q ui e m pê ch e rait toute é criture dans le fich ie r (ici, notre program m e ne s 'ape rce vrait pas de ce tte
anom alie puis q u'ilne te s te pas le code de re tour de fw rite ). En ce q ui conce rne l'indication b, rappe lons q ue ce lle-ci
n'e s t indispensable q ue dans les im plém e ntations q ui distingue nt les fich ie rs de type te xte des autre s . R e voye z
é ve ntue llem e nt les com m e ntaires de l'e xe rcice VII.1.

*Aprè s l'ouve rture du fich ie r, nous e n dé te rm inons la taille (dans la variable nb_e nre g) à l'aide des fonctions fs e e k e t
fte ll. Plus précisém e nt :

fseek (fichier, 0, 2)

nous place à 0 octe t de la fin (code 2) du fich ie r e t :

ftell (fichier)

nous donne la position courante du pointe ur associé au fich ie r (q ui pointe ici sur la fin). Ilnous e s t alors facile de la
transform e r e n un nom bre d'enregistre m e nts, e n la divisant par la taille d'un enregistre m e nt.

*N'oublie z pas q u'aprè s avoir lu un e nre gistre m e nt, ile s t né ce s s aire , avant de le ré é crire , de positionne r à nouve au le
pointe ur dans le fich ie r.

D ISCUSSIO N
222 Exe rcice s e n langage C
*Com m e dans les pré cédents program m e s , nous n'avons pas introduit de prote ctions particuliè re s vis-à -vis des réponses
fournie s par l'utilisate ur (voye z les discussions des précédents program m e s ). Toute fois, ici, la m aniè re m ê m e dont nous
avons program m é la saisie des corre ctions, iln'e xiste pas, à ce nive au, de ris q ue de "plangage " cons é cutif à une
m auvais e ré pons e puis q ue nous n'avons pas fait appe là scanf.

VII-4 Com ptage de l


e ttre s e t m ots d'un fich ie r te xte

________________________________________________________________________________________

Enoncé

Ecrire un program m e q ui, à partir d'un fich ie r te xte , déte rm ine :

- le nom bre de caractè re s q u'ilcontie nt,


- le nom bre de ch acune des lettres de l'alph abet (on ne considérera que les m inuscules ),
- le nom bre de m ots,
- le nom bre de ligne s.
Le s fins de ligne s ne devront pas ê tre com ptabilisées dans les caractè re s . O n adm e ttra q ue deux m ots sont toujours
s é paré s par un ou plusieurs des caractè re s s uivants :

- fin de ligne
- e s pace
- ponctuation : : . , ;?!
- pare nth è s e s : ( )
- guillem e ts : "
- apostroph e : '
O n adm e ttra é galem e nt, pour sim plifie r, q u'aucun m ot ne pe ut ê tre com m e ncé s ur une ligne e t s e poursuivre s ur la
suivante .

Ile s t cons e illé de ré aliser une fonction pe rm e ttant de décide r si un caractè re donné, transm is en argum e nt, e s t un de s
s é parate urs m e ntionné s ci-de s s us. Elle re s titue ra la valeur 1 lors q ue le caractè re e s t un s é parate ur e t la valeur 0 dans le
cas contraire .
VII. Traite m e nt de fich ie rs 223

Exe m pl
e

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

........

110 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

Com m e nous avons déjà e u l'occasion de le voir dans les e xe rcice s I-5 e t I-6, ce type de problèm e pe ut ê tre ré s olu d'au
m oins deux m aniè re s :

- e n e ffe ctuant une ré pé tition du traite m e nt d'un caractè re ,


- e n e ffe ctuant une ré pé tition du traite m e nt d'une ligne , lui-m ê m e constitué de la ré pé tition du traite m e nt de ch acun
des caractè re s q u'e lle contie nt.
Toute fois, ici, nous avons à faire à un fich ie r dans leq ue lla longue ur m axim ale d'une ligne n'e s t pas connue a priori, ce
q ui re nd la s e conde m é th ode difficile à m e ttre e n oe uvre . Nous ch oisirons donc la pre m iè re ;ch aq ue caractè re du fich ie r
s e ra donc lu par fge tc.

R appe lons q ue ce rtaine s im plém e ntations distingue nt les fich ie rs de type te xte des autre s . Dans ce cas, une te lle
distinction n'e s t pas lié e au conte nu m ê m e du fich ie r (e n fait, on pe ut toujours considérer qu'un fich ie r, q ue lq ue s oit son
conte nu, e s t form é d'une suite d'octe ts, donc, finalem e nt, d'une s uite de caractè re s ). Elle a sim plem e nt pour obje ctif de
224 Exe rcice s e n langage C
faire e n sorte q ue , pour le program m e , les "fins de ligne " apparais s e nt toujours m até rialisées par un caractè re uniq ue , à
savoir \n (alors q ue , pré cis é m e nt, ce rtaine s im plém e ntations, DOS notam m e nt, re pré s e nte nt une fin de ligne par un
"coupe " de caractè re s ). Lors q u'une te lle distinction e s t né ce s s aire , ile s t pré vu d'introduire l'indication t, au nive au du
m ode d'ouve rture du fich ie r (de m ê m e q u'on y introduisait l'indication b pour signaler q u'ilne s 'agissait pas d'un fich ie r
de type te xte ).

Bie n e nte ndu, ici, nous avons tout inté rê t à profite r de ce tte possibilité , de m aniè re à nous facilite r la déte ction de s fins
de ligne e t, surtout, à obte nir un program m e portable (à l'e xce ption, é ve ntue llem e nt, de l'indication t).

Le s com ptage s à e ffe ctue r au nive au de s caractè re s (nom bre de caractè re s , nom bre de ch acune des m inuscules ) pe uve nt
ê tre ré alisés de façon nature lle, à condition toute fois de ne pas com ptabiliser \n com m e un caractè re (au contraire , à sa
re ncontre , ilfaudra incré m e nte r le com pte ur de ligne s ).

En ce q ui conce rne les com ptages de m ots, nous procéderons com m e dans le pre m ie r program m e de l'e xe rcice I-6 e n
e m ployant :

- une fonction pe rm e ttant de te s te r si un caractè re e s t un s é parate ur,


- un indicate ur logiq ue : m ot_e n_cours.

Program m e

#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 ;
VII. Traite m e nt de fich ie rs 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 Exe rcice s e n 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) ;
}

Com m e ntaire s

Le fich ie r a é té ouve rt e n m ode rt :

r : ouve rture e n lecture . Si le fich ie r n'e xiste pas, fope n fournit un pointe ur nul.
t : ouve rture e n m ode translaté (voye z à ce propos, le pre m ie r com m e ntaire de l'e xe rcice VII-1).
Note z q ue le ch oix du m ode translaté n'e s t jam ais absolum e nt indispensable. Toute fois, com m e nous l'avons dit dans
l'analyse, ilnous facilite la déte ction de fin de ligne e t, de plus, ilre nd le program m e transportable (par e xe m ple s ous
UNIX, où une fin de ligne e s t re pré s e nté e par \n).
VII. Traite m e nt de fich ie rs 227

D ISCUSSIO N

Nous avons suppos é (im plicite m e nt) q ue notre program m e traitait un vé ritable fich ie r te xte , autre m e nt dit q ue ce dernie r
s e te rm inait par une fin de ligne . Si ce la n'é tait pas le cas :

- la derniè re ligne ne s e rait pas com ptabilisée,


- le dernie r m ot ne s e rait pas com ptabilisé, à m oins d'ê tre s uivi d'au m oins un séparate ur.
VIII : ANALYSE
NUM ERIQUE

Ce ch apitre vous propose q ue lque s applications du langage C à l'analyse num é riq ue . Nous avons ch e rch é à y introduire
les te ch niq ues de program m ation q ui inte rvie nne nt fré q ue m m e nt dans ce dom aine . Citons, par e xe m ple :

- la re pré s e ntation e t les m anipulations de m atrice s ,


- la re pré s e ntation de nom bre s com plexe s ,
- la ré alisation de m odules s usce ptibles de travailler ave c une fonction q ue lconq ue ou ave c des tableaux de dim e nsions
q ue lconq ue s .

VIII-1 Produit de m atrice s ré e l


les

________________________________________________________________________________________

Enoncé

Ecrire une fonction calculant le produit de deux m atrice s ré e lles . O n supposera q ue le pre m ie r indice de ch aq ue tableau
re pré s e ntant une m atrice corre s pond à une ligne .

O n pré voira e n argum e nts :

- les adresses des deux m atrice s à m ultiplie r e t ce lle de la m atrice produit,


- le nom bre de ligne s e t le nom bre de colonnes de la pre m iè re m atrice ,
- le nom bre de colonnes de la s e conde m atrice (son nom bre de ligne s é tant obligatoire m e nt é galau nom bre de
colonnes de la pre m iè re ).
Un program m e principalpe rm e ttra de te s te r ce tte fonction.
230 Exe rcice s e n langage C

Exe m pl
e

MATRICE A
0 1 2 3
1 2 3 4
2 3 4 5
3 4 5 6
4 5 6 7

MATRICE B
0 1 2
1 2 3
2 3 4
3 4 5

PRODUIT A x B
14 20 26
20 30 40
26 40 54
32 50 68
38 60 82

ANALYSE

R appe lons q ue s i A est une m atrice n, p (n ligne s e t p colonne s ) e t si B e s t une m atrice p, q , la m atrice produit :

C=A x B
e s t une m atrice n, q d é finie par :

c = a b
ij ik kj

Program m e

#define N 5
#define P 4
#define Q 3
VIII. Analy s e num é riq u e 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 Exe rcice s e n 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 ;
}
}

Com m e ntaire s

*D ans la fonction prod_m at, nous n'avons pas pu utiliser le "form alism e " des tableaux pour les m atrice s a, b e t c car
ce lles -ci possè dent deux dim e nsions non connue s lors de la com pilation du program m e . R appe lons q u'un te lproblèm e ne
s e pos e pas lors q u'ils'agit de tableaux à une s e ule dim e nsion (car une notation te lle q ue t[i] a toujours un sens, quelle
q ue s oit la taille de t) ou lors q u'ils'agit d'un tableau à plusieurs dim e nsions dont s e ule la pre m iè re e s t inconnue (com pte
te nu de la m aniè re dont les é lém e nts d'un tableau sont rangé s e n m é m oire ).

D ans ce s conditions, nous som m e s obligé de faire appe lau form alism e des pointe urs pour re pé re r un é lém e nt q ue lconq ue
de nos m atrice s . Pour ce faire , nous transm e ttons à la fonction prodm at l'adresse de début des trois m atrice s conce rné e s .
Note z q u'e n toute rigue ur (du m oins d'aprè s la norm e ANSI), dans le program m e m ain, un sym bole te lq ue a e s t du type
(double [P]) * (c'e s t-à -dire q u'ilre pré s e nte un pointe ur sur des blocs de P é lém e nts de type double), e t non pas
sim plem e nt du type double*. Ildoit donc ê tre conve rti dans le type double *, ce tte conve rsion ne m odifiant pas, e n fait,
l'adre s s e corre s pondante (re voye z é ve ntue llem e nt les com m e ntaires de l'e xe rcice V.5 de la pre m iè re partie de ce t
ouvrage ). Ce tte conve rsion q ue lque pe u fictive pe ut soit ê tre m ise en place autom atiq ue m e nt par le com pilate ur, au vu du
VIII. Analy s e num é riq u e 233

prototype , soit ê tre e xplicité e à l'aide d'un opé rate ur de "cast" ;ce tte derniè re façon de faire a souve nt le m é rite d'évite r
des m e s s ages d'ave rtissem e nt inte m pe s tifs ("w arnings").

*Note z q ue , dans la définition de la fonction prodm at, nous avons dû te nir com pte de la m aniè re dont le langage C range
e n m é m oire les é lém e nts d'un tableau à deux dim e nsions (suivant ce q u'on nom m e abusive m e nt les "ligne s " du tableau,
c'e s t-à -dire s uivant l'ordre obte nu e n faisant varie r e n pre m ie r le dernie r indice ). Plus précisém e nt :

- Le sym bole aik re pré s e nte un pointe ur courant sur les é lém e nts a . Pour ch aq ue valeur de i, aik e s t initialisé à
ik
l'adresse du pre m ie r é lém e nt de la ligne i de la m atrice a (a+i*p) e t ile s t incré m e nté d'une colonne , e n m ê m e te m ps
q ue l'indice k (d'où la pré s e nce de aik ++ dans la boucle e n k ).
- D e m ê m e , bk j re pré s e nte un pointe ur courant sur les é lém e nts b . Pour ch aq ue valeur de j, bk j e s t initialisé à
kj
l'adresse du pre m ie r é lém e nt de la colonne j de la m atrice b (b+j) e t ile s t incré m e nté d'une ligne e n m ê m e te m ps
q ue l'indice k (d'où la pré s e nce de bkj=bkj+q dans la boucle e n k ).
- Enfin, cij re pré s e nte un pointe ur courant sur les é lém e nts c . Ile s t initialisé à l'adresse du pre m ie r é lém e nt de la
ij
m atrice c. Ilprogresse de 1 à ch aq ue tour de la boucle la plus inte rne e n j (note z q u'iln'e n aurait pas é té ainsi si nous
avions inve rs é les deux boucles e n i e t j).

D ISCUSSIO N

*O n a souve nt te ndance à dire q u'une fonction com m e prod_m at travaille s ur de s m atrices de dim e nsions variables . En
fait, le te rm e e s t q ue lque pe u am bigu. Ainsi, dans notre e xe m ple, les m atrices dont on transm e t l'adre s s e e n argum e nt à
prod_m at ont une taille bien déte rm inée dans le program m e principal. Iln'e n re s te pas m oins q ue :

- d'une part, la m ê m e fonction pe ut travailler sur des m atrices de tailles diffé re nte s ,
- d'autre part, rie n n'e m pê ch e rait q u'au s e in du program m e principal, les m atrice s a, b e t c voie nt leur taille définie
uniq ue m e nt lors de l'e xé cution e t leurs e m place m e nts alloués dynam iq ue m e nt.

*Au sein d'une fonction com m e prod_m at, ile s t possible d'em ploye r le form alism e des tableaux à la place de ce lui de s
pointe urs e n faisant appe là un artifice . Ce lui-ci consiste à cré e r, pour ch aq ue m atrice , un tableau de pointe urs conte nant
l'adresse de début de ch aq ue ligne . Ainsi, par e xe m ple, pour la m atrice a, on pourrait ré s e rve r un tableau nom m é ada
par :

double * * ada ;

Ils e rait re m pli de la m aniè re s uivante :

for (i=1 ; i<n ; i++)


ada[i] = a + i*p ;
234 Exe rcice s e n langage C

D ans ce s conditions, e n e ffe t, la notation ada [i] [j] corre s pondrait (com pte te nu de l'associativité de gauch e à droite de
l'opé rate ur []) à :

(ada [i]) [j]

c'e s t-à -dire à :

* (ada [i] + j)

Autre m e nt dit, ce tte notation ada [i] [j] désignerait sim plem e nt la val
eur de l'é lém e nt situé à l'inte rs e ction de la ligne i e t
de la colonne j de la m atrice a.

O n note ra q ue pour q ue ce t artifice s oit utilisable au s e in d'une fonction com m e prod_m at, ce ns é e travailler sur des
m atrices de taille q ue lconq ue , ile s t né ce s s aire q ue les e m place m e nts des tableaux de pointe urs te ls q ue ada soient alloué s
dynam iq ue m e nt.

VIII-2 A rith m é tiq ue com pl


e xe

________________________________________________________________________________________

Enoncé

Ecrire deux fonctions calculant la som m e e t le produit de deux nom bre s com plexe s . Ces dernie rs s e ront re pré s e nté s par
une s tructure com portant deux élém e nts de type double, corre s pondant à la partie ré e lle e t à la partie im aginaire .

Ch acune de ce s fonctions com porte ra trois argum e nts :

- l'adresse des deux nom bre s com plexe s (structure s ) conce rné s ,
- l'adresse du ré s ultat (structure ).
Un program m e principalpe rm e ttra de te s te r ces deux fonctions ave c les valeurs com plexe s :

0,5 + i
1+ i
VIII. Analy s e num é riq u e 235

Exe m pl
e

0.500000 + 1.000000 i et 1.000000 + 1.000000 i


ont pour somme 1.500000 + 2.000000 i
et pour produit -0.500000 + 1.500000 i

________________________________________________________________________________________

ANALYSE

Soit deux nom bre s com plexe s :

x = a + ib
y = c + id
O n sait q ue :

x + y = (a+ c) + i (b+ d)
e t q ue :

x y = (ac - bd) + i (ad + bc)

Program m e

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 Exe rcice s e n 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 ;
}

Com m e ntaire s

*Nous avons défini, à un nive au global, un m odè le de structure nom m é com plexe .

*Note z bie n q ue , dans le program m e principal, l'accè s à une s tructure s e fait par l'opé rate ur "." (com m e dans z1.re e l)
car z1 désigne ici la val eur d'une s tructure ;par contre , dans les fonctions, ils e fait par l'opé rate ur -> (com m e dans x-
>re e l) car x dé s igne alors l 'adresse d'une s tructure . O n pe ut toute fois é vite r l'e m ploi de ce t opé rate ur, e n re m arq uant
q ue x-> re e le s t é q uivalent à (*x).re e l.

*En toute rigue ur, d'aprè s la norm e ANSI, ile s t possible de transm e ttre , e n argum e nt d'une fonction, la valeur d'une
structure . Aussi, aurions-nous pu pré voir q ue som m e e t produit re çoive nt les valeurs des com plexe s s ur les q ue ls porte
l'opé ration. En re vanch e , le ré s ultat devrait toujours ê tre transm is par valeur puis q ue déte rm iné par la fonction e lle-
m ê m e . Par e xe m ple, la définition de som m e aurait pu ê tre :

void somme (complexe x, complexe y, complexe * som)


{
prod->reel = x.reel + y.reel ;
prod->imag = x.imag + y.imag ;
}
VIII. Analy s e num é riq u e 237

D ISCUSSIO N

D ans la pratiq ue , les fonctions som m e e t produit s e raie nt com pilée s s é paré m e nt des fonctions les utilisant. Pour ce faire ,
ile s t né ce s s aire q u'e lles disposent de la description de la structure com plexe . O n voit q u'on ris q ue alors d'ê tre am e né à
décrire une m ê m e s tructure à diffé re nte s re pris e s . Ce rte s , ici la ch os e n'e s t pas bien grave , dans la m e s ure où ce tte
définition e s t sim ple. D'une m aniè re gé né rale, toute fois, on a tout inté rê t à ré gler ce type de problèm e e n plaçant une fois
pour toute s une te lle définition dans un fich ie r (d'e xte nsion h , par e xe m ple) q u'on incorpore par #include dans tous les
program m e s e n ayant besoin.

VIII-3 Produit de m atrice s com pl


e xe s

________________________________________________________________________________________

Enoncé

Ecrire une fonction calculant le produit de deux m atrice s com plexe s . Ch aq ue m atrice s e ra dé finie com m e un tableau à
deux dim e nsions dans leq ue lch aq ue é lém e nt s e ra une s tructure re pré s e ntant un nom bre com plexe ;ce tte s tructure s e ra
constituée de deux é lém e nts de type double corre s pondant à la partie ré e lle e t à la partie im aginaire du nom bre . O n
supposera q ue le pre m ie r indice du tableau re pré s e ntant une m atrice corre s pond à une ligne .

O n pré voira e n argum e nts :

- les adresses des deux m atrice s à m ultiplie r,


- l'adresse de la m atrice produit,
- le nom bre de ligne s e t de colonnes de la pre m iè re m atrice ,
- le nom bre de colonnes de la deuxiè m e m atrice (son nom bre de ligne s é tant obligatoire m e nt é galau nom bre de
colonnes de la pre m iè re ).
O n ré alisera un program m e principalpe rm e ttant de te s te r ce tte fonction.
O n pourra é ve ntue llem e nt faire appe laux fonctions som m e e t produit ré alisées dans l'e xe rcice VIII-2 pour calculer la
som m e e t le produit de deux nom bre s com plexe s .

Exe m pl
e

MATRICE A
0+ 0i 1+ 2i 2+ 4i 3+ 6i
238 Exe rcice s e n 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
-14+ 42i -32+ 66i -50+ 90i
-14+ 54i -36+ 90i -58+ 126i
-14+ 66i -40+ 114i -66+ 162i
-14+ 78i -44+ 138i -74+ 198i
-14+ 90i -48+ 162i -82+ 234i

________________________________________________________________________________________

ANALYSE

Le s form ules de définition du produit de m atrice s com plexe s re s te nt ce lles proposées dans l'analyse de l'e xe rcice VIII-1
pour les m atrice s ré e lles ;ilsuffit d'y re m place r les opé rations + e t x portant sur des réels par les opé rations som m e e t
produit de deux com plexe s (les rè gles de ces deux opé rations ont é té e xposées dans l'analyse de l'e xe rcice VIII-2).

Program m e

#define N 5
#define P 4
#define Q 3

typedef struct
{ double reel ;
double imag ;
} complexe ;
VIII. Analy s e num é riq u e 239

main()
{
void prod_mat (complexe *, complexe *, complexe *, int, int, int) ;
complexe 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].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 Exe rcice s e n 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 ;
}
VIII. Analy s e num é riq u e 241

Com m e ntaire s

La fonction prod_m at pe ut ê tre considérée com m e une adaptation de la fonction prod_m at de l'e xe rcice VIII-1. Ce tte
fois, les sym boles aik , bk j e t cij désignent, non plus des pointe urs sur de s ré e ls, m ais des pointe urs sur une s tructure
re pré s e ntant un nom bre com plexe . La souplesse du langage C e n m atiè re d'opérations arith m é tiq ue s s ur les pointe urs fait
q ue les instructions d'incré m e ntation de ce s q uantité s re s te nt les m ê m e s (l'unité é tant ici la structure com plexe - soit 2
é lém e nts de type double, au lie u d'une valeur de type double).

D ISCUSSIO N

Le s re m arq ue s faites dans l'e xe rcice VIII-2, à propos de la description de la structure com plexe re s te nt nature llem e nt
valables ici.

VIII-4 Re ch e rch e de zé ro d'une fonction par dich otom ie

________________________________________________________________________________________

Enoncé

Ecrire une fonction dé te rm inant, par dich otom ie , le zé ro d'une fonction q ue lconq ue (ré e lle d'une variable ré e lle e t
continue ). O n supposera connu un inte rvalle [a,b] sur leq ue lla fonction ch ange de signe, c'est-à -dire te lq ue f(a).f(b) soit
né gatif.

O n pré voira e n argum e nts :

- les valeurs des bornes a e t b (de type double) de l'inte rvalle de départ,
- l'adresse d'une fonction pe rm e ttant de calculer la valeur de f pour une valeur q ue lconq ue de la variable. O n
supposera q ue l'e n-tê te de ce tte fonction e s t de la form e :
double fct (x)
double x ;
242 Exe rcice s e n langage C

- l'adresse d'une variable de type double destiné e à re cue illir la valeur approch ée du zé ro de f,
- la valeur de la pré cision (absolue ) souh aité e (de type double).
Le code de re tour de la fonction s e ra de -1 lors q ue l'inte rvalle fourni e n argum e nt ne convie nt pas, c'e s t-à -dire :

- soit lors q ue la condition a<b n'est pas satisfaite ,


- soit lors q ue la condition f(a).f(b)< 0 n'e s t pas satisfaite .
D ans le cas contraire , le code de re tour s e ra é galà 0.

Un program m e principalpe rm e ttra de te s te r ce tte fonction.

Exe m pl
e

zéro de la fonction sin entre -1 et 1 à 1e-2 près = 0.000000e+000


zéro de la fonction sin entre -1 et 2 à 1e-2 près = 1.953125e-003
zéro de la fonction sin entre -1 et 2 à 1e-12 près = -2.273737e-013

________________________________________________________________________________________

ANALYSE

La dém arch e consiste donc, aprè s avoir vé rifié q ue l'inte rvalle re çu e n argum e nt é tait conve nable, à ré pé te r le traite m e nt
suivant :

- pre ndre le m ilie u m de [a,b] : m = (a+ b)/2


- calculer f(m ),
- si f(m ) = 0, le zé ro e s t e n m ,
- si f(a).f(m )< 0, ile xiste un zé ro sur [a,m ] ;on re m place donc l'inte rvalle [a,b] par [a,m ] e n faisant :
b=m
- si f(a).f(m )> 0, ile xiste un zé ro sur [b,m ] ;on re m place donc l'inte rvalle [a,b] par [b,m ], e n faisant :
a=m
Le traite m e nt e s t inte rrom pu soit lors q ue l'inte rvalle [a,b] aura é té s uffisam m e nt réduit, c'e s t-à -dire lors q ue |b-a| est
infé rie ur à la pré cision souh aité e , soit lors q ue le zé ro a é té localisé exacte m e nt (f(m )=0).
VIII. Analy s e num é riq u e 243

Program m e

#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 Exe rcice s e n 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) ;
}

Com m e ntaire s

*Note z, dans la fonction dich oto :

- la déclaration de l'argum e nt corre s pondant à l'adresse de la fonction dont on ch e rch e le zé ro :


double (*f)(double)
Ce lle-ci s'inte rprè te com m e s uit :
(*f) e s t une fonction re ce vant un argum e nt de type double e t fournissant un ré s ultat de type double,
*f e s t donc une fonction re ce vant un argum e nt de type double e t fournissant un ré s ultat de type double,
f e s t donc un pointe ur sur une fonction re ce vant un argum e nt de type double e t fournissant un ré s ultat de type
double.
- l'utilisation du sym bole f ;ainsi (*f)(a) re pré s e nte la valeur de la fonction (*f) (fonction d'adre s s e f), à laq ue lle on
fournit l'argum e nt a.
Le s m ê m e s ré flexions s'appliq ue nt au prototype s e rvant à déclare r dich oto.

*La fonction dich oto re ce vant e n argum e nt les val eurs des argum e nts a e t b (et non de s adresses), nous pouvons nous
pe rm e ttre de les m odifie r au s e in de la fonction, sans q ue ce la ait d'incide nce s ur les valeurs e ffe ctives des borne s
définies dans le program m e principal.

*Voye z com m e nt, dans le program m e principal, un sym bole com m e sin e s t inte rpré té par le com pilate ur com m e
l'adresse d'une fonction prédéfinie ;ile s t toute fois néce s s aire d'avoir incorporé s on prototype (situé dans m ath .h ) ;e n
VIII. Analy s e num é riq u e 245

l'abs e nce de l'instruction #include corre s pondante , le com pilate ur dé te cte rait un e rre ur puis q ue alors le sym bole sin ne
s e rait pas défini.

D ISCUSSIO N

En th é orie , la m é th ode de dich otom ie conduit toujours à une s olution, ave c une pré cision aussi grande q u'on le désire , à
partir du m om e nt où la fonction ch ange e ffe ctive m e nt de signe sur l'inte rvalle de départ. En pratiq ue , toute fois, les
ch os e s ne s ont pas toujours aussi idylliq ue s , com pte te nu de la lim itation de la pré cision des calculs.

Tout d'abord, si on im pos e une pré cision trop faible par rapport à la pré cision de l'ordinate ur, on pe ut aboutir à ce q ue :

m = (a+b)/2

soit é galà l'une des deux bornes a ou b. Ile s t alors facile de m ontre r q ue l'algorith m e pe ut boucler indé finim e nt.

D 'autre part, les valeurs de f(a) e t de f(b) sont né ce s s aire m e nt é valué e s d e m aniè re approch é e . Dans le cas de form ules
q ue lque pe u com plexe s , on pe ut trè s bien aboutir à une s ituation dans laq ue lle f(a).f(b) e s t positif.

La pre m iè re s ituation e s t as s e z facile à é vite r : ilsuffit de ch oisir une pré cision re lative (atte ntion, ici, notre fonction
travaille ave c une pré cision absolue ) infé rie ure à ce lle de l'ordinate ur. Iln'e n va pas de m ê m e pour la s e conde dans la
m e s ure où iln'e s t pas toujours possible de m aî tris e r la pré cision des calculs des valeurs de f.

Вам также может понравиться