Академический Документы
Профессиональный Документы
Культура Документы
C. Delannoy
2 Exe rcice s e n langage C
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 .
- 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 .
___________________________________________________________________________
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 */
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 *;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 .
___________________________________________________________________________
Enoncé
char c = '\x01' ;
short int p = 10 ;
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.
___________________________________________________________________________
Enoncé
char c = '\x05' ;
int n = 5 ;
long p = 1000 ;
float x = 1.25 ;
double z = 5.5 ;
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.
___________________________________________________________________________
Enoncé
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
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
___________________________________________________________________________
Enoncé
#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
___________________________________________________________________________
Enoncé
#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
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
___________________________________________________________________________
Enoncé
#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
___________________________________________________________________________
Enoncé
#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 :
___________________________________________________________________________
Enoncé
#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 :
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 ).
___________________________________________________________________________
Enoncé
Quelles s e ront les valeurs lues dans les variables n e t p (de type int), par l'instruction suivante :
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 .
___________________________________________________________________________
II. Le s e ntré e s -sortie s conve rsationne lles 15
Enoncé
#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 :
___________________________________________________________________________
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
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 ).
___________________________________________________________________________
Enoncé
#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") ;
}
}
a) 0
b) 1
c) 4
d) 10
e) -5
___________________________________________________________________________
Sol
ution
a)
Nul
Petit
b)
Petit
c)
Moyen
Grand
d)
Grand
e)
Grand
___________________________________________________________________________
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
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 :
ou :
c) Iln'y aura pas d'erreur de com pilation ;toute fois, ils'agit d'une "boucle infinie ".
___________________________________________________________________________
Enoncé
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) ;
___________________________________________________________________________
Enoncé
#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) ;
}
___________________________________________________________________________
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é
#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é
#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
___________________________________________________________________________
Enoncé
#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
___________________________________________________________________________
26 Exe rcice s e n langage C
Enoncé
#include <stdio.h>
main()
{ int 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 :
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
#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) ;
#include <stdio.h>
#include <math.h>
main()
{ double x ;
do
{ printf ("donnez un nombre positif : ") ;
scanf ("%le", &x) ;
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 :
___________________________________________________________________________
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 :
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)..
___________________________________________________________________________
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>
main()
{ int nlignes ; /* nombre total de lignes */
int nl ; /* compteur de ligne */
int nesp ; /* nombre d'espaces précédent une étoile */
int j ;
___________________________________________________________________________
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
___________________________________________________________________________
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") ;
}
___________________________________________________________________________
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) ;
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 .
___________________________________________________________________________
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 :
_______________________________________________________________
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 */
___________________________________________________________________________
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 ;
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.
___________________________________________________________________________
Enoncé
#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 ;
}
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
int fct () ;
ou, é ve ntue llem e nt, sous form e d'un prototype "com plet" :
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).
___________________________________________________________________________
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
}
___________________________________________________________________________
Enoncé
#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() ;
}
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
___________________________________________________________________________
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>
main()
{
float oper (float, float, char) ; /* prototype de oper */
float x, y ;
___________________________________________________________________________
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 */
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>
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>
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
___________________________________________________________________________
Enoncé
#include <stdio.h>
main()
{
int t [3] ;
int i, j ;
int * adt ;
for (adt = t ; adt < t+3 ; adt++) printf ("%d ", *adt) ; /* 4 */
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 :
0 2 4
0 2 4
0 2 4
4 2 0
___________________________________________________________________________
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 :
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] ;
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] ;
max = min = *t ;
for (i=1 ; i<NVAL ; i++)
{ if (*(t+i) > max) max = *(t+i) ;
if (*(t+i) < min) min = *(t+i) ;
}
___________________________________________________________________________
Enoncé
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 ;
___________________________________________________________________________
Enoncé
#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 à :
10 20 30 40
30 21
___________________________________________________________________________
Enoncé
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 :
Sol
ution
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.
int i ;
int * adt ;
som = 0 ;
adt = (float *) t ;
for (i=0 ; i<12 ; i++)
som += * (adt+i);
___________________________________________________________________________
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.
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 :
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 ).
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 :
#include <stdio.h>
main()
___________________________________________________________________________
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 .
_______________________________________________________________
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 ) :
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 :
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 :
#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.
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".
#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
___________________________________________________________________________
Enoncé
#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".
bonjour
monsieur
printf ("bonjour\nmonsieur\n") ;
___________________________________________________________________________
Enoncé
#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".
bon
bonjour
___________________________________________________________________________
Enoncé
_______________________________________________________________
Sol
ution
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++)) ;
}
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 .
___________________________________________________________________________
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 :
___________________________________________________________________________
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.
_______________________________________________________________
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).
#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) */
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!
___________________________________________________________________________
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 é :
ne = 0 ;
i = 0 ;
while (ligne[i]) if (ligne[i++] == 'e') ne++ ;
___________________________________________________________________________
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.
#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") ) ;
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 .
___________________________________________________________________________
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.
_______________________________________________________________
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>
main()
{
char ligne [LG_LIG+1] ; /* pour lire une ligne +1 pour \0 */
char * adr ; /* pointeur à l'intérieur de la ligne */
___________________________________________________________________________
Enoncé
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
D ans les deux cas, on é crira un pe tit program m e d'essai de la fonction ainsi réalisée.
_______________________________________________________________
Sol
ution
#include <stdio.h>
74 Exe rcice s e n langage C
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 .
#include <stdio.h>
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.
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} ;
___________________________________________________________________________
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 ;
} ;
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) ;
}
___________________________________________________________________________
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 .
_______________________________________________________________
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) ;
}
___________________________________________________________________________
Enoncé
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
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 :
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 :
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 :
#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) ;
___________________________________________________________________________
Enoncé
_______________________________________________________________
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>
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]) ;
}
___________________________________________________________________________
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 :
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.
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é .
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 :
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 .
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 :
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 :
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.
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.
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 */
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
________________________________________________________________________________________
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 */
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 :
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 .
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).
________________________________________________________________________________________
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
_________________
________________________________________________________________________________________
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 ;
/* 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.
Exe m pl
es
________________________________________________________________________________________
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 ;
/* 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 ;
}
}
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 :
*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.
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++]
* (mot + i++)
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 :
*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. 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
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 :
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 ;
/* 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
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 ;
/* 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.
________________________________________________________________________________________
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
_______________________________________________________________________________________
ANALYSE
Com m e dans l'e xe rcice pré cédent, ile xiste (au m oins) deux dém arch e s possibles :
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 :
- 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é */
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
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 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).
________________________________________________________________________________________
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 :
Exe m pl
es
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.
- 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
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 .
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 :
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 :
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.
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 :
Tabl
e au de s code s m ors e s
Exe m pl
e
donnez votre message (1 ligne maxi) :
LE LANGAGE C, CONCU EN 1972, EST L'OEUVRE DE DENIS RITCHIE.
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 ;
{ 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 :
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 ".
________________________________________________________________________________________
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
________________________________________________________________________________________
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 :
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 ;
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 :
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 .
________________________________________________________________________________________
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 .
- 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
FACTURE
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>
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 */
/* 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
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
________________________________________________________________________________________
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 */
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 ;
/********************************************************/
/* 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 )
________________________________________________________________________________________
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 */
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 ;
/********************************************************/
/* 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 */
/* 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 */
/*******************************************************/
/* 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 */
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 :
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 .
________________________________________________________________________________________
Enoncé
Exe m pl
e
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 ;
pi = (4.0 * nc) / np ;
III. H asard e t ré cré ations 145
printf ("estimation de pi avec %d points : %e", np, pi) ;
}
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
________________________________________________________________________________________
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.
Program m e
#include <stdio.h>
#include <stdlib.h>
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 ;
/* 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 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.
Program m e
III. H asard e t ré cré ations 151
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
main()
{
void tirage (int []) ; /*****************************/
int entree (int []) ; /* prototypes fonctions */
void analyse(int [], int[], int[], int []) ; /*****************************/
/* 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 ;
/* 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) ;
/**************************************************/
/* 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 ;
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.
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.
________________________________________________________________________________________
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 :
Exe m pl
e
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
________________________________________________________________________________________
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 :
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 ;
Com m e ntaire s
________________________________________________________________________________________
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 :
Exe m pl
e
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
________________________________________________________________________________________
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 ;
/**************************************************/
/* 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
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 .
________________________________________________________________________________________
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 ).
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
________________________________________________________________________________________
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 :
#include <stdio.h>
#include <string.h>
i, j ;
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 :
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!).
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]
* (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 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
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 .
Exe m pl
e
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 :
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
*/
/********************************************************/
/* 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) */
/*******************************************************/
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
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
________________
________________________________________________________________________________________
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 :
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 é ) :
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 ;
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é */
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 ;
}
Com m e ntaire s
- 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 .
Exe m pl
e
________________________________________________________________________________________
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 */
/* 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 :
dans laq ue lle raye e s t un pointe ur sur des entie rs non signé s .
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 :
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 :
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 .
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.
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
fin création
________________________________________________________________________________________
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.
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 ;
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") ;
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.
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.
________________________________________________________________________________________
Enoncé
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
fin création
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 :
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 ;
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 ).
________________________________________________________________________________________
Enoncé
- 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>
main()
{
void creation (t_element * *) ; /* fonction de création de la liste */
void liste (t_element *) ; /* fonction de liste 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 */
/******************************************************/
/* 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 :
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>
}
}
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
- 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
________________________________________________________________________________________
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>
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.
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").
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.
________________________________________________________________________________________
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 .
- 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
____________________
________________________________________________________________________________________
ANALYSE
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 :
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 */
Com m e ntaire s
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 .
________________________________________________________________________________________
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
_______________________
________________________________________________________________________________________
ANALYSE
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 */
Com m e ntaire s
plutôt q ue :
________________________________________________________________________________________
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 :
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 */
/***********************************************************/
/* fonction récursive de calcul de la fonction d'Ackermann */
/***********************************************************/
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) ;
}
________________________________________________________________________________________
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 :
Program m e
#include <stdio.h>
main()
{
void hanoi (int, int, int, int) ;
int nd ; /* nombre total de disques */
/***********************************************/
/* 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 .
________________________________________________________________________________________
Enoncé
Exe m pl
e
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 :
________________________________________________________________________________________
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 .
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 ;
/* fin création */
fclose(sortie) ;
printf ("\n -------- FIN CREATION FICHIER ----------") ;
}
Com m e ntaire s
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 :
*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 .
________________________________________________________________________________________
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.
Exe m pl
e
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
________________________________________________________________________________________
Program m e
#include <stdio.h>
#include <string.h>
/* 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 */
/*************************************************/
Com m e ntaire s
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 :
*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) ;
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 :
________________________________________________________________________________________
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.
- 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
enregistrement numéro : 2
NOM : dunoyer
AGE : 29
NOMBRE D'ENFANTS : 0
________________________________________________________________________________________
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>
main()
{
VII. Traite m e nt de fich ie rs 219
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) ;
/* fin modifications */
fclose(fichier) ;
printf ("\n\n -------- FIN MODIFICATIONS FICHIER ----------\n") ;
}
/*************************************************/
/* fonction d'affichage d'un enregistrement */
/*************************************************/
/***************************************************/
/* fonction de modification d'un enregistrement */
/***************************************************/
gets (ligne) ;
if (strlen(ligne)) bloc->nbenf = 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)
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.
________________________________________________________________________________________
Enoncé
- 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
........
________________________________________________________________________________________
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 :
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 :
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 */
/* initialisations */
for (i=0 ; i<26 ; i++)
compte[i] = 0 ;
ntot = 0 ; nautres = 0 ;
nmots = 0 ;
nlignes = 0 ;
mot_en_cours = FAUX ;
/* 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" */
/*********************************************************/
i = 0 ;
while ( c!=sep[i] && i<nsep ) i++ ;
if (i == nsep) return (0) ;
else return (1) ;
}
Com m e ntaire s
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 :
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 :
________________________________________________________________________________________
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 .
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
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 ;
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)
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.
________________________________________________________________________________________
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 .
- 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
________________________________________________________________________________________
ANALYSE
x = a + ib
y = c + id
O n sait q ue :
x + y = (a+ c) + i (b+ d)
e t q ue :
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
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 :
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.
________________________________________________________________________________________
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 .
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 */
/*********************************************************/
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 ;
}
}
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.
________________________________________________________________________________________
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.
- 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 :
Exe m pl
e
________________________________________________________________________________________
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 :
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 */
int dichoto ( double (* f)(double), double a, double b, double * zero, double eps)
fa = (*f)(a) ;
fb = (*f)(b) ;
if (fa*fb >= 0 || a >= b) return (-1) ; /* intervalle incorrect */
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
*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.