IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
next up previous contents index
Next: Impression formattée Up: Les bases Previous: Inclusion de source

Sous-sections

Les procédures et les fonctions

Définition d'une fonction

       
$\bullet$
Syntaxe :

définition-de-fonction :
$\Rightarrow$ type identificateur ( liste-de-déclarations-de-paramètres )
{
liste-de-déclarationsoption
liste-d'instructions
}

Note


Dans le jargon C, l'ensemble :
type identificateur ( liste-de-déclarations-de-paramètres )
porte le nom bizarre de prototype de fonction.

$\bullet$
Sémantique :

type est le type de la valeur rendue par la fonction ; identificateur est le nom de la fonction ; liste-de-déclarations-de-paramètres est la liste (séparés par des virgules) des déclarations des paramètres formels.        La liste-de-déclarationsoption permet si besoin est, de déclarer des variables qui seront locales à la fonction, elles seront donc inaccessibles de l'extérieur.     La liste-d'instructions est l'ensemble des instructions qui seront exécutées sur appel de la fonction. Parmi ces instructions, il doit y avoir au moins une instruction du type :

return expression ;

  Lors de l'exécution d'une telle instruction, expression est évaluée, et le contrôle d'exécution est rendu à l'appelant de la fonction. La valeur rendue par la fonction est celle de expression.

Exemple

int sum_square(int i,int j)  /*   la fonction sum_square délivre un int      */
                             /*  ses paramètres formels sont les int i et j  */
{
int resultat;                /*   déclaration des variables locales          */

resultat = i*i + j*j;
return(resultat);            /*  retour a l'appelant en délivrant résultat   */
}

Instruction return

L'instruction return est une instruction comme une autre, il est donc possible d'en utiliser autant qu'on le désire dans le corps d'une fonction. Exemple :

int max(int i,int j)       /*   la fonction max délivre un int               */
                           /*   ses paramètres formels sont les int i et j   */
{                          /*   pas de variables locales pour max            */

if (i > j) return(i); else return(j);
}

Si la dernière instruction exécutée par une fonction n'est pas une instruction return, la valeur rendue par la fonction est indéterminée.

Les paramètres formels

Dans le cas où une fonction n'a pas de paramètres formels, le mot clé void est mis en tant que liste-de-déclarations-de-paramètres.   Exemple :
double pi(void)               /*   pas de paramètres formels   */
{                             /*   pas de variables locales    */
return(3.14159);
}

Attention

La syntaxe des déclarations pour les paramètres formels et les variables n'est pas la même. Quand on a besoin de déclarer deux variables du même type, on peut utiliser deux déclarations :
int i;
int j;
Mais on peut aussi n'utiliser qu'une déclaration pour déclarer les deux variables :
int i,j;
Cette possibilité n'existe pas pour une liste de paramètres formels. Il n'est pas possible d'écrire max de la manière suivante :
int max(int i,j)   /*   incorrect   */
{
...
}

   
Appel d'une fonction

$\bullet$
Syntaxe :

expression :
$\Rightarrow$ identificateur ( liste-d'expressions )

$\bullet$
Sémantique :

Les expressions de liste-d'expressions sont évaluées, puis passées en tant que paramètres effectifs à la fonction de nom identificateur, qui est ensuite exécutée.   La valeur rendue par la fonction est la valeur de l'expression appel de fonction.

$\bullet$
Exemple :
{
int a,b,m,s;
double d;

s = sum_square(a,b);          /*   appel de sum_square   */
m = max(a,b);                 /*   appel de max          */
d = pi();                     /*   appel de pi           */
}

Attention

 
1.
Dans le cas d'une fonction sans paramètre, la liste-d'expressions doit être vide : il n'est pas possible d'utiliser le mot clé void en tant que paramètre effectif.
d = pi(void);                 /*   appel incorrect de pi   */

2.
L'ordre d'évaluation des paramètres effectifs n'est pas spécifié. Si certains de ceux-ci réalisent des effets de bords, l'ordre dans lequel ils se feront n'est pas garanti. Exemple :
sum_square(f(x),g(y));
La fonction g sera peut-être exécutée avant f.

   
Les procédures

Le langage C ne comporte pas à strictement parler le concept de procédure. Cependant, les fonctions pouvant réaliser sans aucune restriction tout effet de bord qu'elles désirent, le programmeur peut réaliser une procédure à l'aide d'une fonction qui ne rendra aucune valeur. Pour exprimer l'idée de << aucune valeur >>, on utilise le mot-clé void. Une procédure sera donc implémentée sous la forme d'une fonction retournant void et dont la partie liste-d'instructions ne comportera pas d'instruction return.    

Lors de l'appel de la procédure, il faudra ignorer la valeur rendue c'est à dire ne pas l'englober dans une expression.

Exemple

void print_add(int i,int j)  /*  la procédure et ses paramètres formels   */
{
int r;                       /*  une variable locale à print_add          */

r = i + j;
...                          /*  instruction pour imprimer la valeur de r */
}

void prints(void)            /*   une procédure sans paramètres           */
{
int a,b;                     /*   variables locales à prints              */

a = 12; b = 45;
print_add(a,b);              /*   appel de print_add                      */
print_add(13,67);            /*   un autre appel à print_add              */
}

Problème de vocabulaire

Dans la suite du texte, nous utiliserons le terme de fonction pour désigner indifféremment une procédure ou une fonction, chaque fois qu'il ne sera pas nécessaire de faire la distinction entre les deux.

   
Fonctions imbriquées

À l'inverse de certains langages, les fonctions imbriquées n'existent pas dans le langage C. Il n'est donc pas possible qu'une fonction ne soit connue qu'à l'intérieur d'une autre fonction.

Récursivité

  Il n'y a rien de spécial à faire à la déclaration d'une fonction pour qu'elle puisse être appelée de manière récursive. Voici un exemple de factorielle :
int facto(int n)
{
if (n == 1) return(1);
else return(n * facto(n-1));
}

Référence à une fonction externe

      Quand on désire utiliser une fonction qui est définie ailleurs, il est nécessaire de la déclarer comme étant externe. Cela se fait en préfixant l'en-tête de la fonction du mot clé extern, comme ceci :
extern int sum_square(int i, int j);
Le cas le plus courant d'utilisation de fonction définie ailleurs est l'utilisation des fonctions de la bibliothèque standard. Avant d'utiliser une fonction de la bibliothèque standard, il faudra donc la déclarer en fonction externe. Il y a une méthode permettant de faire cela de manière automatique grâce au mécanisme d'inclusion de source du préprocesseur. Cela est expliqué dans la documentation de chaque fonction.

Comprendre la documentation de la bibliothèque standard

  Dans la documentation concernant les fonctions de la bibliothèque standard, que ce soit dans la norme ou dans les manuels UNIX, l'information concernant l'interface d'appel de la fonction se trouve dans le paragraphe Synopsis. Dans ces paragraphes, se trouve toujours une commande d'inclusion de source. Exemple de la fonction cosinus :
Synopsis
	#include <math.h>
	double cos(double x);
Il faut comprendre que le fichier math.h contient un ensemble de définitions nécessaires à l'utilisation de cos, en particulier la déclaration de cos en fonction externe, à savoir :
extern double cos(double x);
Il faut donc inclure le fichier math.h avant toute utilisation de la fonction cos.

Les fonctions dans le style K&R

    La définition des fonctions est la plus importante différence entre ANSI et K&R. Dans la version K&R du langage, le prototype (c'est à dire la liste-de-déclarations-de-paramètres) est remplacé par une liste d'identificateurs, qui sont les noms des paramètres. La déclaration des types des paramètres se fait juste après, de la manière suivante :
int sum_square(i,j)          /*   liste des noms des paramètres formels      */
int i, int j;                /*   déclaration du type des paramètres         */
{
int resultat;

resultat = i*i + j*j;
return(resultat);
}

En ce qui concerne la déclaration de fonction externe, il faut préfixer avec le mot-clé extern, l'en-tête de la fonction débarrassée de la liste des noms des paramètres, comme ceci :

extern int sum_square();

Dans ce cas, le compilateur ne connaît pas le type des paramètres de la fonction, il lui est donc impossible de détecter une erreur portant sur le type des paramètres lors d'un appel de fonction externe. Cette situation est assez catastrophique et c'est la raison pour laquelle le comité de normalisation a introduit le concept de prototype de fonction vu au chapitre 1.15.1. Il n'a cependant pas voulu faire un changement incompatible, il a donc décidé que les deux méthodes étaient acceptées, en précisant toutefois que la méthode K&R était une caractéristique obsolescente. En clair, cela signifie que cette méthode sera abandonnée dans la prochaine révision de la norme, si prochaine révision il y a.

Remarque

Nous ne pouvons que conseiller l'utilisation exclusive des prototypes de fonctions, et de ne jamais utiliser la méthode K&R. Si nous avons exposé celle-ci, c'est pour permettre au lecteur de comprendre les quantités énormes de source C existant, qui sont écrites à la K&R.


next up previous contents index
Next: Impression formattée Up: Les bases Previous: Inclusion de source
Bernard Cassagne
1998-12-09