Vous êtes sur la page 1sur 35

Chapitre 2 : Boucles, adresses et pointeurs, fonctions

A) Les 3 boucles en C :
1) Boucle do ... while ... (Rpter .... Tant que) :
1.a) Syntaxe :
do
instruction rpter
while (condition de continuer rpter (boucler) encore);
L'instruction rpter peut tre simple (une seule action),
structure (une autre instruction de contrle) ou compose
(c'est le cas le plus frquent).

1.b) Fonctionnement :
tape 1 : effectuer (d'abord) l'instruction rpter
tape 2 : vrifier (aprs) la condition de continuer :
a) si la condition est vraie, on revient
l'tape 1
b) si non (la condition est fausse), on
termine la boucle

Attention :
Contrairement au langage PASCAL, la condition de cette boucle
est la condition pour continuer rpter encore. En
Pascal, c'est la condition d'arrter la boucle.

1.c) Domaines d'utilisation :


On utilise la boucle do ... while ... quand on ne sait pas
l'avance, le nombre de fois qu'on doit rpter le mme
traitement. Les exemples suivants permettent de voir quelques
Chapitre 2 : Boucles de rptitions et fonctions

Page 27

applications possibles avec cette boucle.

1. Rpter le mme traitement en mode conversationnel :


crire un bloc d'instructions permettant de saisir l'ge
et le sexe d'une personne. Ensuite, on affiche un message
du genre :
C'est un enfant de sexe masculin
On rpte le mme traitement jusqu' ce que l'usager dcide
de quitter.
Solution :
int
char

age ;
sexe,
reponse ; /* Oui ou Non l'usager veut continuer */

do
{ printf("\nEntrez le sexe et l'ge ");
scanf("%c%d", &sexe, &age);
printf("C'est un ");
if (age <= 11)
printf("enfant ");
else
if (age < 18 )
printf("adolescent ") ;
else
printf("adulte ");
if ( toupper(sexe) == 'F' ) /* to upper : en majuscule*/
printf("de sexe fminin\n");
else
printf("de sexe masculin\n");
printf("\nVoulez-vous continuer ? (O/N) ");
fflush(stdin);
reponse = toupper (getchar());
}
while ( reponse == 'O' );

Chapitre 2 : Boucles de rptitions et fonctions

Page 28

2. Validation de donnes :
crire un bloc d'instructions permettant de saisir et
valider l'ge d'une personne (un entier situ entre 1 et 125).
Solution :
const int MAXI = 125 ;
int age,
valide ; /* Oui ou non l'ge est valide */
do
{ printf("Entrez l'ge entre 1 et %d

", MAXI);

scanf("%d", &age);
valide = (age >= 1 && age <= MAXI) ;
if ( !valide )
printf("age lu est hors intervalle, retapez S.V.P.\n");
}
while (!valide);

Attention :
La validation d'un type (entier ou non, rel ou non) sera prsente
au chapitre 3 (chanes des caractres).

3. Calcul scientifique :
Exemple 1 :
crire un bloc d'instructions permettant de calculer et
d'afficher la somme suivante :
somme =

10 + 15 + 20 + 25 + ... + 50

Solution :
const int BORNE1 = 10 ,
BORNE2 = 50 ,
LE_PAS = 5 ;
int

terme, somme ;

somme = 0 ;
terme = BORNE1 ;

Chapitre 2 : Boucles de rptitions et fonctions

Page 29

do

{ somme += terme ;
terme += LE_PAS;
}
while (terme <= BORNE2);
printf("La somme calcule est : %d\n", somme);
Exemple 2 :
crire un programme permettant d'estimer la valeur
de PI (3.141....) selon la formule suivante :
PI
----- = 1 - 1/3 + 1/5 - 1/7 + 1/9 + ....
4

1/9999

Solution :
/* Fichier PI.A95 */
#include <stdio.h>
void main()
{
const int LIMITE = 9999 ,
LE_PAS =
2 ;
int

denominateur

= 1

; /* premier dnominateur vaut 1 */

float piSur4 = 0.0, signe = 1.0 ;


do
{ piSur4 += signe / denominateur ;
signe = -signe
; /* changer de signe */
denominateur += LE_PAS
;
}
while ( denominateur <= LIMITE ) ;
printf("La valeur estime de PI est %12.6f\n",4 * piSur4);

printf("\nAppuyez sur Entre ");


getchar();

Excution :
La valeur estime de PI est

3.141397

Appuyez sur Entre

Chapitre 2 : Boucles de rptitions et fonctions

Page 30

Attention :
On utilise souvent cette boucle pour estimer une racine de
l'quation f(x) = 0, pour la recherche squentielle d'un
lment dans un tableau (plus tard dans le cours), etc ....

1.d) Exercices :
Exercice 1 :
crire un programme permettant de saisir et de valider si un
entier lu est positif ou non. Ensuite, on affiche l'entier tel
que lu et l'envers (ici 4327).
Solution :
/* Fichier : Envers1.C */
#include <stdio.h>
void main()
{ int n
;
/* Saisie et valider : */
do
{
printf("Entrez un entier positif ");
scanf("%d", &n);
if ( n < 0 ) printf("n = %d est ngatif\n", n);
}
while (n < 0);
printf("L'entier lu :

%d\n", n);

printf("A l'envers

");

do

{ printf("%d", n % 10);
n /= 10 ;
}
while
( n > 0 );
printf("\n\nAppuyez sur Entre ");
fflush(stdin);
getchar();
}

Chapitre 2 : Boucles de rptitions et fonctions

Page 31

Excution :
Entrez un entier positif -8742
n = -8742 est ngatif
Entrez un entier positif 5329
L'entier lu : 5329
A l'envers : 9235
Appuyez sur Entre
Exercice 2 :
crire un programme permettant de saisir un entier positif
(exemple 5426). Il calcule l'envers du nombre lu (ici 6245)
et affiche ces deux valeurs.
Solution :
/* Fichier Envers2.C

*/

#include <stdio.h>
void main()
{ int n , envers

printf("Entrez un entier positif ");


scanf("%d", &n);
printf("L'entier lu

n = %d\n", n);

/* Calcul de l'envers : */
envers = 0 ;
do
{ envers = 10 * envers + n % 10 ;
n /= 10 ;
}
while ( n > 0 ) ;
printf("A l'envers

%d", envers);

printf("\n\nAppuyez sur Entre ");


fflush(stdin);
getchar();
}

Chapitre 2 : Boucles de rptitions et fonctions

Page 32

Excution :
Entrez un entier positif 1234
L'entier lu
n = 1234
A l'envers : 4321
Appuyez sur Entre
Exercice 3 :
crire un programme permettant de saisir les informations d'un
placement :
- le capital (un rel)
- le taux d'intrt (compos) annuel en %
- la dure du placement en nombre de mois.
Le programme calcule et affiche le capital la fin du terme.
Il fonctionne aussi pour plusieurs clients jusqu' ce que l'usager
dcide de quitter.

Exercice 4 :
crire un programme permettant de saisir et de valider d'un
entier positif (exemple n = 624). Le programme calcule et
affiche l'cran la somme des chiffres qui forment l'entier
lu (ici, la somme des chiffres est 12 = 6 + 2 + 4).
Exercice 5 (scientifique) :
crire un programme permettant de trouver toutes les racines
de l'quation f(x) = 0, o :
3

f(x) = x

- 19x + 30

( Thorme en mathmatique :
Si f est continue dans l'intervalle ]a, b[ (exemple :]0, 2.5[)
et f(a) * f(b) < 0, alors : il y a au moins une racine de
f(x) = 0 dans ]a, b[ )

2) Boucle while ... :


2.a) Syntaxe :
while (condition) instruction
Chapitre 2 : Boucles de rptitions et fonctions

Page 33

L'instruction rpter peut tre simple (une seule action),


structure (une autre instruction de contrle) ou compose
(c'est le cas le plus frquent).
Il n'y a pas de "do" (faire) comme le PASCAL.

2.b) Fonctionnement :
tape 1 : vrifier d'abord la condition
tape 2 : si la condition vaut vraie alors
a) on effectue l'instruction rpter
b) on revient l'tape 1
si non, on quitte la boucle

2.c) Domaines d'utilisation :


On utilise la boucle while ... quand on ne sait pas l'avance
le nombre de fois qu'on doit rpter le mme traitement. On
l'utilise surtout pour la lecture d'un fichier, la recherche d'un
lment dans un tableau, les calculs scientifiques.

1. La lecture d'un fichier :


Supposons qu'on dispose du fichier nomm "Entiers.Dta" qui
contient un entier par ligne, exemple :
65
-12
28
37
etc ...
crire un programme permettant de lire le fichier, d'afficher
son contenu, de compter et d'afficher le nombre d'entiers lus
dont la valeur est suprieur 10.

Notes sur la lecture d'un fichier texte :


. Dclarer
:
FILE * aLire ;
. Ouverture pour la lecture :
aLire = fopen("Entiers.Dta", "r");
o "r" vient de "for reading" (pour lire).
. Test non fin de fichier
. Lecture
. Fermeture

Chapitre 2 : Boucles de rptitions et fonctions

:
:
:

while ( !feof(aLire) )
fscanf(aLire, .......) ;
fclose(aLire) ;

Page 34

Solution :
/* Fichier : WHILE1.A95 */
#include <stdio.h>
void main()
{
const int BORNE = 10 ;
int n

, nbFois = 0 ;

FILE * aLire ;

/* dclaration du fichier */

aLire = fopen("Entiers.Dta", "r");


printf("Les entiers lus dans le fichier :\n\n");
/* La lecture, ligne par ligne : */
while ( !feof(aLire) ) /* Tant que non fin du fichier */
{ fscanf(aLire , "%d\n", &n);
printf("%5d\n", n);
}

if ( n > BORNE ) nbFois = nbFois + 1 ;

fclose(aLire);

/* Fermeture du fichier (fclose) */

printf("Le nombre d'entiers suprieur %d est %d\n",


BORNE, nbFois);
printf("\nAppuyez sur Entre ");
getchar();
}
Excution:
Les entiers lus dans le fichier :
20
-12
15
-30
29
-50
40
Le nombre d'entiers suprieur 10 est 4
Appuyez sur Entre

2. La recherche d'une valeur :


Voir la recherche dichotomique (plus tard dans le cours).

Chapitre 2 : Boucles de rptitions et fonctions

Page 35

3. Quelques exercices de calcul :


Exercice 1 :
crire un programme permettant de saisir et de valider d'un
entier positif (exemple n = 624). Le programme calcule et
affiche l'cran la somme des chiffres qui forment l'entier
lu (ici, la somme des chiffres est 12 = 6 + 2 + 4).
Solution :
/* Fichier : WHILE2.A95 */
#include <stdio.h>
void main()
{
int n , valide, sommeChiffre ;
/* Saisie et valider n : */
valide = 0 ; /* Ce n'est pas valide */
while ( !valide )
{
printf("Entrez un entier suprieur zro ");
scanf("%d", &n);

valide = n > 0 ;
if (!valide)
printf("valeur ngative, retapez, S.V.P.\n\n");

printf("L'entier lu est %d\n", n);


printf("La somme des chiffres de n = %d est ", n);
/* Calcul de la somme des chiffres : */
sommeChiffre = 0 ;
while (n) /* c'est--dire while ( n != 0), ici while (n > 0) */
{ sommeChiffre += n % 10 ; /* on ajoute le dernier chiffre */
n /= 10;
}
printf("%3d\n", sommeChiffre);
printf("\nAppuyez sur Entre ");
fflush(stdin);
getchar();
}

Chapitre 2 : Boucles de rptitions et fonctions

Page 36

Excution :
Entrez un entier suprieur zro -5678
valeur ngative, retapez, S.V.P.
Entrez un entier suprieur zro 8736
L'entier lu est 8736
La somme des chiffres de n = 8736 est

24

Appuyez sur Entre


Exercice 2 :
crire un bloc d'instruction permettant de calculer et d'afficher
la somme suivante :
somme = 1 + 1/3 + 1/5 + 1/7 + ... + 1/999
Solution :
const int BORNE1 =
1 ,
BORNE2 = 999 ;
float denominateur = BORNE1, somme = 0 ;
while ( denominateur <= BORNE2 )
{
somme += 1 / denominateur ;
denominateur += 2;
}
printf("La somme demande : %10.6f\n", somme);
Exercice 3 :
crire un bloc d'instructions utilisant la boucle while qui permet
d'estimer la valeur de PI selon la formule suivante :
PI
----- = 1 - 1/3 + 1/5 - 1/7 + 1/9 + ....
4

Chapitre 2 : Boucles de rptitions et fonctions

1/9999

Page 37

3) Boucle for ... :

3.a) Syntaxe :
for

(<exp1>;<exp2>;<exp3>) instruction

- l'instruction rpter peut tre simple ou compose (bloc)


- l'expression 1 (exp1) : initialisation
l'expression 2 (exp2) : condition de continue qui est value
avant chaque itration
l'expression 3 (exp3) : l'incrmentation ou la dcrmentation
(instruction de fin ditration)
Exemple de syntaxe :
for ( i = 1 ; i <= 10 ; i = i + 1 )
printf("Bonsoir!\n");
Cette boucle fait afficher 10 lignes qui contient le
message Bonsoir!

3.b) Fonctionnement :
Initialiser la variable de contrle de la boucle
Tant que la condition de continue vaut vraie Faire
- effectuer l'instruction rpter
- incrmenter ou dcrmenter la variable de contrle

3.c) Domaines d'utilisation :


On utilise la boucle for quand on sait l'avance le nombre de
fois qu'on rpte le mme traitement. C'est le cas des valeurs
conscutives entre deux bornes donnes. On l'utilise aussi souvent
pour parcourir les indices d'un tableau.

Chapitre 2 : Boucles de rptitions et fonctions

Page 38

1. Le nombre de fois de la rptition est connue :


Exercice 1 :
crire un bloc d'instruction permettant d'afficher les 26
lettres majuscules l'cran :
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Solution :
char lettre ;
for ( lettre = 'A' ; lettre <= 'Z' ; lettre = lettre + 1 )
printf("%c", lettre);
printf("\n");
Exercice 2 :
crire un bloc d'instruction permettant d'afficher les 20
consonnes en minuscules l'cran.
Solution :
char lettre ;
for ( lettre = 'a' ; lettre <= 'z' ; lettre = lettre + 1 )
if (
lettre != 'a' && lettre != 'e' && lettre != 'i'
&& lettre != 'o' && lettre != 'u' && lettre != 'y' )
printf("%c", lettre);
printf("\n");

Exercice 3 :
Donner une autre solution de l'exercice 2 en remplaant le
if par un switch.
Exercice 4 :
crire un bloc d'instruction permettant de calculer et
d'afficher la somme suivante :
somme = 1 + 1/2 + 1/3 + ... + 1/1000

Chapitre 2 : Boucles de rptitions et fonctions

Page 39

Solution :
const int BORNE1 =
1 ,
BORNE2 = 999 ;
int denominateur;
float somme = 0 ;
for ( denominateur = BORNE1 ; denominateur <= BORNE2 ;
denominateur = denominateur + 1 )
somme += 1.0 / denominateur ;
printf("La somme demande : %10.6f\n", somme);

Exercice 5 :
crire un bloc d'instructions utilisant la boucle for qui permet
d'estimer la valeur de PI selon la formule suivante :
PI
----- = 1 - 1/3 + 1/5 - 1/7 + 1/9 + ....
4

1/9999

Observations :
En premire vue, les dnominateurs ne sont pas conscutifs :
1

9 ...

9999

Par contre, on peut les crire diffremment :


(2 * 0 + 1)
1

(2 * 1 + 1) (2 * 2 + 1)
3
5

.... ( 2 * 4999 + 1)
9999

Dans cette nouvelle criture, les valeurs 0, 1, 2 , ... 4999


sont conscutives, on peut utiliser la boucle for.
Solution :
const int LIMITE = 9999 ;
float piSur4 = 0.0, signe = 1.0 ;
int
k ;
for ( k = 0 ; k <= LIMITE / 2 ; k++ )
{
piSur4 += signe / (2 * k + 1) ;
signe = - signe ;
}
printf("La valeur estime de PI est %12.6f ",4 * PI_Sur4);
Chapitre 2 : Boucles de rptitions et fonctions

Page 40

Notes :
Dans l'incrmentation de la variable k de la boucle for, on
utilise trs souvent k++ la place de k = k + 1.
++ est l'oprateur de post-incrmentation (utiliser d'abord
la valeur, incrmenter aprs avoir utilis la valeur) :
Exemples :
1.

int k = 5 ;
printf("k = %d\n", k); /* afficher 5 */
k++ ; /* incrmenter k, k vaut 6 */
printf("k = %d\n", k); /* afficher 6 */

2.

int k = 5 ;
if ( k++ )
printf("k = %d\n", k);
Cette instruction affiche k = 6 l'cran.
En effet, avec if ( k++ ) on a 2 actions :
a) if (5) , comme 5 est non nul => if (vrai)
b) k est incrment => k vaut 6
L'instruction printf... fait afficher la valeur 6 de k.

On utilise souvent l'oprateur ++ pour l'incrmentation dans


la boucle for et dans la manipulation des pointeurs (chapitre 3).
L'oprateur -- pour la dcrmentation est aussi utile pour la
boucle for (voir exercice suivant).
Exercice 6 :
crire un bloc d'instruction permettant d'afficher les 5
lignes suivantes l'cran :
5
4
3
2
1

4 3 2 1
3 2 1
2 1
1

Solution :
#define NB_LIGNE 5
int L, C ;
/* Pour L varie de 5 en descendant 1 faire */
for ( L = NB_LIGNE ; L >= 1 ; L-- )
{ for ( C = L ; C >= 1 ; C-- ) printf("%2d", C);
printf("\n");
}
Chapitre 2 : Boucles de rptitions et fonctions

Page 41

Exercice 1(application scientifique) :


Calcul de l'intgrale dfinie :
Soit f une fonction intgrable dans l'intervalle ]a, b[.
Une mthode permet d'estimer l'intgrale de f(x) dx dans ]a, b[
s'appelle la mthode des trapzes : On divise ]a, b[ en n
sous-intervalles de mme longueur h ( h = (b-a) / n ). Aprs
certains dveloppements mathmatiques, on arrive la formule
suivante:
Intgrale

f(A) + f(B)
= h [ -----------2

n-1

f( A + ih ) ]
i= 1

crire un programme permettant de tester cette mthode en


estimant :
e

1
--- dx
1
x
| e
(Thoriquement, c'est
Log(x) |
= Log(e) - Log(1) = 1 - 0 = 1)
| 1
Exercice 8 :
Parmi les entiers entre 100 et 500, seuls 4 nombres peuvent tre
reprsents par la somme des cubes de leurs chiffres.
crire un bloc d'instructions pour dcouvrir et afficher ces
nombres.
Solution :
#define BORNE1 100
#define BORNE2 500
int nombre, sommeCube , valeur, Chiffre ;
for ( nombre = BORNE1 ; nombre < BORNE2 ; nombre++ )
{
valeur
= nombre ;
sommeCube = 0 ;
while ( valeur > 0 )
{
Chiffre
= valeur % 10 ;
sommeCube = sommeCube + Chiffre * Chiffre * Chiffre ;
valeur
= valeur / 10 ;
}
if ( nombre == sommeCube )
printf("nombre = %5d, somme des cubes de ses chiffres = %5d\n",
nombre, sommeCube);
}
Chapitre 2 : Boucles de rptitions et fonctions

Page 42

Exercice 9 :
Un nombre parfait est un nombre entier qui est gal la somme de
ses diviseurs propres (sauf lui-mme).
Exemples:

6 = 1 + 2 + 3
28 = 1 + 2 + 4 + 7 + 14

6 et 28 sont deux nombres parfaits.


crire un programme qui permet de dcouvrir tous les nombres
parfaits entre 2 et 500 et d'afficher les informations du genre
suivant l'cran :
Voici les nombres parfaits entre 2 et 500 :
1)
6 = 1 + 2 + 3
2)
28 = 1 + 2 + 4 + 7 + 14
etc ...
Exercice 10 :
crire un programme qui permet de dcouvrir et d'afficher tous
les diviseurs du nombre 720 raison de 5 diviseurs par ligne.
L'affichage est du genre :
Voici les diviseurs de 720 :
1)
6)

1 2)
2
etc ....

3)

4)

5)

Solution :
#include <stdio.h>
void main()
{
#define NOMBRE
720
#define PAR_ECRAN
5

/* 5 diviseurs par cran */

int n , k = 0 ; /* Compteur du nombre des viseurs de 720 */


for ( n = 1 ; n <= NOMBRE ; n++ )
if ( NOMBRE % n == 0 ) /* n est diviseur du NOMBRE (reste = 0)*/
{ k++ ; /* un diviseur de plus */
printf("%3d) %4d", k, n);
if ( k % PAR_ECRAN == 0 ) printf("\n");
}

2. Parcourir les indices d'un tableau :


On verra des exemples au chapitre 4 sur les tableaux.

Chapitre 2 : Boucles de rptitions et fonctions

Page 43

B) Adresses vs Pointeurs :
1) Domaines d'utilisation :
On utilise trs souvent les adresses dans :
1. la saisie des donnes avec scanf ;
2. les appels des fonctions pour recevoir des valeurs retournes ;
On utilise des pointeurs dans :
1. la transmission des arguments par pointeurs (rsultats de
retour) ;
2. la manipulation des tableaux ;
3. les fichiers :

FILE * aLire, * aCreer ;

4. les chanes de caractres ;


5. les structures de donnes avances (liste linaire chane,
arbre binaire, ...)
etc .....

2) Introduction aux adresses et aux pointeurs :


En programmation, on utilise trs souvent les caractristiques
suivants d'une variable :
1. son nom (identificateur)
2. son contenu (sa valeur)
3. son type (entier, rel, caractre, ...)
Grce au type de la variable, le compilateur connat le nombre
d'octets (byte) pour mmoriser une telle variable. Le C dispose
d'un oprateur nomm "sizeof" pour cet effet :
sizeof(char)
sizeof(float)
etc ....

vaut 1, signifie qu'on utilise 1 octet pour


mmoriser un caractre
vaut 4, signifie qu'on utilise 4 octets pour
mmoriser un rel

Si a est une variable de type "int", sizeof(a) est quivalent


sizeof(int) :
sizeof (un type) <==> sizeof(une variable du type)

Chapitre 2 : Boucles de rptitions et fonctions

Page 44

2. a) Les adresses :
En C, on ajoute un caractristique de plus une variable :
son adresse (son emplacement en mmoire) dtermine par
l'oprateur & (adresse de)
L'adresse d'une variable est dtermine souvent la compilation
de la manire squentielle :

Exemple d'illustration des adresses :


Soit le programme suivant :
/* Fichier Adresse1.A95 (introduction aux adresses)
Dans ce programme, on affiche une adresse avec le code format %u
u pour "unsigned integer" : entier sans signe (positif) : entre
zro et 65535
*/
#include <stdio.h>
int
float
char
int

a
x
d
c

=
=
=
=

void main()
{
printf("Nom
printf("---

5, b = 23 ;
123.4
;
'V'
;
100
;

Type
-------

Nb. octets
----------

Adresse
-----------

valeur\n");
--------\n");

printf(" a

int
%d
sizeof(a), &a, a);

%u

%d\n",

printf(" b

int
%d
sizeof(b), &b, b);

%u

%d\n",

printf(" x

float
%d
sizeof(x), &x, x);

%u

%6.2f\n",

printf(" d

char
%d
sizeof(char), &d, d);

%u

%c\n",

printf(" c

int
%d
sizeof(int), &c, c);

%u

%d\n",

printf("\n\nL'oprateur * (contenu de l'adresse) :\n");


printf("Le contenu l'adresse %u est %d\n", &b, *(&b));
printf("Le contenu l'adresse %u est %6.2f\n", &x, *(&x));

printf("\n\nAppuyez sur Entre ");


getchar();

Chapitre 2 : Boucles de rptitions et fonctions

Page 45

Excution :
L'excution du programme avec TURBO C++ sur un IBM PS/2 donne
ce qui suit :
Nom
--a
b
x
d
c

Type
------int
int
float
char
int

Nb. octets
---------2
2
4
1
2

Adresse
----------170
172
174
178
179

valeur
-------5
23
123.40
V
100

L'oprateur * (contenu de l'adresse) :


Le contenu l'adresse 172 est 23
Le contenu l'adresse 174 est 123.40
Appuyez sur Entre
Schmas d'explication :
Une case d'un octet dispose d'une adresse en mmoire. Dpendant
du type de la variable le compilateur alloue un nombre de cases
( dans cet environnement de travail : 2 cases pour un entier (maintenant,
les entiers sont sur 4 octets),4 pour un rel, 1 pour un caractre, etc.).
Les valeurs sont codes en binaire. Pour une meilleure comprhension,
on reprsente ici comme des valeurs usuelles (entier, rel, caractre).
Nom
a

d
c

valeur
(en binaire)

23

123.4

'V'

100

Chapitre 2 : Boucles de rptitions et fonctions

Adresse
170

(adresse du dbut de a)

171
172

(adresse du dbut de b)

173
174

(adresse du dbut de x)

175
176
177
178

(adresse du dbut de d)

179

(adresse du dbut de c)

180

Page 46

On voit aussi l'oprateur "*"


*(adresse)
Ainsi :

dont :

<===> le contenu (la valeur) qui se trouve cette adresse


*(&b) <==> le contenu l'adresse 172 <==> 23 (valeur de b)
*(&x) <==> le contenu l'adresse 174 <==> 123.40 (valeur de x)

On revient maintenant la lecture de la premire semaine :


printf("Entrez la valeur de b ");
scanf("%d", &b);
On interprte scanf("%d", &b); comme suit :
Lire la valeur tape et dposer cette valeur l'adresse 172 (l'adresse
de b) ===> b vaut la valeur saisie.

2. b) Les pointeurs :
La valeur d'une variable entire est un entier :

int age = 23 ;

La valeur d'une variable de type rel est un rel : float Poids = 62.0 ;
La valeur d'un pointeur est une adresse.

Premier exemple d'illustration des pointeurs :


Soit le programme suivant :
/* Fichier Pointer1.A95 (introduction aux pointeurs) */
#include <stdio.h>
int
int
float
float

a
*P
x
*Z

=
=
=
=

15, b = 23
&b, S = &a
123.4
&x

;
; /* 2 pointeurs vers le type int */
;
; /* Z, pointeur vers le type float */

void Continuer()
{ printf("\n\nAppuyez sur Entre ");
fflush(stdin);
getchar();
}

Chapitre 2 : Boucles de rptitions et fonctions

Page 47

void Demo1()
{
printf("Premire dmonstration:\n\n");
printf("Nom
printf("---

Type
-------

Nb. octets
----------

Adresse
-----------

valeur\n");
--------\n");

printf(" b

int
%d
sizeof(b), &b, b);

%u

%d\n",

printf(" P

int *
%d
sizeof(P), &P, P);

%u

%u\n\n",

printf(" x

float
%d
sizeof(x), &x, x);

%u

%6.2f\n",

printf(" Z

float *
%d
sizeof(Z), &Z, Z);

%u

%u\n",

printf("\constatations :\n\n");
printf("
printf("
printf("
printf("
printf("
printf("
printf("
printf("

1. valeur d'un pointeur est une adresse :\n");


- valeur du pointeur P (%u) est l'adresse de b (%u)\n",
P, &b);
- valeur du pointeur Z (%u) est l'adresse de x (%u)\n\n",
Z, &x);
2. Plusieurs manires pour les valeurs de *P et de *Z :\n\n");
*P = Le contenu l'adresse %u = %d\n", P, *P);
*Z = Le contenu l'adresse %u = %6.2f\n", Z, *Z);
*P = %6d *(&b) = %6d b = %6d\n", *P, *(&b), b);
*Z = %6.2f *(&x) = %6.2f x = %6.2f\n", *Z, *(&x), x);

Continuer();

void main()
{ Demo1();
}

Excution :
L'excution du programme avec TURBO C++ sur un IBM PS/2 donne
ce qui suit :
Premire dmonstration:
Nom
--b
P
x
Z

Type
------int
int *
float
float *

Nb. octets
---------2
2
4
2

Chapitre 2 : Boucles de rptitions et fonctions

Adresse
----------172
174
178
182

valeur
-------23
172
123.40
178
Page 48

Constatations :
1. valeur d'un pointeur est une adresse :
- valeur du pointeur P (172) est l'adresse de b (172)
- valeur du pointeur Z (178) est l'adresse de x (178)
2. Plusieurs manires pour les valeurs de *P et de *Z :
*P
*Z
*P
*Z

= Le contenu
= Le contenu
=
23 *(&b)
= 123.40 *(&x)

l'adresse
l'adresse
=
23
= 123.40

172 = 23
178 = 123.40
b =
23
x = 123.40

Appuyez sur Entre

Notes :
Pour le bon droulement du cours, on se limite aux explications
suivantes concernant un pointeur :
Soit T le nom d'un type (exemple int, char, float, ...)
1. Dclaration :
2. Consquences:

p ;

p est un pointeur vers une variable de type T.


*p est une variable de type T.
valeur de p est une adresse (exemple yyyy)
valeur de *p est la valeur qui se trouve
l'adresse yyyy.

Ces explications nous permettent de comprendre la transmission


des paramtres par pointeurs et les appels des sous-programmes
avec des adresses.
Aux prochaines chapitres, on expliquera autres caractristiques
des pointeurs (pointeur NULL, allocation dynamique, affectation,
etc ...)

C) Les fonctions en C :
En C, il n'existe qu'une seule sorte de sous-programme : la fonction.

1) Fonction "naturelle" qui retourne un seul rsultat avec return :


Ce genre de fonction est semblable "FUNCTION" en PASCAL.

Chapitre 2 : Boucles de rptitions et fonctions

Page 49

1.a) Syntaxe :
type du rsultat de retour nom de la fonction( liste de paramtre(s))
{ dclarations locales si ncessaire
calcule et retourne (avec return) le rsultat calcul

Exemple d'illustration de la syntaxe

La fonction suivante permet de calculer et de retourner la plus grande


valeur parmi deux rels.
float plusGrand ( float x, float y )
{
if ( x > y )
return x ;
else
return y ;
}

1.b) Remarques :
1. Le nom de la fonction ne contient pas de rsultat de retour
comme le PASCAL.
2. Il faut utiliser le rsultat retourn dans un bon contexte
(affichage, affectation, comparaison, ...).
3. L'instruction "return" provoque la fin de la fonction, on
revient la place qui appelle la fonction.
4. Sur l'en-tte, on ne peut pas grouper les paramtres de mme
type :
float

plusGrand ( float x, y )

/* erron! */

5. Ne pas terminer l'en-tte par le point virgule :


float

plusGrand ( float x, float y ) ;

/* erron! */

1.c) Exemples :
Exemple 1 (fonction qui retourne un entier comme rsultat) :
crire un programme permettant de saisir un entier N suprieur
zro. Le programme calcule (par les fonctions) et affiche l'cran :
- la somme des chiffres du nombre n
- l'envers du nombre n

Chapitre 2 : Boucles de rptitions et fonctions

Page 50

Solution :
/* Fichier : Fonct1.A95 */
#include <stdio.h>
int sommeChiffre ( int n )
{ int somme = 0 ;
while (n)
{ somme += n % 10 ;
n
/= 10
;
}
return somme ;
}
int envers ( int n )
{ int K = 0 ;
while (n)
{ K = K * 10 + n % 10 ;
n /= 10;
}
return K ;
}
void main()
{ int nombre ;
printf("Entrez un entier > 0
scanf("%d", &nombre);

");

printf("La somme des chiffres de %d est %d\n\n", nombre,


sommeChiffre (nombre) );
printf("L'envers du nombre %d est %d\n\n", nombre, envers(nombre));
printf("Appuyez sur Entre ");
fflush(stdin);
getchar();

Excution:
Entrez un entier > 0 875
La somme des chiffres de 875 est 20
L'envers du nombre 875 est 578
Appuyez sur Entre
Exemple 2 (fonction qui retourne un rel comme rsultat) :
crire une fonction permettant de calculer le bonus dpendant
du poste de travail :
poste 'A' (analyste)
: 234.5
poste 'P' (programmeur) : 210.9
poste 'O' (oprateur)
: 189.0
crire quelques appels (utilisations)
Chapitre 2 : Boucles de rptitions et fonctions

$ de bonus
$ de bonus
$ de bonus
valides de cette fonction.
Page 51

Solution :
float bonus ( char poste )
{ float boni ;
switch ( toupper(poste) )
{
case 'A' : boni = 234.5 ; break ;
case 'P' : boni = 210.9 ; break ;
case 'O' : boni = 189.0 ;
}
return boni ;

Quelques utilisations valides :


1. dans l'affichage (plus courant)
printf("Bonus d'un analyste : %6.2f\n", bonus('A') );
2. dans une affectation :
float salaireHebdo, salaireTotal ;
char poste ;
.......
salaireTotal = salaireHebdo + Bonus(poste);
3. dans une comparaison :
if ( Bonus (poste) > 200.0 )
printf("Pas si mal\n");
float salaireHebdo, salaireTotal ;
char poste ;
.......
salaireTotal = salaireHebdo + bonus(poste);
Exemple 3 (fonction qui retourne un caractre comme rsultat) :
crire une fonction permettant de jouer le mme rle que
"toupper" (conversion en MAJUSCULE).
crire une utilisation valide de cette fonction.

Chapitre 2 : Boucles de rptitions et fonctions

Page 52

Solution :
char majuscule ( char c )
{
if ( c >= 'a' && c <= 'z' ) /* lette minuscule */
return c + 'A' - 'a' ; /* code ASCII
else

*/

return c ;

}
Une utilisation valide :
printf("Le caractre %c en majuscule est %c\n", 'e', majuscule ('e') );

Exemple 4 (fonction qui retourne vrai ou faux comme rsultat) :


crire une fonction permettant de retourner vrai (1) ou faux (0)
selon qu'un caractre est une voyelle ou non.
crire une instruction utilisant cette fonction pour afficher
les 20 consonnes en majuscules l'cran.
Solution :
int voyelle ( char lettre )
{ int reponse ;
switch (
{
case
case
case
case
case
case

toupper(lettre) )
'A'
'E'
'I'
'O'
'U'
'Y'

default

:
:
:
:
:
: reponse = 1 ; /* VRAI */
break ;
: reponse = 0 ; /* Faux */

}
return reponse ;
}
L'affichage des 20 consonnes en majuscules l'cran se fait comme
suit :
char lettre ;
for ( lettre = 'A' ; lettre <= 'Z' ; lettre++ )
if ( !voyelle(lettre) ) printf("%c", lettre) ;
printf("\n\n");
Chapitre 2 : Boucles de rptitions et fonctions

Page 53

Remarque :
Pour la clart, on peut aussi utiliser les #define dans le cas des
fonctions boolennes. Exemple : crire une fonction qui retourne
vrai ou faux selon qu'un entier n >= 2 est premier (a 2 diviseurs
seulement : 1 et n) ou non.
int Premier ( int n )
{
#define VRAI 1
#define FAUX 0
int k ;
for ( k = 2 ; k <= n / 2 ; k++ )
if ( n % k == 0 ) return FAUX ;
return VRAI ;
}
Simulation :
1. Avec n = 7 par exemple :
k = 2
k = 3

n % k vaut 1
n % k vaut 1

On quitte la boucle for. On rencontre : return VRAI.


7 est (VRAIement) un nombre premier.
2. Avec n = 6 par exemple :
k = 2

n % k vaut 0 qui provoque "return FAUX" et on


termine la fonction.

6 n'est pas un nombre premier.


Note :
Plusieurs autres exemples seront prsents avec les paramtres
de type tableau.

2) Fonction de type void (pas de return) :


Ce genre de fonction est semblable "PROCEDURE" en PASCAL. Elle
ralise une ou quelques tches (trier, chercher, compter-afficher,
calculer, etc).
Certains livres prsentent la fonction principale comme fonction
qui retourne un entier comme rsultat :

Chapitre 2 : Boucles de rptitions et fonctions

Page 54

int main()
{
........
return 1 ;

Cette manire permet d'viter des avertissements (warning) la


compilation mais n'est pas trs comprhensive. Il est prfrable,
d'utiliser une fonction de type void :

void main() /* rien retourner */


{

.......
}

2.a) Syntaxe :
void nom de la fonction ( liste de paramtre(s) )
{ dclarations locales

raliser l'action confie


(s'il y a des rsultats de retour, ce sont des paramtres
transmis par pointeurs ou par rfrence(plus loin))

Je suggre d'utiliser le nom d'un verbe qui rsume la tche


de la fonction pour nommer la fonction :
void calculer( .... )
void trier ( .... )
etc ....

2.b) Exemples :
Cas 1 :
Quand on utilise des rsultats l'intrieur du corps d'une fonction,
on n'a que des paramtres transmis par valeur.
Exemple :
crire
nombre
crire
de 720

une fonction permettant de compter et d'afficher le


de diviseurs d'un entier n positif donn.
2 appels permettant d'afficher le nombre de diviseurs
et 984.

Chapitre 2 : Boucles de rptitions et fonctions

Page 55

Solution :
void compter ( int n )
{ int k = 1 , /* n est un diviseur de lui-mme */
i
; /* boucle for
*/
for ( i = 1 ; i <= n / 2 ; i++ )
if ( n % i == 0 ) k++ ;
printf("Le nombre de diviseurs de %d est %d\n", n, k);
}
Appels :
compter(720);
compter(984);
Exercice :
Un nombre parfait est un nombre entier qui est gal la somme de
ses diviseurs propres (sauf lui-mme).
Exemples:

6 = 1 + 2 + 3
28 = 1 + 2 + 4 + 7 + 14

6 et 28 sont deux nombres parfaits.


crire un programme utilisant de sous-programmes qui permet de
dcouvrir tous les nombres parfaits entre 2 et 500 et d'afficher
les informations du genre suivant l'cran :
Voici les nombres parfaits entre 2 et 500 :
1)
6 = 1 + 2 + 3
2)
28 = 1 + 2 + 4 + 7 + 14
etc ...
Solution :
/* Fichier : Parfait.E95 */
#include <stdio.h>
/* n est-il parfait */
int estParfait (int n)
{ int sommeDivPropre = 1 , /* le premier diviseur est 1 */
k
; /* est-il un diviseur de n
*/
/* totaliser des diviseurs propres : */
for ( k = 2 ; k <= n / 2 ; k++ )
if ( n % k == 0 ) sommeDivPropre += k ;
}

return n == sommeDivPropre ;

Chapitre 2 : Boucles de rptitions et fonctions

Page 56

/* Affichage selon les exigences demandes : */


void afficher ( int nombreParfait )
{ int k ;
printf("%4d = 1");
for ( k = 2 ; k <= nombreParfait / 2 ; k++ )
if ( nombreParfait % k == 0 ) printf(" + %3d", k);
printf("\n");

void main()
{
const int BORNE1 =
2 ,
BORNE2 = 500 ;
int nombre, compteur = 0 ;
printf("Voici les nombres parfaits entre %d et %d\n\n",
BORNE1, BORNE2 );
for ( nombre = BORNE1 ; nombre <= BORNE2 ; nombre++ )
if ( estParfait (nombre) )
{
printf("%3d) ", ++compteur);
}

afficher (nombre) ;

printf("\n\nAppuyez sur Entre ");


getchar();

Excution du programme :
Voici les nombres parfaits entre 2 et 500
1)
2)
3)

6 = 1 +
28 = 1 +
496 = 1 +

2 +
2 +
2 +

3
4 +
4 +

7 +
8 +

14
16 +

31 +

62 + 124 + 248

Appuyez sur Entre

Cas 2 :
La fonction calcule et retourne des rsultats travers des paramtres
transmis par pointeur.

Chapitre 2 : Boucles de rptitions et fonctions

Page 57

Arguments transmis par pointeur :


L'en-tte :
void
Appel :

nom ( ..... , type_rsultat * P, ..... )


^
|
|
|
v
nom ( ..... , &variable du type_rsultat, ..... ) ;

Sur l'en-tte c'est un pointeur qui pointe vers le type du


rsultat calcul. A l'appel c'est une adresse de la variable
qui reoit le rsultat.
Rappels :
La valeur dun pointeur est une adresse :
float x
= 5.67 ;
float * P ; ==> P est un pointeur vers le type float
P = &x
; ==> la valeur de P est l'adresse de x
Schmas d'illustration :
62000 (par exemple)
(adresse de
dbut de
x en mmoire)

5.67

62000

1. P est un pointeur (il pointe vers x qui est de type float) :

P----------> 5.67

x
2. valeur de P est 62000 (adresse de x).
3. *P est de type float. Sa valeur est le contenu l'adresse
62000, ici c'est 5.67
On reviendra aux notions adresses et pointeurs au chapitre 3 (les
tableaux et les chanes de caractres).

Chapitre 2 : Boucles de rptitions et fonctions

Page 58

Exemples d'illustration :
1.

void calculer ( float a,


/* Calculer et retourner
{
if ( a < b ) *P = a
else
*P = b
}

float b, float * P )
la plus petite valeur */
;
;

Utilisation :
float x, y , plusPetit ;
printf("Entrez 2 rels ");
scanf(&x, &y);
calculer (x, y, &plusPetit);
printf("La plus petite valeur est %8.2f\n", plusPetit);
Explications :
Supposons que x vaut 5.2, y vaut 3.4 et l'adresse de plusPetit
est 50000.
Avec l'appel :

Calculer (x, y, &plusPetit);

On transmet la fonction :
void calculer ( float a, float b, float * P )
1. la valeur 5.2 a
2. la valeur 3.4 b
3. la valeur 50000 P :

52000

???

plusPetit

Adresse
52000

On excute la fonction :
if ( a < b ) *P = a ;
else
*P = b ;
Comme a < b est faux, on a : *P = b ;
C'est--dire : dposer l'adresse "52000" la valeur 3.4.

3.4

plusPetit

Chapitre 2 : Boucles de rptitions et fonctions

Page 59

C'est la fin de la fonction, on revient la fin d'appel. On


rencontre :
printf("La plus petite valeur est %8.2f\n", plusPetit);
Elle fait afficher la valeur 3.4 l'cran.
2.

void echanger ( int * p, int * s )


/* changer le contenu de 2 entiers */
{
int temporaire ;

temporaire = *p ;
*p
= *s ;
*s
= temporaire ;

Utilisation :
int a, b ;
printf("Entrez 2 entiers ");
scanf("%d%d",&a, &b);
printf("Avant l'change, a = %3d, b = %3d\n", a, b);
echanger (&a, &b ) ;
printf("Aprs l'change, a = %3d, b = %3d\n", a, b);
Explications :
Supposons que a vaut 15, b vaut 7 et l'adresse de a est 48000
et l'adresse de b est 48002.
Avec l'appel :

echanger (&a, &b ) ;

On transmet la fonction :
1. la valeur 48000 P
2. la valeur 48002 S

void echanger ( int * p, int * s )


(avec *P vaut 15)
(avec *S vaut 7)

48000

48002

On excute la fonction :
temporaire = *p ; ===> temporaire vaut 15
*p
= *s ; ===> Le contenu l'adresse 48000 est
celui l'adresse 48002
Adresse
48000

a
Chapitre 2 : Boucles de rptitions et fonctions

Adresse
48002

b
Page 60

*s

= temporaire ; ==> Le contenu l'adresse


48002 vaut 15 :

15

Adresse
48002

A la fin de la fonction, les valeurs de a et b sont changes.


Exercice :
crire une fonction qui reoit deux rels a et b comme paramtres
d'entre. Elle calcule et retourne (par pointeurs) les rsultats
suivants :
- la plus grande valeur parmi a et b
- la plus petite valeur parmi a et b
- la diffrence positive entre a et b
(absolue de (a-b)).

2) Fonctions prdfinies (implantes) dans les fichiers d'en-tte :


Chaque fonction ralise souvent une tche (exemple : conversion une
chane de caractres en entier), elle signale aussi le rsultat de la
ralisation (faisable ou non) :
Exemple :
int
char

k , valeur ;
* chaine
; /* une chane des caractres */

chaine = "abcdef" ;
valeur = atoi(chaine) ; /* alphabetic to integer */
printf("valeur = %d\n", valeur) ; /* valeur vaut zro, choue! */
chaine = "573" ;
valeur = atoi(chaine) ; /* alphabetic to integer */
printf("valeur = %d\n", valeur) ; /* valeur vaut 573, correct! */

Chapitre 2 : Boucles de rptitions et fonctions

Page 61