C_facile : Introduction au langage C
Cours

Invocation d'une fonction

Exemple

Considérons le code complet 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 */

}

Considérons la compilation de ce code du point de vue de l'usage de la fonction « plus_un ».

Dans un premier temps, il y a inclusion des définitions des fonctions d'entrée/sortie de la bibliothèque « stdio.h ».

Puis compilation de la fonction « plus_un » comme expliqué avant. Le compilateur rencontre alors l'en-tête de la fonction

 void main ()

Cette fonction ne retourne rien et n'a besoin d'aucun paramètre pour fonctionner. Le compilateur détecte que c'est la fonction principale. C'est elle qui sera exécutée. Tout ce qui est avant n'est que déclaration.

Nous avons ensuite le bloc main, il y a déclaration de deux variables locales pour la fonction main : « x » et « y » de type entier. Le compilateur rencontre ensuite l'instruction :

  x = plus_un(7);

Il vérifie s'il connaît l'identificateur « plus_un », c'est le cas, c'est un identificateur de fonction qu'il a compilé. Vous remarquerez que la valeur 7 est le paramètre réel de la fonction. Il vérifie alors si le nombre de paramètres réels est identique au nombre de paramètres formels attendus par la définition de la fonction. C'est le cas, la fonction est déclarée avec un paramètre formel et on veut l'utiliser avec un paramètre réel. Le compilateur vérifie alors si le type du paramètre réel est compatible avec le type du paramètre formel déclaré. Là encore c'est le cas, la fonction attend un entier, nous lui transmettons une valeur entière.

A ce moment, la vérification de l'invocation de la fonction est terminée.

Le compilateur met en place le mécanisme de recopie de la valeur 7 dans le réceptacle du paramètre formel “a”. Il met ensuite en place le mécanisme de branchement vers le code compilé de la fonction pour exécuter les instructions, puis il réalise le mécanisme de branchement de retour de la fonction et enfin exécute le mécanisme de récupération de la valeur retournée (return inter). La valeur retournée sera recopiée dans la variable « x ».

A ce stade, il reste à compiler l'instruction :

 y = plus_un(x);

La seule chose qui diffère concerne la recopie d'information. Dans ce cas on va recopier la valeur de la variable « x » au sein du réceptacle du paramètre formel « a ». Puis la valeur retournée sera recopiée dans la variable « y ».

La compilation échoue si :

  •  le nombre de paramètres réels n'est pas 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 ne sont pas compatibles avec les types des paramètres formels déclarés. C'est la vérification de la concordance de types.

Considérons maintenant l'exécution du code :

Au point 1

Les variables locales x et y existent, mais ne sont pas initialisées.

La fonction est appelée : il y a création temporaire des réceptacles pour le paramètre formel “a“ et pour la variable locale « inter », puis recopie de 7 dans « a », nous venons d'être “branché” au point « point_fonc », l'instruction « inter = a+1; » est exécutée, « a » vaut toujours 7 mais « inter » vaut 8, puis l'instruction « return inter » est effectuée.

Le mécanisme de récupération de la valeur 8 est activé, un branchement de retour vers l'instruction suivante de la fonction principale « main » est effectué et les réceptacles du paramètre formel « a » et de la variable locale « inter » sont récupérés. Ils n'ont existé que pour la durée d'appel et de travail de la fonction. Enfin la valeur 8 retournée par la fonction est affectée à la variable « x ».

Au point 2

« x » vaut 8, la valeur de « y » est inconnue.

La fonction est appelée à nouveau :

Le paramètre réel est la valeur de « x », c'est donc la valeur 8 qui est recopiée dans “a“, puis cela fonctionne exactement de la même façon que dans le premier cas. La valeur retournée par la fonction est donc 9, qui est affecté à la variable « y ».

Au point 3

x vaut 8, et y vaut 9

Explications

Attention
  •  Les paramètres formels permettent de décrire ce que l'on recevra quand la fonction sera effectivement invoquée

  •  Les paramètres réels sont ceux qui sont effectivement transmis quand on invoque la fonction

  •  L'unique mécanisme de transmission d'information entre paramètres réels et paramètres formels est la recopie des valeurs des paramètres réels dans les réceptacles des paramètres formels.

  •  Les paramètres formels et les variables locales d'une fonction ont une durée de vie limitée, ils n'existent que pendant le temps que la fonction travaille. Au point 1 ni “a” ni “inter” n'existent, pas plus au point 2, pas plus au point 3.

Ils n'existent qu'au point « point_fonc » !

Méthode

Au moment de l'invocation d'une fonction il y a donc :

  1. création du contexte de travail de la fonction, c'est-à-dire création dynamique de réceptacles pour les paramètres formels et les variables locales de la fonction qui est invoquée

  2. recopie des valeurs de paramètres réels dans les réceptacles des paramètres formels

  3. exécution des instructions de la fonction

  4. transmission d'information (si la fonction renvoie une valeur autre que de type void)

  5. destruction du contexte de travail de la fonction

  6. poursuite du déroulement du code qui suit l'invocation de la fonction

Remarque

Vous avez remarqué que nous n'avons volontairement plus parlé de branchements. C'est désormais implicite, le compilateur fait le travail de façon transparente.

Vous remarquerez que l'identificateur de la fonction permet à sa simple lecture de comprendre ce que fait la fonction. Il est de bonne pratique de donner à la fonction que l'on déclare un identificateur significatif de la nature du traitement qu'elle effectue.

Rappel

Nous rappelons qu'une fonction doit être déclarée avant d'être utilisée.

Fondamental

Si la fonction retourne une valeur d'un type différent de « void » alors l'appel peut être inséré dans une expression.

Exemple

Déclaration de la fonction impair

int impair(int x)

{

  return (x%2);

          // (modulo 2) vaut 1 (vrai en C) si impair, 0 (faux) sinon

}

Usage dans une expression :

scanf(“%d“, &truc);

if (impair(truc) && (truc > 10)

    printf(“\n %d est impair et plus grand que 10“, truc);

Exemple

Définition de la fonction qui calcule la norme Euclidienne d'un vecteur de deux coordonnées :

float norm_2(float x, float y)

{

  return sqrt(x*x+y*y);

}

usage de la fonction normalisation du vecteur de coordonnées « a » « b » flottantes :

a = a/norm_2(a,b);

b = b/norm_2(a,b);

ExempleAutre exemples d'appels de fonctions prédéfinies

x = sin (y) ;

    // fonction retournant un double

x = pow (8,3) ;

    // 8*8*8 fonction avec 2 paramètres

print(" i = " , i);

    // fonction utilisée pour afficher

Paramètres formels, paramètres réels et variables locales (page suivante)Déclaration d'une fonction et compilation (page Précédente)
AccueilImprimer creativecommons : by-nc-ndRéalisé avec SCENARI