Professional Documents
Culture Documents
Les fonctions
Ds lors quun programme commence prendre une certaine importance, sa comprhension peut devenir dlicate si lon ne dispose daucun moyen de lorganiser. Certes, lutilisation judicieuse des commentaires peut permettre de mettre en vidence les diffrentes parties du programme ; il nen reste pas moins que le lecteur de ce programme en a une vision squentielle, la manire du lecteur dun livre qui ne disposerait pas de table des matires. Par ailleurs, dans un gros programme, il est frquent que lon ait raliser en plusieurs endroits un travail comparable, par exemple le tri dun tableau de nombres. Dans ces conditions, il est regrettable davoir introduire, diverses reprises, des instructions identiques ou presque. En fait, la plupart des langages, y compris le C, permettent de raliser ce que lon nomme des sous-programmes. Il sagit dinstructions quon crit une seule fois en leur attribuant un nom. Le sous-programme peut ensuite tre utilis en tout point dun programme en se contentant den crire le nom. Cela permet, en quelque sorte, de travailler avec des lments prfabriqus au lieu dtre rduit, chaque fois, assembler les briques de base que sont les instructions du langage. De surcrot, le sous-programme peut tre paramtr, de faon que son travail puisse sadapter des situations semblables, mais non identiques ; par exemple, un sousprogramme calculant la valeur dun polynme pourra travailler avec diffrentes valeurs des coefcients ou de la variable ; un sous-programme de tri dun tableau pourra travailler avec diffrents tableaux, ventuellement de tailles diffrentes
ditions Eyrolles
135
Dans beaucoup de langages, on traite sparment le cas des sous-programmes qui se contentent de calculer une valeur, partir dun ou plusieurs paramtres quon leur fournit ; on les nomme des fonctions, par analogie avec la notion mathmatique de mme nom. Assez curieusement, le langage C ne fait pas cette distinction et, de plus, il emploie ce terme de fonction pour dcrire toutes les formes de sous-programmes. Dans ce chapitre, nous allons apprendre progressivement mettre en uvre ces fonctions. Nous commencerons par un exemple simple de (vraie) fonction un paramtre, avant daborder des situations plus gnrales : vraie fonction plusieurs paramtres, fonction sans paramtre, fonction sans rsultat
on y prcisera le nom (ici, nous choisirons le nom cube), le type de son unique paramtre (ici oat) et le type de la valeur quelle est cense calculer (ici oat) ;
s son utilisation, au sein dun programme.
Par souci de simplicit, nous commencerons par le deuxime aspect ; nous supposons donc que la fonction a t convenablement dnie et nous allons voir comment lutiliser. Il nous sera ensuite plus facile de voir comment la dnir.
dsigne simplement le rsultat (de type oat) obtenu en excutant les instructions de la fonction cube, laquelle on fournit en paramtre la valeur de a. On dit galement que cette notation dsigne un appel de la fonction cube. Cette notation peut apparatre au sein dune expression arithmtique quelconque ; par exemple, si c est galement de type oat, vous pourrez crire laffectation suivante :
c = cube(a) * 3.0 ;
136
ditions Eyrolles
chapitre n 8
Les fonctions
Pour traduire convenablement une telle instruction, le compilateur doit connatre les caractristiques de la fonction cube (nom, type du paramtre, type de la valeur fournie en rsultat) ; on lui fournit cette information laide dune dclaration approprie, nomme prototype, laquelle, dans notre cas, se prsente ainsi :
float cube (float) ;/* prototype de notre fonction cube */
Voici un exemple de programme utilisant notre fonction (attention, il est incomplet, car il y manque la dnition de la fonction) :
main() { float cube (float) ; float a, c ; a = 1.5 ; c = cube(a) ; printf (c : %f\n, c) ; c = cube(a) * 3.0 ; /* prototype de la fonction cube */
/* premire utilisation de cube */ /* seconde utilisation dans une expression arithmtique */ /* troisime utilisation */
N e confondez pas la dclaration de la fonction avec sa dnition (que nous tudions dans le
paragraphe suivant).
Vous constatez que sa structure est voisine de celle dun programme, savoir : un en-tte suivi dun corps form dun bloc (suite dinstructions entre { et }). Len-tte :
float cube (float x)
ditions Eyrolles
137
prcise ici, outre le nom de la fonction (ici cube), le nom du paramtre (ici x), son type (oat), ainsi que le type de la valeur que nous fournira la fonction (ici oat). Son rle peut tre schmatis ainsi : oat cube (oat x) | | | type du nom de la type et nom du premier rsultat fonction (et, ici, unique) paramtre Notez bien que le nom x dsigne la valeur du paramtre qui sera reu par cube, lorsquon lappellera ; il est choisi librement comme nimporte quel nom de variable. Il va nous servir, dans le corps de la fonction, expliciter lusage qui sera fait de ce paramtre. Si lon considre le corps de notre fonction, on y trouve une instruction de dclaration : oat y ; Elle prcise que, pour effectuer son travail, la fonction a besoin dune variable de type oat que nous nommons y. Il sagit l dune dclaration analogue celle que lon pouvait trouver dans un programme ; la seule diffrence est que cette variable y nest connue que dans les instructions de dnition de notre fonction : on dit que la variable y est locale la fonction cube. Linstruction daffectation qui suit est classique. Enn, linstruction :
return y ;
prcise la valeur qui sera fournie (on dit aussi renvoye ou retourne) par la fonction, la n de son travail (on dit aussi de son excution).
Remarques E n fait, on emploie parfois le terme de programme principal pour dsigner ce que nous avions
appel jusquici programme ; le terme de programme tant plus gnral et pouvant, le cas chant, correspondre au regroupement dun programme principal et de une ou plusieurs fonctions.
L a valeur fournie par une fonction se nomme aussi rsultat ou valeur de retour. E n C, le programme principal nest rien dautre quune fonction comme les autres dont seul le
nom est impos : main (qui signie principal(e) en anglais). Les variables que nous avions dclares jusquici taient en fait des variables locales cette fonction main.
138
ditions Eyrolles
chapitre n 8
Les fonctions
ou aprs la dnition de la fonction cube. Voici un exemple complet de ce que pourrait tre notre programme (main + cube), accompagn dun exemple dexcution :
/* exemple de programme utilisant notre fonction cube */ main() { float cube (float) ; /* prototype de la fonction cube */ float a, c ; a = 1.5 ; c = cube(a) ; /* premire utilisation de la fonction cube */ printf (c : %f\n, c) ; c = cube(a) * 3.0 ; /* seconde utilisation dans une exp. arith. */ printf (c : %f\n, c) ; printf (cube de 2.2 : %f, cube(2.2)) ; /* troisime utilisation */ } /* dfinition de notre fonction cube */ float cube (float x) /* en-tte de la fonction cube */ { float y ; /* variable locale la fonction cube */ y = x * x * x ; return y ; }
D ans la dnition de notre fonction cube, nous pouvons utiliser pour le paramtre et pour la
variable locale, des noms quelconques, sans nous proccuper de ceux qui risquent dapparatre dans un programme amen lutiliser. Ainsi, bien que la fonction main dispose de variables locales nommes a et c, nous aurions pu, sans problme, dnir cube de cette faon : float cube (float a) { float c ; c = a * a * a ; return c ; } On dit que la porte des variables locales (comme c) et des paramtres (comme a) est limite la fonction o ils sont dnis.
ditions Eyrolles
139
Remarques L orsque
la dnition de la fonction est fournie en premier, sa dclaration (prototype) est facultative ; ceci provient de ce que, au moment o le compilateur rencontre des instructions utilisant la fonction, il a dj effectu la traduction de sa dnition et, donc, il dispose des informations ncessaires. Nous vous conseillons cependant de conserver lhabitude de dclarer votre fonction, mme dans cette situation.
E n fait, il est possible de compiler sparment le programme principal (main) et la fonction cube. Dans ce cas, les deux rsultats de compilation (quon nomme des modules objet) doivent tre runis par la suite, dans une opration nomme dition de liens. Ces possibilits dites de compilation spare constituent un atout majeur du langage C ; elles sortent cependant du cadre de cet ouvrage dinitiation.
plusieurs ou aucun ;
s il sagissait dune vraie fonction, cest--dire que son rle tait limit un calcul ;
comme nous lavons dit en introduction, en C, une fonction peut raliser nimporte quelles actions et, ventuellement, ne pas fournir de rsultat.
140
ditions Eyrolles
chapitre n 8
Les fonctions
Cette fois, len-tte mentionne trois paramtres et correspond au schma suivant : int | type du rsultat max | nom de la fonction (int a, | premier paramtre (nom a, type int) int b, | deuxime paramtre (nom b, type int) int c) | troisime paramtre (nom c, type int)
Ici encore, notre fonction comporte une variable locale, nomme m, qui servira dterminer la valeur maximale cherche. Voici un exemple de programme complet comportant la fois la dnition et lutilisation de cette fonction (notez que, l encore, le prototype de la fonction se dduit facilement de son en-tte, en liminant les noms de paramtres) :
main() { int max (int, int, int) ; /* prototype de notre fonction max */ int n, p, q, m ; n = 3 ; p = 5 ; q = 2 ; m = max (n, p, q) ; printf (max de %d %d %d : %d\n, n, p, q, m) ; m = max (5*n, n+p, 12) ; printf (valeur : %d\n , m) ; } int max (int a, int b, int c) { int m ; m = a ; if ( b>m ) m = b ; if ( c>m ) m = c ; return m ; }
ditions Eyrolles
141
max de 3 5 2 : 5 valeur : 15
VIII.2
crivez la dnition dune fonction calculant la valeur de lexpression : ax2 + bx + c lorsquon lui transmet, en paramtre, les valeurs de a, b, c et x (supposes de type oat, ainsi que le rsultat de la fonction).
Son en-tte montre quelle comporte un paramtre entier (nomm nfois) ; cette fois, il est prcd du mot void qui indique que la fonction ne fournit pas de rsultat. Si lon examine les instructions du corps de la fonction, on constate quelles afchent un certain nombre de fois (correspondant la valeur transmise en paramtre) le mme texte : il fait beau. L encore, pour effectuer son travail, notre fonction a eu besoin dune variable locale (i). Notez quaucune instruction return ne gure dans la dnition de notre fonction. En ce qui concerne lutilisation de notre fonction, compte tenu de ce quelle ne fournit aucun rsultat, il nest plus possible den mentionner un appel au sein dune expression arithmtique, comme, par exemple :
y = optimist (k) ;/* incorrect */
En fait, il suft de se contenter den provoquer lappel laide dune instruction de la forme :
optimist (k) ; /* instruction simple provoquant lappel de optimist laquelle on transmet en paramtre, la valeur de k */
142
ditions Eyrolles
chapitre n 8
Les fonctions
il il il il il il
VIII.3
a) crivez les instructions permettant dafcher un triangle dastrisques se prsentant ainsi (le nombre de lignes, ici 4, tant indiqu dans une variable nomme nl ) : * ** *** ****
ditions Eyrolles
143
b) Transformez ces instructions en une fonction nomme triangle recevant en paramtres le nombre de lignes souhaites. c) crivez un programme principal utilisant la fonction triangle ainsi obtenue, de faon afcher le motif suivant : * ** * ** *** * ** *** ****
3. Quelques rgles
3.1 Les paramtres formels (muets) et les paramtres effectifs
Les paramtres gurant dans len-tte dune fonction se nomment des paramtres muets (ou encore paramtres formels). Leur rle est de permettre, au sein du corps de la fonction, de dcrire ce quelle doit faire. Comme nous lavons dj dit, leur porte est limite la dnition de la fonction concerne ; ils nentrent donc pas en conit avec dventuelles variables locales dautres fonctions (y compris main). Les paramtres fournis lors de lutilisation (lappel) de la fonction se nomment des paramtres effectifs. Comme le montrent nos prcdents exemples, on peut utiliser nimporte quelle expression comme paramtre effectif ; au bout du compte, cest la valeur de cette expression qui sera transmise la fonction lors de son appel. Notez quune telle libert naurait aucun sens dans le cas des paramtres formels : il serait impossible dcrire un en-tte de fexple sous la forme oat fexple (oat a+b, ), pas plus quen mathmatiques, vous ne dniriez une fonction f par f(x+y)=5 !
chapitre n 8
Les fonctions
Si le type de lexpression gurant dans return est diffrent du type du rsultat tel quil a t dclar dans len-tte, le compilateur nindiquera pas derreur ; il mettra automatiquement en place des instructions de conversion appropries, par exemple :
float f (...) { int n ; ..... return n ; }
Le rle de linstruction return est double : dune part, il prcise la valeur qui sera fournie en rsultat, dautre part, il met n lexcution des instructions de la fonction. Jusquici, notre instruction return apparaissait comme la dernire de la dnition de notre fonction, mais il est thoriquement possible de placer plusieurs instructions return dans une mme fonction. Par exemple, une fonction dterminant le maximum de deux valeurs qui scrit ainsi avec une seule instruction return :
float max (float a, float b) { float m ; if (a>b) m = a ; else m = b ; return m ; }
I l est toujours possible de ne pas utiliser le rsultat dune fonction, mme si elle en produit un.
Bien entendu, cela na dintrt que si la fonction fait autre chose que de calculer un rsultat.
ditions Eyrolles
145
est len-tte dune fonction ne recevant aucun paramtre et fournissant un rsultat de type entier. Sa dclaration (prototype) serait, bien sr :
int fexple1 (void) ;
Ici, elle est identique len-tte, au point-virgule prs, puisque la fonction na aucun paramtre. De mme :
void fexple2 (void)
est len-tte dune fonction ne recevant aucun paramtre et ne fournissant aucun rsultat. Son prototype serait simplement :
void fexple2 (void) ;
Lappel dune fonction sans paramtres doit quand mme comporter des parenthses vides.Par exemple, lappel de fexple1 scrira fexple1() et non simplement fexple1. ce propos, notez bien que ce nest pas parce quune fonction ne reoit aucun paramtre que son comportement ne peut pas diffrer dun appel un autre ; en effet, elle peut trs bien lire des informations en donnes, effectuer ce que lon nomme des calculs alatoires, ou encore utiliser des variables globales (que nous tudierons un peu plus loin).
VIII.4
a) crivez la dnition dune fonction, nomme bonjour, qui se contente dafcher le message bonjour chaque fois quon lappelle. b) crivez le prototype corrrespondant.
/* le rsultat fourni par fct est affect directement p */ /* le rsultat fournit par fct, de type int, doit tre converti en float avant dtre affect y */
146
ditions Eyrolles
chapitre n 8
Les fonctions
Mais, dune faon gnrale, le prototype permet de mettre en place dventuelles conversions des valeurs des paramtres transmettre la fonction, par exemple :
float fct (float) ; int n ; float y ; ..... y = fct (n) ;
/* cest le rsultat de la conversion de la valeur de n en float qui sera transmis en paramtre fct */
nest alors connu que dans ladite fonction ; ctait le cas dans les exemples rencontrs jusquici ;
s soit avant toutes les dnitions de fonctions, un niveau global (comme les variables glo-
bales dont nous parlerons un peu plus loin) ; dans ce cas, il est utilisable par toutes les fonctions du programme.
on obtiendra lafchage de la valeur 10, chaque appel de afche. Notez que les variables dclares dans le programme principal (fonction main) sont dans ce cas mais on ne sen rend pas vraiment compte, dans la mesure o, de par sa nature mme, la fonction main nest appele quune seule fois. Tout se passerait donc de la mme manire si ces variables taient effectivement initialises au moment de la compilation (cest dailleurs ce que nous vous avions dit dans le chapitre III, par souci de simplicit).
ditions Eyrolles
147
/* prototype de f2 */
Naturellement, il est ncessaire que les prototypes voulus soient connus aux endroits ncessaires (ici, les deux prototypes gurent au sein des uniques fonctions en ayant besoin ; ils auraient pu tre regroups avant la dnition du main). Notez cependant que, si les appels de fonctions peuvent ainsi simbriquer, il nen va pas de mme de leurs dnitions ; chaque dnition de fonction est totalement indpendante des autres dnitions de fonctions.
il parat gnralement vident que la fonction max va recevoir les valeurs des expressions 5*n, p et 12. Mais les choses peuvent devenir plus subtiles, comme le montre lexemple suivant :
main() { void echange (int a, int b) ; int n=10, p=20 ; printf (avant appel : %d %d\n, n, p) ; echange (n, p) ; printf (aprs appel : %d %d, n, p) ; }
148
ditions Eyrolles
chapitre n 8
Les fonctions
void echange (int a, int b) { int c ; printf (dbut echange : %d %d\n, a, b) ; c = a ; a = b ; b = c ; printf (fin echange : %d %d\n, a, b) ; }
: : : :
10 10 20 10
20 20 10 20
La fonction echange reoit deux valeurs correspondant ses deux paramtres muets a et b. Elle effectue un change de ces deux valeurs. Mais, lorsque lon est revenu dans le programme principal, aucune trace de cet change ne subsiste sur les paramtres effectifs n et p. En effet, lors de lappel de echange, il y a eu transmission de la valeur des expressions n et p. On peut dire que ces valeurs ont t recopies localement dans la fonction echange dans des emplacements nomms a et b. Cest effectivement sur ces copies qua travaill la fonction echange, de sorte que les valeurs des variables n et p nont, quant elles, pas t modies. Cest ce qui explique le rsultat constat. On traduit cela en disant quen langage C : les paramtres dune fonction sont toujours transmis par valeur Ce mode de transmission semble donc interdire a priori quune fonction produise une ou plusieurs valeurs en retour, autres que celle de la fonction elle-mme. Or, il ne faut pas oublier quen C tous les sous-programmes doivent tre crits sous forme de fonction. Autrement dit, ce simple problme dchange des valeurs de deux variables doit pouvoir se rsoudre laide dune fonction. Nous verrons que ce problme possde plusieurs solutions, savoir :
s Transmettre en paramtre la valeur de ladresse dune variable. La fonction recevra, cer-
tes, toujours une copie de cette adresse, mais elle pourra ventuellement agir sur ce qui se trouve cette adresse. Cest prcisment ce que nous faisons lorsque nous utilisons la fonction scanf. Nous examinerons cette technique en dtail dans le chapitre consacr aux pointeurs.
s Utiliser des variables globales, comme nous le verrons dans le prochain paragraphe ;
cette deuxime solution devra toutefois tre rserve des cas exceptionnels, compte tenu des risques quelle prsente.
ditions Eyrolles
149
il il il il il
Vous constatez que la variable nfois a t dclare avant la dnition du programme principal et de la fonction optimist. Dans ces conditions, elle est accessible la fois au programme principal et la fonction optimist. Ainsi, le programme principal affecte nfois des valeurs qui se trouvent utilises par la fonction optimist. En revanche, la variable i est reste locale la fonction optimist. Elle nest pas accessible depuis le programme principal. Notez quici la fonction optimist se contente dutiliser la valeur de nfois mais rien ne lempcherait de la modier. Cest prcisment ce genre de remarque qui doit vous inciter nutiliser les variables globales que dans des cas limits. En effet, toute variable globale peut tre modie insidieusement par nimporte quelle fonction ; on parle dans ce cas deffets de bord.
150
ditions Eyrolles
chapitre n 8
Les fonctions
Lorsque vous aurez crire des fonctions susceptibles de modier la valeur de certaines variables, il sera beaucoup plus judicieux de prvoir den transmettre ladresse en paramtre (comme vous apprendrez le faire dans le prochain chapitre). En effet, dans ce cas, lappel de la fonction montrera explicitement quelle est la variable qui risque dtre modie et, de plus, ce sera la seule qui pourra ltre.
Remarques U ne variable globale peut, comme une variable locale, tre initialise lors de sa dclaration. Mais,
cette fois, lintialisation est ralise une seule fois, au moment de la compilation (alors que, rappelons-le, les variables locales sont initialises chaque appel de la fonction o elles apparaissent). De plus, une variable globale non initialise lors de sa dclaration est automatiquement initialise zro par le compilateur (alors que les variables locales non initialises sont indnies).
L es dclarations de variables ou de fonctions peuvent tre soit globales, soit locales. Une telle
distinction nexiste pas pour la directive #dene ; rappelons, en effet, quelle sapplique (avant la compilation proprement dite), tout le texte qui la suit, sans que la structure du programme nintervienne en diffrentes fonctions.
ditions Eyrolles
151
VIII.5
crivez une fonction sans paramtres ni valeur de retour, nomme init, plaant la valeur 1 dans les 10 lments dun tableau dentiers nomm t, dclar de faon globale. Peut-on utiliser la fonction init pour initialiser un autre tableau que t ?
On pourrait ventuellement placer une telle instruction au dbut de chaque fonction ayant besoin des prototypes correspondants. Cependant, il vaut mieux la placer avant toute dnition de fonction ou du programme principal (comme les variables globales) ; dans ce cas, en effet, les protoypes ainsi incorpors sont accessibles toutes les fonctions qui suivent. Bien entendu, pour utiliser une fonction prdnie, il est ncessaire de connatre le type de ses paramtres, celui de son ventuel rsultat et le nom du chier en-tte qui en contient le prototype. titre indicatif, voici quelques fonctions mathmatiques prdnies, dont les prototypes gurent dans math.h ; elles possdent toutes un seul paramtre ottant et elles fournissent un rsultat ottant : sin (sinus), cos (cosinus), tan (tangente), exp (exponentielle), log (logarithme nprien), log10 (logarithme base 10), sqrt (racine carre), fabs (valeur absolue).
152
ditions Eyrolles
chapitre n 8
Les fonctions
Labsence de dclaration dune fonction telle que printf ou scanf na gnralement pas dincidence sur la compilation du programme qui les utilise ; lexplication est assez technique et lie au fait que ces fonctions ont des paramtres dont le type, hormis le premier qui est le format, nest pas connu la compilation. Cest la raison pour laquelle nous avons pu nous passer jusqu maintenant de linstruction #include <stdio.h>. Il nen va pas de mme pour la plupart des autres fonctions.
ditions Eyrolles
153
s nous lutilisons pour un tableau nomm t1. main() { ..... int t1[5] ; ..... void raz (int [5]) ; ..... raz (t1) ; }
Notez que le prototype de raz doit spcier le type de son paramtre ; ici, il sagit de int[5] ; cette notation peut surprendre mais, en fait, elle dcoule, comme dhabitude, de len-tte de la fonction, en liminant le nom du paramtre. Cette fois, lors de lappel de raz, il ny a pas recopie des valeurs de t1 au sein de la fonction ; le mcanisme utilis est tel que tout se passe comme si, au bout du compte, la fonction raz travaillait directement avec le tableau mentionn lors de lappel. Voici un exemple complet reprenant le canevas prcdent, montrant comment les valeurs du tableau ont bien t modies par la fonction :
main() { int i ; int t1[5] = { 1, 2, 3, 4, 5 } ; /* initialisation du tableau */ void raz (int [5]) ; /* prototype de la fonction raz */ printf (tableau t1 avant appel de raz : ) ; for (i=0 ; i<5 ; i=i+1) printf (%d , t1[i]) ; printf (\n) ; raz (t1) ; /* appel de raz, laquelle on transmet en paramtre le tableau t1 */ printf (tableau t1 aprs appel de raz : ) ; for (i=0 ; i<5 ; i=i+1) printf (%d , t1[i]) ; } void raz (int v[5]) { int i ; for (i=0 ; i<5 ; i=i+1) v[i] = 0 ; }
1 2 3 4 5 0 0 0 0 0
154
ditions Eyrolles
chapitre n 8
Les fonctions
b) Le mcanisme
Si lon sintresse de plus prs la manire dont le tableau est transmis en paramtre la fonction, il faut savoir que :
s pour le compilateur, un nom de tableau (par exemple t1) est identique son adresse, cest-
cest--dire en fait de ladresse du tableau t1 (il y a bien toujours mcanisme de transmission par valeur, mais cette valeur se trouve tre celle dune adresse) ;
s dans la fonction raz, chaque appel, le symbole t est remplac par sa valeur, cest--dire
En dnitive, on voit bien quil ny a quun seul mcanisme de transmission des paramtres, savoir par recopie de la valeur au sein de la fonction.
ment rserv au sein du programme principal (ou dune autre fonction appelant raz),
s dautre part, il nest pas prvu de recopier les valeurs dudit tableau au sein de la fonction raz.
En fait, au sein de la fonction raz, le compilateur doit simplement tre en mesure de localiser correctement lemplacement de v[i] ; pour ce faire, il na nullement besoin de connatre la dimension exacte du tableau ; il lui suft de savoir quil sagit dun tableau dentiers. Cest la raison pour laquelle vous ntes pas oblig, dans un en-tte ou un prototype de fonction, de prciser la taille des tableaux une dimension. Notre fonction prcdente pourrait tout aussi bien scrire :
void raz (int v[]) { int i ; for (i=0 ; i<5 ; i=i+1) v[i] = 0 ; }
ditions Eyrolles
155
Et voici quelques exemples dutilisation (notez, dans le prototype de raz, le type int [ ] qui correspond un tableau une dimension dentiers ; l encore, il dcoule de len-tte correspondant en liminant de int v[ ], le nom de tableau v) :
main() { int t1[10], t2[15], t3[100] ; void raz (int [], int) ; /* ..... raz (t1, 10) ; /* mise raz (t2, 15) ; /* mise raz (t3, 100) ; /* mise ..... }
prototype de raz */ zro des 10 lments de t1 */ zro des 15 lments de t2 */ zro des 100 lments de t3 */
Remarques R ien ne vous interdit, par mgarde, de demander raz de traiter plus dlments que vous nen
avez rservs, comme dans : int t_petit [5] ; raz (t_petit, 200) ; dans ce cas, il y aura crasement de 195 (200-5) valeurs situes au-del du tableau t_petit ; on retrouve l les risques habituels inhrents au dbordement dindice.
R ien ne vous empche de ne traiter quune partie des lments dun tableau, en crivant, par exemple :
raz (t1, 4) ;/* mise zro des 4 premiers lments de t1 */
156
ditions Eyrolles
chapitre n 8
Les fonctions
Qui plus est, il serait possible de procder ainsi pour mettre zro 5 lments conscutifs de t1, partir du troisime (dindice 2) : raz (&t1[2], 5) ; /* mise zro de 5 lments de t1, partir du troisime */
N otre fonction raz avait deux particularits : elle ne fournissait pas de rsultat et elle modiait les
valeurs du tableau reu en paramtre. Lexercice suivant vous propose une situation diffrente.
VIII.6
crivez une fonction nomme max_tab fournissant en rsultat la valeur maximale dun tableau dentiers ; on prvoira en paramtres la fois le tableau concern et son nombre dlments. crivez un petit programme principal utilisant cette fonction.
Bien entendu, le mcanisme dcrit prcdemment pour les tableaux une dimension sapplique toujours ici : la fonction raun recevra, en fait, ladresse de dbut dun tableau ; les deux appels pourraient tre remplacs par :
raun (&tab[0][0]) ; raun (&truc[0][0]) ;
ditions Eyrolles
157
Mais si, pour localiser tous les lments dun tableau une dimension, il suft au compilateur de connatre son adresse et son type, pour un tableau deux dimensions, en revanche, les choses sont moins simples. En effet, il faut tenir compte de la manire dont ses lments sont rangs en mmoire, savoir en faisant se succder ses diffrentes lignes ; par exemple, pour un tableau dclar ainsi :
float t[5] [3] ;/* 5 lignes de 3 lments */
Ainsi, pour localiser convenablement un lment donn, il faudra connatre, en plus de ladresse de dbut du tableau, le nombre dlments dune ligne (le nombre dlments des colonnes tant, en revanche, inutile). Dans ces conditions, on voit que len-tte de raun pourra ventuellement ne pas prciser le nombre de lignes :
raun (int t[][3])
On voit que si lon souhaite raliser une fonction traitant des tableaux deux dimensions de taille variable, les choses ne resteront possibles que si seule la premire dimension est susceptible de varier, la seconde restant xe ; cela enlve manifestement beaucoup dintrt la chose. On peut toutefois traiter une telle situation en considrant un tableau deux dimensions comme un tableau une seule dimension et en effectuant soi-mme les calculs dadresse permettant den localiser les diffrents lments ; nous ninsisterons pas davantage sur ces techniques qui sortent du cadre de cet ouvrage.
158
ditions Eyrolles
chapitre n 8
Les fonctions
Rsum
Une fonction est un ensemble dinstructions, dot dun nom et pouvant ventuellement comporter des paramtres. On peut lappeler (en provoquer lexcution) depuis une autre fonction (ventuellement de la fonction main correspondant au programme principal) en citant son nom, suivi, entre parenthses, dune liste de paramtres, condition davoir pralablement dclar cette fonction par un prototype (qui peut gurer, soit un niveau local, soit un niveau global). Il existe deux grandes classes de fonctions : s celles qui ne renvoient pas de rsultat (fonction action) ; leur appel ne peut se faire que sous forme dune instruction simple comme : fct (n, a) ; s celles qui fournissent un rsultat ; leur appel peut se faire, soit comme prcdemment (leur rsultat nest alors pas utilis), soit au sein dune expression arithmtique, comme dans : z = fct(n, x) * 2 + s ; Dans la dnition dune fonction : s Len-tte prcise son nom, le type des paramtres (dits alors muets) et le type de sa valeur de retour ; on utilise le mot void pour indiquer labsence de paramtres ou de valeur de retour. s On peut dclarer des variables qui sont alors dites locales la fonction, utilisables uniquement dans les instructions de la fonction. Dans lutilisation dune fonction, les paramtres mentionns (dits alors effectifs) peuvent ltre sous la forme dune expression quelconque. Cest leur valeur qui est transmise la fonction. Dans un programme (ensemble de fonctions), on peut fournir les fonctions dans lordre de son choix ; en particulier, la fonction main ne gure pas obligatoirement en premier. On peut dnir des variables globales, en les dclarant avant toute fonction, y compris la fonction main. Ces variables sont accessibles toute fonction qui peut ventuellement en modier la valeur. Il existe un grand nombre de fonctions prdnies quon peut utiliser en incorporant le prototype laide dune instruction #include approprie. Lorsquun tableau est mentionn en paramtre effectif, cest en fait la valeur de son adresse qui est transmise la fonction. Cette dernire peut alors, contrairement ce qui se passe pour les variables scalaires, modier le contenu de ce tableau. Dans le cas dun tableau une dimension mentionn en paramtre muet, la dimension est sans importance et elle peut ventuellement tre omise de len-tte et du prototype. Il est facile de raliser une fonction travaillant avec des tableaux une dimension dont le nombre dlments peut diffrer dun appel un autre. Dans le cas dun tableau deux dimensions indiqu en paramtre muet, seule la premire dimension est sans importance et peut, ventuellement, tre omise de len-tte et du prototype. Si lon souhaite quune fonction modie la valeur dune variable scalaire, il faudra utiliser un pointeur, ce que nous apprendrons faire dans le prochain chapitre.
ditions Eyrolles
159