Prototypage de fonction
Considérons le programme suivant :
#include <stdio.h>
int plus_un(int a)
{
int inter;
/* point point_fonc */
inter = a+1;
return inter;
}
void main()
{
int x,y;
/* point 1 */
x = plus_un(7);
/* point 2 */
y = plus_un(x);
/* point 3 */
}
Nous avons vu précédemment les vérifications effectuées par le compilateur au moment de l'invocation de la fonction « plus_un(7) » et « plus_un(x) » sur :
-
le nombre de paramètres réels qui doit être le même que le nombre de paramètres formels déclarés
-
les types des paramètres réels utilisés pour l'appel de la fonction qui doivent être compatibles avec les types des paramètres formels déclarés. C'est la vérification de la concordance de types.
La compilation échoue sinon.
C'est une vérification syntaxique.
Ensuite et seulement ensuite, les mécanismes de création de contexte, de recopie, de branchements, de récupération d'informations, et de branchement de retour sont mis en place par le compilateur.
En fait, la compilation effectue d'abord la vérification syntaxique, puis après avoir fait toutes les vérifications elle ré-effectue une « passe » sur le code pour la mise en place des mécanismes. Cette technique est plus efficace que de faire du travail qui sera éventuellement annulé s'il y a une erreur de syntaxe qui fait échouer la compilation.
Nous pouvons remarquer que seul l'en-tête de la fonction est nécessaire pour les vérifications. Nous pouvons donc inverser l'ordre d'écriture des fonctions « main » et « plus_un » à condition que l'on fournisse au compilateur un « prototype » de la fonction qui lui permet de faire son travail de vérification préalable.
Voici le code utilisant cette technique :
#include <stdio.h>
int plus_un(int a);
// prototypage de la fonction, c'est juste l'en-tête
// suivi d'un délimiteur « ; »
void main()
{
int x,y;
/* point 1 */
x = plus_un(7);
// ici le compilateur peut vérifier
// la syntaxe de l'invocation
/* point 2 */
y = plus_un(x);
// ici le compilateur peut vérifier
// la syntaxe de l'invocation
/* point 3 */
}
int plus_un(int a)
// ici nous déclarons réellement la fonction
// le compilateur « recollera » les morceaux pour vous.
{
int inter;
/* point point_fonc */
inter = a+1;
return inter;
}
La règle « on ne peut utiliser quelque chose que si cette chose a été préalablement déclarée » est ainsi respectée.
Une fonction doit être déclarée avant d'être utilisée, nous avons désormais deux solutions :
-
déclarer entièrement une fonction avant son invocation
-
déclarer le prototype de la fonction avant son invocation, ce qui rend possible la vérification, puis déclarer la fonction plus tard.
L'inclusion d'un fichier comme « stdio.h » permet de déclarer les prototypes des fonctions. Ces fichiers d'en-tête (nous les avons d'ailleurs appelés ainsi dans le chapitre 1) contiennent les prototypes de fonctions (des en-têtes avec le « ; ») dont les codes exécutables sont dans les bibliothèques de codes pré-compilées.