Академический Документы
Профессиональный Документы
Культура Документы
Introduction ......................................................................................................................... 1
Un exemple ............................................................................................................................. 2
I-
Introduction
Un long programme est difficile apprhender globalement. Il vaut donc mieux le scinder en
petits programmes : un programme principal fait appel des sous-programmes, qui
peuvent eux-mmes faire appel des sous-programmes, du programme principal ou de celuici, et ainsi de suite. Cest le principe du raffinement successif. De plus certains sousprogrammes peuvent servir dans dautres programmes, cest le principe de la modularit. Ces
principes sont mis en uvre en langage C grce aux fonctions.
On peut distinguer, en langage C, les fonctions prdfinies des bibliothques (telles que
printf() ou scanf()), livres avec le compilateur et intgres au programme lors de
ldition des liens, et les fonctions que le programmeur crit lui-mme en tant que partie du
texte source. Nous avons dj vu comment utiliser les premires dans le module
programmation 1. Nous allons donc nous intresser ici aux secondes. Nous verrons aussi,
dailleurs, la faon de concevoir les fonctions prdfinies.
Un exemple
Commenons par donner un exemple simple de dfinition et dutilisation dune fonction.
Considrons nouveau, pour cela, notre exemple de fonction que lon veut valuer en un
certain nombre de points. Une nouvelle amlioration consiste dgager la dfinition de la
fonction en utilisant un sous-programme.
Programme : La mise en place suivante est intuitivement comprhensible, nous la
dtaillerons ensuite :
/* Fonct_1.c */
#include <stdio.h>
#include <math.h>
double f(double x)
{
return((sin(x) + log(x))/(exp(x) + 2));
}
void main(void)
{
float x, y;
printf("x = ");
scanf("%f",&x);
while (x != 1000)
{
y = f(x);
printf("f(%f) = %f\n", x, y);
printf("x = ");
scanf("%f",&x);
}
}
Lamlioration provient ici plus particulirement du fait que lon ne soccupe pas de la
fonction particulire dans le corps du programme, mais uniquement du fait que lon veut
afficher sa valeur en un certain nombre de points, ce qui est lessence du programme. Il suffit
de changer le sous-programme, bien mis en vidence, lorsquon veut changer de fonction.
II-
La dfinition des fonctions, dans des cas simples, est suffisamment claire au vu du
programme Prcdent. Mais lexplicitation suivante donne les limites dune utilisation
intuitive.
Une fonction ou procdure ou encore sous programme est une suite d'instructions
lmentaires, mais vue du programme principal main(), elle reprsente une seule action.
Elle est la base des langages structurs.
En effet, l'criture de tout programme commence par dfinir un algorithme qui dcrit
l'enchanement de toutes les actions (fonctions). La ralisation d'un algorithme c'est la
dcomposition en une suite de fonctions simples pouvant raliser une ou plusieurs fonctions
beaucoup plus compliques. Un algorithme doit tre le plus clair possible.
III-
2) La dclaration de fonction.
Elle dcrit l'enchanement de toutes les instructions permettant de raliser la fonction. Une
variable dclare dans une fonction est locale celle-ci et elle n'a aucune existence en dehors
de la fonction.
Syntaxe:
<type_iden_sortie> iden_fonc(<type> iden_1,..., <type> iden_n)
{
/* Dclaration de variable(s) locale(s) */
<type> iden_2,...,iden_m;
.
.
.
/* renvoie dans le paramtre de sortie */
return(valeur);
}
3) L'appel de fonction:
Il dirige le programme principal ou une autre fonction sur la fonction excuter en donnant
les variables d'entres et ou l'affectation de la variable de sortie.
Syntaxe:
iden_var_sortie = iden_fonc(iden_1_var,...,iden_n_var) ;
ou
iden_fonc.(iden_1_var,...,iden_n_var) ;
4) Exemples de dfinition
Donnons quelques exemples varies de dfinitions pour illustrer ce que nous venons de dire.
Exemple 1.- (La fonction sinc)
La fonction f dfinie par lexpression f(x) = sin(x)/x est a priori dfinie sur R*, mais on sait la
prolonger par continuit en 0 en lui attribuant la valeur 1. Ceci permet de dfinir la fonction
suivante :
double f(double x)
{
double y;
if (x == 0) y = 1;
else y = sin(x)/x;
return(y);
}
Ou
double f(double x)
{
if (x == 0) return(1);
else return(sin(x)/x));
}
Ceci nous donne un exemple avec deux utilisations de return, cause de la dfinition par cas.
Exemple 2.- (Lexponentiation)
Programmons lexponentiation, cest--dire lapplication de RN dans R qui au couple (x, n)
associe :
double puiss(double x, int n)
{
double y;
int i;
y = 1;
for (i=1; i <= n; i++) y = y*x;
return(y);
}
Cet exemple est intressant plusieurs titres : la fonction a deux paramtres et, de plus, de
types diffrents ; le corps comporte des dclarations de variables, dites variables locales ; on
utilise une boucle pour dfinir la fonction. On remarquera que nous sommes obligs
dintroduire la variable locale y car puiss ne pourrait pas tre utilise droite dune affectation.
Exemple 3.- (Fonction sans paramtre)
Considrons le programme suivant :
/* intro.c */
#include <stdio.h>
void welcome(void)
{
printf("Welcome to this fantastic program");
printf("\nwhich proves to you the power");
printf("\nof the modularity. \n");
}
void bienvenue(void)
{
printf("Bienvenue dans ce merveilleux");
printf("\nprogramme qui vous montre la");
printf("\npuissance de la modularite. \n");
}
void main(void)
{
char c;
printf("Do you want to continue in");
printf("\nEnglish or in French (E/F) ");
scanf("%c",&c);
if (c == E) welcome();
else bienvenue();
}
Les fonctions welcome() et bienvenue() nont ni paramtre ni valeur de retour. Remarquons
cependant lutilisation du type void et le couple de parenthses pour lappel de ces fonctions.
Le fait quil ny ait pas de type de retour rend lutilisation de return superflue.
Exemple 4.- (Le pgcd)
Nous avons dj rencontr des exercices sur le calcul du pgcd de deux entiers non nuls,
lutilisation des fonctions amliore successivement les algorithmes pour le calculer. Le
programme suivant repose sur lalgorithme dEuclide.
int pgcd(int a, int b)
{
int r;
r = 1;
while (r != 0)
{
r = a % b;
a = b;
b = r;
}
return(a);
}
Cet exemple est intressant pour deux raisons : la fonction a deux paramtres de mme type et
lalgorithme est non trivial.
5) Situation de la dclaration
La dclaration locale dune fonction se fait au dbut du bloc de la dfinition dune fonction,
avant les dclarations de variables. La fonction ainsi dclare nest connue que de la fonction
dans laquelle elle est dclare. On peut aussi utiliser une dclaration globale dune fonction
avant toute dfinition de fonction.
Exemple de dclaration locale :
Le programme suivant illustre la notion de dclaration locale de fonction :
/* dec_loc.c */
#include <stdio.h>
#include <math.h>
void main(void)
{
double f(double x);
float x, y;
printf("x = ");
scanf("%f",&x);
while (x != 1000)
{
y = f(x);
printf("f(%f) = %f\n", x, y);
printf("x = ");
scanf("%f",&x);
}
}
double f(double x)
{
return((sin(x) + log(x))/(exp(x) + 2));
}
Exemple de dclaration globale :
Le programme suivant illustre la notion de dclaration globale de fonction :
/* dec_glo.c */
#include <stdio.h>
#include <math.h>
double f(double x);
void main(void)
{
float x, y;
printf("x = ");
scanf("%f",&x);
while (x != 1000)
{
y = f(x);
printf("f(%f) = %f\n", x, y);
printf("x = ");
scanf("%f",&x);
}
}
double f(double x)
{
return((sin(x) + log(x))/(exp(x) + 2));
}
Pragmatique : Pour mettre un peu dordre dans un programme faisant intervenir beaucoup de
fonctions, on peut commencer par la dfinition de la fonction principale, prcde de la
dclaration des fonctions dont elle a besoin. On dfinit ensuite ces fonctions auxiliaires,
chacune delles prcde des dclarations des fonctions auxiliaires de second niveau dont
elles ont besoin, et ainsi de suite.
Dclaration de fonctions dans des fichiers en-tte :
La dclaration dune fonction personnelle ne doit pas ncessairement tre incluse
explicitement dans le mme fichier du programme source que la fonction principale. Elle peut
aussi se trouver dans un fichier en-tte, comme pour les fonctions prdfinies. Ce dernier est
ensuite insr dans le programme grce une commande du prprocesseur.
Exemple : En mettant dans le mme rpertoire les deux fichiers suivants :
/* calcul.c */
#include <stdio.h>
#include <math.h>
#include "defini.h"
void main(void)
{
float x, y;
printf("x = ");
scanf("%f",&x);
while (x != 1000)
{
y = f(x);
printf("f(%f) = %f\n", x, y);
printf("x = ");
scanf("%f",&x);
}
}
et :
/* defini.h */
double f(double x)
{
return((sin(x) + log(x))/(exp(x) + 2));
}
On obtient un programme correct qui fonctionne.
Commentaires :
Le fichier en-tte definiti.h contient ici non pas la dclaration dune fonction mais sa
dfinition. On place en principe le code des fonctions directement dans le programme
ou dans des bibliothques. Les dfinitions des fonctions livres avec le compilateur se
trouvent (sous forme de code objet) dans de telles bibliothques ayant lextension .lib
(pour LIBrary).
Le fichier en-tte est crit entre guillemets verticaux " et non entre les symboles < et >
car il ne se trouve pas dans le rpertoire standard des en-ttes mais dans le rpertoire
contenant le programme source. On peut aussi indiquer le chemin complet, par
exemple en MS-DOS (Windows) : #include "c:\tcwin\programmes\definiti.h"
IV-
Pour une fonction mathmatique f deux arguments, lorsquon value cette fonction, en
effectuant par exemple y = f(a, b), on sattend ce que la valeur de y soit change mais pas
les valeurs de a ou de b. En informatique il en va autrement. On peut, par exemple, dsirer
crire un sous-programme :
echange(a,b) dont le but est dchanger les valeurs de a et de b. Nous savons crire le corps
de ce programme en langage C depuis longtemps. Par exemple si a et b sont des entiers on a :
int c;
c = a;
a = b;
b = c;
Comment crire le sous-programme correspondant dans un langage de programmation ?
Passage par valeur et passage par rfrence : Lorsquon ne veut pas changer la valeur dun
paramtre on parle de transmission par valeur (en anglais call by value), cest--dire que le
Sous-programme ne reoit quune copie de la valeur. Lorsquon veut se permettre la
possibilit de changer la valeur on parle de transmission par reference (en anglais call by
reference).
Mise en place : Beaucoup de langages permettent ces deux types de transmission. Cest le
cas, par exemple, du langage PASCAL. Le langage C, quant lui, ne connait que la
transmission par valeur, la transmission par rfrence est mule en envoyant la valeur de
ladresse grce aux pointeurs (comme nous le verrons dans le chapitre 2 : La
2) Variable globale
Une variable globale est une variable dclare avant toute dfinition de fonction. Lintrt est
quelle est alors connue de toutes les fonctions, on peut donc changer sa valeur dans chacune
10
delle. Linconvnient est quon peut aussi changer sa valeur par inattention. Il vaut donc
mieux utiliser le passage par rfrence quune variable globale. Voyons cependant comment
cela fonctionne.
Exemple 1.- Considrons le programme suivant :
/* global_1.c */
#include <stdio.h>
int n = 3;
void affiche(void)
{
n = 4;
printf("%d \n", n);
}
void main(void)
{
affiche();
printf("%d \n", n);
}
Lexcution de ce programme fait afficher 4 puis 4. La fonction affiche() a donc chang la
valeur de n dans la fonction principale.
Remarque : Lutilisation des variables globales dans un programme est permise, mais il vaut
mieux viter de sen servir. Cela va en effet lencontre de la clart de la modularit.
Lorsquon considre un sous-programme on devrait voir dun seul coup dil toutes les
variables qui y interviennent. Cest tout lintrt des arguments.
Exemple 2 : Remarquons que, dans le cas de la variable globale ci-dessus, la fonction
affiche() navait pas dargument. Considrons, par opposition, le programme suivant :
/* global_2.c */
#include <stdio.h>
int n = 3;
void affiche(int n)
{
n = 4;
printf("%d \n", n);
}
void main(void)
{
affiche(n);
printf("%d \n", n);
}
Son excution fait afficher 4 puis 3. La dclaration de largument n dans la fonction affiche()
le fait considrer comme une variable locale. La variable n du corps de cette fonction
correspond la dernire variable dclare, donc la variable locale, et non la variable
globale ; ceci explique pourquoi la valeur de la variable globale, elle, na pas change.
La classe dallocation des variables globales
11
Dune manire gnrale, les variables globales existent pendant toute lexcution du
programme dans lequel elles apparaissent. Leurs emplacements en mmoire sont parfaitement
dfinis lors de ldition de liens. On traduit cela en disant quelles font partie de la classe
dallocation statique.
De plus, ces variables se voient initialises zro, avant le dbut de lexcution du
programme, sauf, bien sr, si vous leur attribuez explicitement une valeur initiale au moment
de leur dclaration.
3) Variable locale
La procdure elle-mme peut avoir besoin de variables qui nont pas dintrt pour le
programme en son entier, comme nous lavons vu propos de la fonction puiss(). On parle
alors de variable locale.
Remarque : Le nom de variable locale se justifie de par la position de sa dclaration. Elle se
justifie aussi car une telle variable nest pas connue du programme dans son entier mais
uniquement de la fonction (plus gnralement du bloc) dans laquelle elle a t dclare.
Ceci montre lintrt dutiliser le plus possible des variables locales : cest une protection
Supplmentaire contre les erreurs de programmation.
Nom dune variable locale : Nous avons dit prcdemment (avant de considrer les
fonctions) quon ne peut pas dclarer le mme identificateur comme variable deux endroits
diffrents. Il faut assouplir un petit peu cette rgle avec lutilisation des sous-programmes : en
effet, pour un (gros) programme, des sous-programmes diffrents peuvent tre conus par des
personnes diffrentes, qui peuvent donc penser au mme nom de variable ; ceci sera dtect
lors de la compilation, certes, mais cela risque de ne pas faciliter les choses. On permet donc
en gnral, dans un langage de programmation, la possibilit dutiliser un mme identificateur
plusieurs fois comme variable locale.
Priorit des variables locales sur les variables globales : Un problme se pose alors :
Quelle est la variable considre lors de son utilisation ?
La rponse est simple : celle qui a t dclare le plus rcemment, cest -dire la plus locale
lintrieur du sous-programme (et, plus gnralement, lintrieur du bloc).
12
Dautre part, les valeurs transmises en arguments une fonction sont traites de la mme
manire que les variables locales. Leur dure de vie correspond galement celle de la
fonction.
13
Le langage C autorise la rcursivit des appels de fonctions. Celle-ci peut prendre deux
aspects :
Voici un exemple fort classique (dailleurs inefficace sur le plan du temps dexcution) dune
fonction calculant une factorielle de manire rcursive :
long fac (int n)
{
if (n>1) return (fac(n-1)*n) ;
else return(1) ;
}
Il faut bien voir qualors chaque appel de fac entrane une allocation despace pour les
variables locales et pour son argument n (apparemment, fac ne comporte aucune variable
locale ; en ralit, il lui faut prvoir un emplacement destin recevoir sa valeur de retour).
Or chaque nouvel appel de fac, lintrieur de fac, provoque une telle allocation, sans que les
emplacements prcdents soient librs.
Il y a donc un empilement des espaces allous aux variables locales, paralllement un
empilement des appels de la fonction. Ce nest que lors de lexcution de la premire
instruction return que lon commencera dpiler les appels et les emplacements et donc
librer de lespace mmoire.
V-
Si le langage C est effectivement un langage que lon peut qualifier doprationnel, cest en
partie grce ses possibilits dites de compilation spare. En C, en effet, il est possible de
compiler sparment plusieurs programmes (fichiers) source et de rassembler les modules
objet correspondants au moment de ldition de liens. Dailleurs, dans certains
environnements de programmation, la notion de projet permet de grer la multiplicit des
fichiers (source et modules objet) pouvant intervenir dans la cration dun programme
excutable.
Cette notion de projet fait intervenir prcisment les fichiers considrer ; gnralement, il est
possible de demander de crer le programme excutable, en ne recompilant que les sources
ayant subi une modification depuis leur dernire compilation.
Indpendamment de ces aspects techniques lis lenvironnement de programmation
considr, les possibilits de compilation spare ont une incidence importante au niveau de
la porte des variables globales. Cest cet aspect que nous nous proposons dtudier
maintenant. Dans le paragraphe suivant, nous serons alors en mesure de faire le point sur les
diffrentes classes dallocation des variables.
14
Notez que, partir du moment o lon parle de compilation spare, il existe au moins (ou il a
exist) deux programmes source ; dans la suite, nous supposerons quils figurent dans des
fichiers, de sorte que nous parlerons toujours de fichier source.
Pour linstant, voyons lincidence de cette compilation spare sur la porte des variables
globales.
source 2
fct2()
{
.....
}
fct3()
{
.....
}
Il ne semble pas possible, dans les fonctions fct2 et fct3 de faire rfrence la variable
globale x dclare dans le premier fichier source (alors quaucun problme ne se poserait si
lon runissait ces deux fichiers source en un seul, du moins si lon prenait soin de placer les
instructions du second fichier la suite de celles du premier).
En fait, le langage C prvoit une dclaration permettant de spcifier quune variable globale a
dj t dfinie dans un autre fichier source. Celle-ci se fait laide du mot-cl extern. Ainsi,
en faisant prcder notre second fichier source de la dclaration : extern int x ;
Il devient possible de mentionner la variable globale x (dclare dans le premier fichier
source) dans les fonctions fct2 et fct3.
Remarque : Cette dclaration extern neffectue pas de rservation demplacement de
variable. Elle ne fait que prciser que la variable globale x est dfinie par ailleurs et elle en
prcise le type.
15
subsiste aucune trace du nom aprs compilation, le nom des variables globales continue
exister au niveau des modules objet. On retrouve l un mcanisme analogue ce qui se passe
pour les noms de fonctions, lesquels doivent bien subsister pour que lditeur de liens soit en
mesure de retrouver les modules objet correspondants.
Dautre part, aprs compilation du second fichier source, on trouve dans le module objet
correspondant, une indication mentionnant quune certaine variable de nom x provient de
lextrieur et quil faudra en fournir ladresse effective.
Ce sera effectivement le rle de lditeur de liens que de retrouver dans le premier module
objet ladresse effective de la variable x et de la reporter dans le second module objet.
Ce mcanisme montre que sil est possible, par mgarde, de rserver des variables globales de
mme nom dans deux fichiers source diffrents, il sera, par contre, en gnral, impossible
deffectuer correctement ldition de liens des modules objet correspondants (certains diteurs
de liens peuvent ne pas dtecter cette anomalie). En effet, dans un tel cas, lditeur de liens se
trouvera en prsence de deux adresses diffrentes pour un mme identificateur, ce qui est
illogique.
16
Remarque : Le cas des fonctions. La fonction est considre par le langage C comme un objet
global. Cest ce qui permet dailleurs lditeur de liens deffectuer correctement son travail.
Il faut noter toutefois quil nest pas ncessaire dutiliser une dclaration extern pour les
fonctions dfinies dans un fichier source diffrent de celui o elles sont appeles (mais le faire
ne constitue pas une erreur).
En tant quobjet global, la fonction peut voir sa porte limite au fichier source dans lequel
elle est dfinie laide dune dclaration static comme dans : static int fct (...)
4) Tableau rcapitulatif
Voici un tableau rcapitulant la porte et la classe dallocation des diffrentes variables
suivant la nature de leur dclaration (la colonne Type donne le nom quon attribue
usuellement de telles variables).
17
Exercices :
Exercice 1
Ecrire une fonction SOMME de type float qui calcule la somme de deux nombres rels.
Ecrire une fonction MOYENNE de type float qui calcule la moyenne arithmtique de deux
nombres rels.
Ecrire un programme se servant de ces deux fonctions pour afficher la somme et la moyenne de deux
nombres rels saisis au clavier.
Exercice 2
Ecrire une fonction MIN et une fonction MAX qui dterminent le minimum et le maximum de deux
nombres rels.
Ecrire un programme se servant des fonctions MIN et MAX pour dterminer le minimum et le
maximum de :
deux nombres rels entrs au clavier.
Quatre nombre rels entrs au clavier.
Exercice 3
crire une fonction rcursive calculant la valeur de la fonction dAckermann A dfinie
Pour m>0 et n>0 par :
A(m,n) = A(m-1,A(m,n-1)) pour m>0 et n>0
A(0,n) = n+1 pour n>0
A(m,0) = A(m-1,1) pour m>0
Exercice 4
Ecrire un programme se servant d'une fonction F pour afficher la table de valeurs de la fonction
dfinie par : f(x) = sin(x) + ln(x) O x est un entier compris entre 1 et 10.
Exercice 5
1. Ecrire la fonction ECRIRE_TAB deux paramtres TAB (un tableau de type int) et N (la
taille du tableau) qui affiche les N valeurs du tableau TAB.
2. Ecrire la fonction SOMME_TAB qui calcule la somme des N lments d'un tableau
TAB du type int. N et TAB sont fournis comme paramtres; la somme est retourne
comme rsultat du type long.
A l'aide des fonctions prcdentes, crire un programme qui lit un tableau A d'une dimension
infrieure ou gale 100 et affiche le tableau et la somme des lments du tableau.
Exercice 6
1. Ecrire une fonction DOUBLE qui remplace un nombre rel X par son double (2*X)
2. Ecrire une fonction qui change le contenu de deux variables.
18
Exercice 8
Ecrire une fonction qui prend comme paramtres un entier X, un tableau A de type int , et la taille du
tableau A et limine toutes les occurrences de X dans A en tassant les lments restants.
Ecrire un programme qui teste cette fonction l'aide de valeurs lues au clavier.
Exercice 9
Ecrire une fonction INVERSE qui range les lments d'un tableau A de type int dans l'ordre inverse.
Ecrire un petit programme qui teste la fonction INVERSE.
Exercice 10
Ecrire la fonction NCHIFFRES de type int qui obtient une valeur entire N (positive ou ngative) de
type long comme paramtre et qui fournit le nombre de chiffres de N comme rsultat.
Ecrire un petit programme qui teste la fonction NCHIFFRES:
Exemple:
Introduire un nombre entier : 6457392
Le nombre 6457392 a 7 chiffres.
Exercice 11
Ecrire un programme qui construit et affiche le triangle de Pascal en calculant les coefficients
binomiaux:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
. . .
On n'utilisera pas de tableau, c.--d. il faudra calculer les coefficients d'aprs la formule ci-dessous,
tout en dfinissant et utilisant les fonctions adquates.
19
Exercice 12
On souhaite crer un programme d'annuaire trs simplifi qui associe un nom de personne
un numro de tlphone :
1. Crer une structure Personne pouvant contenir ces informations (nom et tlphone).
Le nom peut contenir 32 caractres et le numro 16 caractres.
2. Crer une nouvelle structure qui va reprsenter le carnet d'adresses. Cette structure
Carnet contiendra un tableau de 20 Personnes et un compteur indiquant le nombre de
personnes dans le tableau.
3. Crer ensuite une fonction qui cre et qui renvoie une structure Personne contenant un
nom et un tlphone passs en argument.
4. Rajouter une fonction qui affiche les informations contenues dans la structure
Personne passe en argument.
5. Crer une fonction qui ajoute une personne dans un carnet, lajout se fait par insertion,
le carnet doit rester tri par ordre alphabtique des noms aprs lajout.
6. Crer une fonction qui recherche un contact dans le carnet par nom, la fonction
retourne lindice de la personne recherche ou -1 si le contact nexiste pas.
7. Crer une fonction qui affiche un carnet.
8. Crer une fonction qui supprime un contact du carnet, la fonction prend en paramtre
le carnet, le nom du contact et retourne le nouveau carnet.
9. A partir des tapes prcdentes, faire un programme grant un carnet d'adresse. Crer
un menu qui propose d'ajouter une nouvelle personne, de supprimer une personne, de
rechercher une personne par nom, d'afficher le carnet ou de quitter:
1 : pour ajouter un contact
2 : pour supprimer un contact
3 : pour afficher la liste des contacts
4 : pour rechercher un contact par nom
5 : pour quitter le programme
Entrez votre choix [1-5] :
20