| |
Une fonction est définie par son entête, suivie d'un bloc d'instructions entête : type_retourné nom_fonction(liste_arguments) (pas de ;) Avant la norme ANSI , le type_retourné pouvait être omis si int. Désormais il est obligatoire, si la fonction ne retourne rien on indique : void. La liste_arguments doit être typée (ANSI), alors qu'auparavant les types étaient précisés entre l'entête et le bloc :
ANSI: float truc(int a, float b) {bloc}
K&R: float truc(a,b) int a;float b; {bloc} Si la fonction n'utilise pas d'arguments il faut la déclarer (ANSI) nom(void) ou (K&R) nom(). L'appel se fera dans les deux cas par nom() (parenthèses obligatoires). Les argument s (formel s) sont des variables locales à la fonction. Les valeurs fournies à l'appel de la fonction (arguments réels ) y sont recopiés à l'entrée dans la fonction. Les instructions de la fonction s'exécutent du début du bloc ({) jusqu'à return(valeur) ou la sortie du bloc (}). La valeur retournée par la fonction est indiquée en argument de return. exemple : float produit(float a;float b) { float z; z=a*b; return(z); }
Une fonction peut s'appeler elle-même : int factorielle(int i) { if (i>1) return(i*factorielle(i-1)); else return(1); } analysons la pile en appelant factorielle(3) :
|
|
i=1
|
|
|
|
|
i=2
|
i=2
|
i=2
|
|
|
i=3
|
i=3
|
i=3
|
i=3
|
i=3
|
|
(a)
|
(b)
|
(c)
|
(d)
|
(e)
|
|
- (a) appel de factorielle(3), création de i, à qui on affecte la valeur 3. comme i>1 on calcule i*factorielle(i-1) : i=3,i-1=2 on appelle factorielle(2)
- (b) création i, affecté de la valeur 2, i>1 donc on appelle factorielle(1)
- (c) création de i, i=1 donc on quitte la fonction, on libère le pile de son sommet, on retourne où la fonction factorielle(1) a été appelée en rendant 1.
- (d) on peut maintenant calculer i*factorielle(1), i (sommet de la pile) vaut 2, factorielle(1) vaut 1, on peut rendre 2, puis on "dépile" i
- (e) on peut calculer i*factorielle(2), i vaut 3 (sommet de la pile), factorielle(2) vaut 2 3*2=6, on retourne 6, la pile est vidée et retrouve sont état initial.
Attention, la récursivité est gourmande en temps et mémoire, il ne faut l'utiliser que si l'on ne sait pas facilement faire autrement : int factorielle(int i) { int result; for(result=1;i>1;i--) result*=i; return(result); } Imaginons la fonction : void échange(int i;int j)
{int k;k=i;i=j;j=k;} Lors d'un appel à cette fonction par échange(x,y), les variables locales i,j,k sont créées sur la pile, i vaut la valeur de x, j celle de y. Les contenus de i et j sont échangés puis la pile est libérée, sans modifier x et y. Pour résoudre ce problème, il faut passer par des pointeur s. On utilisera les opérateurs unaires : & (adresse de) et * (contenu de). Définissons donc la fonction ainsi : void échange(int *i;int *j)
{int k;k=*i;*i=*j;*j=k;} On appelle la fonction par échange(&x,&y); les deux arguments formels de la fonction (i et j) sont des pointeurs sur des int, c'est à dire qu'à l'appel, on crée sur la pile des variables i et j pouvant contenir une adresse, dans i on recopie l'argument réel qui et l'adresse de x, et l'adresse de y dans j. en entrant dans le bloc, on crée une variable locale k pouvant contenir un entier. Puis on met dans k non pas la valeur de i mais le contenu pointé par i (donc ce qui se trouve à l'adresse marquée dans i, qui est l'adresse de x), donc le contenu de x. On place la valeur pointée par j (donc y) à l'adresse pointée par j (donc x). Puis on place la valeur de k à l'adresse pointée par j (y). On a donc échangé x et y. On a tout intérêt à essayer cet exemple en se fixant des adresses et des valeurs, et voir l'évolution des contenus des variables. En conclusion, pour effectuer un passage d'argument par adresse, il suffit d'ajouter l'opérateur & devant l'argument réel (à l'appel de la fonction), et l'opérateur * devant chaque apparition de l'argument formel , aussi bien dans l'entête que le bloc de la fonction. Si on le désire, la fonction main peut rendre un entier (non signé) au système d'exploitation (sous MSDOS, on récupère cette valeur par ERRORLEVEL). Une valeur 0 signale en général une fin normale du programme, sinon elle représente un numéro d'erreur. L'arrivée sur le } final retourne la valeur 0, dans le cas où on n'a pas indiqué de return (code). De même, le système d'exploitation peut transmettre des arguments au programme. La déclaration complète de l'entête de la fonction main est : int main(int argc ,char *argv [], char *env[])
Le dernier argument est optionnel). On peut aussi utiliser char **argv , mais cela peut paraître moins clair. argc indique le nombre de mots de la ligne de commande du système d'exploitation, argv est un tableau de pointeurs sur chaque mot de la ligne de commande, env pointe sur les variables de l'environnement (sous MSDOS obtenues par SET , mais aussi très utilisées sous UNIX par env ). Si votre programme s'appelle COPIER, et que sous MSDOS vous ayez entré la commande COPIER TRUC MACHIN alors argc vaut 3, argv[0] pointe sur "COPIER", argv[1] sur "TRUC" et argv[2] sur "MACHIN". argv[3] vaut le pointeur NULL. env est un tableau de pointeurs sur les variables d'environnement, on n'en connaît pas le nombre mais le dernier vaut le pointeur NULL. type*fonc(arguments) est une fonction qui renvoie un pointeur. exemple : int *max(int tableau[], int taille) { int i,*grand; for(grand=tableau,i=1;i<taille;i++) if(tableau[i]>*grand) grand=tableau+i; return(grand); } Cette fonction rend l'adresse du plus grand entier du tableau. type (*fonc)(arguments) est un pointeur sur une fonction exemple : int max(int,int); int min(int,int); void main(void); { int (*calcul)(int,int); /* calcul est un pointeur donc une variable qui peut être locale */ char c; puts("utiliser max (A) ou min (I) ?"); do c=getchar(); while ((c!='A')&&(c!='I')); calcul=(c= ='A')?&max:&min; printf("%d\n",(*calcul)(10,20)); } int max(int a,int b) {return(a>b?a:b);} int min(int a,int b) {return(a<b?a:b);} Cette fonctionnalité du C est assez peu utilisée, mais est nécessaire dans les langages orientés objets. Permet de sortir de la fonction actuelle (y compris main), en se branchant à son dernier }. Return permet également (et surtout) de rendre la valeur résultat de la fonction. structure : return ou return(valeur) exemple :
int max(int a, int b) {if (a>b) return(a); else return(b);} Ceci n'est pas un mot clef du C mais une fonction disponible dans la plupart des compilateurs (définie par ANSI, dans STDLIB.H). Elle permet de quitter directement le programme (même depuis une fonction). On peut lui donner comme argument le code de sortie (celui que l'on aurait donné à return dans main). Cette fonction libère la mémoire utilisée par le programme (variables + alloc) et ferme (sur beaucoup de compilateurs) les fichiers ouverts. structure : exit(); ou exit(valeur);
[Precédent] [Sommaire] [Suivant] [Haut] |
|