Les Prototypes




Dans le chapitre précédent je vous avais demandé d'implémenter vos fonctions au-dessus de la fonction main, je vais vous expliquer le pourquoi du comment !

En plaçant nos fonctions au-dessus de la fonction main, le compilateur va connaître la fonction lors de l'appel à la fonction, il pourra donc nous dire par exemple si on a bien mis le bon nombre de paramètres et qu'ils sont du bon type.


Quelques tests


Nous allons dans cette partie réaliser quelques tests, observer les résultats puis conclure au fur et à mesure.


En exemple, je vous propose d'envoyer trop de paramètres à la fonction triple du chapitre précédent :

int triple(int nb)
{
    return 3 * nb;
}

int main()
{
    int nombre = 4, deuxiemeNombre = 2;

    nombre = triple(nombre, deuxiemeNombre); // Appel de la fonction triple
    printf("Le triple de ce nombre est %d.", nombre);

    return 0;
}


Appuyez sur F9 et observez l'erreur de compilation...



Comme je suis un bon en anglais (l'accent en particulier), je vous traduis l'erreur :

Le compilateur nous indique qu'il y a trop de paramètres envoyés à la fonction triple à la ligne 14 dans la fonction main du fichier main.c !


Vous voyez bien que la compilation est tout de suite interrompue ! C'est un avantage monstrueux qui vous serait utile chaque jour.


Maintenant, imaginez la fonction triple sous la fonction main :


int main()
{
    int nombre = 4, deuxiemeNombre = 2;

    nombre = triple(nombre, deuxiemeNombre); // Appel de la fonction triple que le compilateur ne connaît pas !
    printf("Le triple de ce nombre est %d.", nombre);

    return 0;
}

int triple(int nb)
{
    return 3 * nb;
}


F9 et le programme se compile en nous renvoyant un simple warning !


Le programme fonctionne mais ce warning est très très déconseillé. Il nous indique que la déclaration de la fonction est implicite ! Ca veut dire que la fonction est quand même déclarée, mais à la fin de la compilation.

Le compilateur pourra juste nous dire si la fonction triple existe ou pas mais en aucun cas nous aurons des informations sur ses paramètres etc...


Mais je vois pas d'erreurs donc c'est bon !


Non, non, non et non ! Il y a beaucoup d'opérations ignorées comme les conversions implicites.


Si on envoie par exemple 5.5 à la fonction triple, le .5 sera ignoré et donc 5.5 * 3 vaudra 15.


int triple(int nb)
{
    return 3 * nb;
}

int main()
{
    double nombre = 5.5;

    nombre = triple(nombre); // Appel de la fonction triple
    printf("Le triple de ce nombre est %f.", nombre);

    return 0;
}


La fonction triple est placée au-dessus de la fonction main, donc le compilateur saura que la fonction attend un int, donc si on lui envoie un double, il effectuera une conversion implicite.


On n'oublie pas le %f et on obtient le résultat attendu :


Le triple de ce nombre est 15.000000.


Mais si le compilateur ne connaît pas la fonction triple au moment de son appel, on peut donc imaginer une grosse erreur bizarre...

Testons donc avec triple au-dessous de main.


int main()
{
    double nombre = 5.5;

    nombre = triple(nombre); // Appel de la fonction triple que le compilateur ne connaît pas !
    printf("Le triple de ce nombre est %f.", nombre);

    return 0;
}

int triple(int nb)
{
    return 3 * nb;
}


Evidemment, le compilateur ne bronche pas mais on a un résultat incompréhensible :


Le triple de ce nombre est 0.000000.


Voilà une des raisons du pourquoi le compilateur doit toujours connaître les fonctions avant leur appel.


Retenez seulement l'essentiel :


  • Des erreurs incompréhensibles sont évitées.
  • Des informations sur l'appel des fonctions sont affichées s'il y a erreur.


Déclarer une fonction


Moi je ne veux pas m'embêter à implémenter chaque fonction avant leur appel !


Rassurez-vous, les programmeurs ont pensé à ça avant vous et ont inventé les prototypes . Nouveau terme à connaître bien entendu. 


La déclaration d'une fonction se nomme donc le prototype.


Qu'est ce qu'un prototype ? 


Bien c'est une information qu'on donne au compilateur pour pouvoir implémenter nos fonctions n'importe où dans notre code source.


Grâce à ça, le compilateur va reconnaître tous les appels et ça aura donc le même effet que de mettre la fonction au-dessus de chaque appel (ici, de la fonction main).

Il faut donc toujours mettre le prototype au-dessus de chaque appel, un prototype n’alourdit en aucun cas l'exécutable final, on peut donc dire qu'ils sont ignorés à la fin de la compilation.


C'est bien beau la théorie mais vous n'avez pas encore vu un prototype !

Regardez la fonction triple :


int triple(int nb)
{
    return 3 * nb;
}


Bien, son prototype est la première ligne en ajoutant un point-virgule à la fin, c'est tout !


int triple(int nb); // Prototype de triple


Voilà, vous avez juste à mettre son prototype avant l'appel de la fonction triple (je vous conseille de toujours mettre vos prototypes avant toutes les fonctions après les directives du préprocesseur.


int triple(int nb);

int main()
{
    int nombre = 4;

    nombre = triple(nombre); // Appel de la fonction triple que le compilateur connaît grâce au prototype !
    printf("Le triple de ce nombre est %d.", nombre);

    return 0;
}

int triple(int nb)
{
    return 3 * nb;
}


Vous êtes maintenant libre de placer vos fonctions où vous voulez dans le code source sans engendrer d'erreurs. Le compilateur saura qu'il existe une fonction nommée triple qui renvoie un int et qui attend un int en entrée.


Vous l'aurez remarqué, le compilateur se fiche du nom des paramètres, on aurait très bien pu mettre un prototype de ce style (cette méthode n'est pas très recommandée) :


int triple(int);


Conclusion, n'oubliez jamais les prototypes des fonctions !


Pour finir, un exemple complet avec 2 fonctions pour bien comprendre les prototypes :


int triple(int);
int somme(int, int);

int main()
{
    int nombre = 4;

    nombre = somme(nombre, triple(nombre)); // Appel de la fonction triple et somme
    printf("Le resultat est %d.", nombre);

    return 0;
}

int triple(int nb)
{
    return 3 * nb;
}

int somme(int a, int b)
{
    return a + b;
}


Combien fera le résultat ?

3 * 4 + 4 vaut 16 donc le résultat vaudra 16 !


Le resultat est 16.

Créé avec HelpNDoc Personal Edition: Produire des aides en ligne pour les applications Qt