Académique Documents
Professionnel Documents
Culture Documents
Exercices en Langage C
Exercices en Langage C
C. Delannoy
Ce tte pre m i re partie vous propose des exercices, r s oudre , de prf re nce , pe ndant la ph ase d'tude du langage C luim 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 contrle ;les fonctions ;les tableaux e t les pointe urs ;
les ch a
nes de caract re s ;les s tructure s .
Ch aq ue ch apitre com porte :
- des exe rcices d'application im m diate destin s facilite r l'assim ilation du cours corre s pondant,
- des exe rcice s , sans grande difficult algorith m iq ue m e ttant e n oe uvre les diff re nte s notions acq uis e s au cours des
pr cdents 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 abords 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 .
I : TYPES D E BASE,
O PERA TEURS
ET EXPRESSIO NS
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)
a = (x=y) + 2
a = (x==y)
(a<b) && (c<d)
(i++) * (n+p)
/*
/*
/*
/*
/*
expression
expression
expression
expression
expression
1
2
3
4
5
*/
*/
*/
*/
*/
___________________________________________________________________________
Sol
ution
a = x+5
/* expression 1 */
/* 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 */
/* expression 4 */
/* expression 5 */
L'op rate ur + + e s t prioritaire s ur *;e n re vanch e , *e s t prioritaire s ur + ;de sorte q u'on ne pe ut lim ine r les derni re s
pare nth s e s .
Enonc
Soie nt les dclarations :
char c = '\x01' ;
short int p = 10 ;
+
+
+
*
3
1
c
p + 5 * c
/* 1 */
/* 2 */
/* 3 */
/* 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.
Enonc
Soie nt les dclarations :
char c = '\x05' ;
int n = 5 ;
long p = 1000 ;
float x = 1.25 ;
double z = 5.5 ;
/*
/*
/*
/*
1
2
3
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 cdent. 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 procder 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 prcise 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 dfinition 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
Soie nt les dclarations suivante s :
int n = 5, p = 9 ;
int q ;
float x ;
Quelle e s t la valeur affe ct e aux diff re nte s variables conce rn e s par ch acune des instructions suivante s :
q
q
q
x
x
x
x
q
q
=
=
=
=
=
=
=
=
=
n < p ;
n == p ;
p % n + p > n ;
p / n ;
(float) p / n ;
(p + 0.5) / n ;
(int) (p + 0.5) / n ;
n * (p > n ? n : p) ;
n * (p < n ? n : p) ;
/*
/*
/*
/*
/*
/*
/*
/*
/*
1
2
3
4
5
6
7
8
9
*/
*/
*/
*/
*/
*/
*/
*/
*:
___________________________________________________________________________
Sol
ution
1) 1
2) 0
3) 5 (p%n vaut 4, tandis q ue p> n vaut 1)
4) 1 (p/n e s t d'abord valu e n int, ce q ui fournit 1 ;puis le r s ultat e s t conve rti e n float, avant d' tre affe ct x).
5) 1.8 (p e s t conve rti e n float, avant d' tre divis par le r s ultat de la conve rsion de n en float).
6) 1.9 (p e s t conve rti e n float, avant d' tre ajout 0.5 ;le r s ultat e s t divis par le r s ultat de la conve rsion de n en
float).
7) 1 (p e s t conve rti e n float, avant d' tre ajout 0.5 ;le r s ultat (5.5) e s t alors conve rti e n int avant d' tre divis par n).
8) 25
9 ) 45
Enonc
Quels r s ultats fournit le program m e s uivant :
#include <stdio.h>
main ()
{
int i, j, n ;
i = 0 ; n = i++ ;
printf ("A : i = %d
n = %d \n", i, n ) ;
i = 10 ; n = ++ i ;
printf ("B : i = %d
n = %d \n", i, n ) ;
___________________________________________________________________________
Sol
ution
A
B
C
D
E
:
:
:
:
:
i
i
i
i
i
=
=
=
=
=
1 n = 0
11 n = 11
21 j = 6 n = 120
18 n = 18
12 j = 12 n = 6
Enonc
Quels r s ultats fournira ce program m e :
#include <stdio.h>
main()
{
int n=10, p=5, q=10, r ;
r = n == (p = q) ;
printf ("A : n = %d
p = %d
q = %d
n = p = q = 5 ;
n += p += q ;
printf ("B : n = %d
p = %d
q = %d\n", n, p, q) ;
r = %d\n", n, p, q, r) ;
q = %d\n", n, p, q) ;
q = %d\n", n, p, q) ;
___________________________________________________________________________
Sol
ution
A
B
C
D
:
:
:
:
n
n
n
n
=
=
=
=
10
15
15
16
p
p
p
p
=
=
=
=
10
10
11
11
q
q
q
q
=
=
=
=
10
5
10
15
r = 1
Enonc
Quels r s ultats fournira ce program m e :
#include <stdio.h>
main()
{
int n, p, q ;
n = 5 ; p = 2 ;
q = n++ >p || p++ != 3 ;
printf ("A : n = %d p = %d
n = 5 ; p = 2 ;
q = n++<p || p++ != 3 ;
printf ("B : n = %d p = %d
n = 5 ; p = 2 ;
/* cas 1 */
q = %d\n", n, p, q) ;
/* cas 2 */
q = %d\n", n, p, q) ;
/* cas 3 */
10
q = %d\n", n, p, q) ;
/* cas 4 */
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
B
C
D
:
:
:
:
n
n
n
n
=
=
=
=
6
6
6
6
p
p
p
p
=
=
=
=
2
3
2
3
q
q
q
q
=
=
=
=
1
1
0
1
Enonc
Quels s e ront les r s ultats fournis par ce program m e :
#include <stdio.h>
main ()
{
int n = 543 ;
int p = 5 ;
float x = 34.5678;
printf ("A : %d %f\n", n, x) ;
printf ("B : %4d %10f\n", n, x) ;
printf ("C : %2d %3f\n", n, x) ;
printf ("D : %10.3f %10.3e\n", x, x) ;
printf ("E : %-5d %f\n", n, x) ;
printf ("F : %*d\n", p, n) ;
printf ("G : %*.*f\n", 12, 5, x) ;
printf ("H : %x : %8x :\n", n, n) ;
printf ("I : %o : %8o :\n", n, n) ;
}
_______________________________________________________________
Sol
ution
A : 543 34.567799
B : 543 34.567799
12
:
:
:
:
:
:
:
543 34.567799
34.568 3.457e+01
543
34.567799
543
34.56780
21f :
21f :
1037 :
1037 :
Enonc
Quels s e ront les r s ultats fournis par ce program m e :
#include <stdio.h>
main()
{
char c ;
int n ;
c = 'S' ;
printf ("A : %c\n", c)
n = c ;
printf ("B : %c\n", n)
printf ("C : %d %d\n",
printf ("D : %x %x\n",
}
;
;
c, n) ;
c, n) ;
_______________________________________________________________
Sol
ution
A
B
C
D
:
:
:
:
S
S
83 83
53 53
13
Enonc
Quelles s e ront les valeurs lues dans les variables n e t p (de type int), par l'instruction suivante :
scanf ("%d %d", &n, &p) ;
lors q u'on lui fournit les donnes 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 utiliss 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 :
scanf ("%4d %2d", &n, &p) ;
lors q u'on lui fournit les donnes 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
a)
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 considrs 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 ptabiliss dans la longue ur im pos e .
15
Enonc
Soit le program m e s uivant :
#include <stdio.h>
main()
{
int n, p ;
do
{ printf ("donnez 2 entiers (0 pour finir) : ") ;
scanf("%4d%2d", &n, &p) ;
printf ("merci pour : %d %d\n", n, p) ;
}
while (n) ;
}
Quels r s ultats fournira-t-il, e n supposant q u'on lui e ntre les donnes suivante s (atte ntion, on supposera q ue les donnes
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 cdent. 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 spcifi s (e t non pas des variables indiq u e s ), e lle e n atte nd de
nouve lles . Voici finalem e nt les r s ultats obte nus :
donnez 2 entiers (0 pour finir)
1 2
merci pour : 1 2
16
finir)
finir)
finir)
finir)
finir)
finir)
finir)
finir)
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
(n)
LIMITE-1 : printf ("un peu moins") ;
LIMITE
: printf ("juste") ;
LIMITE+1 : printf ("un peu plus") ;
_______________________________________________________________
Sol
ution
1) Ilm anq ue un point-virgule la fin du pre m ie r printf :
if (a<b) printf ("ascendant") ;
else printf ("non ascendant") ;
2) Le s valeurs suivant le m ot cas e doive nt obligatoire m e nt tre des "e xpre s s ions constante s ", c'e s t- -dire d e s e xpre s s ions
calculables par le com pilate ur lui-m m e . Ce n'e s t pas le cas de n.
3) Aucune e rre ur, les e xpre s s ions te lles q ue LIM ITE-1 tant bien des expressions constante s .
4) Ici, les e xpre s s ions suivant le m ot cas e ne s ont plus des expre s s ions constante s , car le sym bole LIM ITE a t dfini
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
Soit le program m e s uivant :
#include <stdio.h>
main()
{
int n ;
scanf ("%d", &n) ;
switch (n)
{ case 0 : printf ("Nul\n") ;
case 1 :
Sol
ution
a)
Nul
Petit
b)
Petit
c)
Moyen
Grand
d)
Grand
e)
Grand
19
20
Enonc
Quelles e rre urs ont t com m ises dans ch acune des instructions suivante s :
a)
do c = getchar() while (c != '\n') ;
b)
do while ( (c = getchar()) != '\n') ;
c)
do {} while (1) ;
___________________________________________________________________________
Sol
ution
a) Ilm anq ue un point-virgule :
do c = getchar() ; while (c != '\n') ;
b) Ilm anq ue une instruction ( ve ntue llem e nt "vide") apr s le m ot do. O n pourrait crire , par e xe m ple :
do {} while ( (c = getchar()) != '\n') ;
ou :
do ; while ( (c = getchar()) != '\n') ;
c) Iln'y aura pas d'erreur de com pilation ;toute fois, ils'agit d'une "boucle infinie ".
Enonc
Ecrire plus lisiblem e nt :
do {} while (printf("donnez un nombre >0 "), scanf ("%d", &n), n<=0) ;
___________________________________________________________________________
21
Sol
ution
Plusieurs possibilit s e xiste nt, puis q u'il "suffit" de re porte r, dans le corps de la boucle, des instructions figurant
"artificie llem e nt" sous form e d'expressions dans la condition de poursuite :
do
printf("donnez un nombre >0 ") ;
while (scanf ("%d", &n), n<=0) ;
ou, m ie ux :
do
{ printf("donnez un nombre >0 ") ;
scanf ("%d", &n) ;
}
while (n<=0) ;
Enonc
Soit le pe tit program m e s uivant :
#include <stdio.h>
main()
{ int i, n, som ;
som = 0 ;
for (i=0 ; i<4 ; i++)
{ printf ("donnez un entier ") ;
scanf ("%d", &n) ;
som += n ;
}
printf ("Somme : %d\n", som) ;
}
22
Exe rcice s e n langage C
a) une instruction w h ile,
b) une instruction do ... w h ile.
___________________________________________________________________________
Sol
ution
a)
#include <stdio.h>
main()
{ int i, n, som ;
som = 0 ;
i = 0 ;
/* ne pas oublier cette "initialisation" */
while (i<4)
{ printf ("donnez un entier ") ;
scanf ("%d", &n) ;
som += n ;
i++ ;
/* ni cette "incrmentation" */
}
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 "incrmentation" */
}
while (i<4) ;
/* attention, ici, toujours <4 */
printf ("Somme : %d\n", som) ;
}
Enonc
Quels r s ultats fournit le program m e s uivant :
#include <stdio.h>
main()
{ int n=0 ;
do
{ if (n%2==0) { printf ("%d est pair\n", n) ;
n += 3 ;
continue ;
}
if (n%3==0) { printf ("%d est multiple de 3\n", n) ;
n += 5 ;
}
if (n%5==0) { printf ("%d est multiple de 5\n", n) ;
break ;
}
n += 1 ;
}
while (1) ;
}
___________________________________________________________________________
Sol
ution
0 est pair
3 est multiple de 3
9 est multiple de 3
15 est multiple de 3
20 est multiple de 5
23
24
Enonc
Quels r s ultats fournit le program m e s uivant :
#include <stdio.h>
main()
{
int n, p ;
n=0 ;
while (n<=5) n++ ;
printf ("A : n = %d\n", n) ;
n=p=0 ;
while (n<=8) n += p++ ;
printf ("B : n = %d\n", n) ;
n=p=0 ;
while (n<=8) n += ++p ;
printf ("C : n = %d\n", n) ;
n=p=0 ;
while (p<=5) n+= p++ ;
printf ("D : n = %d\n", n) ;
n=p=0 ;
while (p<=5) n+= ++p ;
printf ("D : n = %d\n", n) ;
}
___________________________________________________________________________
Sol
ution
A
B
C
D
:
:
:
:
n
n
n
n
=
=
=
=
6
10
10
15
Enonc
Quels r s ultats fournit le program m e s uivant :
#include <stdio.h>
main()
{
int n, p ;
n=p=0 ;
while (n<5) n+=2 ; p++ ;
printf ("A : n = %d, p = %d \n", n, p) ;
n=p=0 ;
while (n<5) { n+=2 ; p++ ; }
printf ("B : n = %d, p = %d \n", n, p) ;
}
___________________________________________________________________________
Sol
ution
A : n = 6, p = 1
B : n = 6, p = 3
25
26
Enonc
Quels r s ultats fournit le program m e s uivant :
#include <stdio.h>
main()
{
int i, n ;
for (i=0, n=0 ; i<5 ; i++) n++ ;
printf ("A : i = %d, n = %d\n", i, n) ;
for (i=0, n=0 ; i<5 ; i++, n++) {}
printf ("B : i = %d, n = %d\n", i, n) ;
for (i=0, n=50 ; n>10 ; i++, n-= i ) {}
printf ("C : i = %d, n = %d\n", i, n) ;
for (i=0, n=0 ; i<3 ; i++, n+=i, printf ("D : i = %d, n = %d\n", i, n) ) ;
printf ("E : i = %d, n = %d\n", i, n) ;
}
___________________________________________________________________________
Sol
ution
A
B
C
D
D
D
E
:
:
:
:
:
:
:
i
i
i
i
i
i
i
=
=
=
=
=
=
=
5,
5,
9,
1,
2,
3,
3,
n
n
n
n
n
n
n
=
=
=
=
=
=
=
5
5
5
1
3
6
6
27
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 ngative s . Son e x cution s e pr s e nte ra ainsi :
donnez un nombre
sa racine carre
donnez un nombre
svp positif
donnez un nombre
sa racine carre
donnez un nombre
positif : 2
est : 1.414214e+00
positif : -1
positif : 5
est : 2.236068e+00
positif : 0
R appe lons q ue la fonction s q rt fournit la racine carr e (double) de la valeur (double) q u'on lui fournit e n argum e nt.
___________________________________________________________________________
Sol
ution
Ile xiste beaucoup de "rdactions possibles " ;e n voici 3 :
#include <stdio.h>
#include <math.h>
main()
{ double x ;
do
{ printf ("donnez un nombre positif : ") ;
scanf ("%le", &x) ;
if (x < 0) printf ("svp positif \n") ;
if (x <=0) continue ;
printf ("sa racine carre est : %le\n", sqrt (x) ) ;
}
while (x) ;
}
28
#include <stdio.h>
#include <math.h>
main()
{ double x ;
do
{ printf ("donnez un nombre positif : ") ;
scanf ("%le", &x) ;
if (x < 0) { printf ("svp positif \n") ;
continue ;
}
if (x>0) printf ("sa racine carre est : %le\n", sqrt (x) ) ;
if (x==0) break ;
}
while (1) ;
}
R e m arque :
Ilne faut surtout pas oublie r #include < m ath .h > car, sinon, le com pilate ur consid re (e n l'absce nce du prototype )
q ue s q rt fournit un r s ultat de type int.
Enonc
Calculer la som m e des n pre m ie rs te rm es de la "s rie h arm oniq ue ", c'e s t- -dire la som m e :
1 + 1/2 + 1/3 + 1/4 + ..... + 1/n
La valeur de n s e ra lue e n donn e .
___________________________________________________________________________
Sol
ution
#include <stdio.h>
main()
{
int nt ;
float som ;
int i ;
do
{ printf ("combien de termes : ") ;
scanf ("%d", &nt) ;
}
while (nt<1) ;
for (i=1, som=0 ; i<=nt ; i++) som += (float)1/i ;
printf ("Somme des %d premiers termes = %f", nt, som) ;
}
R e m arques :
1) R appe lons q ue dans :
som += (float)1/i
29
30
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 cdem 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
*
***
*****
*******
*********
***********
*************
***************
31
*****************
*******************
___________________________________________________________________________
Sol
ution
#include <stdio.h>
#define car '*'
/* caractre de remplissage */
main()
{
int
int
int
int
nlignes ;
nl ;
nesp ;
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
F
F
F
F
F
F
F
F
F
F
F
F
F
F
F
F
F
F
F
F
F
F
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
45
40
35
30
25
20
15
10
5
20
45
40
35
10
5
6
10
5
4
5
2
10
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
2c 2
2c 4
2c 6
2c 8
2c 10
2c 12
2c 14
2c 16
2c 18
5c
2c 1
2c 2
2c 4
2c 2
2c 4
5c 7
2c 8
2c 2
5c 8
2c 9
5c 9
10c
X
X
X
X
X
X
X
X
X
5c
5c
5c
5c
5c
5c
5c
5c
5c
X
X
X
X
X
X
X
X
X
X
X
10c
5c
5c
5c
5c
10c
10c
5c
10c
10c
10c
1
1
7
7
X
X
X
X
10c
10c
10c
10c
8 X 10c
___________________________________________________________________________
Sol
ution
#include <stdio.h>
main()
{
int nbf ;
int n10 ;
int n5 ;
int n2 ;
/*
/*
/*
/*
nbf = 0 ;
for (n10=0 ; n10<=10 ; n10++)
for (n5=0 ; n5<=20 ; n5++)
for (n2=0 ; n2<=50 ; n2++)
33
100)
2c ", n2 ) ;
5c ", n5 ) ;
10c", n10) ;
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 ;
int n ;
int i ;
34
/*
autre formulation possible :
/* for (i=3 ; i<=n ; i++, u1=u2, u2=u3) u3 = u1 + u2 ;
*/
*/
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 :
donnez
donnez
donnez
donnez
une
une
une
une
note
note
note
note
(-1
(-1
(-1
(-1
pour
pour
pour
pour
finir)
finir)
finir)
finir)
:
:
:
:
12
8
13
7
une
une
une
une
une
note
note
note
note
note
(-1
(-1
(-1
(-1
(-1
pour
pour
pour
pour
pour
finir)
finir)
finir)
finir)
finir)
:
:
:
:
:
11
12
7
9
-1
_______________________________________________________________
Sol
ution
#include <stdio.h>
main()
{
int
int
int
int
int
note ;
max ;
min ;
nmax ;
nmin ;
/*
/*
/*
/*
/*
note "courante" */
note maxi */
note mini */
nombre de fois o la note maxi a t trouve */
nombre de fois o la note mini a t trouve */
max = -1 ;
/* initialisation max (possible car toutes notes >=0 */
min = 21 ;
/* initialisation min (possible car toutes notes < 21) */
while (printf ("donnez une note (-1 pour finir) : "),
scanf ("%d", ¬e),
note >=0)
{ if (note == max) nmax++ ;
if (note > max) { max = note ;
nmax = 1 ;
}
if (note == min) nmin++ ;
if (note < min) { min = note ;
nmin = 1 ;
}
}
/* attention, si aucune note (cad si max<0) */
/* les rsultats sont sans signification
*/
if (max >= 0)
{ printf ("\nnote maximale : %d attribue %d fois\n", max, nmax) ;
35
36
}
}
Enonc
Ecrire un program m e q ui affich e la "table de m ultiplication" de s nom bres de 1 10, sous la form e s uivante :
I
1
2
3
4
5
6
7
8
9 10
----------------------------------------------1 I
1
2
3
4
5
6
7
8
9 10
2 I
2
4
6
8 10 12 14 16 18 20
3 I
3
6
9 12 15 18 21 24 27 30
4 I
4
8 12 16 20 24 28 32 36 40
5 I
5 10 15 20 25 30 35 40 45 50
6 I
6 12 18 24 30 36 42 48 54 60
7 I
7 14 21 28 35 42 49 56 63 70
8 I
8 16 24 32 40 48 56 64 72 80
9 I
9 18 27 36 45 54 63 72 81 90
10 I 10 20 30 40 50 60 70 80 90 100
_______________________________________________________________
Sol
ution
#include <stdio.h>
#define NMAX 10
/* nombre de valeurs */
main()
{
int i, j ;
/* affichage ligne en-tte */
printf ("
I") ;
for (j=1 ; j<=NMAX ; j++) printf ("%4d", j) ;
37
IV : LES FO NCTIO NS
N.B. Ici, on ne trouve ra aucun e xe rcice faisant inte rve nir de s pointe urs, e t par cons q ue nt aucun e xe rcice m e ttant e n
oe uvre une transm ission d'argum e nts par adre s s e . De te ls e xe rcice s appara
tront dans le ch apitre s uivant.
Enonc
a) Que fournit le program m e s uivant :
#include <stdio.h>
main()
{
int n, p=5 ;
n = fct (p) ;
printf ("p = %d, n = %d\n", p, n) ;
}
int fct (int r)
{ return 2*r ;
}
40
_______________________________________________________________
Sol
ution
a) Bie n q u'ilne poss de pas de dclaration 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 dclar e , auq ue lcas e lle e s t considre com m e fournissant un r s ultat de type int.
Ce tte facilit e s t toute fois forte m e nt dcons e ille (e t e lle ne s e ra plus acce pt e d e C+ + ). Voici les r s ultats fournis par
le program m e :
p = 5, n = 10
b) La dclaration la plus br ve s e ra :
int fct () ;
ou, ve ntue llem e nt, sous form e d'un prototype "com plet" :
int fct (int r) ;
D ans ce dernie r cas, le nom r n'a aucune s ignification : on utilise souve nt le m m e nom (lors q u'on le conna
t!) q ue dans
l'e n-t te de la fonction, m ais ilpourrait s'agir de n'im porte q ue lautre nom de variable).
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 possdera 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 dclar 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
Enonc
Quels r s ultats fournira ce program m e :
#include <stdio.h>
int n=10, q=2 ;
main()
{
int fct (int) ;
void f (void) ;
int n=0, p=5 ;
n = fct(p) ;
printf ("A : dans main, n = %d, p = %d, q = %d\n", n, p, q) ;
f() ;
}
int fct (int p)
{
int q ;
q = 2 * p + n ;
printf ("B : dans fct,
return q ;
}
void f (void)
{
int p = q * n ;
printf ("C : dans f,
}
_______________________________________________________________
IV. Le s fonctions
43
Sol
ution
B : dans fct, n = 10, p = 5, q = 20
A : dans main, n = 20, p = 5, q = 2
C : dans f,
n = 10, p = 20, q = 2
Enonc
Ecrire une fonction q ui re oit e n argum e nts 2 nom bre s flottants e t un caract re e t q ui fournit un r s ultat corre s pondant
l'une des 4 op rations appliq u e s ses deux pre m ie rs argum e nts, e n fonction de la valeur du de rnie r, savoir : addition
pour le caract re + , soustraction pour -, m ultiplication pour *e t division pour / (tout autre caract re q ue l'un de s 4 cit s
s e ra inte rpr t com m e une addition). O n ne tie ndra pas com pte des ris q ues de division par z ro.
Ecrire un pe tit program m e (m ain) utilisant ce tte fonction pour e ffe ctue r les 4 op rations sur de ux nom bre s fournis e n
donn e .
_______________________________________________________________
Sol
ution
#include <stdio.h>
float oper (float v1, float
{
float res ;
switch (op)
{ case '+' : res = v1 +
break ;
case '-' : res = v1 break ;
case '*' : res = v1 *
break ;
case '/' : res = v1 /
v2 ;
v2 ;
v2 ;
v2 ;
44
break ;
: res = v1 + v2 ;
}
return res ;
}
main()
{
float oper (float, float, char) ;
float x, y ;
/* prototype de oper */
("leur
("leur
("leur
("leur
somme est :
%e\n", oper
diffrence est : %e\n", oper
produit est :
%e\n", oper
quotient est :
%e\n", oper
(x,
(x,
(x,
(x,
y,
y,
y,
y,
'+')
'-')
'*')
'/')
)
)
)
)
;
;
;
;
Enonc
Transform e r le program m e (fonction + m ain) crit dans l'e xe rcice pr cdent 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
char op ;
45
v2)
v2 ;
v2 ;
v2 ;
v2 ;
v2 ;
main()
{
float oper (float, float) ;
/* prototype de oper */
float x, y ;
printf ("donnez deux nombres rels : ") ;
scanf ("%e %e", &x, &y) ;
op = '+' ;
printf ("leur somme est :
%e\n", oper (x, y) ) ;
op = '-' ;
printf ("leur diffrence 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
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 le s ous la form e :
appel numro 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
initialise une seule fois z ro (ou toute autre valeur ve ntue llem e nt e xplicit e ) au dbut de l'e x cution du program m e .
Ici, nous avons, de plus, pr vu un pe tit program m e d'essai.
#include <stdio.h>
void fcompte (void)
{
static int i ;
/* il est inutile, mais pas dfendu, d'crire i=0 */
i++ ;
printf ("appel numro %d\n", i) ;
}
/* petit programme d'essai de fcompte */
main()
{ void fcompte (void) ;
int i ;
for (i=0 ; i<3 ; i++) fcompte () ;
}
L e ncore , la dm 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
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 prciser 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
il est
il est
il est
un entier : 12
pair
multiple de 3
divisible par 6
_______________________________________________________________
Sol
ution
#include <stdio.h>
int mul2 (int n)
{
if (n%2) return 0 ;
else return 1 ;
}
int mul3 (int n)
{
if (n%3) return 0 ;
else return 1 ;
}
main()
{
int mul2 (int) ;
48
;
("il est pair\n") ;
("il est multiple de 3\n") ;
("il est divisible par 6\n") ;
V : TA BLEAUX ET
PO INTEURS
Enonc
Quels r s ultats fournira ce program m e :
#include <stdio.h>
main()
{
int t [3] ;
int i, j ;
int * adt ;
for (i=0, j=0 ; i<3 ; i++) t[i] = j++ + i ;
/* 1 */
/* 2 */
/* 3 */
for (adt = t ; adt < t+3 ; adt++) printf ("%d ", *adt) ;
printf ("\n") ;
/* 4 */
/* 5 */
50
_______________________________________________________________
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 lm 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
faon q uivalente :
for (i=2 ; i>=0 ; i--) printf ("%d ", t[i]) ;
2
2
2
2
4
4
4
0
51
Enonc
Ecrire , de deux faons diff re nte s , un program m e q ui lit 10 nom bre s e ntie rs dans un tableau avant d'en rech e rch e r le plus
grand e t le plus petit :
a) e n utilisant uniq ue m e nt le "form alism e tableau",
b) e n utilisant le "form alism e pointe ur", ch aq ue fois q ue ce la e s t possible
_______________________________________________________________
Sol
ution
a) La program m ation e s t, ici, "classique". Nous avons sim plem e nt dfini un sym bole NVALdestin conte nir le nom bre
de valeurs du tableau. Note z bie n q ue la dclaration 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 dfini ce sym bole NVAL par une "constante sym boliq ue "
(const int NVAL = 10).
#include <stdio.h>
#define NVAL 10
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
main()
{
int i, min, max ;
52
max = min = *t ;
for (i=1 ; i<NVAL ; i++)
{ if (*(t+i) > max) max = *(t+i) ;
if (*(t+i) < min) min = *(t+i) ;
}
printf ("valeur max : %d\n", max) ;
printf ("valeur min : %d\n", min) ;
}
Enonc
Soie nt deux tableaux t1 e t t2 d clar s ainsi :
float t1[10], t2[10] ;
Ecrire les instructions perm e ttant de re copie r, dans t1, tous les lm e nts positifs de t2, e n com pltant 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 lm 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++)
53
M ais, on pe ut re copie r d'abord dans t1 les lm e nts positifs de t2, avant de com plte 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 cdente , 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
Quels r s ultats fournira ce program m e :
#include <stdio.h>
main()
{ int t[4] = {10, 20, 30, 40} ;
int * ad [4] ;
int i ;
for (i=0 ; i<4 ; i++) ad[i] = t+i ;
for (i=0 ; i<4 ; i++) printf ("%d ", * ad[i]) ;
printf ("\n") ;
printf ("%d %d \n", * (ad[1] + 1), * ad[1] + 1) ;
}
/* 1 */
/* 2 */
/* 3 */
_______________________________________________________________
Sol
ution
Le tableau ad e s t un tableau de 4 lm e nts ;ch acun de ce s lm 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 lm e nts du tableau t. L'instruction /*2 */ affich e finalem e nt les 4 lm e nts
du tableau t ;e n e ffe t, *ad[i] re pr s e nte la valeur situ e l'adre s s e ad[i]. /*2 */ e s t q uivalente ici :
for (i=0 ; i<4 ; i++) printf ("%d", t[i]) ;
54
Exe rcice s e n langage C
Enfin, dans l'instruction /*3 */, *(ad[1] + 1) re pr s e nte la valeur situ e l'e ntie r suivant ce lui d'adre s s e ad[1] ;ils'agit
donc de t[2]. En re vanch e , *ad[1] + 1 re pr s e nte la valeur situ e l'adre s s e ad[1] augm e nt e d e 1, autre m e nt dit t[1] +
1.
Voici, e n d finitive , les r s ultats fournis par ce program m e :
10 20 30 40
30 21
Enonc
Soit le tableau t dclar ainsi :
float t[3] [4] ;
Ecrire les (s e ules ) instructions perm e ttant de calculer, dans une variable nom m e som , la som m e d e s lm e nts de t :
a) e n utilisant le "form alism e usueldes tableaux deux indice s ",
b) e n utilisant le "form alism e pointe ur".
_______________________________________________________________
Sol
ution
a) La pre m i re s olution ne pos e aucun problm e particulie r :
int i, j ;
som = 0 ;
for (i=0 ; i<3 ; i++)
for (j=0 ; j<4 ; j++)
som += t[i] [j] ;
b) Le form alism e pointe ur e s t ici m oins facile appliq ue r q ue dans le cas des tableaux un indice . En e ffe t, ave c, par
e xe m ple, float t[4], t e s t de type int * e t ilcorre s pond un pointe ur sur le pre m ie r lm e nt du tableau. Ilsuffit donc
d'incr m e nte r conve nablem e nt t pour parcourir tous les lm e nts du tableau.
i ;
* adt ;
= 0 ;
= (float *) t ;
(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 lm e nts d'un tableau de flottants transm is, ainsi q ue s a
dim e nsion, e n argum e nt.
Ecrire un pe tit program m e d'essai.
1 Attention, cel
a n'e s t vrai q ue parce que l'on passe de pointeurs sur des groupes d'lm ents un pointeur sur ces lm 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'lm e nt (de type int), nous le transm e ttrons classiquem e nt par valeur. L'e n-t te de notre fonction pourra s e pr s e nte r
sous l'une des form e s s uivante s :
float somme (float t[], int n)
float somme (float * t, int n)
float somme (float t[5], int n)
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 lm e nt du tableau2 e t e lle ne s e rt pas "alloue r" un e m place m e nt puis q ue le tableau e n
q ue s tion aura t allou dans la fonction appe lant som m e ).
Voici ce q ue pourrait tre la fonction de m and e :
float somme (float t[], int n)
int i ;
float s = 0 ;
for (i=0 ; i<n ; i++)
s += t[i] ;
return s ;
Pour ce q ui e s t du program m e d'utilisation de la fonction som m e , on pe ut, l e ncore , crire le "prototype " sous
diff re nte s form e s :
float somme (float [], int ) ;
float somme (float * , int ) ;
float somme (float [5], int ) ;
57
{
float somme (float *, int) ;
float t[4] = {3, 2.5, 5.1, 3.5} ;
printf ("somme de t : %f\n", somme (t, 4) ) ;
}
Enonc
Ecrire une fonction q ui ne re nvoie aucune valeur e t q ui d te rm ine la valeur m axim ale e t la valeur m inim ale d'un tableau
d'entie rs ( un indice ) de taille q ue lconq ue . Ilfaudra donc pr voir 4 argum e nts : le tableau, sa dim e nsion, le m axim um e t
le m inim um .
Ecrire un pe tit program m e d'essai.
_______________________________________________________________
Sol
ution
En langage C, un tableau ne pe ut tre transm is que par adresse (en toute rigue ur, C n'autoris e q ue la transm ission par
valeur m ais, dans le cas d'un tableau, on transm e t une valeur de type pointe ur q ui n'e s t rie n d'autre q ue l'adresse du
tableau!). En ce q ui conce rne s on nom bre d'lm 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 dte rm in s par la fonction. Ilfaut donc obligatoire m e nt pr voir de pas s e r de s pointe urs sur de s
float. L'e n-t te de notre fonction pourra donc s e pr s e nte r ainsi (nous ne donnons plus toute s les criture s possibles ) :
void maxmin (int t[], int n, int * admax, int * admin)
L'algorith m e de re ch e rch e de m axim um e t de m inim um pe ut tre calqu s ur ce lui de l'e xe rcice V.2, e n re m plaant m ax
par *adm ax e t m in par *adm in. Ce la nous conduit la fonction suivante :
void maxmin (int t[], int n, int * admax, int * admin)
58
Si l'on souh aite vite r les "indire ctions" q ui apparais s e nt syst m atiq ue m e nt dans les instructions de com paraison, on pe ut
"travailler" te m poraire m e nt sur des variables locales la fonction (nom m e s ici m ax e t m in). Ce la nous conduit une
fonction de la form e s uivante :
void maxmin (int t[], int n, int * admax, int * admin)
{
int i, max, min ;
max = t[1] ;
min = t[1] ;
for (i=1 ; i<n ; i++)
{ if (t[i] > max) max = t[i] ;
if (t[i] < min) min = t[i] ;
}
*admax = max ;
*admin = min ;
}
59
Enonc
Ecrire une fonction q ui fournit e n re tour la som m e des valeurs d'un tableau de flottants
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 dclare 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
lm 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 considrer qu'on reoit un pointe ur (de type float*) sur le dbut du tableau e t d'en parcourir tous
les lm e nts (au nom bre de n*p si n et p d s igne nt les dim e nsions du tableau) com m e s i l'on avait affaire un tableau
une dim e nsion.
Ce la nous conduit ce tte fonction :
float somme (float * adt, int n, int p)
{
int i ;
float s ;
for (i=0 ; i<n*p ; i++) s += adt[i] ;
return s ;
}
/* ou s += *(adt+i) */
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 dbut 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 dj dit dans
l'e xe rcice V.5, on y gagne ra e n lisibilit (e t e n ve ntue ls m e s s ages d'ave rtissem e nt!) e n faisant appe l l'op rate ur de
"cast".
Voici finalem e nt un e xe m ple d'un te lprogram m e d'utilisation de notre fonction :
#include <stdio.h>
main()
{
60
Enonc
Quels r s ultats fournira ce program m e :
#include <stdio.h>
main()
{
char * ad1 ;
ad1 = "bonjour" ;
printf ("%s\n", ad1) ;
ad1 = "monsieur" ;
printf ("%s\n", ad1) ;
}
_______________________________________________________________
Sol
ution
L'instruction ad1 = "bonjour" place dans la variable ad1 l'adresse de la ch a
ne constante "bonjour". L'instruction printf
("%s\n", ad1) s e conte nte d'affich e r la valeur de la ch a
ne dont l'adre s s e figure dans ad1, c'e s t- -dire , e n l'occurre nce
"bonjour". D e m ani re com parable, l'instruction ad1 = "m onsie ur" place l'adresse de la ch a
ne constante "m onsieur"
62
Exe rcice s e n langage C
dans ad1 ;l'instruction printf ("%s\n", ad1) affich e la valeur de la ch a
ne ayant m ainte nant l'adre s s e conte nue dans ad1,
c'e s t- -dire m ainte nant "m onsieur".
Finalem e nt, ce program m e affich e tout sim plem e nt :
bonjour
monsieur
Enonc
Quels r s ultats fournira 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++]) ;
}
/* 1 */
/* 2 */
/* 3 */
_______________________________________________________________
Sol
ution
La dclaration /*1 */ place dans la variable adr, l'adresse de la ch a
ne constante bonjour. L'instruction /*2 */ affich e
les caract re s adr[0], adr[1] e t adr[2], c'e s t- -dire les 3 pre m ie rs caract res de ce tte ch a
ne . L'instruction /*3 */ affich e
tous les caract re s partir de ce lui d'adre s s e adr, tant q ue l'on a pas affaire un caract re nul;com m e notre ch a
ne
VI. Le s ch a
ne s d e caract re s
63
"bonjour" e s t pr cis m e nt te rm in e par un te lcaract re nul, ce tte instruction affich e finalem e nt, un par un, tous les
caract res de "bonjour".
En d finitive , le program m e fournit sim plem e nt les r s ultats suivants :
bon
bonjour
Enonc
Ecrire le program m e pr cdent (Exe rcice VI.2), sans util
iser l
e "form al
ism e tabl
eau" (ile xiste plusieurs solutions).
_______________________________________________________________
Sol
ution
Voici de ux solutions possibles :
a) O n pe ut re m place r syst m atiq ue m e nt la notation adr[i] par *(adr+ i), ce q ui conduit ce program m e :
#include <stdio.h>
main()
{
char * adr = "bonjour" ;
int i ;
for (i=0 ; i<3 ; i++) putchar (*(adr+i)) ;
printf ("\n") ;
i = 0 ;
while (adr[i]) putchar (*(adr+i++)) ;
}
64
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 dm 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 dclaration com portant
une intialisation de la form e :
char * jour [7] = { "lundi", "mardi", ...
VI. Le s ch a
ne s d e caract re s
65
#include <stdio.h>
main()
{
char * jour [7] = { "lundi",
"mardi", "mercredi", "jeudi",
"vendredi", "samedi", "dimanche"
} ;
int i ;
do
{ printf ("donnez un nombre entier entre 1 et 7 : ") ;
scanf ("%d", &i) ;
}
while ( i<=0 || i>7) ;
printf ("le jour numro %d de la semaine est %s", i, jour[i-1]) ;
}
Enonc
Ecrire un program m e q ui lit deux nom bre s e ntie rs fournis obligatoire m e nt sur une m m e ligne . Le program m e ne devra
pas "s e plante r" e n cas de r pons e incorre cte (caract re s invalides) com m e le fe rait scanf ("%d %d", ...) m ais
sim plem e nt affich e r un m e s s age e t redem ande r une autre r pons e . Ildevra e n aller de m m e lors q ue la r pons e fournie
ne com porte pas as s e z d'inform ations. En re vanch e , lors q ue la r pons e com porte ra trop d'inform ations, les derni re s
devront tre ignor e s .
Le traite m e nt (dem ande de 2 nom bre s e t affich age ) devra s e poursuivre jus q u' ce q ue le pre m ie r nom bre fourni soit 0.
Voici un e xe m ple d'excution d'un te lprogram m e :
--- donnez deux
rponse errone
merci pour 2 15
--- donnez deux
rponse errone
merci pour 4 12
--- donnez deux
merci pour 4 8
--- donnez deux
entiers :
- redonnez-la : 2 15
entiers : 5
- redonnez-la : 4 12
entiers : 4 8 6 9
entiers : 5 3
66
Sol
ution
Com m e le s ugg re la re m arq ue de l' nonc , on pe ut r s oudre les problm 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, valide par
"re turn") ave c la fonction ge ts,
- "dcodage " 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 prcdente s jus q u' ce q ue la valeur fournie par sscanf soit gale 2.
L' nonc ne fait aucune h ypoth s e s ur le nom bre m axim alde caract re s q ue l'utilisate ur pourra tre am e n fournir. Ici,
nous avons suppos q u'au plus 128 caract re s s e raie nt fournis ;ils'agit l d'une h ypoth s e q ui, dans la pratiq ue , s'av re
r aliste , dans la m e s ure o on ris q ue rare m e nt de frappe r de s ligne s plus longue s ;de surcro
t, ils'agit m m e d'une
lim itation "nature lle" de ce rtains e nvironne m e nts (DOS, e n particulie r).
Voici le program m e dem and :
#include <stdio.h>
#define LG 128
main()
{
int n1, n2 ;
int compte ;
char ligne [LG+1] ;
VI. Le s ch a
ne s d e caract re s
67
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 utiliss 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 xcdentaire 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 plm 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, utilise la place de ge ts (ou fge ts) pe rm e t de
r gler le problm 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 xcdentaire s n'e xiste plus!
fge ts (en gnral, elle e s t introduite dans le ch apitre relatif au traitem ent des fich iers). Certains e xercices de la s e conde partie de cet ouvrage feront
appel fgets, et/ou s s canf.
68
Enonc
Ecrire un program m e dte 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 dpassant pas 128 caract re s . O n ch e rch e ra, ici, n'utiliser aucune des fonctions de
traite m e nt de ch a
ne .
_______________________________________________________________
Sol
ution
Com pte te nu de s contrainte s im pos e s par l' nonc , nous ne pouvons pas faire appe l la fonction strlen. Pour "e xplore r"
notre ch a
ne , nous utiliserons le fait q u'e lle e s t te rm in e par un caract re nul(\0]. D'o le program m e propos :
#define LG_LIG 128
#include <stdio.h>
main()
{
char ligne [LG_LIG+1] ;
int i ;
int 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 dduira de l'adresse de dbut 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 plaant, 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 donne (ici la te rm inaison)
une adresse donn e (ici, ce lle dj utilise 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 ranges dans un tableau de ch a
ne s constante s (plus prcism e nt, dans un
tableau de pointe urs sur de s ch a
ne s constante s ). Nous fe rons de m m e pour les diff re nts suje ts (je , tu...) ;e n re vanch e ,
ici, nous ne ch e rch e rons pas les "concat ne r" au ve rbe conjugu ;nous nous conte ntons de les crire , au m om e nt
opportun.
Voici finalem e nt le program m e dem and :
#include <stdio.h>
#include <string.h>
#define LG_VERBE 30
/* longueur maximale du verbe fourni en donne */
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
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 dpassant pas 128
caract re s ) fourni e n donn e . O n s'arrange ra pour q ue le te xte ainsi m odifi s oit cr e n m m oire , l
a pl
ace de
l
'ancien.
N.B. on pourra utiliser la fonction strch r.
_______________________________________________________________
Sol
ution
La fonction strch r pe rm e t de trouve r un caract re donn dans une ch a
ne . Elle e s t donc tout fait appropri e pour
localiser les 'e ' ;ilfaut toute fois note r q ue , pour localiser tous les 'e ', ile s t n ce s s aire de r p te r l'appe lde ce tte
VI. Le s ch a
ne s d e caract re s
71
fonction, e n m odifiant ch aq ue fois l'adresse de dbut de la ch a
ne conce rn e (ilfaut vite r de boucler sur la re ch e rch e
du m m e caract re 'e ').
La fonction strch r fournit l'adre s s e laq ue lle on a trouv le pre m ie r caract re indiq u (ou la valeur 0 si ce caract re
n'e xiste pas). La suppre s s ion du 'e ' trouv pe ut s e faire e n re copiant le "re s te " de la ch a
ne l'adre s s e o l'on a
trouv le 'e '.
Voici une s olution possible :
#include <stdio.h>
#include <string.h>
#define LG_LIG 128
#define CAR 'e'
main()
{
char ligne [LG_LIG+1] ;
char * adr ;
Enonc
Soit le m od le (type ) de structure s uivant :
struct s_point
{ char c ;
int x, y ;
} ;
Ecrire une fonction q ui re oit e n argum e nt une s tructure de type s_point e t q ui e n affich e le conte nu sous la form e :
point B de coordonnes 10 12
Sol
ution
a) Voici la fonction de m and e :
#include <stdio.h>
74
Note z q ue s a com pilation n ce s s ite obligatoire m e nt la dclaration 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 cdente :
main()
{
void affiche (struct s_point) ;
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 cdente s 'appliq ue galem e nt ici. En pratiq ue , la dclaration de la structure s_point
figure ra dans un fich ie r d'e xte nsion h q ue l'on s e conte nte ra d'incorpore r par #include au m om e nt de la com pilation. D e
m m e , ile s t n ce s s aire d'inclure stdio.h .
b) Voici la nouve lle fonction de m and e :
#include <stdio.h>
void affiche (struct s_point * adp)
{
printf ("point %c de coordonnes %d %d\n", adp->c, adp->x, adp->y) ;
}
Note z q ue l'on doit, ce tte fois, faire appe l l'op rate ur -> , la place de l'op rate ur point (.), puis q ue l'on "travaille"
sur un pointe ur sur une s tructure , e t non plus sur la valeur de la structure e lle-m m e . Toute fois l'usage de -> n'e s t pas
totalem e nt indispensable, dans la m e s ure o, par e xe m ple, adp-> x e s t q uivalent (*adp).x.
Voici l'adaptation du program m e d'essai pr cdent :
main()
{
VII. Le s s tructure s
75
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 (dfini dans l'e xe rcice
pr cdent) 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 dclaration de
s_point) :
#include <stdio.h>
struct s_point
{ char c ;
int x, y ;
} ;
void raz (struct s_point * adr)
76
adr->c = 0 ;
adr->x = 0 ;
adr->y = 0 ;
Voici, titre indicatif, un pe tit program m e d'essai (sa com pilation n ce s s ite la dclaration de s_point, ainsi que le fich ie r
stdio.h ) :
main()
{
struct s_point p ;
void raz (struct s_point *) ;
// dclaration de raz
raz (&p) ;
/* on crit c en %d pour voir son code */
printf ("aprs : %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 (dfini dans l'e xe rcice VII.1) e t q ui
re nvoie e n r s ultat une s tructure de m m e type corre s pondant un point de m m e nom (c) e t de coordonn e s oppos e s .
Ecrire un pe tit program m e d'essai.
_______________________________________________________________
Sol
ution
Bie n q ue l' nonc ne pr cis e rie n, le r s ultat de notre fonction ne pe ut tre transm is que par valeur. En e ffe t, ce r s ultat
doit tre cr au s e in de la fonction e lle-m m e ;ce la signifie q u'ils e ra d truit d s la sortie de la fonction ;e n transm e ttre
l'adre s s e re vie ndrait re nvoye r l'adre s s e d e q ue lque ch ose destin dispara
tre ...
Voici ce q ue pourrait tre notre fonction (ici, e ncore , nous avons re produit la dclaration 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 possibilits 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
Soit la structure s uivante , re pr s e ntant un point d'un plan :
struct s_point
{ char c ;
int x, y ;
} ;
1) Ecrire la dclaration d'un tableau (nom m courbe ) de NP points (NP suppos dfini 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
3) Ecrire un program m e q ui :
- lit e n donnes des valeurs pour le tableau courbe ;on utilisera de prf 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 donne ne peut pas dpas s e r
128 caract re s ,
- fait appe l la fonction pr cdente pour les affich e r.
_______________________________________________________________
Sol
ution
1) Ilsuffit de dclare r un tableau de s tructure s :
struct s_point courbe [NP] ;
2) Com m e courbe e s t un tableau, on ne pe ut q u'e n transm e ttre l'adre s s e e n argum e nt de affich e . Ile s t pr f rable de
pr voir galem e nt e n argum e nt le nom bre de points. Voici ce q ue pourrait tre notre fonction :
void affiche (struct s_point courbe [], int np)
/* courbe : adresse de la premire structure du tableau */
/*
(on pourrait crire struct s_point * courbe)
*/
/* np : nombre de points de la courbe */
{
int i ;
for (i=0 ; i<np ; i++)
printf ("point %c de coordonnes %d %d\n", courbe[i].c,
courbe[i].x, courbe[i].x) ;
}
Com m e pour n'im porte q ue ltableau une dim e nsion transm is en argum e nt, ile s t possible de ne pas e n m e ntionne r la
dim e nsion dans l'e n-t te de la fonction. Bie n e nte ndu, com m e , e n fait, l'ide ntificate ur courbe n'e s t q u'un pointe ur de
type s_point*(pointe ur sur la pre m i re s tructure du tableau), nous aurions pu galem e nt crire s_point*courbe .
Note z q ue , com m e l'accoutum e , le "form alism e tableau" e t le "form alism e pointe ur" pe uve nt tre indiff re m m e nt
utiliss (voire com bin s ). Par e xe m ple, notre fonction aurait pu galem e nt s' crire :
void affiche (struct s_point * courbe, int np)
{
struct s_point * adp ;
int i ;
VII. Le s s tructure s
79
3) Com m e nous avons appris le faire dans l'e xe rcice VI.5, nous lirons les inform ations re lative s aux diff re nts points
l'aide des deux fonctions :
- ge ts, pour lire , sous form e d'une ch a
ne , une ligne d'inform ation,
- sscanf, pour d code r suivant un form at le conte nu de la ch a
ne ainsi lue .
Voici ce q ue pourrait le program m e dem and (ici, nous avons re produit, la fois la dclaration de s_point e t la fonction
affich e pr cdente ) :
#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 donne */
main()
{ struct s_point courbe [NP] ;
int i ;
char ligne [LG_LIG+1] ;
void affiche (struct s_point [], int) ;
/* lecture des diffrents points de la courbe */
for (i=0 ; i<NP ; i++)
{ printf ("nom (1 caractre) et coordonnes point %d : ", i+1) ;
gets (ligne) ;
sscanf (ligne, "%c %d %d", &courbe[i].c, &courbe[i].x, &courbe[i].y) ;
}
affiche (courbe, NP) ;
}
void affiche (struct s_point courbe [], int np)
{
int i ;
for (i=0 ; i<np ; i++)
printf ("point %c de coordonnes %d %d\n", courbe[i].c,
courbe[i].x, courbe[i].x) ;
}
80
Enonc
Ecrire le program m e de la q ue s tion 3 de l'e xe rcice pr cdent, sans util
iser de structures. O n pr voira toujours une
fonction pour lire les inform ations re lative s un point.
_______________________________________________________________
Sol
ution
Ici, ilnous faut obligatoire m e nt pr voir 3 tableaux diff re nts de m m e taille : un pour les nom s de points, un pour leurs
abscis s e s e t un pour leurs ordonn e s . Le program m e ne pr s e nte pas de difficult s particuli re s (son principalint r t e s t
d' tre com par au pr cdent!).
#include <stdio.h>
#define NP 10
/* nombre de points d'une courbe */
#define LG_LIG 128
/* longueur maximale d'une ligne de donne */
main()
{
char c [NP] ;
/* noms des diffrents points */
int x [NP] ;
/* abscisses des diffrents points */
int y [NP] ;
/* ordonnes des diffrents points */
int i ;
char ligne [LG_LIG+1] ;
void affiche (char [], int[], int[], int) ;
/* lecture des diffrents points de la courbe */
for (i=0 ; i<NP ; i++)
{ printf ("nom (1 caractre) et coordonnes point %d : ", i+1) ;
gets (ligne) ;
sscanf (ligne, "%c %d %d", &c[i], &x[i], &y[i]) ;
}
affiche (c, x, y, NP) ;
}
VII. Le s s tructure s
81
void affiche (char c[], int x[], int y[], int np)
{
int i ;
for (i=0 ; i<np ; i++)
printf ("point %c de coordonnes %d %d\n", c[i], x[i], x[i]) ;
}
Enonc
Soie nt les deux m od les de structure date e t pe rsonne dclar s ainsi :
#define LG_NOM 30
struct date
{ int jour ;
int mois ;
int annee ;
} ;
struct personne
{ char nom [LG_NOM+1] ;
/* chane de caractres reprsentant le nom */
struct date date_embauche ;
struct date date_poste ;
} ;
Ecrire une fonction q ui re oit e n argum e nt une s tructure de type pe rsonne e t q ui e n re m plit les diff re nts ch am ps ave c un
dialogue s e pr s e ntant sous l'une des 2 form e s s uivante s :
nom : DUPONT
date embauche (jj mm aa) : 16 1 75
date poste = date embauche ? (O/N) : O
nom : DUPONT
date embauche (jj mm aa) : 10 3 81
date poste = date embauche ? (O/N) : N
date poste (jj mm aa) : 23 8 91
82
Exe rcice s e n langage C
_______________________________________________________________
Sol
ution
Notre fonction doit m odifie r le conte nu d'une s tructure de type pe rsonne ;ile s t donc n ce s s aire q u'e lle e n re oive
l'adre s s e e n argum e nt. Ici, l' nonc n'im posant aucune prote ction particuli re conce rnant les lecture s au clavie r, nous
lirons "classiquem e nt" le nom par ge ts e t les trois autre s inform ations num riq ue s par scanf. Voici ce q ue pourrait tre la
fonction de m and e :
void remplit (struct personne * adp)
{
char rep ;
/* pour lire une rponse de type O/N */
printf ("nom : ") ;
gets (adp->nom) ;
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 problm 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 dm arch e ainsi utilise n'est pas infaillible : si l'utilisate ur fournit des inform ations supplm 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 "problm e s h abitue ls" li s la fourniture d'inform ations e xcdentaire 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 dclarations 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 conus les problm e s proposs 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 rdaction de s program m e s
corre s pondants.
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 dtails 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 dj 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 propose, iln'e n re s te pas m oins q u'ilfaille le considrer com m e une rdaction
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 dfauts de te lle ou te lle rdaction, tant q ue l'on n'a pas prcis les crit re s re te nus (vitesse d'e x cution,
taille m m oire , clart de la rdaction, 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 dj aux e xe rcice s propos s
pr cdem m e nt dans la pre m i re partie de ce t ouvrage m ais ave c m oins d'accuit .
87
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 ges 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 problm e de contrle des donnes 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 ncontres 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 ande dans l' nonc lui-m m e .
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, utilises bon e s cie nt, e lles pe uve nt am liore r la concision et le te m ps d'excution 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
procdant 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 rdaction 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 dj re m arq u dans l'avant-propos, la norm e ANSI acce pte deux form es de dfinition de
fonctions. Voici, par e xe m ple, deux faons d'crire l'e n-t te d'une fonction fct re ce vant deux argum e nts de type int e t
ch are t re nvoyant une valeur de type double :
double fct (int x, char * p)
Ilne s 'agit l q ue de sim ples diff re nces de rdaction, 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 dclares 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 dfinie , dans le m m e s ource , avant d' tre utilise.
c) D ans les dclarations 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 possibilits 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 .
Ce ch apitre vous propose des problm 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 contrle,
- 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 lm e nts pris p p).
n,p
Ce tte valeur e s t place 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 nant 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
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 propose 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 lm e nts e t dcide 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
Program m e
#include <stdio.h>
#define NMAX 15
main()
{ int t [NMAX],
92
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.
*Plutt q ue d'utiliser directe m e nt la constante 15 dans notre program m e , nous avons prf r faire appe l l'instruction
#de fine du pr proce s s e ur pour d finir un sym bole NMAX possdant 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 dclaration 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 lm e nts (c'e s t- -dire 15), dont les indice s varie nt de 0 14.
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
supplm e ntaire s nous auraie nt alors perm is une lg re rduction 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 , alatoire m ais, de toute
faon, 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 dte 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 considrs (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 prcism 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 considr (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 considr
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 considr soit suprieur 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 rays de la liste .
94
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 fixe dans le program m e )
Exe m pl
e
entre 1 et 1000, les nombres premiers sont :
2
3
5
7
11
13
31
37
41
43
47
53
73
79
83
89
97
101
127
131
137
139
149
151
179
181
191
193
197
199
233
239
241
251
257
263
283
293
307
311
313
317
353
359
367
373
379
383
419
421
431
433
439
443
467
479
487
491
499
503
547
557
563
569
571
577
607
613
617
619
631
641
661
673
677
683
691
701
739
743
751
757
761
769
811
821
823
827
829
839
877
881
883
887
907
911
947
953
967
971
977
983
17
59
103
157
211
269
331
389
449
509
587
643
709
773
853
919
991
19
61
107
163
223
271
337
397
457
521
593
647
719
787
857
929
997
23
67
109
167
227
277
347
401
461
523
599
653
727
797
859
937
29
71
113
173
229
281
349
409
463
541
601
659
733
809
863
941
________________________________________________________________________________________
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 lm e nt).
En r alit , le bon droulem 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 droulem 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
Program m e
#include <stdio.h>
#define N 1000
#define VRAI 1
#define FAUX 0
main()
{
int raye [N+1],
prem,
na,
i ;
/* initialisations */
for (i=1 ; i<=N ; i++)
96
/* 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 rsultats */
printf ("entre 1 et %d, les nombres premiers sont :\n", N) ;
na = 0 ;
for (i=1 ; i<=N ; i++)
if ( !raye[i] )
{ printf ("%7d",i) ;
na++ ;
if ( na%10 == 0) printf ("\n") ;
/* 10 nombres par ligne */
}
}
Com m e ntaire s
*La re ch e rch e du prem ie r nom bre non e ncore ray est r alise par la s e ule instruction :
while (raye[++prem] && prem<N) {}
aurait conduit une boucle infinie s ur le pre m ie r nom bre pre m ie r trouv , c'e s t- -dire 2 (du m oins si N est suprieur ou
gal 2). Ilsuffirait toute fois d'incr m e nte r pre m une fois avant d'entre r dans la boucle pour q ue ce la fonctionne .
*Nous avons cons e rv le garde -fou :
prem < N
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 possibilits de ge s tion dynam iq ue du langage C offre nt une s olution plus agr able ce problm 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 dclars de type int, ce q ui, dans ce rtaine s im plm 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+ + utiliss 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
donnez un
donnez un
la lettre
la lettre
la lettre
la lettre
donnez un
donnez un
la lettre
la lettre
la lettre
________________________________________________________________________________________
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
Program m e
#include <stdio.h>
#include <string.h>
#define LMAX 26
main()
{
char mot1 [LMAX+1],
mot2 [LMAX+1] ;
int i, j ;
/* premier mot */
/* deuxime mot */
100
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 donnes 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 faons 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 plm 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 plm e ntations (Turbo/Borland C/C+ + , C/Quick C M icrosoft), une fonction (non
portable!) nom m e cge ts.
101
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 rponse par la frappe de la
touch e re turn.
L e ncore , on pr voira d'affich e r plusieurs fois une lettre q ui appara
t plusieurs reprises dans ch acun de s m ots.
O n s'astre indra n'utiliser pour la lecture au clavie r q ue l
a seul
e fonction getch ar. De plus, on r alisera une fonction
destin e lire un m ot dans un tableau q u'on lui transm e ttra e n argum e nt ;e lle fournira, e n re tour, la longue ur e ffe ctive
du m ot ainsi lu.
Exe m pl
es
Voir ce ux de l'e xe rcice pr cdent
________________________________________________________________________________________
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 reprsente 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 prvoir 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
Program m e
#include <stdio.h>
#define LMAX 26
main()
{
int lire(char []) ;
char mot1 [LMAX],
mot2 [LMAX] ;
int l1,
l2,
i, j ;
/*
/*
/*
/*
/*
103
/* recherche '\n' */
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 upplm 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 dclar 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 dclar e e s t suppose produire un rsultat 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
dclaration, la form e "prototype " autoris e par la norm e ANSI (ce prototype assure les contrles de types d'argum e nts e t
m e t e n place d've ntue lles conve rsions).
Par ailleurs, l'e n-t te de notre fonction lire a t crit suivant la form e "m ode rne ". La norm e ANSI aurait autoris le
re m place m e nt de note e n-t te par :
int lire (mot)
char mot [LMAX] ;
*Le tableau de caract re s re pr s e ntant l'uniq ue argum e nt de lire doit obligatoire m e nt tre transm is par adre s s e puis q ue
ce tte fonction doit tre e n m e s ure d'en m odifie r le conte nu. N'oublie z pas ce pe ndant q u'e n langage C un nom de tableau
e s t inte rpr t (par le com pilate ur) com m e un pointe ur (constant) sur son pre m ie r lm e nt. C'e s t ce q ui justifie la
pr s e nce , dans les appe ls la fonction lire , de m ot1 ou m ot2, e t non de & m ot1 ou & m ot2.
*La dclaration de l'argum e nt de lire , dans son e n-t te :
char mot [LMAX]
ou m m e :
char * mot
104
Exe rcice s e n langage C
D ans le pre m ie r cas, on continue de sp cifie r (au lecte ur du program m e plus q u'au com pilate ur) q ue m ot e s t un tableau
de caract re s m ais q ue s a dim e nsion n'a pas besoin d' tre connue au s e in de lire . Dans le s e cond cas, on e xprim e plus
claire m e nt q ue , finalem e nt, l'argum e nt re u par lire n'e s t rie n d'autre q u'un pointe ur sur des caract re s . Ce s
form ulations sont totalem e nt q uivalente s pour le com pilate ur e t, dans tous les cas (m m e le dernie r), ilre s te possible de
faire appe lau "form alism e " tableau au s e in de lire , e n utilisant une notation te lle q ue :
mot [i++]
D ISCUSSIO N
*Le sym bole
M ais, si ce tte
(#de fine ) dans
faire appe l
LM A X e s t dfini pour l'e ns e m ble du source conte nant, ici, le program m e principale t la fonction lire .
fonction de vait tre com pile s par m e nt du re s te , ils e rait alors nce s s aire de faire figure r la dfinition
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
l'une des dm arch e s s uivante s :
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 dpas s e r 127 caract re s . Par ailleurs, on fe ra
l'h ypoth s e (pe u re s trictive e n pratiq ue ) q ue les "code s " des lettre s m inuscules a z sont cons cutifs (ce q ui e s t le cas,
notam m e nt, ave c le code A SCII).
Exe m pl
e
donnez votre texte, en le terminant par une ligne vide
je me figure ce zouave qui joue
du xylophone en buvant du whisky
106
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 dm arch e s possibles :
- e ffe ctue r une r p tition du traite m e nt d'un caract re ,
- e ffe ctue r une r p tition du traite m e nt d'une ligne , lui-m m e constitu de la r p tition du traite m e nt de ch acun de s
caract re s q u'e lle contie nt.
a) La pre m i re dm arch e aboutit une s im ple boucle ave c com pte ur. Elle ne dem ande d'accder q u' un s e ulcaract re
la fois (par e xe m ple, par ge tch ar). Elle n ce s s ite , par contre , l' lim ination de s caract res de fin de ligne \n (q ui sont
transm is com m e les autre s par ge tch ar), puis q u'ils ne font pas vraim e nt partie du te xte .
D e s urcro
t, la dte ction de la fin du te xte oblige cons e rve r e n pe rm ane nce le "caract re pr cdent". 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 cdent 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 dm 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 problm 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,
cprec ;
int compte[26] ;
int numl,
ntot,
nautres,
i ;
/*
/*
/*
/*
/*
/*
/* initialisations */
107
Com m e ntaire s
*L'e xpre s s ion :
c - 'a'
pe rm e t d'obte nir le "rang" dans l'alph abet du caract re conte nu dans c. N'oublie z pas q ue le langage C consid re le type
ch ar com m e num riq ue . Plus prcism e nt, dans le cas prsent, 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
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] ;
int compte[26] ;
int numl,
ntot,
nautres,
i ;
/*
/*
/*
/*
/*
/* initialisations */
ntot = 0 ; nautres = 0 ;
for (i=0 ; i<26 ; i++) compte[i]=0 ;
/* lecture texte et comptages */
printf ("donnez votre texte, en le terminant par une ligne vide\n") ;
do
{ gets(ligne) ;
for (i=0 ; i<strlen(ligne) ; i++, ntot++)
{ numl = ligne[i] - 'a' ;/* on donne le rang 0 la lettre 'a' */
if (numl >=0 && numl < 26) compte[numl]++ ;
else nautres++ ;
}
}
while (strlen(ligne)) ;
/* affichage rsultats */
109
D ISCUSSIO N
*Aucun des deux program m e s propos s ne pose de problm e de prote ction vis- -vis des rponses 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 dcide 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 m pl
e
donnez votre texte, en le terminant par une ligne vide
Le langage C a t conu en 1972 par Denis Ritchie avec un objectif
trs prcis : crire un "systme d'exploitation" (UNIX). A cet effet,
il s'est inspir du langage B (cr par K. Thompson) qu'il a hauss
au niveau de langage volu, notamment en l'enrichissant de structures
et de types, et ceci tout en lui conservant ses aptitudes de
programmation proche de la machine.
ANALYSE
Com m e dans l'e xe rcice pr cdent, ile xiste (au m oins) deux dm arch e s possibles :
- e ffe ctue r une r p tition du traite m e nt d'un caract re ,
- e ffe ctue r une r p tition du traite m e nt d'une ligne , lui-m m e constitu de la r p tition du traite m e nt de ch acun de s
caract re s q u'e lle contie nt.
La pre m i re dm arch e aboutit une s im ple boucle ave c com pte ur. Elle dem ande s im plem e nt d'accder un s e ul
caract re (par e xe m ple par ge tch ar).
La s e conde dm 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 rle, condition d'y inclure \n (si l'on travaille
ave c ge tch ar) ou \0 (si l'on travaille ave c ge ts). O n pe ut alors dire que l'on a progress d'un m ot dans le te xte , ch aq ue
fois q ue l'on a r alis la s q ue nce s uivante :
- re ch e rch e du prem ie r caract re diff re nt d'un sparate ur,
- re ch e rch e du prem ie r caract re gal un s parate ur.
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
#define FAUX 0
main()
{ int sep(char) ;
char c,
cprec ;
int nmots,
fin_texte,
mot_en_cours ;
/*
/*
/*
/*
/*
/*
112
}
/*******************************************/
/*
fonction d'examen d'un caractre
*/
/*******************************************/
int sep (char c)
{
char sep[12] = {'\n',
/* fin de ligne */
' ',
/* espace */
',', ';', ':', '.', '?', '!',
/* ponctuation */
'(', ')',
/* parenthses */
'"', '\'' } ;
/* guillemets, apostrophe*/
int nsep=12,
/* nombre de sparateurs */
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 dte 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 dj la valeur VR A I ;ce la nous vite un te s t supplm 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 dj ).
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 sparate 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
#define FAUX 0
main()
{
int sep(char) ;
char ligne[128] ;
int nmots,
mot_en_cours,
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
}
/********************************************/
/*
fonction d'examen d'un caractre
*/
/********************************************/
int sep (char c)
{
char sep[12] = {'\0',
/* fin de ligne (chane) */
' ',
/* espace */
',', ';', ':', '.', '?', '!',
/* ponctuation */
'(', ')',
/* parenthses */
'"', '\'' } ;
/* guillemets, apostrophe*/
int nsep=12,
/* nombre de sparateurs */
i ;
i = 0 ;
while ( c!=sep[i] && i++<nsep-1 ) ;
if (i == nsep) return (0) ;
else return (1) ;
}
Com m e ntaire s
Nous avons d :
- d'une part, au s e in de la fonction s e p, re m place r le s parate ur \n par \0,
- d'autre part, dans la boucle de traite m e nt des caract res d'une ligne , traite r com m e les autre s ce caract re de fin de
ligne (c'e s t- -dire faire varie r i de 0 strlen(ligne ) e t non strlen(ligne )-1), afin d' vite r de com pte r pour un s e ulm ot
le dernie r m ot d'une ligne (non suivi d'un sparate ur) e t le pre m ie r de la suivante .
D is cus s ion
*En ce q ui conce rne la fonction d'e xam e n d'un caract re (nom m e s e p), vous constate z (dans les deux ve rsions
propos e s ) q ue nous l'avons dclare 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 problm e de prote ction vis- -vis des rponses 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 :
Capricorne
Verseau
Poisson
23 dcembre - 19 janvier
20 janvier - 19 fvrier
20 fvrier - 20 mars
116
21
20
21
21
22
23
23
23
22
mars - 19 avril
avril - 20 mai
mai - 20 juin
juin - 21 juillet
juillet - 22 aot
aot - 22 septembre
septembre - 22 octobre
octobre - 21 novembre
novembre - 22 dcembre
Exe m pl
es
donnez votre jour et votre mois (sans accent) de naissance ?
11 july
*** erreur de nom de mois ***
_______________________
donnez votre jour et votre mois de naissance ?
16 janvier
vous tes n sous le signe suivant : Capricorne
________________________________________________________________________________________
ANALYSE
Le program m e doit tre e n m e s ure d'tablir une corre s pondance e ntre le nom d'un signe e t les deux date s lim ite s
corre s pondante s . O n pe ut dj note r q ue la date de fin d'un signe e s t la ve ille de ce lle de dbut du suivant. Nous nous
conte nte rons donc de ne cons e rve r q u'une s e ule de ces deux inform ations, par e xe m ple la date de fin.
La corre s pondance s ouh ait e pe ut tre r alise :
- 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 lm 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 donne se fait alors de la m ani re s uivante :
- O n ch e rch e tout d'abord l' lm 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 .
117
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' lm 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 dbut du signe figurant dans
l' lm e nt x ;ilsuffit donc d'e xam ine r l' lm e nt suivant pour obte nir la r pons e voulue . Toute fois, si x est le
dernie r lm e nt de notre tableau, ilfaudra considrer que son suivant e s t e n fait le pre m ie r lm 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'lm 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",
"Blier",
21, "mai",
"Taureau",
21, "juin",
"Gmeau",
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
Com m e ntaire s
*Nous avons dfini 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 prvu 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 prvu 11 caract re s pour le s igne .
Le tableau nom m date e s t un tableau de 12 lm e nts ayant ch acun le type s_date . Nous l'avons initialis dans sa
dclaration, ce q ui pe rm e t de m e ttre facilem e nt e n parallle 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 prvu, ici, de prote ction vis- -vis d've ntue lles
e rre urs de frappe de l'utilisate ur (ce la n' tait pas dem and par l' nonc ).
*R appe lons q ue la fonction stricm p com pare , sans te nir com pte de la distinction m ajuscules /m inuscules , les deux ch a
ne s
dont on lui fournit l'adre s s e e n argum e nt. Elle re s titue une valeur non nulle (q u'on pe ut inte rpr te r com m e vrai) lors q ue
les deux ch a
ne s s ont diff re nte s e t une valeur nulle (faux) lors q u'e lles s ont gales .
*La re ch e rch e du nom de m ois e s t r alise par la s e ule instruction :
while ( stricmp(date[i].mois, mois_n) && i++<11 ) {}
Ce lle-ci poss de un double avantage ;tout d'abord, ce lui de la concision ;e nsuite , ce lui de nous perm e ttre de savoir
dire cte m e nt si la re ch e rch e a t fructue us e ou non.
Toute fois, ce tte instruction n'e s t pas q uivalente la pr cdente . En e ffe t, lors q ue i vaut 11, ce la pe ut signifie r :
- soit q ue l' lm e nt ch e rch e s t e n position 11 (pre m ie r te s t satisfait),
- soit q ue l' lm e nt ch e rch ne figure pas dans la table (s e cond te s t satisfait).
Pour tranch e r, ile s t donc n ce s s aire , dans ce cas, d'e ffe ctue r une com paraison supplm e ntaire .
Note z q ue , par contre , une instruction te lle q ue :
while ( stricmp(date[i].mois, mois_n) && i++ <= 11) {}
s e rait q ue lque pe u e rron e . En e ffe t, dans le cas o l' lm 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 dsignant un lm 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 problm 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 tolrance .
120
*Notre re ch e rch e du nom de m ois a t r alise ici par un algorith m e dit de rech erch e squentiel
l
e en tabl
e (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 dpassant pas une "ligne " de 127
caract re s . Le s caract re s s usce ptibles d' tre cod s e n m ors e s ont :
- les 26 lettres de l'alph abet (supposes tap e s e n m ajuscul
es),
- les 10 ch iffres de 0 9 ,
- le point,
Si le te xte contie nt d'autre s caract re s q ue ce ux-ci, le program m e affich e ra sim plem e nt des points d'inte rrogation la
place du code m ors e .
Tabl
e au de s code s m ors e s
A
F
K
P
U
Z
0
5
...-.
-..--.
..--..
----.....
B
G
L
Q
V
.
1
6
-...
--.
.-..
--.....-.-..----....
C
H
M
R
W
-.-.
....
-.-.
.--
D
I
N
S
X
-..
..
-.
...
-..-
E
J
O
T
Y
.
.-----.--
2
7
..----...
3
8
...----..
4
9
....----.
Exe m pl
e
donnez votre message (1 ligne maxi) :
LE LANGAGE C, CONCU EN 1972, EST L'OEUVRE DE DENIS RITCHIE.
voici la traduction de votre message
121
.-..
. ??????
.-..
.-.
--.
.--.
.
??????
-.-. ?????? ??????
-.-.
---.
-.-.
..- ??????
.
-. ?????? .---- ----. --... ..--- ?????? ??????
.
...
- ??????
.-.. ??????
--.
......-.
. ??????
-..
. ??????
-..
.
-.
..
...
??????
.-.
..
-.-.
....
..
. .-.-.________________________________________________________________________________________
ANALYSE
Le program m e doit donc tre e n m e s ure d'tablir une corre s pondance e ntre un caract re e t son code m ors e . L e ncore ,
nous pourrions utiliser deux tableaux re li s par une valeur com m une d'un indice . M ais l'e m ploi d'un tableau de
structure s pe rm e t de m ie ux m e ttre e n vidence la corre s pondance e ntre les inform ations, lors de l'initialisation. Ch aq ue
lm 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
main()
{
struct code { char lettre ;
char * morse ;
} ;
struct code table[NL] =
{ 'A', ".-",
'D', "-..",
'G', "--.",
'J', ".---",
'M', "--",
'P', ".--.",
'S', "...",
'V', "...-",
'B',
'E',
'H',
'K',
'N',
'Q',
'T',
'W',
"-...",
".",
"....",
"-.-",
"-.",
"--.-",
"-",
".--",
/* code morse */
'C', "-.-.",
'F', "..-.",
'I', "..",
'L', ".-..",
'O', "---",
'R',".-.",
'U', "..-",
'X', "-..-",
122
"-.--",
".-.-.-",
"-----",
"...--",
"-....",
"----."
'Z', "--..",
'1', ".----",
'4', "....-",
'7', "--...",
'2', "..---",
'5', ".....",
'8', "---..",
Com m e ntaire s
*Nous avons dfini 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 cdent, nous avons prvu 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
dclaration. Ilne faut pas oublie r, e n e ffe t, q u'une notation te lle q ue :
".-.-."
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 dbut). Le s constante s ch a
ne s , par contre , voie nt leurs e m place m e nts dfinis d s la com pilation.
Si notre tableau table avait t dclar 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 considrer les variables dclares 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 alise par la s e ule instruction :
while (ligne[i] != table[j].lettre && j++<NL-1) ;
D ISCUSSIO N
D ans un program m e "r e l", ilfaudrait pr voir d'acce pte r un m e s s age de plus d'une ligne , ce q ui pos e rait le problm 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
Enonc
Ecrire un program m e pe rm e ttant de dcode 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 sparer 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 dpas 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 cdent (II-2).
Exe m pl
e
donnez votre message (1 ligne maxi) :
-...
--- -.
.----..-
.-.
.-.-.-
.-.-.
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 cdent. Le dcodage 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 principalproblm e q ui s e pos e alors nous e s t ce lui de l'acc s ch acun de s code s m ors e s conte nus dans ligne ;e n
e ffe t, ce ux-ci sont crits ave c un gabarit variable e t s par s par un nom bre variable d'espace s .
Nous proposons de r p te r le traite m e nt suivant, fond s ur l'e m ploi d'un pointe ur de caract re s (indice ) dans le tableau
ligne :
- Avance r le pointe ur, tant q u'ildsigne un espace.
Program m e
#include <stdio.h>
#include <string.h>
#define NL 37
#define LG 127
main()
{
struct code { char lettre ;
char * morse ;
} ;
struct code table[NL] =
{ 'A', ".-",
'D', "-..",
'G', "--.",
'J', ".---",
'M', "--",
'P', ".--.",
'S', "...",
'V', "...-",
'Y', "-.--",
'.', ".-.-.-",
'0', "-----",
'3', "...--",
'6', "-....",
'9', "----."
} ;
char ligne[LG+1] ;
char code[7] ;
int i, j ;
'B',
'E',
'H',
'K',
'N',
'Q',
'T',
'W',
'Z',
"-...",
".",
"....",
"-.-",
"-.",
"--.-",
"-",
".--",
"--..",
'1', ".----",
'4', "....-",
'7', "--...",
/* code morse */
'C', "-.-.",
'F', "..-.",
'I', "..",
'L', ".-..",
'O', "---",
'R',".-.",
'U', "..-",
'X', "-..-",
'2', "..---",
'5', ".....",
'8', "---..",
126
*/
*/
*/
*/
*/
*/
*/
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 constitue du caract re \0 (q ui n'e s t pas considr
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 nant par \0.
*La re ch e rch e du code m orse dans le tableau table e s t r alise par la s e ule instruction :
while ( stricmp (code, table[j].morse) && j++<NL-1) ;
Le s re m arq ue s faites dans le q uatri m e com m e ntaire de l'e xe rcice II-1, propos de la re ch e rch e s q ue ntie lle e n table,
s'appliq ue nt galem e nt ici.
D ISCUSSIO N
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 dfinies dans le s ource m m e du program m e (e t non dans un
fich ie r de donnes). Elle s e ront toute fois plac e s un nive au gl
obal
, de m ani re pouvoir, le cas ch ant, faire l'obje t
d'un source s par , appe lable par #include .
O n pr voira de ux fonctions :
- une pour re ch e rch e r les inform ations re lative s un article, partir de s on num ro de code ,
- une pour affich e r la facture r capitulative .
Exe m pl
e
combien d'articles facturer ? 3
code article ? 25
quantit de Centrifugeuse au prix unitaire de
code article ? 7
** article inexistant - redonnez le code : 16
370.00 ? 33
128
TOTAL
NBRE
P-UNIT
MONTANT
33
12
6
370.00
199.50
295.25
12210.00
2394.00
1771.50
16375.50
________________________________________________________________________________________
ANALYSE
L' nonc nous prcise 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 lm 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 , dclar un nive au globale t initialis dans sa dclaration.
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 dfini. 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 nce 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 rves dans deux tableaux (nom m s q te e t rangart) com portant autant d'lm e nts q ue
d'articles facture r (on e n pr voira un nom bre m axim al).
Program m e
#include <stdio.h>
/* ------ structure contenant les informations relatives aux
*/
/*
diffrents articles
-------------- */
#define NBART 6
/* nombre total d'articles */
typedef struct { int code ;
/* code article */
char * lib ;
/* libell */
float pu ;
/* prix unitaire */
} t_article ;
t_article article [NBART] =
{ 11, "Gaufrier",
268.0,
14, "Cafetire 12 T",
235.0,
16, "Grille-pain",
199.50,
19, "Balance de mnage", 278.0,
25, "Centrifugeuse",
370.0,
26, "Four raclette 6P", 295.25
} ;
/* ----------------------------------------------------------------------*/
#define NAFMAX 10
main()
{
int recherche(int) ;
void facture(int[], int[], int) ;
int naf,
rang,
codart,
i ;
int rangart [NAFMAX],
qte [NAFMAX] ;
/*
/*
/*
/*
/*
130
}
/***********************************************************/
/*
fonction de recherche d'un code article
*/
/***********************************************************/
int recherche (int codart)
{
int rang ;
/* rang courant d'un article */
rang = 0 ;
while (article[rang].code != codart && rang++ < NBART-1) {} ;
if (rang <NBART) return (rang) ;
else return (-1) ;
}
/***********************************************************/
/*
fonction d'affichage de la facture
*/
/***********************************************************/
void facture(int rangart[], int qte[], int naf)
/* rangart : tableau des rangs des codes articles */
/* qte :tableau des prix unitaires */
/* naf :nombre d'articles facturer */
{
float somme,
/* total facture */
montant ;
/* montant relatif un article */
int i ;
131
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, dclar 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 dsagr 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 problm 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
*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 alatoire s e n
langage C.
III-1 Tirage al
atoire
________________________________________________________________________________________
Enonc
Ecrire une fonction fournissant un nom bre e ntie r tir au h asard e ntre 0 (inclus) e t une valeur n (inclus e ) fournie e n
argum e nt.
R aliser un program m e principalutilisant ce tte fonction pour e xam ine r la "distribution" de s valeurs ainsi obte nues dans
l'inte rvalle [0, 10]. Le nom bre de tirage s r aliser sera lu e n donn e e t le program m e affich e ra le nom bre de fois o
ch acune de ce s valeurs aura t obte nue .
Exe m pl
e
combien de tirages : 1100
nombre de tirages obtenus
0 :
106
1 :
95
134
:
:
:
:
:
:
:
:
:
115
95
91
103
103
101
92
94
105
ANALYSE
Ilfaut faire appe l la fonction rand. Ce lle-ci fournit un nom bre e ntie r, tir de faon "ps e udo-alatoire " 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 dfinie 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 dm 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 alatoire , 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>
#define N 10
main()
{
135
Com m e ntaire s
*D ans la fonction aleat, la division par RAND_M AX+ 1 doit bien sr 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 dpas s e m e nt de capacit n' tant jam ais dte ct e n cas d'oprations 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 excution une
autre e t ne pas dpendre d'une quelconq ue inform ation fournie par l'utilisate ur.
Com m e dans l'e xe rcice pr cdent, 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 alatoire ", 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 faon 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 excutions diff re ntes du m m e program m e )
combien de tirages : 1100
nombre de tirages obtenus
0 :
124
1 :
104
2 :
97
3 :
97
4 :
89
5 :
93
6 :
105
:
:
:
:
137
109
110
89
83
___________________
ANALYSE
En langage C, la fonction srand pe rm e t d'initialiser le g n rate ur de nom bre s alatoire 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 alatoire 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 dbut 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 excution une autre . Une s olution ce problm e
consiste ch oisir une "gra
ne " alatoire . Bie n sr, 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
utilise com m e gra
ne .
Ce tte initialisation du g n rate ur de nom bre s alatoires doit toute fois n' tre r alise 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 problm 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
Program m e
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 10
main()
{
int aleat (int) ;
int ntir,
t[N+1],
i ;
139
/* gnration 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 , dclar e d e
clas s e s tatiq ue . Ce tte variable e s t initialise 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 le 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 " dpendant de
l'im plm e ntation. Le type de ce tte valeur d pe nd, lui aussi, de l'im plm e ntation ;toute fois, la norm e pr voit q u'ile xiste ,
dans tim e .h , un sym bole tim e _t (dfini 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 "dgradante ", 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 utilise par ailleurs) e t q ui doit, ici, absolum e nt tre dclare dans le "bon type ", sous peine de risquer
d'aboutir un cras e m e nt inte m pe s tif de donnes (dans le cas o on aurait dclar date d'un type "plus petit" q ue le type
e ffe ctif).
III-3 A l
a d' toil
es
________________________________________________________________________________________
140
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 superposes.
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 lm 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 prvoirons 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 dpart, nous initialiserons tous les lm 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 lm 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.
Program m e
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define NY 25
#define NX 80
main()
{
int aleat (int) ;
int ny,
nx,
ix,
iy,
nb_points,
i, j ;
char ecran [NX] [NY] ;
const char blanc = ' ',
point = '*' ;
/* pour memset */
/* nombre total de lignes de l'cran */
/* nombre total de colonnes de l'cran */
/*
/*
/*
/*
/*
/*
/* image de l'cran */
/* caractre de remplissage */
/* reprsentation d'un point */
142
}
/*******************************************************/
/* fonction de tirage alatoire d'un nombre dans [0,n] */
/*******************************************************/
int aleat (int n)
{
int i ;
static int prem = 1 ;
/* drapeau premier appel */
time_t date ;
/* pour l'argument de time */
/* initialisation gnrateur au premier appel */
if (prem)
{ srand (time(&date) ) ;
prem = 0 ;
}
/* gnration nombre alatoire */
i = rand() / (RAND_MAX + 1.) * (n+1) ;
return (i) ;
}
Com m e ntaire s
143
Ici, nous avons prf 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 lm e nts d'un tableau, ilsoit possible de lim ite r
l'initialisation nx*NY lm e nts cons cutifs).
*Nous avons re pris la fonction aleat de l'e xe rcice pr cdent. 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 alatoire s .
Enonc
Calculer une valeur approch e d e pi, par la m th ode s uivante :
- on tire un ce rtain nom bre de points au h asard dans un carr donn.
- on d te rm ine le rapport e ntre le nom bre de ce s points apparte nant au ce rcle inscrit dans le carr e t le nom bre total
de points tir s . Ce rapport fournit une e s tim ation de la valeur de pi/4.
Le nom bre totalde points tire r s e ra fourni e n donn e .
Exe m pl
e
combien de points ? 10000
estimation de pi avec 10000 points : 3.164800e+000
144
________________________________________________________________________________________
ANALYSE
Nous ch oisirons un carr de ct 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 prcism e nt, pour ch aq ue point,
nous dte 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 xpose 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 considrerons 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) ;
float x, y,
d2,
pi ;
int np,
nc,
i ;
/*
/*
/*
/*
/*
/*
145
D ISCUSSIO N
Notre fonction de tirage alatoire 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 alise 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 dpas s , le
program m e affich e ra la r capitulation des diff re nte s propositions.
Exe m pl
e
Devinez le nombre que j'ai choisi (entre 1 et 1000)
votre proposition : 500
----------- trop grand
146
trop grand
trop grand
trop grand
trop grand
trop grand
trop grand
trop petit
trop grand
exact
________________________________________________________________________________________
ANALYSE
Le program m e com m e nce ra par tire r un nom bre e ntie r au h asard, suivant la dm arch e e xpose dans l'analyse de
l'e xe rcice III-1.
Ildevra e nsuite r p te r l'action :
faire joue r le joue ur
jusqu' ce q ue le joue ur ait gagn ou q u'ilait dpas s le nom bre m axim alde coups perm is.
L'action e n q ue s tion consiste s im plem e nt :
147
Program m e
#include <stdio.h>
#include <stdlib.h>
#define NCOUPS 15
#define NMAX 1000
main()
{
int aleat(int) ;
int nc,
ndevin,
n,
prop[NMAX],
i ;
/*
/*
/*
/*
/*
/* droulement 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 rsultats */
148
}
/*******************************************************/
/* fonction de tirage alatoire 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 alatoire 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 propose 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
________________________________________________________________________________________
149
Enonc
R aliser un program m e q ui ch oisit au h asard une com binaison de 5 ch iffre s (com pris e ntre 1 e t 8) e t q ui de m ande
l'utilisate ur de la devine r. A ch aq ue proposition, le program m e pr cis e ra :
- le nom bre de ch iffre s exacts propos s la bonne pl
ace,
- le nom bre de ch iffre s exacts m ais proposs la m auvaise pl
ace.
Les diff re nte s propositions du joue ur s e ront fournie s s ous la form e de 5 ch iffre s cons cutifs (sans aucun s parate ur),
valids par re turn.
Le program m e devra traite r conve nablem e nt le cas des r pons e s incorre cte s : lettre la place d'un ch iffre , r pons e trop
courte ou trop longue , ch iffre incorre ct (nulou suprieur 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
proposition ? :
45678
proposition ? :
** incorrect **
proposition ? :
** incorrect **
proposition ? :
56789
proposition ? :
11333
proposition ? :
11313
0 P 1 C
0 P 0 C
1133
11332
3 P 1 C
4 P 0 C
5 P 0 C
vous avez trouv en 7 coups
________________________________________________________________________________________
150
ANALYSE
Ilpara
t as s e z nature ld'utiliser un tableau 5 lm 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 faon, 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 considrer 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 concide 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 plaant, 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 concide 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 considrer les ch iffres dj supprim s du tirage : ils ris q ue raie nt d' tre trouv s gaux
d'autre s ch iffre s s upprim s de la proposition.
Ce tte m th ode q ui d truit le tirage nous oblige n ce s s aire m e nt e n faire une copie avant d'entam e r l'analyse de la
proposition.
Nous avons ch oisi de raliser trois fonctions :
- tirage : tirage au h asard de la com binaison (tableau de 5 e ntie rs) devine r.
- e ntre e : e ntr e d e la proposition du joue ur. Ilpara
t logiq ue q ue ce tte fonction fournis s e ce tte proposition dans un
tableau d'e ntie rs. Toute fois, afin de traite r conve nablem e nt les cas de r pons e s incorre cte s , la proposition du joue ur
s e ra tout d'abord lue dans une ch a
ne l'aide de la fonction cge ts (son m canism e e s t dcrit dans l'e xe rcice II-4).
- analy s e : analyse de la proposition du joue ur, suivant l'algorith m e dcrit pr cdem m e nt.
Program m e
/* nombre de positions */
/* nombre de chiffres (ici, de 1 a 8) */
/* nombre maximal de coups */
main()
{
void tirage (int []) ;
int entree (int []) ;
void analyse(int [], int[], int[], int []) ;
int tir[NPOS],
prop[NPOS],
ncoup,
bpos,
bchif ;
/*
/*
/*
/*
/*
/*****************************/
/*
prototypes fonctions */
/*****************************/
/* initialisations */
tirage (tir) ;
ncoup = 0 ;
/* droulement 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 rsultats */
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") ;
}
}
151
152
/*************************************************/
/* fonction de tirage de la combinaison secrte */
/*************************************************/
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] ;
/* chane o sera lue la proposition du joueur */
int i ;
/* lecture proposition joueur dans chane ch */
ch[0] = NPOS+1 ;
/* prparation longueur maxi chane lue */
cgets (ch) ;
/* contrles */
if (strlen (&ch[2]) != NPOS) return(-1) ;
for (i=2 ; i<NPOS+2 ; i++)
if (ch[i] < '1' || ch[i] > '1'+NCHIF-1) return(-1) ;
/* extraction des chiffres choisis */
for (i=0 ; i<NPOS ; i++)
prop[i] = ch[2+i] -'0' ;
return (0) ;
}
/**************************************************/
/* fonction d'analyse de la proposition du joueur */
/**************************************************/
void analyse (int prop [], int tir [], int bpos [] , int bchif [])
{
int tirbis[NPOS],
/* double de la combinaison secrte */
i, j ;
/* recopie de la combinaison secrte */
for (i=0 ; i<NPOS ; i++) tirbis[i] = tir[i] ;
153
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 dfinis 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 contrles
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 dcision
de dem ande r, e n cas d'erreur, une nouve lle proposition au joue ur e s t prise dans le program m e principale t non dans la
fonction e lle-m m e .
*Le s argum e nts de la fonction analy s e sont transm is par leur adre s s e , afin q ue leur valeur puis s e tre m odifi e . C'e s t ce
q ui justifie leur d claration sous form e de pointe urs sur de s e ntie rs. N'oublie z pas q ue les nom s de tableaux
corre s ponde nt leur adre s s e ;c'e s t ce q ui justifie q ue dans l'appe lde analy s e , on trouve e ffe ctive m e nt les sym boles prop
e t tir, alors q ue , par ailleurs, on y trouve & bpos e t & bch if.
*D ans la boucle s uivante (du program m e principal) :
154
l'e xpre s s ion figurant dans w h ile utilise un "oprate 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 nlve 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 alatoire 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 dcroissantes d'un tableau d'e ntie rs, e n utilisant l'algorith m e dit "par e xtraction
sim ple" q ui se dfinit de la m ani re s uivante :
- O n re ch e rch e le plus grand de s n lm e nts du tableau.
- O n ch ange ce t lm e nt ave c le pre m ie r lm e nt du tableau.
- Le plus petit lm e nt s e trouve alors e n pre m i re position. O n pe ut alors appliq ue r les deux op rations prcdente s
aux n-1 lm e nts re s tants, puis aux n-2, ... e t ce la jus q u' ce q u'ilne re s te plus q u'un s e ul lm e nt (le dernie r - q ui
e s t alors le plus petit).
Le program m e affich e ra tous les "r s ultats inte rm diaire s ", c'e s t- -dire les valeurs du tableau, apr s ch aq ue ch ange de
deux lm e nts.
Exe m pl
e
combien de valeurs trier : 8
donnez vos valeurs trier
156
3 9 2 7 11 6 2 8
---- valeurs trier ---3
9
2
7
11
6
11
11
11
11
11
11
11
6
6
6
6
3
3
3
2
2
2
2
2
2
2
8
8
2
2
2
2
2
9
9
9
9
9
9
9
2
2
8
8
8
8
8
7
7
7
7
7
7
7
3
3
3
3
6
6
6
________________________________________________________________________________________
ANALYSE
L'algorith m e propos par l' nonc pe ut s e form aliser com m e s uit, e n te nant com pte des conve ntions d'indice s propre s au
langage C :
R p te r, pour i variant de 0 n-2 :
- re ch e rch e r k
Program m e
#include <stdio.h>
#define NMAX 100
main()
{
int t [NMAX],
nval,
kmax,
tempo,
/*
/*
/*
/*
Com m e ntaire s
Ce program m e fonctionne pour toute s les valeurs de NMAX, en particulie r :
- pour NM A X inf rie ur ou gal 0, ilne fait rie n,
- pour NM A X = 1, illit une valeur q u'ilaffich e te lle q ue lle.
*/
*/
*/
*/
*/
157
158
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 dfinit ainsi (n re pr s e ntant le nom bre d'lm e nts du tableau) :
O n parcourt l'e ns e m ble du tableau, de puis sa fin jus q u' son dbut, e n com parant deux lm 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 lm 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 lm e nts re s tants, puis ave c les n-2 lm e nts re s tants, e t
ainsi de suite ... jus q u' ce q ue :
- soit l'avant-dernier lm e nt ait t clas s (le dernie r tant alors obligatoire m e nt sa place ),
- soit q u'aucune pe rm utation n'ait e u lie u pe ndant la derni re pas s e (ce q ui prouve alors q ue l'e ns e m ble du tableau
e s t conve nablem e nt ordonn ).
O n pr voira e n argum e nts :
- l'adresse du tableau trie r,
- son nom bre d'lm e nts,
- un indicate ur pr cisant si l'on souh aite q ue la fonction affich e les valeurs du tableau apr s ch aq ue pe rm utation (0
pour non, 1 pour oui).
Exe m pl
e
combien de valeurs trier : 6
donnez vos valeurs trier
2 8 4 7 0 8
---- valeurs trier ---2
8
4
7
0
8
2
2
8
8
4
0
0
4
7
7
8
8
0
2
2
2
8
8
4
4
4
4
8
7
7
7
7
8
159
8
8
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 spcifiant le rang partir duq ue lle tableau
n'e s t pas e ncore tri . Initialem e nt, ilfaudra pr voir q u'aucun lm 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 lm 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 dsigne pas le dernie r lm 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 lm e nts de rang j e t j+1, j
dcrivant tous les lm e nts depuis l'avant-dernier jus q u' ce lui de rang i+1 (autre m e nt dit, j dcroissant 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 sr, pris soin d'initialiser
pe rm ut FAUX au dbut 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 dbut de travail.
Program m e
#include <stdio.h>
#define VRAI 1
#define FAUX 0
#define NMAX 100
main()
{
160
161
{ permut = FAUX ;
for (j=nval-2 ; j>i ; j--)
{ if ( t[j] > t[j+1] )
{ permut = VRAI ;
tempo = t[j] ;
t[j] = t[j+1] ;
t[j+1] = tempo ;
if (affich)
{ for (k=0 ; k<nval ; k++)
printf ("%5d", t[k]) ;
printf ("\n") ;
}
}
}
i++ ;
}
}
Com m e ntaire s
D ans la fonction bullle, la dclaration :
int * t ;
e s t q uivalente :
int t[] ;
D ISCUSSIO N
Les deux algorith m e s proposs dans l'e xe rcice pr cdent 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 dbouch 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 , dlicat 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
lm e nt de l'une s oit inf rie ur ou gal tout lm e nt de l'autre . Une te lle s e gm e ntation pe ut tre r alise par
l'algorith m e s uivant :
- Pre ndre un lm e nt au h asard (on pe ut pre ndre l' lm e nt du m ilie u). Soit m sa valeur.
- R e ch e rch e r, de puis le dbut du tableau, le pre m ie r lm 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 lm 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 lm 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 (dcrite dans l'e xe rcice IV-1) pour trie r un tableau de
ch a
ne s , par ordre alph abtiq ue (sans distinction e ntre m ajuscules e t m inuscules ).
Ce tte fonction re ce vra, e n argum e nt :
- l'adresse d'un tableau de pointe urs sur les ch a
ne s conce rn e s ,
- le nom bre de ch a
ne s trie r.
Le tri propre m e nt dit porte ra, non sur les valeurs des ch a
ne s e lles -m m e s , m ais uniq ue m e nt sur le tableau de pointe urs.
O n te s te ra ce tte fonction l'aide d'un program m e principalcr ant un sim ple tableau de ch a
ne s (ayant donc ch acune une
longue ur m axim ale donne).
163
Exe m pl
e
combien de chanes trier ? 7
donnez vos 7 chanes (validez chacune par 'return')
C
Turbo C
Basic
Pascal
Turbo Pascal
Fortran
ADA
ANALYSE
La m th ode de tri a t dcrite dans l'e xe rcice IV-1. Il e s t ce pe ndant n ce s s aire de procder
d'adaptations :
plusieurs sorte s
164
Program m e
#include <stdio.h>
#include <string.h>
#define NCHMAX 100
/* nombre maximal de chanes traiter */
#define LGMAX 25
/* longueur maximale d'une chane (sans \0) */
main()
{
void trichaines (char * *, int ) ; /* prototype fonction de tri */
char chaines [NCHMAX] [LGMAX+1] ;
/* tableau des chanes */
char * adr [NCHMAX] ;
/* tableau pointeurs sur les chanes */
int nch,
/* nombre de chane trier */
i ;
/* lecture des chanes et prparation du tableau de pointeurs */
printf ("combien de chanes trier ? ") ;
scanf ("%d", &nch) ;
if (nch > NCHMAX) nch = NCHMAX ;
getchar() ;
/* pour forcer la lecture de fin de ligne */
printf ("donnez vos %d chanes (validez chacune par 'return')\n", nch) ;
for (i=0 ; i<nch ; i++)
{ fgets (chaines[i], LGMAX+1, stdin) ; /* lit au maximum LGMAX caractres */
adr[i] = chaines[i] ;
}
/* tri des pointeurs sur les chanes */
trichaines (adr, nch) ;
/* affichage des chanes aprs tri */
/* attention aux chanes de longueur maximum !! */
printf ("\n\nvoici vos chanes tries\n") ;
for (i=0 ; i<nch ; i++)
printf ("%s", adr[i]) ;
}
void trichaines (char * * adr, int nch)
/* adr : adresse tableau de pointeurs sur chanes trier */
/* nch : nombre de chanes
*/
{
char * tempo ;
/* pointeur temporaire pour l'change de 2 pointeurs */
int kmax,
165
i, j ;
for (i=0 ; i<nch-1 ; i++)
{ kmax = i ;
for (j=i+1 ; j<nch ; j++)
if ( stricmp (adr[kmax], adr[j]) > 0 ) kmax = j ;
tempo = adr[kmax] ;
adr[kmax] = adr[i] ;
adr[i] = tempo ;
}
}
Com m e ntaire s
*Ici, les ch a
ne s trie r ont t plac e s (par le program m e principal) dans un tableau de caract re s (nom m ch aine s )
deux dim e nsions. Note z bie n q u'ilne s e rait pas possible d'en inve rs e r l'ordre des dim e nsions ;ile s t e n e ffe t n ce s s aire
q ue tous les caract res d'une m m e ch a
ne s oie nt cons cutifs.
*Bie n q ue ce la n'ait pas t e xplicite m e nt dem and par l' nonc , nous avons prvu un contrle s ur la longue ur de s
ch a
ne s fournie s au clavie r ;pour ce faire , nous avons fait appe l la fonction fge ts, e n l'appliq uant au fich ie r stdin.
L'instruction :
fgets (chaines[i], LGMAX+1, stdin) ;
lit au m axim um LGM AX caract re s s ur stdin e t les range l'adre s s e ch aine [i], e n com pltant le tout par un z ro de fin de
ch a
ne . Ainsi, on vite les ris q ues de dborde m e nt m m oire q ue pr s e nte ge ts.
Toute fois un lge 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 dlim 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 prcism 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 prsent).
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 problm 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 cdent, il s e rait
n ce s s aire d'oprer une lecture d'une ch a
ne par ge ts (ilfaudrait pr voir un e m place m e nt ce t e ffe t!).
*D ans la fonction trich aine s , le pre m ie r argum e nt adr a t dclar par :
char * * adr
Ils'agit d'un pointe ur sur le tableau de pointe urs sur les diff re nte s ch a
ne s . Nous aurions pu galem e nt le dclare 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]
*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 dfinie 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 dfini par le code des caract re s (sans te nir
com pte de la diff re nce e ntre m ajuscules e t m inuscules pour les 26 lettres de l'alph abet),
- nulle s i les deux ch a
ne s s ont gales ,
- n gative s i la pre m i re ch a
ne arrive avant la s e conde .
D ISCUSSIO N
D 'une m ani re g n rale, iln'e s t pas nce s s aire q ue les ch a
ne s trie r soient, com m e ici, im plant e s e n m m oire de
m ani re cons cutive .
D e m m e , la fonction trich aine s propos e pourrait tout aussi bien oprer 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).
167
Enonc
R aliser une fonction q ui fusionne deux tableaux d'e ntie rs ordonn s par valeurs croissante s .
O n pr voira e n argum e nts :
- les adresses des trois tableaux conce rn s ,
- le nom bre de valeurs de ch acun des deux tableaux fusionne r.
Pour te s te r ce tte fonction, on crira un program m e principalq ui lit au clavie r de ux e ns e m bles de valeurs q ue l'on trie ra
au pr alable l'aide de la fonction bulle r alise dans l'e xe rcice IV-2.
Exe m pl
e
combien de
donnez vos
3 9 2 8 11
combien de
donnez vos
12 4 6 3 1
12
168
ANALYSE
La dm 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 lproblm e . Ilsuffit, e n
e ffe t, d'avance r e n parallle 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 lm e nts e t e n l'introduisant dans le tableau r s ultant t. Plus prcism e nt, nous som m e s am e n s utiliser
trois indice s :
- i1 : pre m ie r lm e nt de t1 non e ncore pris e n com pte ,
- i2 : pre m ie r lm e nt de t2, non e ncore pris e n com pte ,
- i : e m place m e nt du proch ain lm e nt introduire dans t.
Nous initialisons ces trois indice s z ro (com pte te nu de s conve ntions du C). Nous r p tons alors le traite m e nt suivant :
Ch oisir le plus petit des lm 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' lm 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
#define NMAX2 100
main()
{
void fusion(int [], int [], int [], int, int ) ;
/* proto fonction de fusion */
void bulle(int [], int) ;
/* proto fonction servant assurer l'ordre des tableaux
*/
int t1 [NMAX1],
t2 [NMAX2],
t [NMAX1+NMAX2] ;
int nval1,
nval2,
k ;
/*
/*
/*
/*
/*
premier tableau
second tablleau
tableau rsultant
nombre de valeurs
nombre de valeurs
fusionner */
fusionner */
de la fusion */
prlever dans t1 */
prlever dans t2 */
") ;
/********************************************************/
/*
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 rsultant
*/
/* 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 rsultant */
k ;
169
170
/*******************************************************/
/* fonction de tri d'un tableau (mthode de la bulle) */
/*******************************************************/
void bulle (int t[], int nval)
{
int i, j, tempo, k, permut ;
i = -1 ; permut = 1 ;
while (i < nval-1 && permut)
{ permut = 0 ;
for (j=nval-2 ; j>i ; j--)
if ( t[j] > t[j+1])
{ permut = 1 ;
tempo = t[j] ; t[j] = t[j+1] ; t[j+1] = tempo ;
}
i++ ;
}
}
Com m e ntaire s
*Pour e ffe ctue r le tri pr alable des deux tableaux fournis e n donn e , nous avons re pris la fonction bulle r alise 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 .
171
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 , dclar 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 lre r la re ch e rch e e n procdant com m e s uit :
- O n consid re l' lm 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' lm e nt ch e rch ,
*on e s t sr q u'ilne figure pas dans le tableau.
Exe m pl
es
code article recherch : 24
le code 24 n'existe pas
________________
code article recherch : 19
article de code 19
libell : Balance de mnage
prix :
278.00
________________________________________________________________________________________
172
ANALYSE
L'algorith m e propos par l' nonc s ugg re d'utiliser trois variables pe rm e ttant de spcifie r, un instant donn , la partie
du tableau dans laq ue lle s 'e ffe ctue la re ch e rch e :
gauch e : dbut de la partie re s tant e xplore r,
droite : fin de la partie re s tant e xplore r,
m ilie u : position ch oisie pour le "m ilie u" de ce tte partie re s tant e xplore r.
Note z dj q ue ce tte notion de m ilie u e s t q ue lque pe u am bigu. Nous convie ndrons q u'e lle corre s pond la partie e nti re
de la m oye nne des indice s gauch e e t droite .
L'algorith m e de re ch e rch e par dich otom ie pe ut alors s' nonce r ainsi (t dsignant le tableau, n le nom bre de code s e t x
l' lm e nt ch e rch ) :
- Initialiser gauch e e t droite de faon q u'ils dsignent l'e ns e m ble du tableau.
- R p te r le traite m e nt suivant :
*D te rm ine r le m ilie u de la partie e xplore r :
m ilie u = (gauch e + droite ) / 2
*Com pare r l' lm e nt ch e rch x ave c t(m ilie u) :
+ S'ils sont gaux, l' lm e nt ch e rch e s t localis en position m ilie u,
+ Si x e s t suprieur t(m ilie u), l' lm e nt ch e rch ne pe ut s e s itue r q ue dans la partie droite ;on r alise
l'affe ctation :
debut = m ilie u + 1
+ dans le cas contraire , l' lm e nt ch e rch ne pe ut s e s itue r q ue dans la partie gauch e ;on r alise l'affe ctation :
fin = m ilie u - 1
Ilnous re s te spcifie r la condition d'arr t (ou de poursuite ) de ce tte r p tition. O n pe ut dj note r q ue , ch aq ue
parcours de la boucle, soit la valeur de gauch e augm e nte , soit ce lle de droite dim inue . Ainsi, on e s t sr q u'au bout d'un
nom bre fini de tours on aboutira l'une des situations suivante s :
- l' lm e nt a t localis.
- la valeur de gauch e e s t suprieure ce lle de droite .
Elles nous fournis s e nt donc tout nature llem e nt la condition de fin de notre boucle.
173
Note z q ue , dans un pre m ie r te m ps, la valeur de gauch e devie nt gale ce lle de droite ;m ais, dans ce cas, nous ne
savons pas encore si le s e ul lm 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
supplm e ntaire pour s'e n assure r.
Program m e
#include <stdio.h>
/* ------ structure contenant les informations relatives aux
/*
diffrents articles
-------------#define NBART 6
/* nombre total d'articles */
typedef struct { int code ;
/* code article */
char * lib ;
/* libell */
float pu ;
/* prix unitaire */
} t_article ;
*/
*/
/*
/*
/*
/*
/*
/*
174
Com m e ntaire s
*Note z bie n la condition r gissant la boucle while :
gauche <= droite && !trouve
- D 'une part, com m e nous l'avons dit dans notre analyse, nous poursuivons notre e xploration, m m e q uand les
valeurs de gauch e e t droite sont gales , de m ani re savoir si le s e ul lm 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
supplm 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 droulem 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 donnes 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 donnes
statiq ue s s ont dfinies d s la com pilation ;la ge s tion des donnes autom atiq ue s re s te transpare nte au program m e ur e t
s e ules les donnes 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 donnes dynam iq ue s fournit des solutions des problm e s te ls q ue :
- ge s tion de donnes 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 considrer ne sera pas fix par le program m e , m ais fourni e n donn e . Le
program m e alloue ra dynam iq ue m e nt l'e m place m e nt m m oire n ce s s aire au d roulem e nt de l'algorith m e . En cas de
m m oire insuffisante , ildem ande ra l'utilisate ur de form uler une dem ande m oins im portante .
O n s'astre indra ici utiliser la fonction m al
l
oc.
Exe m pl
e
combien d'entiers voulez-vous examiner : 200
176
17
59
103
157
19
61
107
163
23
67
109
167
29
71
113
173
________________________________________________________________________________________
ANALYSE
L'algorith m e lui-m m e a dj 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 dm 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
#define FAUX 0
main()
{
unsigned n,
* raye,
prem,
i ;
int na ;
177
/* 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 rsultats */
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 alise par l'instruction :
raye = (unsigned *) malloc ( (n+1)*sizeof(unsigned) ) ;
dans laq ue lle raye e s t un pointe ur sur des entie rs non sign s .
O r, le prototype de m alloc e s t pr cis m e nt :
void * malloc (size_t) ;
Le r s ultat fourni par m alloc e s t un "pointe ur g n riq ue " q ui pe ut tre conve rti im plicite m e nt e n un pointe ur de n'im porte
q ue ltype . Aussi, l'op rate ur de "cast" (unsigne d *) n'e s t pas indispensable ici. Notre instruction d'allocation m m oire
aurait pu s' crire :
raye = malloc ( (n+1) * sizeof(unsigned) ) ;
178
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 dfini (par type de f) dans stdlib.h . Le type
e xact corre s pondant dpend de l'im plm 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 dbut 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 dfini 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 dsignait un tableau
d'entie rs, tandis q u'ici ildsigne un pointe ur sur des entie rs. Ce la e s t possible parce q u'e n langage C, un nom de tableau
e s t un pointe ur (constant).
D ISCUSSIO N
*Le ch oix du type unsigne d pour n e s t q ue lque pe u arbitraire ;ile s t guid par le fait q ue m alloc adm e t g n ralem e nt un
argum e nt de ce type . En supposant q ue te le s t le cas, on constate q u'alors l'e xpre s s ion :
(n+1) * sizeof (unsigned)
conduit des valeurs e rrones d s q ue la valeur de n*size of(int) dpas s e la capacit du type int (n'oublie z pas q u'iln'y a
pas de dte ction de dpas 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 problm 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 plm 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 plm e ntation conce rn 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 allous 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 rves dans un tableau de pointe urs. Ce dernie r s e ra r s e rv dans le program m e (e n clas s e autom atiq ue ) e t sa taille
(fixe ) im pos e ra donc une valeur m axim ale au nom bre de ch a
ne s q u'ils e ra ainsi possible de traite r.
L'utilisate ur signalera q u'ila fourni sa derni re ch a
ne e n la faisant suivre d'une ch a
ne "vide".
Le program m e affich e ra e nsuite les ch a
ne s lue s , titre de sim ple contrle.
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 dpas s e r 127
caract re s .
Exe m pl
e
----- chane
C
----- chane
Turbo C
----- chane
Basic
----- chane
Pascal
----- chane
Turbo Pascal
----- chane
fin cration
180
Basic
------- chane numro 4
Pascal
------- chane numro 5
Turbo Pascal
________________________________________________________________________________________
ANALYSE
L' nonc nous im pose donc de dfinir, 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 orise dans le tableau de pointe urs.
Le traite m e nt s e ra inte rrom pu :
- soit q uand le tableau de pointe urs e s t plein,
- soit q uand l'utilisate ur fournit une ch a
ne vide.
D e plus, ch aq ue allocation r alise par m alloc, on s'assure ra q ue l'e s pace m m oire n ce s s aire a pu tre obte nu. D ans le
cas contraire , on pr voira d'inte rrom pre le program m e .
Program m e
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NCHMAX 1000
#define LGLIGNE 127
main()
{
char ligne [LGLIGNE+1],
* adr [NCHMAX],
* ptr ;
int nch,
i ;
/*
/*
/*
/*
181
Com m e ntaire s
*Ici, com pte te nu de ce q ue nous prcisait 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;prdfini dans le
fich ie r stdlib.h , ilcorre s pond la constante pointe ur nulle.
*La cr ation de s ch a
ne s e s t r alise par une boucle tant q u e (instruction w h ile), dans laq ue lle nous avons prvu de ux
autre s s ortie s :
182
Exe rcice s e n langage C
- une s ortie par bre ak , dans le cas o l'utilisate ur a fourni une ch a
ne vide,
- un arr t e xce ptionne ldu program m e par e xit, dans le cas o l'allocation dynam iq ue a ch ou . Ce tte fonction (dont
le prototype figure dans stdlib.h ) re q uie rt un argum e nt ;sa valeur e s t transm ise au syst m e e t e lle pourrait
ve ntue llem e nt tre r cup r e par d'autre s program m e s . Note z q ue , e n l'abs e nce de l'instruction #include re lative
stdlib.h , le com pilate ur acce pte un appe lde e xit sans argum e nt (ile s t incapable de dte cte r l'e rre ur - laq ue lle n'a
d'ailleurs aucune incide nce s ur l'e x cution du program m e lui-m m e ).
Nature llem e nt, beaucoup d'autre s form ulations s e raie nt possibles .
D ISCUSSIO N
*Le fait de r s e rve r le tableau dans le program m e (e n clas s e autom atiq ue ) im pos e une lim ite au nom bre de ch a
ne s q u'il
e s t ainsi possible de traite r ;ce tte lim ite e s t ind pe ndante de la m m oire r e llem e nt disponible. O n pe ut am liore r
q ue lque pe u la situation e n faisant galem e nt al
l
ouer dynam iquem ent l
'espace ncessaire 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 donne fournie par l'utilisate ur com m e dans l'e xe rcice s uivant.
Enonc
Ecrire un program m e pe rm e ttant de trie r par ordre alph abtiq ue des
pr cdent, on alloue ra dynam iq ue m e nt des em place m e nts m m oire
leurs adre s s e s s e ront cons e rves dans un tableau de pointe urs.
e m place m e nt allou dynam iq ue m e nt e n dbut de program m e ;pour
valeur m axim ale du nom bre de ch a
ne s q u'ils e ra am e n fournir.
ch a
ne s fournie s e n donn e . Com m e dans l'e xe rcice
aux ch a
ne s , au fur e t m e s ure de leur lecture , e t
Par contre , ici, ce dernie r ve rra, lui aussi, son
ce faire , on de m ande ra l'utilisate ur de fournir une
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
183
de chanes ? 100
numro 1 (return pour finir)
numro 2 (return pour finir)
numro 3 (return pour finir)
numro 4 (return pour finir)
numro 5 (return pour finir)
numro 6 (return pour finir)
numro 7 (return pour finir)
numro 8 (return pour finir)
fin cration
ANALYSE
Ilnous suffit e n fait d'adapte r le program m e de l'e xe rcice pr cdent, e n lui adjoignant :
- la r s e rvation dynam iq ue du tableau de pointe urs,
- le tri du tableau de ch a
ne s ainsi cr , par r organisation de s pointe urs. Nous utiliserons pour cela l'algorith m e de tri
par e xtraction sim ple Ce lui-ci a t e xpos dans l' nonc de l'e xe rcice V-1 e t son adaptation au tri de ch a
ne s a t
e xpliq ue dans l'analyse de l'e xe rcice V-2.
184
Program m e
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LGLIGNE 127
main()
{
char ligne [LGLIGNE+1],
* * adr,
* ptr,
* tempo ;
unsigned nchmax,
nch,
i, j, kmax ;
/*
/*
/*
/*
/*
/*
*/
;
*/
*/
185
Com m e ntaire s
*D ans le program m e de l'e xe rcice V-2, le sym bole adr dsignait un tableau de pointe urs. Ici, ce m m e sym bole dsigne
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 cdent program m e , adr tait une constante pointeur dont la valeur tait ce lle de l'adresse de dbut 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
dbut 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 alises 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 donnes incorrecte s (ch a
ne trop longue
pour ge ts, donn e non num riq ue pour scanf), un problm e s upplm 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 supplm e ntaire s ). Si la lecture s uivante e s t, son tour, e ffe ctu e par
scanf, aucun problm 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 alise, 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
Ecrire un program m e q ui cr e une liste ch a
ne d' lm e nts com portant ch acun :
- un nom (ch a
ne ) d'au m axim um 10 caract re s ,
- un ge .
Le s inform ations corre s pondante s s e ront lue s au clavie r e t l'utilisate ur frappe ra un nom "vide" apr s les donnes relative s
au de rnie r lm e nt.
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 possderont com m e uniq ue argum e nt
l'adresse de dbut de la liste (pointe ur sur le pre m ie r lm e nt).
Exe m pl
e
om : Laurence
age : 19
nom : Yvette
age : 35
nom : Catherine
age : 20
nom : Sebastien
age : 21
nom :
NOM
Sebastien
Catherine
Yvette
Laurence
AGE
21
20
35
19
________________________________________________________________________________________
ANALYSE
Ch aq ue lm 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
lm e nt de m m e type . Ce la fait inte rve nir une ce rtaine "r cursivit " dans la dclaration 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 lm 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 donnes corre s pondante s ont t introduite s .
- Ajoute r ch aq ue nouve l lm e nt e n dbut 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 donnes 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 alise 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 dsigner le pre m ie r lm 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 lm e nt e n dbut de liste ,
savoir :
- cr e r dynam iq ue m e nt un e m place m e nt pour un nouve l lm 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 lm e nt l'ancie nne valeur de de but,
- affe cte r de but l'adresse de ce nouve l lm e nt.
Nous convie ndrons, de plus, q ue le dernie r lm e nt de la liste poss de un pointe ur nul, ce q ui nous facilite ra
l'initialisation de l'algorith m e ;e n e ffe t, ce lle-ci s e ram ne alors l'affe ctation de but d'une valeur nulle.
Program m e
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LGNOM 20
main()
{
void creation (t_element * *) ;
void liste (t_element *) ;
t_element * debut ;
creation (&debut) ;
liste (debut) ;
}
/****************************************************/
/*
fonction de cration d'une liste chane
*/
/****************************************************/
void creation (t_element * * adeb)
{
char nomlu [LGNOM+1] ;
/* pour lire un nom au clavier */
t_element * courant ;
/* pour l'change de valeurs de pointeurs */
* adeb = NULL ;
while (1)
/* boucle de cration apparemment infinie ... */
{
/* ... mais, en fait, interrompue sur "nom vide" */
printf ("nom : ") ;
gets (nomlu) ;
if (strlen(nomlu))
{ courant = (t_element *) malloc (sizeof(t_element)) ;
strcpy (courant->nom, nomlu) ;
printf ("age : ") ;
scanf ("%d", &courant->age) ;
getchar() ;
/* pour sauter le \n */
courant->suivant = * adeb ;
* adeb = courant ;
}
else break ;
/* sortie boucle si nom vide */
}
}
/******************************************************/
/*
fonction de liste d'une liste chane
*/
/******************************************************/
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 ;
}
}
189
19 0
Com m e ntaire s
*Nous avons ici ch oisi de dclare r notre s tructure un nive au globale t de faire appe l type de f. Ce tte dclaration 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 dclarations des
lm 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 dfinition de notre s tructure , l' criture :
struct element * suivant
par :
t_element * suivant
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 plutt des "e xe rcices d'cole" destin s vous faire e xplore r diff re nte s
situations e n vous forant 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 problm 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
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 dj fait dans ce rtains des exe rcice s pr cdents.
Nous consid re rons la r ponse de l'utilisate ur com m e corre cte lors q ue le code de re tour de sscanf s e ra gal 1. Si te l
n'e s t pas le cas, nous fe rons nouve au appe l la m m e fonction lecture .
Program m e
#include <stdio.h>
#define LG_LIG 20
main()
{
void lecture (int *) ;
int n ;
/* compteur du nb de valeurs OK */
/* pour lire une ligne au clavier par fgets */
/*
+1 pour tenir compte du \0 de fin
*/
VI. R cursivit
19 3
}
}
Com m e ntaire s
*Note z bie n q u'au s e in de la fonction lecture , au nive au de l'appe lde sscanf, nous voyons appara
tre p e t non & p,
puis q ue ici p e s t dj 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 dfinir une taille
m axim ale pour la ch a
ne lue au clavie r ;nous aurions couru le ris q ue de "dbordem e nt m m oire ", dans le cas o
l'utilisate ur aurait fourni une r pons e trop longue .
D ISCUSSIO N
Ch aq ue nouve lappe lde lecture e ntra
ne l'allocation autom atiq ue , sur la pile, d'e m place m e nts pour :
- l'argum e nt p,
- les obje ts locaux : com pte e t ligne .
O r, e n fait, ne s ont n ce s s aire s q ue les valeurs corre s pondant au de rnie r appe lde lecture (ce lui o la lecture s 'e s t
conve nablem e nt droule ) ;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 upplm 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
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 cdent dans leq ue lle m m e problm e e s t r s olu par l'e m ploi d'une
fonction ave c argum e nt e t sans valeur de re tour.
Exe m pl
e
donnez un nombre entier : un
** rponse incorrecte - redonnez la : '
** rponse incorrecte - redonnez la : 40
-- merci pour 40
________________________________________________________________________________________
ANALYSE
Com m e pr cdem m e nt, au s e in de notre fonction (nom m e lecture ), nous lirons la valeur atte ndue l'aide de fge ts
associ sscanf. Nous consid re rons la r ponse de l'utilisate ur com m e corre cte lors q ue le code de re tour de sscanf s e ra
gal 1. Si ce la n'e s t pas le cas, nous fe rons de nouve au appe l la m m e fonction lecture .
VI. R cursivit
19 5
Program m e
#include <stdio.h>
#define LG_LIG 20
main()
{
int lecture (void) ;
int n ;
/* compteur du nb de valeurs OK */
/* entier lire */
/* pour lire une ligne au clavier par fgets */
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 "dpilem e nt" des appe ls.
*Pre ne z garde ne pas crire :
19 6
car la fonction ne re nve rrait une valeur q ue lors q ue la lecture s e s e rait droule conve nablem e nt. Note z d'ailleurs q ue
dans ce cas, bon nom bre de com pilate urs vous prvie ndrait par un m e s s age d'ave rtissem e nt ("w arning").
Par contre , ils e rait tout fait corre ct (e t q uivalent) d'crire :
if (!compte)
{ printf ("** rponse incorrecte - redonnez la : ") ;
return (lecture()) ;
}
else return (p) ;
D ISCUSSIO N
Le s re m arq ue s faites dans le pr cdent e xe rcice
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 .
Ce tte fois, la fonction possdera 3 argum e nts :
- le m e s s age q u'e lle doit im prim e r avant de lire une valeur (le m e s s age "donne z un nom bre e ntie r :" ne s e ra donc
plus affich par le program m e principal),
- l'adresse de la variable dans laq ue lle on doit lire une valeur,
- le nom bre m axim ald'essais autoris s .
VI. R cursivit
19 7
Elle fournira un code de re tour gal 0 si la lecture a fini par aboutir e t -1 lors q ue la lecture n'a pas pu aboutir dans le
nom bre d'essais im partis.
Com m e dans les deux pr cdents e xe rcice s , on fe ra appe l fge ts associ e sscanf.
Exe m pl
es
donnez un nombre entier : huit
** rponse incorrecte - redonnez-la : 8
-- merci pour 8
____________________
donnez un nombre entier : un
** rponse incorrecte - redonnez-la
** rponse incorrecte - redonnez-la
** rponse incorrecte - redonnez-la
** rponse incorrecte - redonnez-la
-- nombre d'essais dpass
:
:
:
:
deux
trois
quatre
cinq
________________________________________________________________________________________
ANALYSE
Le m e s s age im prim e r s e ra transm is sous form e de l'adresse d'une ch a
ne . La fonction affich e ra ce m e s s age d s son
appe l. Son conte nu de vra donc tre :
donne z un nom bre e ntie r :
dans l'appe linitialde la fonction (r alis dans le program m e principal), e t :
**r pons e incorre cte - re donne z -a :
dans l'appe lde la fonction par e lle-m m e e n cas de r pons e incorre cte .
En ce q ui conce rne le nom bre m axim ald'appe ls, on le transm e ttra par valeur e t on s'arrange ra pour faire dcro
tre s a
valeur de 1 ch aq ue appe l.
La r cursivit des appe ls ce s s e ra lors q ue l'une des deux conditions suivante s s e ra satisfaite :
- valeur lue corre cte - on fournira alors 0 com m e valeur de re tour,
19 8
Exe rcice s e n langage C
- nom bre m axim ald'appe ls dpas s - on fournira alors -1 com m e valeur de re tour.
Program m e
#include <stdio.h>
#define LG_LIG 20
/* longueur maxi information lue au clavier */
main()
{
int lecture (char *, int *, int) ; /* proto fonction (rcursive) de lecture */
int n ;
/* entier lire */
const nessais = 5 ;
/* nombre d'essais autoriss */
if ( lecture ("donnez un nombre entier : ", &n, nessais) != -1)
printf ("-- merci pour %d", n) ;
else printf ("-- nombre d'essais dpasss") ;
}
int lecture (char * mes, int * p, int nmax)
/* mes : adresse message afficher avant lecture */
/* p : adresse de la valeur lire
*/
/* nmax : nombre d'essais autoriss
*/
{
int compte ;
/* compteur du nb de valeurs OK */
char ligne [LG_LIG] ;
/* pour lire une ligne au clavier par fgets */
printf ("%s", mes) ;
fgets (ligne, LG_LIG, stdin) ;
compte = sscanf (ligne, "%d", p) ;
if (!compte)
if (--nmax)
return (lecture ("** rponse incorrecte - redonnez la : ",
p, nmax) ) ;
else return (-1) ;
else return (0) ;
}
Com m e ntaire s
*Nous avons ch oisi ici de faire affich e r le m e s s age :
nom bre d'e s s ais dpass
VI. R cursivit
19 9
dans le program m e principal. Ils'agit l d'un ch oix arbitraire puis q ue nous aurions tout aussi bien pu le faire affich e r par
la fonction e lle-m m e .
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
x = 1 /x
k
k -1
k /2
x = (x
x = (x
pour k positif,
)x
)x
O n te s te ra ce tte fonction l'aide d'un program m e principalpe rm e ttant l'utilisate ur de fournir e n donn e s les valeurs de
x e t de k .
Exe m pl
es
donnez une valeur relle : 4
donnez une puissance entire : -2
4.000000e+000 la puissance -2 = 6.250000e-002
_______________________
200
ANALYSE
L' nonc fournit les "dfinitions r cursive s " e m ploye r.
Program m e
#include <stdio.h>
main()
{
double puissance(double, int) ;
double x ;
int n ;
/* puissance ngative */
/* x puissance 0 gale 1 */
/* x puissance 1 gale x */
/* puissance paire */
/* puissance impaire */
VI. R cursivit
201
Com m e ntaire s
Ile s t pr f rable d'crire :
z = puissance (x, n/2) ;
return (z*z) ;
plutt q ue :
return (puissance (x,n/2) * puissance (x,n/2) ) ;
Enonc
Ecrire une fonction r cursive calculant la valeur de la fonction d'Ack e rm ann, d finie pour m e t n, e ntie rs positifs ou
nuls, par :
A(m ,n) = A(m -1, A(m ,n-1) ) pour m > 0 e t n> 0,
A(0,n) = n+ 1 pour n> 0,
A(m ,0) = A(m -1,1) pour m > 0.
Ce tte fonction possdera e n argum e nt les valeurs de m e t de n et fournira e n r s ultat la valeur de A corre s pondante .
O n visualisera l'e m pilem e nt des appe ls e t leur d pilem e nt e n affich ant un m e s s age accom pagn de la valeur des deux
argum e nts lors de ch aq ue e ntre 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 m pl
e
valeurs de m et
** entre Acker
** entre Acker
** entre Acker
-- sortie Acker
-- sortie Acker
** entre Acker
-- sortie Acker
-- sortie Acker
n ?
(1,
(1,
(0,
(0,
(1,
(0,
(0,
(1,
: 1 1
1)
0)
1)
1) = 2
0) = 2
2)
2) = 3
1) = 3
Acker (1, 1) = 3
________________________________________________________________________________________
Program m e
#include <stdio.h>
main()
{
int m, n, a ;
int acker (int, int) ;
VI. R cursivit
203
if (m<0 || n<0)
a = -1
;
/* cas arguments incorrects */
else if (m == 0)
a = n+1 ;
else if (n == 0)
a = acker (m-1, 1) ;
else
a = acker (m-1, acker(m, n-1) ) ;
printf ("-- sortie Acker (%d, %d) = %d\n", m, n, a) ;
return (a) ;
}
Enonc
R aliser une fonction r cursive proposant une s olution au problm 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 dpart, ces disques sont
e m pils par taille dcroissante s ur le piq ue t num ro 1. Le but du je u e s t de dplace 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 dplace 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
dplace r (n).
Si vous n' te s pas fam iliaris ave c ce type de problm 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
dplacer un disque de 1 en 2
204
dplacer
dplacer
dplacer
dplacer
dplacer
dplacer
dplacer
dplacer
dplacer
dplacer
dplacer
dplacer
dplacer
dplacer
un
un
un
un
un
un
un
un
un
un
un
un
un
un
disque
disque
disque
disque
disque
disque
disque
disque
disque
disque
disque
disque
disque
disque
de
de
de
de
de
de
de
de
de
de
de
de
de
de
1
2
1
3
3
1
1
2
2
3
2
1
1
2
en
en
en
en
en
en
en
en
en
en
en
en
en
en
3
3
2
1
2
2
3
3
1
1
3
2
3
3
________________________________________________________________________________________
ANALYSE
Pour n=1, la solution e s t vidente ;ilsuffit de dplace 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 suprieur 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 considrer que le problm e consiste dplace 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
dcom pos e e n trois oprations plus sim ples :
- dplace 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 ,
- dplace 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
dplace r).
Ce la nous conduit la r alisation d'une fonction r cursive possdant com m e argum e nts :
- le nom bre de disques dplace r,
- le num ro du piq ue t "de dpart",
- le num ro du piq ue t "d'arriv e ",
- le num ro du piq ue t "inte rm diaire ".
VI. R cursivit
Program m e
#include <stdio.h>
main()
{
void hanoi (int, int, int, int) ;
int nd
;
/* nombre total de disques */
printf ("combien de disques ? ") ;
scanf ("%d", &nd) ;
hanoi (nd, 1, 3, 2) ;
}
/***********************************************/
/* fonction rsolvant le pb des tours de hanoi */
/***********************************************/
void hanoi (int n, int depart, int but, int inter)
/* n
: nombre de disques dplacer */
/* depart : tour d'o l'on part */
/* but
: tour o l'on arrive */
/* inter : tour intermdiaire */
{
if (n>0)
{ hanoi (n-1, depart, inter, but) ;
printf ("dplacer un disque de %d en %d\n", depart, but) ;
hanoi (n-1, inter, but, depart) ;
}
}
205
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
Ecrire un program m e de cration squentiel
l
e d'un fich ie r com portant, pour un ce rtain nom bre de pe rsonne s , les
inform ations suivante s , fournie s au clavie r :
- nom (au m axim um 20 caract re s ),
- ge ,
- nom bre d'enfants,
- ge de ch acun des diff re nts e nfants ;on ne dem ande ra (e t donc on n'e nre gistre ra) q ue l' ge des 15 pre m ie rs e nfants
(m ais le nom bre figurant dans le ch am p pr cdent pourra tre s up rie ur 15).
L'utilisate ur fournira un nom "vide" pour signaler q u'iln'a plus de personne s e nre gistre r.
208
Exe rcice s e n langage C
O n ne pr voira aucun contrle particulie r au nive au de la saisie des donnes
Exe m pl
e
donnez le nom du fichier crer : person
----- pour terminer la saisie, donnez un nom 'vide' --nom
: dubois
age
: 32
nombre enfants : 1
age enfant no 1 : 7
nom
age
nombre enfants
: dunoyer
: 29
: 0
nom
age
nombre enfants
age enfant no 1
age enfant no 2
age enfant no 3
:
:
:
:
:
:
nom
dutronc
45
3
21
18
17
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 dcide 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 (prdfini dans stdio.h ). La valeur de ce pointe ur nous e s t
fournie par fope n ;ce tte fonction re s titue un pointe ur nule n cas d'erreur d'ouve rture .
La cr ation du fich ie r consiste s im plem e nt r p te r les actions :
Program m e
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define LGNOM 20
#define NBENFMAX 15
#define LNOMFICH 20
main()
{ char nomfich [LNOMFICH+1] ;
FILE * sortie ;
struct { char nom [LGNOM+1] ;
int age ;
int nbenf ;
int agenf [NBENFMAX] ;
} bloc ;
int i ;
209
210
*/
*/
*/
*/
*/
}
while (1) ;
/* fin cration */
fclose(sortie) ;
printf ("\n -------- FIN CREATION FICHIER ----------") ;
}
Com m e ntaire s
*Note z le "m ode d'ouve rture " w b :
w : ouve rture e n criture ;si le fich ie r n'e xiste pas, ile s t cr . S'ile xiste , son ancie n conte nu e s t pe rdu.
b : m ode dit "binaire " ou "non translat ".
En fait, l'indication b ne s e justifie q ue dans les im plm 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
considr 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
.......
.......
211
while (1) ;
q ue nous inte rrom pons au m om e nt opportun par bre ak . Nous aurions pu galem e nt ch oisir d'introduire les pre m i re s
instructions de la boucle dans l'e xpre s s ion conditionnant une instruction w h ile, de ce tte m ani re :
while (printf("nom
*Com m e pr vu par l' nonc , aucun contrle particulie r n'e s t e ffe ctu s ur les donnes qui sont donc lue s par scanf e t
ge ts. L e ncore s e pos e le problm 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 dtails sur ce problm 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 dbut 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 dsigne 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 dcrivant 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 problm e e n attribuant au sym bole LNO M FICH une valeur
suprieure au nom bre de caract re s q ue l'on pe ut frappe r au clavie r dans l'im plm 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 upplm 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 plm e ntations (notam m e nt Turbo/Borland C e t C/Quick C M icrosoft), ile s t possible de r gler
com plte m e nt le problm 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 utiliss (ave c plus ou m oins de
bonh e ur) par une lecture s uivante . L e ncore , le problm e ne pe ut tre conve nablem e nt r gl q ue d'une faon
dpendant de l'im plm e ntation, par e xe m ple ave c la fonction cge ts (associ e , ce tte fois, sscanf) cit e
pr cdem 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 plm e ntations, cge ts (associ e sscanf) pe rm e t de r gler totalem e nt le problm 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 cdent. Le program m e pr s e nte ra un s e ule nre gistre m e nt la fois, accom pagn d'un num ro
pr cisant son rang dans le fich ie r (on attribue ra le num ro 1 au pre m ie r e nre gistre m e nt) ;ilatte ndra q ue l'utilisate ur
frappe la touch e re turn avant de pas s e r l'e nre gistre m e nt suivant.
L'affich age des inform ations s e ra r alis par une fonction laq ue lle on transm e ttra e n argum e nt l'e nre gistre m e nt
affich e r e t son num ro. Le m od le m m e de la structure corre s pondante s e ra, q uant lui, d fini un nive au global.
Le program m e devra s'assure r de l'e xiste nce du fich ie r liste r.
Exe m pl
e
donnez le nom du fichier lister : person
enregistrement numro : 1
NOM
AGE
NOMBRE D'ENFANTS
: dubois
: 32
: 1
213
enregistrement numro : 2
NOM
AGE
NOMBRE D'ENFANTS
: dunoyer
: 29
: 0
enregistrement numro : 3
NOM
AGE
NOMBRE D'ENFANTS
AGE ENFANT 1
AGE ENFANT 2
AGE ENFANT 3
:
:
:
:
:
:
dutronc
45
3
21
18
17
Program m e
#include <stdio.h>
#include <string.h>
#define LGNOM 20
#define NBENFMAX 15
#define LNOMFICH 20
214
main()
{
void affiche (struct enreg *, int) ;
char nomfich [LNOMFICH+1] ;
FILE * entree ;
struct enreg bloc ;
int num ;
/*
/*
/*
/*
/*
fonction d'affichage */
nom du fichier lister */
descripteur fichier (niveau 2) */
enregistrement fichier */
numro d'enregistrement */
215
Com m e ntaire s
*Note z le m ode d'ouve rture rb :
r : ouve rture e n lecture . Si le fich ie r n'e xiste pas, fope n fournit un pointe ur nul.
b : ouve rture e n m ode "binaire " ou "non translat " (pour plus d'inform ations sur la diff re nce e ntre les m ode s
translat e t non translat , voye z les com m e ntaires de l'e xe rcice VII-1).
*R appe lons q ue la fonction de lecture fre ad poss de 4 argum e nts, com parables ce ux de fw rite :
- l'adresse de dbut d'un e ns e m ble de blocs lire ,
- la taille d'un bloc (e n octe ts),
- le nom bre de blocs de ce tte taille lire ,
- l'adresse de la structure dcrivant le fich ie r (e lle a t fournie par fope n).
*La fonction fe of pre nd la valeur vrai (1) lors q ue la fin de fich ie r a t e ffe ctive m e nt re ncontr e . Autre m e nt dit, ilne
suffit pas, pour d te cte r la fin d'un fich ie r, d'avoir sim plem e nt lu son dernier octe t ;ile s t, de plus, n ce s s aire d'avoir
te nt de lire au-de l. C'e s t ce q ui justifie q ue ce tte condition soit e xam in e apr s fre ad e t non avant.
*Voye z la faon 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) ;
216
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 cdent.
*Le passage l'e nre gistre m e nt suivant e s t dclench par la frappe de re turn. M ais si l'utilisate ur frappe un ou plusieurs
caract re s (valids par re turn), ilve rra d filer plusieurs enregistre m e nts de suite . La solution ce problm e dpe nd, ici
e ncore , de l'im plm e ntation. Par e xe m ple, dans un environne m e nt D O S, ave c Turbo/Borland C/C+ + ou Quick C/C
M icrosoft, ilsuffira de "vider le tam pon du syst m e " par :
while (kbhit()) getch ;
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.
217
L'utilisate ur d s igne ra un e nre gistre m e nt par son num ro d'ordre dans le fich ie r. Le program m e s 'assure ra de s on
e xiste nce e t l'affich e ra d'abord te lq ue lavant de dem ande r les m odifications lui apporte r. Ces derni re s s e ront
e ffe ctu e s ch am p par ch am p. Pour ch aq ue ch am p, le program m e e n affich e ra nouve au la valeur, puis ildem ande ra
l'utilisate ur d'e ntre r une ve ntue lle valeur de re m place m e nt. Si aucune m odification n'e s t souh ait e , ilsuffira ce
dernie r de r pondre directe m e nt par la frappe de re turn.
O n pr voira de ux fonctions :
- une pour l'affich age d'un enregistre m e nt (on pourra re pre ndre la fonction affich e de l'e xe rcice pr cdent),
- une pour la m odification d'un e nre gistre m e nt.
Exe m pl
e
donnez le nom du fichier modifier : person
numro enregistrement modifier (0 pour fin) : 14
numro enregistrement modifier (0 pour fin) : 2
enregistrement numro : 2
NOM
AGE
NOMBRE D'ENFANTS
: dunoyer
: 29
: 0
218
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 alises par les fonctions fre ad e t
fw rite .
L' nonc ne nous im pos e pas de contrle 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 lproblm 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 dce 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 plm 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 plm 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 "dcode r" le conte nu de ce tte ch a
ne . Ce la pe ut s e
faire , soit ave c la fonction sscanf assortie (ici) d'un form at %d, soit ave c la fonction standard atoi. Par souci de dive rsit ,
nous avons ch oisi ici la s e conde .
Program m e
#include <stdio.h>
#include <string.h>
#define
#define
#define
#define
#define
VRAI 1
FAUX 0
LGNOM 20
NBENFMAX 15
LNOMFICH 20
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
fonction d'affichage */
fonction de modif d'un enreg */
nom du fichier lister */
descripteur fichier (niveau 2) */
enregistrement fichier */
numro d'enregistrement */
indicateur "logique" */
nbre d'enregistrements du fichier */
position courante (octets) dans fich */
*/
*/
*/
*/
*/
*/
*/
*/
219
220
/* fin modifications */
fclose(fichier) ;
printf ("\n\n -------- FIN MODIFICATIONS FICHIER ----------\n") ;
}
/*************************************************/
/*
fonction d'affichage d'un enregistrement
*/
/*************************************************/
void affiche (struct enreg * bloc, int num)
{
int i ;
printf ("\n\nenregistrement numro : %d\n\n", num) ;
printf ("NOM
: %s\n", bloc->nom) ;
printf ("AGE
: %d\n", bloc->age) ;
printf ("NOMBRE D'ENFANTS
: %d\n", bloc->nbenf) ;
for (i=0 ; i < bloc->nbenf && i < NBENFMAX ; i++)
printf ("AGE ENFANT %2d
: %2d\n", i+1, bloc->agenf[i]) ;
}
/***************************************************/
/*
fonction de modification d'un enregistrement */
/***************************************************/
void modifie (struct enreg * bloc)
{
char ligne[127] ;
/* chane de lecture d'une ligne d'cran */
int i ;
printf ("\n\n\entrez vos nouvelles infos (return si pas de modifs)\n") ;
printf ("NOM
: ") ;
gets (ligne) ;
if (strlen(ligne)) strcpy (bloc->nom, ligne) ;
printf ("AGE
: ") ;
gets (ligne) ;
if (strlen(ligne)) bloc->age = atoi(ligne) ;
printf ("NOMBRE D'ENFANTS
: ") ;
221
gets (ligne) ;
if (strlen(ligne)) bloc->nbenf = atoi(ligne) ;
for (i=0 ; i < bloc->nbenf && i < NBENFMAX ; i++)
{ printf ("AGE ENFANT %2d
: ", i+1) ;
gets (ligne) ;
if (strlen(ligne)) bloc->agenf[i] = atoi(ligne) ;
}
}
Com m e ntaire s
*Nous avons ouve rt le fich ie r dans le m ode r+ b, leq ue lautoris e la m ise jour (lecture e t criture e n un e m place m e nt
q ue lconq ue du fich ie r). Note z q u'ilfaut vite r d' crire ici rb+ , ce q ui ne provoq ue rait g n ralem e nt pas d'erreur
d'ouve rture , m ais q ui e m p ch e rait toute criture dans le fich ie r (ici, notre program m e ne s 'ape rce vrait pas de ce tte
anom alie puis q u'ilne te s te pas le code de re tour de fw rite ). En ce q ui conce rne l'indication b, rappe lons q ue ce lle-ci
n'e s t indispensable q ue dans les im plm 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 prcism e nt :
fseek (fichier, 0, 2)
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 cdents program m e s , nous n'avons pas introduit de prote ctions particuli re s vis- -vis des rponses
fournie s par l'utilisate ur (voye z les discussions des prcdents 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
Ecrire un program m e q ui, partir d'un fich ie r te xte , dte rm ine :
- le nom bre de caract re s q u'ilcontie nt,
- le nom bre de ch acune des lettres de l'alph abet (on ne considrera que les m inuscules ),
- le nom bre de m ots,
- le nom bre de ligne s.
Le s fins de ligne s ne devront pas tre com ptabilises dans les caract re s . O n adm e ttra q ue deux m ots sont toujours
s par s par un ou plusieurs des caract re s s uivants :
- fin de ligne
- e s pace
- ponctuation : : . , ;?!
- pare nth s e s : ( )
- guillem e ts : "
- apostroph e : '
O n adm e ttra galem e nt, pour sim plifie r, q u'aucun m ot ne pe ut tre com m e nc s ur une ligne e t s e poursuivre s ur la
suivante .
Ile s t cons e ill de r aliser une fonction pe rm e ttant de dcide 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 .
223
Exe m pl
e
donnez le nom du fichier examiner : b:letfic.c
votre fichier contient 87 lignes, 371 mots
et 3186 caractres dont :
69 fois la lettre a
6 fois la lettre b
74 fois la lettre c
36 fois la lettre d
163 fois la lettre e
........
110 fois la lettre t
63 fois la lettre u
7 fois la lettre v
3 fois la lettre w
6 fois la lettre x
0 fois la lettre y
1 fois la lettre z
et 1979 autres caractres
________________________________________________________________________________________
ANALYSE
Com m e nous avons dj e u l'occasion de le voir dans les e xe rcice s I-5 e t I-6, ce type de problm e pe ut tre r s olu d'au
m oins deux m ani re s :
- e n e ffe ctuant une r p tition du traite m e nt d'un caract re ,
- e n e ffe ctuant une r p tition du traite m e nt d'une ligne , lui-m m e constitu de la r p tition du traite m e nt de ch acun
des caract re s q u'e lle contie nt.
Toute fois, ici, nous avons faire un fich ie r dans leq ue lla longue ur m axim ale d'une ligne n'e s t pas connue a priori, ce
q ui re nd la s e conde m th ode difficile m e ttre e n oe uvre . Nous ch oisirons donc la pre m i re ;ch aq ue caract re du fich ie r
s e ra donc lu par fge tc.
R appe lons q ue ce rtaine s im plm 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 considrer 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 rialises par un caract re uniq ue ,
savoir \n (alors q ue , pr cis m e nt, ce rtaine s im plm 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 dte 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 aliss de faon 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 procderons com m e dans le pre m ie r program m e de l'e xe rcice I-6 e n
e m ployant :
- une fonction pe rm e ttant de te s te r si un caract re e s t un s parate ur,
- un indicate ur logiq ue : m ot_e n_cours.
Program m e
#include <stdio.h>
#define LNOMFICH 20
#define VRAI 1
#define FAUX 0
main()
{
int sep (char) ;
/* fonction test "caractre sparateur?" */
char nomfich [LNOMFICH+1] ; /* nom du fichier examiner */
FILE * entree ;
char c ;
int compte [26],
numl,
ntot,
nautres,
nmots,
nlignes,
mot_en_cours,
i ;
/*
/*
/*
/*
/*
/*
/*
/*
/*
/* initialisations */
for (i=0 ; i<26 ; i++)
compte[i] = 0 ;
ntot = 0 ; nautres = 0 ;
nmots = 0 ;
nlignes = 0 ;
mot_en_cours = FAUX ;
/* boucle d'examen de chacun des caractres du fichier */
while ( c = fgetc (entree), ! feof (entree) )
{
if (c == '\n') nlignes++ ;
/* comptages au niveau caractres */
else
{ ntot++ ;
numl = c -'a' ;
if (numl >= 0 && numl < 26) compte[numl]++ ;
else nautres++ ;
}
if (sep(c))
{ if (mot_en_cours)
{ nmots++ ;
mot_en_cours = FAUX ;
}
}
else mot_en_cours = VRAI ;
}
/* affichage rsultats */
printf ("\nvotre fichier contient %d lignes, %d mots\n",
nlignes, nmots) ;
225
226
}
/*********************************************************/
/*
fonction de test "caractre sparateur"
*/
/*********************************************************/
int sep (char c)
{
char sep[12] = {'\n',
' ',
',', ';', ':', '.', '?', '!',
'(', ')',
'"', '\'' } ;
int nsep=12,
i ;
/*
/*
/*
/*
/*
/*
fin de ligne */
espace */
ponctuation */
parenthses */
guillemets, apostr*/
nbre sparateurs */
i = 0 ;
while ( c!=sep[i] && i<nsep ) i++ ;
if (i == nsep) return (0) ;
else return (1) ;
}
Com m e ntaire s
Le fich ie r a t ouve rt e n m ode rt :
r : ouve rture e n lecture . Si le fich ie r n'e xiste pas, fope n fournit un pointe ur nul.
t : ouve rture e n m ode translat (voye z ce propos, le pre m ie r com m e ntaire de l'e xe rcice VII-1).
Note z q ue le ch oix du m ode translat n'e s t jam ais absolum e nt indispensable. Toute fois, com m e nous l'avons dit dans
l'analyse, ilnous facilite la dte 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).
227
D ISCUSSIO N
Nous avons suppos (im plicite m e nt) q ue notre program m e traitait un v ritable fich ie r te xte , autre m e nt dit q ue ce dernie r
s e te rm inait par une fin de ligne . Si ce la n' tait pas le cas :
- la derni re ligne ne s e rait pas com ptabilise,
- le dernie r m ot ne s e rait pas com ptabilis, m oins d' tre s uivi d'au m oins un sparate ur.
VIII : ANALYSE
NUM ERIQUE
Ce ch apitre vous propose q ue lque s applications du langage C l'analyse num riq ue . Nous avons ch e rch y introduire
les te ch niq ues de program m ation q ui inte rvie nne nt fr q ue m m e nt dans ce dom aine . Citons, par e xe m ple :
- la re pr s e ntation e t les m anipulations de m atrice s ,
- la re pr s e ntation de nom bre s com plexe s ,
- la r alisation de m odules s usce ptibles de travailler ave c une fonction q ue lconq ue ou ave c des tableaux de dim e nsions
q ue lconq ue s .
Enonc
Ecrire une fonction calculant le produit de deux m atrice s r e lles . O n supposera q ue le pre m ie r indice de ch aq ue tableau
re pr s e ntant une m atrice corre s pond une ligne .
O n pr voira e n argum e nts :
- les adresses des deux m atrice s m ultiplie r e t ce lle de la m atrice produit,
- le nom bre de ligne s e t le nom bre de colonnes de la pre m i re m atrice ,
- le nom bre de colonnes de la s e conde m atrice (son nom bre de ligne s tant obligatoire m e nt galau nom bre de
colonnes de la pre m i re ).
Un program m e principalpe rm e ttra de te s te r ce tte fonction.
230
Exe m pl
e
MATRICE A
0
1
2
1
2
3
2
3
4
3
4
5
4
5
6
3
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 =
ij
Program m e
#define N 5
#define P 4
#define Q 3
a b
ik
kj
231
232
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 lproblm 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 lm 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 lm 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 dbut 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 lm 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
233
prototype , soit tre e xplicit e l'aide d'un op rate ur de "cast" ;ce tte derni re faon 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 dfinition 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 lm 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 prcism e nt :
- Le sym bole aik re pr s e nte un pointe ur courant sur les lm e nts a . Pour ch aq ue valeur de i, aik e s t initialis
ik
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 dte rm ine 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 dfinie
uniq ue m e nt lors de l'e x cution e t leurs e m place m e nts allous 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 dbut 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 ;
234
D ans ce s conditions, e n e ffe t, la notation ada [i] [j] corre s pondrait (com pte te nu de l'associativit de gauch e droite de
l'op rate ur []) :
(ada [i]) [j]
c'e s t- -dire :
* (ada [i] + j)
Autre m e nt dit, ce tte notation ada [i] [j] dsignerait sim plem e nt la val
eur de l' lm 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 lm e nts de type double, corre s pondant la partie r e lle e t la partie im aginaire .
Ch acune de ce s fonctions com porte ra trois argum e nts :
- l'adresse des deux nom bre s com plexe s (structure s ) conce rn s ,
- l'adresse du r s ultat (structure ).
Un program m e principalpe rm e ttra de te s te r ces deux fonctions ave c les valeurs com plexe s :
0,5 + i
1+ i
235
Exe m pl
e
0.500000 + 1.000000 i
et
1.000000 + 1.000000 i
ont pour somme
1.500000 + 2.000000 i
et pour produit -0.500000 + 1.500000 i
________________________________________________________________________________________
ANALYSE
Soit deux nom bre s com plexe s :
x = a + ib
y = c + id
O n sait q ue :
x + y = (a+ c) + i (b+ d)
e t q ue :
x y = (ac - bd) + i (ad + bc)
Program m e
typedef struct
{ double reel ;
double imag ;
} complexe ;
main()
{
void somme (complexe *, complexe *, complexe *) ;
void produit (complexe *, complexe *, complexe *) ;
complexe z1, z2, s, p ;
z1.reel = 0.5 ; z1.imag = 1.0 ;
z2.reel = 1.0 ; z2.imag = 1.0 ;
somme
(&z1, &z2, &s) ;
produit (&z1 ,&z2, &p) ;
printf ("%lf + %lf i
et
%lf + %lf i \n",
236
}
void somme (complexe * x, complexe * y, complexe * som)
{
som->reel = x->reel + y->reel ;
som->imag = x->imag + y->imag ;
}
void produit (complexe * x, complexe * y, complexe * prod)
{
prod->reel = x->reel * y->reel - x->imag * y->imag ;
prod->imag = x->reel * y->imag + x->imag * y->reel ;
}
Com m e ntaire s
*Nous avons dfini, 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 dsigne 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 dte rm in par la fonction e llem m e . Par e xe m ple, la dfinition de som m e aurait pu tre :
void somme (complexe x, complexe y, complexe * som)
{
prod->reel = x.reel + y.reel ;
prod->imag = x.imag + y.imag ;
}
237
D ISCUSSIO N
D ans la pratiq ue , les fonctions som m e e t produit s e raie nt com pile 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
dcrire 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
dfinition 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 problm e e n plaant une fois
pour toute s une te lle dfinition 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 lm 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
constitue de deux lm e nts de type double corre s pondant la partie r e lle e t la partie im aginaire du nom bre . O n
supposera q ue le pre m ie r indice du tableau re pr s e ntant une m atrice corre s pond une ligne .
O n pr voira e n argum e nts :
- les adresses des deux m atrice s m ultiplie r,
- l'adresse de la m atrice produit,
- le nom bre de ligne s e t de colonnes de la pre m i re m atrice ,
- le nom bre de colonnes de la deuxi m e m atrice (son nom bre de ligne s tant obligatoire m e nt galau nom bre de
colonnes de la pre m i re ).
O n r alisera un program m e principalpe rm e ttant de te s te r ce tte fonction.
O n pourra ve ntue llem e nt faire appe laux fonctions som m e e t produit r alises dans l'e xe rcice VIII-2 pour calculer la
som m e e t le produit de deux nom bre s com plexe s .
Exe m pl
e
MATRICE A
0+
0i
1+
2i
2+
4i
3+
6i
238
1i
2i
3i
4i
2+
3+
4+
5+
3i
4i
5i
6i
3+
4+
5+
6+
5i
6i
7i
8i
MATRICE B
0+
0i
1+
1i
2+
2i
3+
3i
1+
2+
3+
4+
2i
3i
4i
5i
2+
3+
4+
5+
4i
5i
6i
7i
PRODUIT A x B
-14+ 42i
-32+ 66i
-14+ 54i
-36+ 90i
-14+ 66i
-40+ 114i
-14+ 78i
-44+ 138i
-14+ 90i
-48+ 162i
-50+
-58+
-66+
-74+
-82+
90i
126i
162i
198i
234i
4+
5+
6+
7+
7i
8i
9i
10i
________________________________________________________________________________________
ANALYSE
Le s form ules de dfinition du produit de m atrice s com plexe s re s te nt ce lles proposes 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 rels 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 xposes 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 ;
/* 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") ;
239
240
}
/*********************************************************/
/* fonction de calcul de produit de 2 matrices complexes */
/*********************************************************/
void prod_mat ( complexe * a, complexe * b, complexe * c,
int n, int p, int q)
{
void produit() ;
int i, j, k ;
complexe s, pr ;
complexe *aik, *bkj, *cij ;
cij = c ;
for (i=0 ; i<n ; i++)
for (j=0 ; j<q ; j++)
{ aik = a + i*p ;
bkj = b + j ;
s.reel = 0 ; s.imag = 0 ;
for (k=0 ; k<p ; k++)
{ produit (aik, bkj, &pr) ;
s.reel += pr.reel ; s.imag += pr.imag ;
aik++ ;
bkj += q ;
}
* (cij++) = s ;
}
}
void produit (x, y, prod)
complexe *x, *y, *prod ;
{
prod->reel = x->reel * y->reel - x->imag * y->imag ;
prod->imag = x->reel * y->imag + x->imag * y->reel ;
}
241
Com m e ntaire s
La fonction prod_m at pe ut tre considre 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 dsignent, 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'oprations 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
lm 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.
O n pr voira e n argum e nts :
- les valeurs des bornes a e t b (de type double) de l'inte rvalle de dpart,
- 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
- l'adresse d'une variable de type double destin e re cue illir la valeur approch e du z ro de f,
- la valeur de la pr cision (absolue ) souh ait e (de type double).
Le code de re tour de la fonction s e ra de -1 lors q ue l'inte rvalle fourni e n argum e nt ne convie nt pas, c'e s t- -dire :
- soit lors q ue la condition a<b n'est pas satisfaite ,
- soit lors q ue la condition f(a).f(b)< 0 n'e s t pas satisfaite .
D ans le cas contraire , le code de re tour s e ra gal 0.
Un program m e principalpe rm e ttra de te s te r ce tte fonction.
Exe m pl
e
zro de la fonction sin entre -1 et 1 1e-2 prs = 0.000000e+000
zro de la fonction sin entre -1 et 2 1e-2 prs = 1.953125e-003
zro de la fonction sin entre -1 et 2 1e-12 prs = -2.273737e-013
________________________________________________________________________________________
ANALYSE
La dm arch e consiste donc, apr s avoir v rifi q ue l'inte rvalle re u e n argum e nt tait conve nable, r p te r le traite m e nt
suivant :
- pre ndre le m ilie u m de [a,b] : m = (a+ b)/2
- calculer f(m ),
- si f(m ) = 0, le z ro e s t e n m ,
- si f(a).f(m )< 0, ile xiste un z ro sur [a,m ] ;on re m place donc l'inte rvalle [a,b] par [a,m ] e n faisant :
b=m
- si f(a).f(m )> 0, ile xiste un z ro sur [b,m ] ;on re m place donc l'inte rvalle [a,b] par [b,m ], e n faisant :
a=m
Le traite m e nt e s t inte rrom pu soit lors q ue l'inte rvalle [a,b] aura t s uffisam m e nt rduit, c'e s t- -dire lors q ue |b-a| est
inf rie ur la pr cision souh ait e , soit lors q ue le z ro a t localis exacte m e nt (f(m )=0).
Program m e
#include <stdio.h>
#include <math.h>
/* pour la fonction sin */
main()
{
/* fonction de recherche d'un zro par dichotomie */
int dichoto ( double (*(double)(), double, double, double *, double) ;
double z,
/* zro recherch */
a, b,
/* bornes de l'intervalle de recherche */
eps ;
/* prcision souhaite */
dichoto (sin, -1.0, 1.0, &z, 1.0e-2) ;
printf ("zro de la fonction sin entre -1 et 1 1e-2 prs = %le\n",z);
dichoto (sin, -1.0, 2.0, &z, 1.0e-2) ;
printf ("zro de la fonction sin entre -1 et 2 1e-2 prs = %le\n",z);
dichoto (sin, -1.0, 2.0, &z, 1.0e-12) ;
printf ("zro de la fonction sin entre -1 et 2 1e-12 prs = %le\n",z);
}
/*************************************************************/
/* fonction de recherhce dichotomique du zro d'une fonction */
/*************************************************************/
int dichoto ( double (* f)(double), double a, double b, double * zero, double eps)
/*
/*
/*
/*
{
double m,
fm,
fa, fb ;
fa = (*f)(a) ;
fb = (*f)(b) ;
if (fa*fb >= 0 || a >= b) return (-1) ;
/* intervalle incorrect */
243
244
/* zro atteint */
= m ;
= fm ;
= m ;
= fm ;
}
* zero = m ;
return (0) ;
}
Com m e ntaire s
*Note z, dans la fonction dich oto :
- la dclaration de l'argum e nt corre s pondant l'adresse de la fonction dont on ch e rch e le z ro :
double (*f)(double)
Ce lle-ci s'inte rpr te com m e s uit :
(*f) e s t une fonction re ce vant un argum e nt de type double e t fournissant un r s ultat de type double,
*f e s t donc une fonction re ce vant un argum e nt de type double e t fournissant un r s ultat de type double,
f e s t donc un pointe ur sur une fonction re ce vant un argum e nt de type double e t fournissant un r s ultat de type
double.
- l'utilisation du sym bole f ;ainsi (*f)(a) re pr s e nte la valeur de la fonction (*f) (fonction d'adre s s e f), laq ue lle on
fournit l'argum e nt a.
Le s m m e s r flexions s'appliq ue nt au prototype s e rvant dclare r dich oto.
*La fonction dich oto re ce vant e n argum e nt les val
eurs des argum e nts a e t b (et non de s adresses), nous pouvons nous
pe rm e ttre de les m odifie r au s e in de la fonction, sans q ue ce la ait d'incide nce s ur les valeurs e ffe ctives des borne s
dfinies 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 prdfinie ;ile s t toute fois nce s s aire d'avoir incorpor s on prototype (situ dans m ath .h ) ;e n
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 dfini.
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 dsire ,
partir du m om e nt o la fonction ch ange e ffe ctive m e nt de signe sur l'inte rvalle de dpart. 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.