Prototypes de fonctions

Nous avons vu qu’il était possible de déclarer une fonction, ou de la définir entièrement. L’une et l’autre opération ne se font que dans la « partie globale » du programme, c’est-à-dire à l’extérieur de toute autre fonction. Le C++ ne permet pas en effet de créer des fonctions imbriquées comme le Pascal ou d’autres langages de programmation.

La syntaxe de la déclaration seule (qui indique le mode d'usage de la fonction) est la suivante :

type_resultat nom_fonction  (arguments);

tandis que la définition (qui indique la réalisation effective de la fonction) a la syntaxe suivante :

type_resultat nom_fonction  (arguments)
{
     implantation
}

Noter l’absence de point-virgule dans ce cas.

Nous savons déjà ce que sont les arguments, le nom et le type de résultat de la fonction. Reste à expliquer quel peut être l’intérêt de simplement déclarer une fonction, au lieu de la définir complètement.

Lorsque le compilateur rencontre un appel de fonction dans une instruction, il doit déjà avoir rencontré une déclaration de cette fonction (comme pour tout objet de C++ d’ailleurs) ; cette déclaration, si elle est seule, est souvent appelée prototype de la fonction, car elle permet au compilateur de savoir exactement le type du résultat renvoyé, et celui des arguments ; il peut alors vérifier que les paramètres effectivement passés correspondent aux arguments, ou du moins qu’ils peuvent être transformés en de tels arguments. Dans le cas contraire, il affiche un message d’erreur signalant la non-concordance des types. Cela renforce considérablement la sécurité d’écriture des programmes.

En contrepartie, il faut, avant d’utiliser une fonction, soit la définir entièrement, soit en donner un prototype (la déclarer). Cela permet notamment de placer les fonctions dans un ordre différent. Par exemple, beaucoup de programmeurs préfèrent placer la fonction main en début de programme. Il faut alors donner le prototype des autres fonctions avant, comme ceci :

void a(void);void b(void);        // fonctions appelées par main
				
main()
{
     a();
     b();
     ...
}
				
int c(void)
{
      // fonction définie entièrement, utilisée par a()
}
				
void a(void)
{
     c();
     ...
}
				
void b(void)
{
     ...
}

Une fonction déclarée doit obligatoirement être définie ultérieurement, sinon l’éditeur de liens proteste (voir chapitre 10). Le prototype de la fonction doit correspondre exactement à la définition, il n’est pas permis de changer d’avis en cours de route :

int f(int a, float x); // prototype
				
// autres...
				
int f(long a, float x) // erreur ! différent du prototype
{
      // implantation
}

Cependant, il n’est pas nécessaire de donner des noms aux arguments dans le prototype ; la déclaration précédente peut se faire sous la forme :

int f(int, float);            // prototype
          ....
int f(int a, float x)        // définition
{
     // implantation
}

car les noms donnés sont de toute façon ignorés ; ils sont toutefois utiles dans certains cas pour indiquer ce qu’ils signifient :

char *strcpy(char *dest, char *source);

indique clairement dans quel ordre placer les deux chaînes, ce qui est évidemment essentiel dans ce cas.

Les prototypes sont beaucoup utilisés dans les fichiers d’en-têtes (voir chapitre 10), et dans la récursivité croisée (voir paragraphe sur la récursivité).

Précédent Précédent Sommaire Sommaire Suivant Suivant