paumier@univ-mlv.fr 1 Cours de C Fonctions et passages d'adresses Sébastien Pau
paumier@univ-mlv.fr 1 Cours de C Fonctions et passages d'adresses Sébastien Paumier Illustrations provenant du site http://tux.crystalxp.net/ paumier@univ-mlv.fr 2 Les fonctions ●une fonction a un prototype: – un nom – des paramètres – un type de retour ●exemple de définition: float average(int t[]) { float sum=0; int i; for (i=0;i<N;i++) { sum=sum+t[i]; } return sum/N; } paumier@univ-mlv.fr 3 void ●type spécial: – en valeur de retour: pas de valeur de retour – en paramètre: pas de paramètres (facultatif) void print_sum(int a,int b) { /* ... */ } char get_upper_char(void) { /* ... */ } char get_upper_char() { /* ... */ } paumier@univ-mlv.fr 4 Définition vs déclaration ●définition = code de la fonction ●déclaration = juste son prototype avec ; ●dans les .h ●dans un .c pour une fonction qu'on ne veut pas exporter void get_hen(void); void get_egg(void) { /* ... */ get_hen(); /* ... */ } void get_hen(void) { /* ... */ get_egg(); /* ... */ } paumier@univ-mlv.fr 5 return ●quitte la fonction ●renvoie une valeur si retour ≠ void ●inutile seulement en fin de fonction void: void print_sum(float a,float b) { printf("%f+%f=%f\n",a,b,a+b); } int print_sum2(float a,float b) { printf("%f+%f=%f\n",a,b,a+b); } $>gcc -Wall -ansi function.c function.c: In function `print_sum2': function.c:25: warning: control reaches end of non-void function paumier@univ-mlv.fr 6 Valeurs de retour ●on peut ignorer une valeur de retour: – printf, scanf ●on ne peut pas utiliser une fonction void dans une expression: void print_sum(float a,float b) { printf("%f+%f=%f\n",a,b,a+b); } int main(int argc,char* argv[]) { float s=print_sum(3.5,1.1); printf("sum=%f\n",s); return 0; } $>gcc -Wall -ansi function.c function.c: In function `main': function.c:8: void value not ignored as it ought to be paumier@univ-mlv.fr 7 Le cas de main ●fonction particulière: – return quitte le programme et – renvoie le code de retour du programme ●par convention: 0=OK ≠0=erreur ●si main est appelée explicitement par une fonction, return marche normalement paumier@univ-mlv.fr 8 Paramètres de main ●int main(int argc,char* argv[]) {...} Nombre de paramètres, y compris l'exécutable Tableau de chaînes contenant les paramètres int main(int argc,char* argv[]) { int i; for (i=0;i<argc;i++) { printf("arg #%d=%s\n",i,argv[i]); } return 0; } $>./a.out AA e "10 2" arg #0=./a.out arg #1=AA arg #2=e arg #3=10 2 paumier@univ-mlv.fr 9 Écrire une fonction ●réfléchir à l'utilité de la fonction ●1 fonction = 1 seule tâche ●on ne mélange pas calcul et affichage! int minimum(int a,int b) { int min=(a<b)?a:b; printf("minimum=%d\n",min); return min; } int minimum(int a,int b) { return (a<b)?a:b; } int main(int argc,char* argv[]) { int min=minimum(4,5); printf("min=%d\n",min); return 0; } paumier@univ-mlv.fr 10 Les paramètres ●ne pas mettre trop de paramètres! int get_choice(char a,char c1, char c2,char c3, char c4,char c5) { if (a==c1 || a==c2 || a==c3 || a==c4 || a==c5) return a; return -1; } int get_choice2(char a,char c[]) { int i; for (i=0;i<N;i++) { if (a==c[i]) return a; } return -1; } paumier@univ-mlv.fr 11 Définir le prototype ●de quoi la fonction a-t-elle besoin ? ●retourne-t-elle quelque chose ? ●y a-t-il des cas d'erreurs ? ●si oui, 3 solutions: – mettre un commentaire – renvoyer un code d'erreur – afficher un message et quitter le programme paumier@univ-mlv.fr 12 Le commentaire ●utile quand la fonction n'est pas censée être appelée dans certains cas: /** * Copies the array 'src' into the 'dst' one. * 'dst' is supposed to be large enough. */ void copy(int src[],int dst[]); /** * Returns 1 if 'w' is an English word; * 0 otherwise. 'w' is not supposed to be * NULL; */ int is_english_word(char* w); paumier@univ-mlv.fr 13 Le code d'erreur ●pas de problème si la fonction ne devait rien renvoyer: ●sinon, attention à la valeur choisie – on doit toujours pouvoir distinguer un cas d'erreur d'un cas normal int init(int t[],int size) { if (size<=0) return 0; int i; for (i=0;i<size;i++) t[i]=0; return 1; } paumier@univ-mlv.fr 14 Le code d'erreur ●attention à la valeur: /** * Returns the length of the given * string or -1 if NULL. */ int length(char* s) { if (s==NULL) return -1; int i; for (i=0;s[i]!='\0';i++); return i; } int minimum(int t[],int size) { if (size<=0) return -1; int min=t[0]; int i; for (i=1;i<size;i++) { if (min<t[i]) min=t[i]; } return min; } -1 : on ne peut pas savoir si on a une erreur ou si le minimum est -1 paumier@univ-mlv.fr 15 Le code d'erreur ●si toutes les valeurs possibles sont prises, il faut utiliser un passage par adresse pour le résultat ou pour le code d'erreur int quotient(int a,int b,int *res) { if (b==0) return 0; *res=a/b; return 1; } paumier@univ-mlv.fr 16 L'interruption du programme ●à n'utiliser que dans des cas très graves – plus de mémoire – erreurs d'entrées-sorties – mauvais paramètres passés au programme ●message sur stderr + exit(≠0); List* new_List(int n,List* next) { List* l=(List*)malloc(sizeof(List)); if (l==NULL) { fprintf(stderr,"Not enough memory !\n"); exit(1); } /* On met un message parlant */ l->n=n; /* et non pas quelque chose */ l->next=next; /* d'inutile comme "Error" */ return l; } paumier@univ-mlv.fr 17 Tests d'erreur ●quitter dès que possible en évitant les else inutiles ⇨ améliore la lisibilité int get_value(char* s) { int ret; if (s==NULL) { ret=-1; } else if (!isdigit(s[0])) { ret=-1; } else { ret=s[0]-'0'; int i=1; while (isdigit(s[i])) { ret=ret*10+s[i]-'0'; i++; } } return ret; } int get_value(char* s) { if (s==NULL) return -1; if (!isdigit(s[0])) return -1; int ret=s[0]-'0'; int i=1; while (isdigit(s[i])) { ret=ret*10+s[i]-'0'; i++; } return ret; } paumier@univ-mlv.fr 18 Esthétique des fonctions ●soigner la présentation: – commentaires – noms explicites – indentation ●regrouper les fonctions de même thème dans des .c paumier@univ-mlv.fr 19 Passage d'adresse ●pourquoi ? void add_3(int a) { a=a+3; } int main(int argc,char* argv[]) { int foo=14; add_3(foo); printf("foo=%d\n",foo); return 0; } $>./a.out foo=14 paumier@univ-mlv.fr 20 Passage d'adresse ●regardons la pile: 14 a (copie de foo) ... 14 foo 14 foo 17 a ... 14 foo 14 foo int foo=14; add_3(foo); a=a+3; printf("foo=%d\n",foo); paumier@univ-mlv.fr 21 Passage d'adresse ●Comment faire ? ⇨ donner l'adresse de foo pour pouvoir modifier cette zone mémoire-là ●opérateur &: – &abc = adresse mémoire de la variable abc – comme dans scanf, qui a besoin de savoir où stocker les résultats de la saisie paumier@univ-mlv.fr 22 Passage d'adresse ●pour indiquer qu'une fonction reçoit une adresse, on met * entre le type et le nom du paramètre: void copy(int a,int *b); ●b représente l'adresse d'un int ●*b est le contenu de la zone mémoire d'adresse b ●la taille de la zone dépend du type: *a différent dans char *a et float *a paumier@univ-mlv.fr 23 Passage d'adresse ●ça marche! void add_3(int *a) { *a=*a+3; } int main(int argc,char* argv[]) { int foo=14; add_3(&foo); printf("foo=%d\n",foo); return 0; } $>./a.out foo=17 paumier@univ-mlv.fr 24 Passage d'adresse ●regardons la pile: BF73 a (adresse de foo) ... BF73 14 foo BF73 14 foo BF73 a ... BF73 17 foo BF73 17 foo int foo=14; add_3(&foo); *a=*a+3; printf("foo=%d\n",foo); paumier@univ-mlv.fr 25 Passage d'adresse ●utile: – quand on doit modifier un paramètre – quand on doit retourner plusieurs valeurs – quand on doit retourner une ou plusieurs valeurs + un code d'erreur paumier@univ-mlv.fr 26 Modifier une variable ●exemple classique: les compteurs ●attention: *n++ n'est pas (*n)++ /** * Reads from the given file the first * character that is not a new line. Returns * it or -1 if the end of file is reached. If * there are new lines, '*n' is updated. */ int read_char(FILE* f,int *n) { int c; while ((c=fgetc(f))!=EOF) { if (c=='\n') (*n)++; else return c; } return -1; } paumier@univ-mlv.fr 27 Retourner plusieurs valeurs /** * x/y is the irreducible fraction * so that x/y = a1/b1 + a2/b2 */ void add_fractions(int a1,int b1, int a2,int b2, int *x,int *y) { *x=a1*b2+a2*b1; *y=b1*b2; int p=gcd(*x,*y); *x=*x/p; *y=*y/p; } /** * Returns the minimum of * the given array. Its * position is stored in '*pos'. */ float get_minimum(float t[], int *pos) { int i; *pos=0; float min=t[0]; for (i=1;i<N;i++) { if (t[i]<min) { min=t[i]; *pos=i; } } return min; } paumier@univ-mlv.fr 28 Avec un code d'erreur ●on choisit plutôt de retourner le code d'erreur et de passer par adresse des variables pour stocker les résultats ●exemple: scanf int main(int argc,char* argv[]) { int a,b,c,n; n=scanf("%d %d %d",&a,&b,&c); printf("%d %d %d %d\n",n,a,b,c); return 0; } $>./a.out 4 8 hello 2 4 8 4198592 paumier@univ-mlv.fr 29 Fonction récursive ●fonction s'appelant elle-même: void hanoi(int n,char src, char tmp,char dest) { if (n==1) { printf("%c --> %c\n",src,dest); return; } hanoi(n-1,src,dest,tmp); printf("%c --> %c\n",src,dest); hanoi(n-1,tmp,src,dest); } int main(int argc,char* argv[]) { hanoi(3,'A','B','C'); return 0; } $>./a.out A --> C A --> B C --> B A --> C B --> A B --> C A --> C paumier@univ-mlv.fr 30 Fonction récursive ●attention à la condition d'arrêt ●fait gonfler la pile ●à éviter autant que possible: ●utilisation judicieuse: cf cours d'algo int factorial(int n) { if (n==1) return 1; return n*factorial(n-1); } int factorial(int n) { int r=1; while (n>1) { r=r*n--; } return r; } paumier@univ-mlv.fr 31 Fonction récursive ●si possible, ne tester qu'une seule fois les cas d'erreur int sum(Tree* t,int *s) { if (t==NULL) return ERROR; *s=*s+t->value; if (t->left!=NULL) uploads/S4/ c4-fonctions.pdf
Documents similaires










-
21
-
0
-
0
Licence et utilisation
Gratuit pour un usage personnel Attribution requise- Détails
- Publié le Dec 03, 2022
- Catégorie Law / Droit
- Langue French
- Taille du fichier 0.1840MB