Bonjour! On vous a déjà parlé de fonctions. Aujourd'hui, on va rentrer dans les détails, mieux préciser les conventions d'écriture, la différence entre <i>#define</i> et <i>fonctions</i>, parler de l’exécution interne et voir les différents types de fonctions. Concernant les règles d'écritures, l'habitude est d'avoir les constantes en majuscules. Et on a déjà vu <i>OUTPUT</i>, <i>HIGH</i>. Je ne respecte pas tellement cette convention parce qu'avec des noms explicites, c'est assez lourd comme écriture. Les variables, la règle est d'avoir les minuscules, on a déjà vu le <i>i</i> dans les boucles <i>FOR</i>, <i>compteur</i>, <i>niveauMaximum</i>. Certains préfèrent mettre des soulignés, à vous décider. Du côté des définitions, on a une première lettre qui est majuscule et les définitions s'appliquent aux matériels. Du côté des fonctions, on a également des majuscules en première position, mais une fonction va désigner une action. Donc là, j'insiste, et je vais encore insister, les définitions c'est concernant le matériel, les fonctions c'est concernant la fonctionnalité. Dans certains langages, on fait une différence subtile entre procédure et fonction. Le <i>C</i> ne fait pas cette différence. Je reviens encore la dessus <i>define</i> pour décrire le matériel, <i>fonction</i> pour la fonctionnalité. Je prend un exemple avec un robot simple. Ce robot simple a deux moteurs. Si vous regardez l'arrière du moteur, là où les fils partent, vous avez un plus et un moins. Et si vous envoyez le courant sur le plus, tous les moteurs du même fabriquant vont tourner dans le même sens. Donc je définis ici, le plus de mon moteur va être sur la pin <i>quatre</i>, le moins sur la pin <i>trois</i>. Et maintenant pour que le moteur avance, c'est-à-dire pour que ce moteur tourne dans le sens qui fera avancer le robot, je dois mettre sur la pin <i>quatre</i> un <i>zéro</i> sur la pin <i>trois</i> un <i>un</i>. Et là, ça ne sera pas accepté par le compilateur puisque je n'ai pas mis des majuscules comme c'est reconnu. Alors maintenant, je vais définir la même chose pour le moteur gauche mais le moteur gauche, pour qu'il avance, il faudra que je permute ces deux définitions. Les moteurs sont symétriques donc si le moteur droit, si la pin <i>plus</i> doit être à <i>zéro</i>, sur le moteur gauche, la pin <i>plus</i> devra être à <i>un</i>. Alors on peut tâtonner et sortir son fer à souder pour déplacer les fils mais vous voyez que si on réfléchit bien, on aura quelque chose qui est correcte, qui réagira selon la définition du matériel. Et là, si on veut faire avancer le robot, c'est une action qui nécessite d'avoir les deux moteurs qui avancent. On a défini ce qui les faisait avancer. On a plus besoin de se préoccuper du câblage et du matériel. Alors maintenant, on peut rajouter une fonction qui va faire un aller-retour avec une certaine durée qui va fixer la distance de déplacement. On avance, on attend, on recule, on attend. La durée étant un paramètre de cette fonction. Et dans le programme principal, on va avoir <i>FaireAllerRetour</i>, la durée d'action et évidemment, c'est un cas un petit peu simplifié qui montre bien la hiérarchisation des fonctions. Ce que l'on vient de faire, c'est une programmation descendante, on est parti du matériel pour aller vers la complexité. Dans les cas réels, on se préoccupe d'abord de l'application, de son cahier des charges, de la structure du programme principal, des appels système que l'on va utiliser et qui eux-mêmes vont faire des appels à des fonctions plus simples, qui vont s'appuyer sur le matériel. Donc <i>top-down</i> ou <i>bottom-up</i> ce sont les deux options. Alors, prenons des exemples en commençant par les choses simples. Vous avez la fonction <i>ClignoteUneFois</i>, vous l'appelez par <i>ClignoteUneFois</i>. Il n'y a pas de paramètre qui vont être transférer sur la pile, la parenthèse est vide. Vous avez la fonction <i>ClignoteXFois</i>, là vous avez un paramètre et il faut dire quel va être le nom de ce paramètre, mais c'est un nom local, c'est un nom uniquement utilisé là-dedans. Par contre, on a dû bien préciser quelle était la taille à réserver sur la pile pour transférer ce paramètre. Et quand on appelle, on a clignoté tant de fois, le paramètre, une constante ou une variable, mais de type <i>byte</i>. Si on mettait un nombre trop grand ici, le compilateur devrait rouspéter. Ensuite, on peut avoir deux paramètres qui n'ont pas forcément le même type. On veut clignoter <i>X</i> fois avec une certaine période. On va donner le type <i>byte</i> pour le nombre de fois, le type <i>int</i> pour la période et on aura les deux paramètres et on pourra tester maintenant ce programme. Il faut toujours tester les fonctions quand on les a écrites avec une petite boucle: On clignote trois fois, et puis, on ne fait plus rien. Si vous supprimez ce <i>while</i>, qu'est ce qui se passe? Vous clignotez trois fois, vous clignotez trois fois, vous clignotez trois fois. Vous avez l'impression que votre programme est faux. Il faut rajouter un délai ici, pour qu'après chaque <i>clignotement trois fois</i>, vous avez un espace qui vous permet de vérifier quand se trouve le début de l'appel de la fonction. Une fonction peut avoir un paramètre de sortie. Prenons un exemple dans une application. Combien d'interrupteurs sont positionnés à un moment donné ? On va le simuler avec le <i>lancer bot</i> où on peut presser sur un/deux interrupteurs poussoirs simultanément. Dans le programme, ce qu'on aimerait écrire : le <i>NbPoussoirs</i>, qui est la variable qui m'intéresse, est égale à une fonction qui va analyser le nombre de poussoirs pressés. Cette fonction, on va l'écrire comme d'habitude, lui donner un nom explicite. On n'a pas de paramètres d'entrée, la parenthèse va être vide. Et l'accolade qui encadre les instructions de la fonction. Alors la nouveauté maintenant, c'est qu'on doit retourner une valeur. Vous voyez que c'est l'équivalent d'une variable. Et on va retourner cette variable avec l'instruction <i>return</i>, la variable qui a été calculée dans le fond par la fonction. Ce qui est important pour le compilateur qui doit générer du code, c'est de savoir quelle place réserver en mémoire au moment on fait l'appel <i>Combien De Fonction</i>. Et juste qu'à présent on mettait <i>void</i>, on avait rien à mettre dans cette case. Et maintenant, on va dire j'ai un certain nombre qui est inférieur à huit bits, donc on va écrire <i>byte</i> ici. Et puis, on va retourner cette variable. Ici, il faudra déclarer un nom. Alors, écrivons la fonction de façon un peu plus précise. <i>byte CombienDePoussoirs</i> Il nous faut un compteur, ce compteur sera du même type. Et puis, on donne un nom qui est une variable locale <i>cp = 0</i>. Et maintenant, les instructions sont triviales. Si le poussoir <i>un</i> est actif, on incrémente le compteur. Si le poussoir <i>deux</i> est actif, on incrémente le pointeur. Il faudrait pas mettre <i>cp</i> égale <i>1</i>, <i>cp</i> égale <i>2</i>. Ça serait une autre fonctionnalité dans le fond pour savoir avec une numérotation adéquate quel est le poussoir qui a été pressé. Maintenant, on va retourner la variable que l'on a défini. Donc, au niveau du programme, on va avoir une variable globale, qui est le <i>nbPoussoirs</i>. On va avoir dans la boucle principale, l'instruction qui nous intéresse. <i>nbPoussoirs = CombienDePoussoirs</i> Prenons un autre exemple un peu plus traditionnel que l'on voit dans tous les livres : la multiplication. Vous définissez une fonction pour multiplier deux nombres <i>a</i> et <i>b</i>. Souvenez-vous qu'il faut préciser les types de variable qui sont dans la parenthèse. Le résultat de cette multiplication de seize bits par seize bits, c'est trente-deux bits. On peut déclarer seize bits ici, mais il va y avoir un problème s'il y a dépassement de capacité puisque le <i>C</i> ne va pas vous signaler qu'il y a dépassement de capacité. On a définit notre fonction, on a réservé la place en mémoire. Il suffit d'écrire, on retourne la valeur qui nous intéresse qui est dans ce cas-là immédiate, <i>a</i> fois <i>b</i>. Donc c'est un exemple assez académique. Et puis, quand on va appelez maintenant la multiplication, on dira <i>valeur</i> que l'on a déclaré de type <i>long</i>, c'est la fonction multiplication avec deux nombres, deux variables, etc. Ce que j'aimerai mettre en évidence, c'est que l'on a pas eu besoin de déclarer une variable locale, mais vous pouvez très bien le faire et c'est clair que lors de fonctions plus complexes qu'une bête multiplication on va devoir déclarer une ou plusieurs variables locales. Ici, on a déclaré de type <i>int</i>.. J'avais décidé avant de type <i>long</i>, maintenant j'ai décidé que ça serait de type seize bits. Et puis, la variable c'est le produit et on retourne la variable et c'est comme avant. Voilà ces fonctions avec un paramètre d'entrée. Est-ce qu'on peut avoir plusieurs paramètres en sortie? Non, c'est pas possible. On calcule une seule variable chaque fois ou l'équivalent d'une variable. Mais on peut utiliser un pointeur ou un tableau de données. Ce qui se fait abondamment dans tous les <i>C</i> qui travaillent sur écran et qui gère vos fichiers d'adresses, etc. Il y a encore une solutions qui est d'agir sur des variables globales. Ça nous permet de faire un petit rappel sur les solutions qu'on a à disposition maintenant pour écrire des fonctions. La solution habituelle que l'on a vu, c'est la solution de fonction avec des paramètres. Le paramètre est défini, mais comme une variable locale dans la fonction. Et puis, au niveau de l'appel, on veut clignoter trois fois. On va mettre <i>Cligno</i>, parenthèse, le paramètre qui sera interprété dans le fond dans la fonction en faisant des calculs avec cette variable locale. Alors, si on fait une variable globale, ça veut dire que je définis ma variable au début du programme et qu'elle va être valable dans tout le programme. Alors, la fonction clignotement n'a plus de paramètre local explicite ici. Elle ne va pas changer, si j'ai utilisé le même nom. Je vais trouver exactement les mêmes instructions. Et au niveau de l'appel, vous aurez un appel qui ne mentionne pas cette variable globale. Il faudra la déclarer avant comme étant <i>nbCli = 3</i>. Et on aime pas du tout programmer de cette façon parce que vous savez la définition de la variable globale peut avoir été faite relativement longtemps avant le clignotement ou peut être avoir été modifiée par une quelconque instruction. Alors, il y a encore une solution qu'on a vu, qu'on a beaucoup utilisée, mais sur des cas plus simples, c'est ce qu'on appel les macros, les <i>#define</i>. Vous pouvez très bien écrire <i>#define Cligno</i> et aligner une suite d'instruction qui correspond aux fonctions qu'on avait écrites. Il y a possibilité de mettre des paramètres, mais ce n'est pas encouragé. On utilise ces macros que dans des cas simples. On veut simplement économiser de recopier sans arrêt un certain nombre d'instructions. La différence au niveau de l'appel, c'est qu'on écrit <i>Cligno;</i> sans parenthèses. Donc, on avait déjà signalé cette difficulté, il faut faire très attention de savoir ce qu'on a écrit si c'est des <i>define</i> qui n'ont pas de parenthèses ou une fonction avec la parenthèse. Et le <i>C</i>, malheureusement, si vous oubliez la parenthèse dans un cas comme ça, il va accepter et ne rien exécuter du tout. Regardons un petit peu plus précisément ce que fait le compilateur avec ces différentes expressions d'une fonction ou d'une macro. Avec la macro, vous faites une définition avec un texte quelconque, parfois on substitue une variable, parfois on peut écrire un certain nombre d'instructions. Dans le programme, chaque fois que vous avez écrit <i>Cli;</i> vous allez mettre cette ensemble d'instructions dans la mémoire. Donc chaque fois, ça sera répété. Si vous avez beaucoup d'instructions, vous voyez bien que ça perdra de la place. Si on écrit une fonction, cette fonction va être mise en mémoire dans un coin séparé dans le fond du programme. Et toutes les fois que l'on appelle la fonction, on va voir beaucoup moins d'instructions qui permettent de se référer à la position mémoire où on a la fonction, pour l'exécuter. Et les variables auront été réservées dans un coin de la mémoire. Alors évidemment, il y a un truc supplémentaire c'est qu'une fois qu'on a appelé la fonction, il faut pouvoir revenir au programme pour l'exécuter. Donc, c'est quelque chose de câblée à l'intérieur du processeur. Au moment où vous appelez la fonction, vous transmettez dans une zone mémoire l'adresse de retour qui vous permettra de continuer. Donc, à la fin de la fonction, quand vous avez cette accolade finale, le compilateur va vous rajouter une instruction qui va prendre cette adresse de retour, puis permettre de continuer l'exécution. Ça se complique un petit peu, car, si maintenant, dans cette fonction, vous voulez appeler une autre fonction, il faudra aussi mettre cette adresse de retour dans la mémoire, et puis, faut naturellement pas la mettre au même endroit. Il faut l'empiler et comme ça quand on revient, on peut dépiler, récupérer les adresses, les unes aux autres. Cette pile pour les adresses de retour, sur laquelle on met aussi beaucoup d'autres choses, est un concept extrêmement important. Et cette notion d'appel de routine, d'appel de fonction, a été une des grandes inventions de l'informatique dans les années 50 quand il y a eu les premiers ordinateurs performants qui apparaissaient. On a souvent évoqué les activités du compilateur. C'est très compliqué de savoir ce qu'il fait mais je crois que c'est utile d'avoir quand même un petit modèle. Vous avez préparé un petit programme qui va clignoter une LED sur une pin de votre processeur. Vous lancez la compilation, qu'est ce qui se passe ? Votre code source est copié, vous savez que vous pouvez continuer à travailler pendant la compilation. Il est copié, donné à un préprocesseur. Ce préprocesseur va convertir les <i>define</i> et les <i>include</i> pour simplement, que ça soit des instructions compatibles avec le <i>C</i>. C'est le grand avantage de préprocesseur, c'est qu'on peut rajouter une couche de définition, de programme. Mais les bons programmeurs exploitent énormément ces facilités même pour faire des compilations conditionnelles. Par exemple, pour changer la langue dans des commentaires. Le compilateur, lui, va traduire tout ça dans un langage assembleur qui dépend du processeur. Donc, là c'est effectivement quelque chose d'assez futé pour convertir dans le fond dans les instructions, qui -- tous les processeurs sont construits avec le même principe, on l'avait vu à propos de la machine de <i>Fibonacci</i> pour simplement calculer <i>Fibonacci</i>. Ensuite, il y a un problème d'adresse et ces adresses sont calculées au niveau d'un <i>linker</i>, qui s'appelle <i>linker</i> parce qu'en fait, nous travaillons avec de petits programmes mais les professionnels préparent des modules de programmes, ont une librairie et tous ces programmes préparés séparément sont assemblés à ce moment pour générer le code qui va être compatible avec le processeur. Alors ce code, il faut le transporter dans votre processeur, ce qui se fait avec un câble USB. Et vous avez un chargeur qui est, en fait, d'une part un programme qui transfère le code par une ligne série et puis, qui est le driver en fait qui dépend de beaucoup de choses. Et puis un chargeur, qui est à l'intérieur du processeur, qui va prendre les bytes et les mettre dans la mémoire, dans le bon endroit. Alors ensuite, au moment où l'exécution démarre, vous avez une activité dans les registres. Certains de ces registres sont en communication avec l'extérieur. Vous avez des variables globales qui ont été mises dans une zone mémoire qui s'appelle le <i>Tas</i> : <i>heap</i>. Et vous avez les variables locales, qui sont dans une zone plus élastique puisque les variables locales vont apparaître quand on appelle une fonction, et puis, elle vont disparaître pour faire la place à des variables d'une autre fonction. Donc ça, il faudra parfois faire attention avec ces variables locales et on en reparlera. Et puis, vous avez cette pile qui se remplit, qui se vide en fonction des activités et dans des gros programmes, il faut faire attention qu'il y ait suffisament de mémoire vive à disposition. Bien, on a donc vu comment s'exécute une fonction. Et on a mis en évidence l'importance de définir des noms clairs en français, en anglais, en mélange. Je crois que c'est très difficile de définir des noms clairs. Donc, on va pouvoir maintenant utiliser des fonctions dans nos prochains exemples.