30
Royaume du Maroc OFFICE DE LA FORMATION PROFESSIONNELLE ET DE LA PROMOTION DU TRAVAIL Chap 13T Langage C et Gestion des Entr�es/Sorties R�sum� de Th�orie Version préliminaire Deuxième Année Programme de Formation des Techniciens Spécialisés en Électronique DIRECTION DE LA RECHERCHE ET INGENIERIE DE LA FORMATION Septembre 1996

CH06_13T

  • Upload
    red-all

  • View
    213

  • Download
    0

Embed Size (px)

DESCRIPTION

CH06_13T

Citation preview

Page 1: CH06_13T

Royaume du Maroc

OFFICE DE LA FORMATION PROFESSIONNELLE ET DE LA PROMOTION DU TRAVAIL

Chap 13TLangage C et Gestion des Entrées/Sorties

Résumé de ThéorieVersion préliminaire

Deuxième Année

Programme de Formation des TechniciensSpécialisés en Électronique

DIRECTION DE LA RECHERCHE ET INGENIERIE DE LA FORMATION

Septembre 1996

Page 2: CH06_13T

Résumé de Théorie Langage C et Gestion des Entrées/Sorties

TECCART INTERNATIONAL 2000 inc.3155, rue Hochelaga,

Montréal, Québec (Canada)

H1W 1G4

RÉDACTION

Robert Pesant

DESSINS ET CONCEPTION GRAPHIQUE

Robert Pesant

RÉVISION TECHNIQUE

Robert Pesant

RÉVISION LINGUISTIQUE

François Gaudreau

COMMISSION DE VALIDATION

Formateurs de l’OFPPT

Les droits de reproduction et de diffusion de ce document sont cédés par Teccart International 2000 inc. à l’Office de la Formation Professionnelle et de la Promotion du Travail du Royaume du Maroc, pour sa propre utilisation au Maroc.

Mis à part l’OFPPT, toute reproduction, en tout ou en partie, par quelque procédé que ce soit, est interdite.

Imprimé à Montréal, le j 卯月金曜日document.doc/

Fonctions Page 2 OFPPT/TECCART

Page 3: CH06_13T

Résumé de Théorie Langage C et Gestion des Entrées/Sorties

TABLE DES MATIÈRES

6. FONCTIONS

06.1 Fonctions et Standard ANSI06.1.1 Qu’est-ce qu’une fonction?.....................................................................................16.1.2 Prototype de fonction..............................................................................................26.1.3 Définition de fonction.............................................................................................36.1.4 Déclaration de fonction...........................................................................................46.1.5 Types de fonctions..................................................................................................56.1.6 Appel de fonction...................................................................................................

16.2 Rédaction d'un programme avec fonctions66.2.1 Documentation d'un programme.............................................................................76.2.2 Programme structuré avec des fonctions: FONCTION.C........................................

26.3 Passage des paramètres par les valeurs86.3.1 Variables locales.....................................................................................................96.3.2 Variables et modèle des récipients..........................................................................106.3.3 Mécanisme de passage des paramètres par les valeurs...........................................

Fonctions Page 3 OFPPT/TECCART

Page 4: CH06_13T

6Fonctions

6.1 Fonctions et Standard ANSI

6.1.1 Qu’est-ce qu’une fonction?

Une fonction est un ensemble d'instructions et de variables regroupées dans un bloc pour effectuer une tâche spécifique.

Le rôle premier d'une fonction est d'éviter à l'usager de répéter le même code en différents endroits du programme. Lorsque le code de la fonction sera écrit et que l'usager lui aura attribué un identificateur, c'est-à-dire un nom, il pourra en faire l'appel dans son programme aussi souvent que nécessaire, comme c'est le cas pour des fonctions de la bibliothèque de Borland C++ tels que printf() et getch().

Par l'usage des fonctions, la programmation devient structurée. Le programme principal, c'est-à-dire la fonction main(), pourra se construire à partir d'un modèle prédéfini et se composer presque exclusivement d'appels de fonctions. Il n'en sera que plus facile à rédiger, à comprendre, à dépanner et à entretenir.

Pour rendre une fonction indépendante de la fonction main() ainsi que de toutes les autres fonctions, le langage C permet l'usage de variables "privées". Ces variables dites locales ne sont accessibles que par la fonction où elles sont définies. L'usager peut donc utiliser les identificateurs de son choix, même s'il les a déjà utilisés ailleurs dans une autre fonction. Nous reviendrons sur cette notion importante vers la fin de ce chapitre.

Pour échanger des données avec le programme qui l'appelle, une fonction peut accepter plusieurs paramètres mais ne peut retourner qu'une seule et unique valeur.

Pour manipuler correctement une fonction, il faut savoir...

1. la définir,2. la déclarer3. et en faire l'appel.

Deux notions fondamentales sont nécessaires pour y parvenir:

4. le prototype de fonction, adopté par le Standard ANSI;5. la technique du passage de paramètres par les valeurs.

6.1.2 Prototype de fonction

Le prototype d'une fonction est composé de trois éléments écrits sur une seule ligne:

6. le type de donnée de la valeur retournée;7. l'identificateur de la fonction;8. le type de donnée et l'identificateur de chaque paramètre, séparés par une virgule et

encadrés par une paire de parenthèses.

Prenons comme exemple une fonction qui attend le choix de l'usager lorsqu'on lui présente un menu. Son prototype peut s'écrire comme suit: int get_choix(void). Cette fonction se nomme get_choix. Son rôle est de retourner, sous forme d'un int, le code ASCII de la touche pressée par l'usager. Comme l'information recueillie par cette fonction provient directement du clavier, elle n'a besoin d'aucun paramètre: on retrouve donc le mot-clé void entre ses parenthèses.

Examinons maintenant le prototype d'une fonction qui affiche les codes ASCII compris entre deux valeurs spécifiées par l'usager: void affi_ascii(int code_min, int code_max). La fonction affi_ascii affiche des caractères à l'écran; elle n'a donc pas de valeur numérique à retourner au programme d'appel; c'est ce qui justifie l'emploi du mot-clé void devant son identificateur. Par

Page 5: CH06_13T

contre, cette fonction a besoin de connaître les codes de début et de fin qu'elle doit afficher: le premier paramètre, de type int, est le code initial identifié par code_min; le deuxième paramètre, également de type int, est le code final code_max.

Sans même connaître comment ces deux fonctions vont accomplir leur boulot respectif, nous avons déjà une très bonne idée de la façon de les utiliser: entre autres choses, il ne faut pas passer à affi_ascii() des variables de type double!

Le prototype de fonction est une nouveauté du langage C introduite par le Standard ANSI. C'est l'outil qu'utilise le compilateur pour détecter et signaler par des avertissements (warnings) que l'usager n'utilise pas les bons types de données dans le passage de paramètres.

6.1.3 Définition de fonction

Une définition de fonction se compose:

9. du prototype de la fonction;10. du bloc d'instructions.

Voici la définition de la fonction get_choix():

int get_choix(void){ int choix;

do { gotoxy(63, 11); putch('*'); gotoxy(63, 11); choix = getche(); } while(CLE_INTERDITE);

return(choix);}

Après la définition de la variable choix, la boucle do ... while() effectue le verrouillage explicite des touches interdites. L'expression à tester est décrite par la directive suivante:

#define CLE_INTERDITE !((choix == '1') || (choix == '2') || (choix == ESC)) .

Lorsque l'usager choisit une touche permise, 1, 2 ou Esc, le code ASCII déposé dans choix par getche() est affiché à l'écran puis transféré ou retourné au programme d'appel par l'instruction return(choix);. La variable choix a été obligatoirement définie de type int, pour être conforme au type de donnée spécifié par le prototype de la fonction.

L'exécution d'une fonction peut se terminer de deux façons différentes: par la rencontre de l'accolade délimitant la fin du bloc de la fonction ou par l'exécution de l'instruction return.

Voyons maintenant la définition de la fonction affi_ascii():

void affi_ascii(int code_min, int code_max){ int i;

gotoxy(1, 17); if((code_min < 0) || (code_min > 255) || (code_max < 0) || (code_max > 255)) { puts("ERREUR!!! Au moins un des deux codes n'est pas compris entre 0 et 255."); puts("Vous devez recommencer..."); return; } if(code_max < code_min) { puts("ERREUR!!! Le code final est plus petit que le code initial."); puts("Vous devez recommencer..."); return; }

Page 6: CH06_13T

for(i = code_min; i <= code_max ; i++) { putch(i); putch(' '); }}

Les deux paramètres, que requiert cette fonction, sont automatiquement définis par le prototype qui précède le bloc d'instructions. Seule la variable i, qui n'est pas un paramètre, est définie dans le bloc de la fonction par l'instruction int i;.

Puis, deux instructions if vérifient la validité des codes ASCII de début et de fin que reçoit la fonction par l'intermédiaire de ses paramètres. Si l'un ou l'autre des codes est inférieur à ASCII 0 ou supérieur à ASCII 255, ou si le code de fin est inférieur au code de début, un message d'erreur approprié est affiché. L'exécution de la fonction est alors interrompue par l'instruction return;. Étant donné que la fonction affi_ascii() ne retourne rien au programme d'appel, le mot-clé return doit donc être utilisé seul, sans aucune expression, pour pouvoir terminer la fonction ailleurs qu'à l'accolade de fin de bloc.

Par contre, si les codes de début et de fin sont valides, une boucle for affiche les caractères ASCII délimités par ces deux valeurs. L'exécution de la fonction se termine alors à l'accolade de fin de bloc.

6.1.4 Déclaration de fonction

Une déclaration de fonction est constituée:

11. du prototype de la fonction;12. suivie d'un point virgule.

La déclaration d'une fonction doit nécessairement se faire avant l'instruction d'appel. Dans l'exemple qui suit, les déclarations de get_choix() et affi_ascii() précèdent la définition de la fonction main() qui utilise ces deux fonctions:

‚‚

La déclaration d'une fonction indique au compilateur comment préparer le code objet (code machine) qui permettra le saut à la sous-routine correspondant à cette fonction. L'appel de cette fonction peut alors être fait même si sa définition, c'est-à-dire son code, apparaît plus loin dans le programme.

Page 7: CH06_13T

Une déclaration de fonction ne génère pas de code objet. Elle ne peut donc pas remplacer la définition de fonction. Par contre, la définition de fonction peut servir de déclaration, si elle précède l'appel de cette fonction:

‚‚

6.1.5 Types de fonctions

La Tableau 6-1 présente les quatre types de fonctions que l'on peut rencontrer.

TABLEAU 6-1: LES TYPES DE FONCTIONS

Type Valeur retournée Paramètres1 Aucune Aucun2 Une seule Aucun3 Aucune Un ou plusieurs4 Une seule Un ou plusieurs

En voici des exemples:void menu (void); <--------------------------------------------------- Type 1int get_choix (void); <--------------------------------------------------- Type 2void affi_ascii (int code_min, int code_max); <----------------------------- Type 3double mise_a_echelle (double courant, double portee_min, double portee_max); <--- Type 4

6.1.6 Appel de fonction

Pour faire l'appel d'une fonction de type 1, il suffit d'écrire son nom, suivi de parenthèses vides et d'un point virgule:

Fonction de l'usager Fonction de la bibliothèque

menu(); clrscr();

Dans le cas du type 2, il faut au préalable définir une variable de même type que la valeur retournée pour pouvoir la récupérer à la fin de l'exécution de cette fonction:

Fonction de l'usager Fonction de la bibliothèque

int choix; int touche;

choix = get_choix(); touche = getch();

Lors de son appel, une fonction de type 3 doit recevoir des valeurs numériques par l'intermédiaire de ses paramètres. Ces valeurs peuvent être des variables ou des constantes, en autant qu'elles soient du même type de donnée que les paramètres:

Fonction de l'usager Fonction de la bibliothèque

int ascii_min = 33, ascii_max = 65; gotoxy(15, 10);

affi_ascii(ascii_min, ascii_max);

Page 8: CH06_13T

Enfin, l'appel d'une fonction de type 4 combine les techniques utilisées pour le type 2 et le type 3:

Fonction de l'usager Fonction de la bibliothèque

double courant = 14.5, temperature; int c_maj, c_min = 'a';

temperature = mise_a_echelle(courant, -25.0, 75.0); c_maj = toupper(c_min);

6.2 Rédaction d'un programme avec fonctions

6.2.1 Documentation d'un programme

Avant même de songer à rédiger des fonctions pour structurer son programme, le programmeur consciencieux se doit de bien le documenter. Il sera beaucoup plus simple à comprendre, à dépanner et à entretenir. On entend par entretien d'un programme, les modifications qu'on doit lui faire subir pour améliorer ses performances après un certain temps d'utilisation.

La technique de documentation de programme préconisée dans ce cours favorise dans l'ordre les quatre éléments suivants.

1. Un commentaire en début de programme contenant:

· le titre;· le nom de l'auteur;· la date de création;· la date de la dernière révision;· la version;· le nom de volume du disque ou de la disquette de sauvegarde;· le nom du fichier, incluant le chemin du répertoire;· la description du fonctionnement du programme.

2. Un commentaire de fonction précédant la définition et contenant:

· le nom de la fonction;· la description du fonctionnement;· la description des paramètres;· la description de la valeur retournée.

3. L'utilisation d'identificateurs de variables et de fonctions qui ont du sens comme:

· temperature, courant;· menu(), get_choix();

plutôt que:

· var1, var2;· fonc1(), fonc2().

4. Un commentaire d'instruction ou de groupe d'instructions, lorsque le commentaire de fonction ou le choix des identificateurs ne sont pas suffisants à une bonne compréhension.

Page 9: CH06_13T

6.2.2 Programme structuré avec des fonctions: FONCTION.C

Le programme FONCTION.C est construit selon le Modèle #1 présenté au chapitre précédent. L'usager se voit offrir un menu de deux options: l'affichage des codes ASCII compris entre deux valeurs de son choix et la simulation d'un transmetteur de température 4-20 mA. Ce programme fait appel à la technique du verrouillage explicite des touches interdites. De plus, il utilise les fonctions qui ont servi d'exemples à la section précédente. Voici son listage.

/* Programme: FONCTION.C * ========= ========== * * Auteur: Robert Pesant * Date: 22/02/1993 * Révision: 25/04/1995 * Version: V1.2 * * Volume: COURS_13T * Fichier: \CHAP.007\FONCTION.C * * Description: Programme de démonstration qui utilise des fonctions définies par l'usager. */

/* Les fichiers d'en-tête de la bibliothèque de Borland C++ -------------- */

#include <stdio.h>#include <conio.h>#include <string.h>#include <process.h>#include <dos.h>

/* Les constantes symboliques -------------------------------------------- */

#define MENU_1 ""#define MENU_2 " Programme de démonstration "#define MENU_3 " contenant des fonctions "#define MENU_4 " rédigées par l'usager "#define MENU_5 ""

#define MENU_6 "Affichage des codes ASCII .................. < 1 >"#define MENU_7 "Mise à l'échelle d'un transmetteur 4-20 mA . < 2 >"#define MENU_8 "Quitter .................................... <Esc>"#define MENU_9 "Votre choix ................................ < * >"

#define RETOUR "Retour au menu: une touche, SVP"#define ASCII_1 "Entrez le code ASCII initial: "#define ASCII_2 "Entrez le code ASCII final: "#define ECHELLE_1 "Transmetteur de température 4-20 mA (-25 C à +75 C)"#define ECHELLE_2 "====================================================="#define ECHELLE_3 "Entrez la valeur du courant du transmetteur .: "#define ECHELLE_4 "La température mesurée correspondante est ...: "

#define CODE_ASCII '1'#define TX_4_20_MA '2'#define ESC 27#define CLE_INTERDITE !((choix == '1') || (choix == '2') || (choix == ESC))

/* Les déclarations des fonctions de l'usager ---------------------------- */

void menu (void);int get_choix (void);void clr_lines (int y);void affi_ascii (int code_min, int code_max);double mise_a_echelle (double courant, double portee_min, double portee_max);

Page 10: CH06_13T

void main(void){ int choix, ascii_min, ascii_max; double courant, temperature;

clrscr(); menu();

do { choix = get_choix(); switch(choix) { case CODE_ASCII: gotoxy(1, 15); printf(ASCII_1); scanf("%d", &ascii_min); fflush(stdin); gotoxy(41, 15); printf(ASCII_2); scanf("%d", &ascii_max); fflush(stdin); affi_ascii(ascii_min, ascii_max); break; case TX_4_20_MA: gotoxy(1, 15); puts(ECHELLE_1); gotoxy(1, 16); puts(ECHELLE_2); gotoxy(1, 18); printf(ECHELLE_3); scanf("%lf", &courant); fflush(stdin); gotoxy(1 + strlen(ECHELLE_3), 18); printf("%6.2f mA", courant); if((courant < 4.0) || (courant > 20.0)) { puts(" (...???)"); } temperature = mise_a_echelle(courant, -25.0, 75.0); gotoxy(1, 19); printf(ECHELLE_4); printf("%6.2f C", temperature); break; case ESC: clrscr(); exit(0); }

gotoxy(41, 25); printf(RETOUR); getch(); clr_lines(15); } while(1);}

/* Fonction: menu() * ======== ====== * * Description: Affiche le menu du programme * * Paramètres: 1. Aucun * * Valeur/retour: 1. Aucune */

void menu(void){ gotoxy(41 - strlen(MENU_1 )/2, 1); puts(MENU_1 ); gotoxy(41 - strlen(MENU_2 )/2, 2); puts(MENU_2 ); gotoxy(41 - strlen(MENU_3 )/2, 3); puts(MENU_3 ); gotoxy(41 - strlen(MENU_4 )/2, 4); puts(MENU_4 ); gotoxy(41 - strlen(MENU_5 )/2, 5); puts(MENU_5 );

gotoxy(41 - strlen(MENU_6 )/2, 8); puts(MENU_6 ); gotoxy(41 - strlen(MENU_7 )/2, 9); puts(MENU_7 ); gotoxy(41 - strlen(MENU_8 )/2, 10); puts(MENU_8 ); gotoxy(41 - strlen(MENU_9 )/2, 11); puts(MENU_9 );}

Page 11: CH06_13T

/* Fonction: get_choix() * ======== =========== * * Description: Attend et retourne le choix de l'usager. * Les touches interdites du clavier sont verrouillées. * * Paramètres: 1. Aucun * * Valeur/retour: 1. Le code ASCII du choix de l'usager */

int get_choix(void){ int choix;

do { gotoxy(63, 11); putch('*'); gotoxy(63, 11); choix = getche(); } while(CLE_INTERDITE);

return(choix);}

/* Fonction: clr_lines() * ======== =========== * * Description: Efface le bas de l'écran en faisant remonter le texte * à partir de la ligne choisie par l'usager. * * Paramètres: 1. La ligne à partir de laquelle on efface l'écran. * * Valeur/retour: 1. Aucune. */

void clr_lines(int y){ int i;

gotoxy(1, y); for(i=y; i<=25; i++) { delline(); delay(50); }}

/* Fonction: affi_ascii() * ======== ============ * * Description: Affiche les codes ASCII demandés par l'usager * et compris entre 0 et 255. * * Paramètres: 1. Le code ASCII initial qui doit être affiché. * 2. Le code ASCII final qui doit être affiché. * * Valeur/retour: 1. Aucune. */

void affi_ascii(int code_min, int code_max){ int i;

gotoxy(1, 17); if((code_min < 0) || (code_min > 255) || (code_max < 0) || (code_max > 255)) { puts("ERREUR!!! Au moins un des deux codes n'est pas compris entre 0 et 255."); puts("Vous devez recommencer..."); return; } if(code_max < code_min) { puts("ERREUR!!! Le code final est plus petit que le code initial."); puts("Vous devez recommencer..."); return; }

Page 12: CH06_13T

for(i = code_min; i <= code_max ; i++) { putch(i); putch(' '); }}

/* Fonction: mise_a_echelle() * ======== ================ * * Description: Effectue la mise à l'échelle d'un transmetteur 4-20 mA * en fonction des portées minimale et maximale de son * étendue de mesure. * * Exemple: Transmetteur de température 4-20 mA dont l'étendue * de mesure est comprise entre -25 C et +75 C. * * 20 mA ----------- 75 C <-- portée maximale * * * D B * I ----------- T <-- grandeur mesurée * C A * * 4 mA ----------- -25 C <-- portée minimale * * Calcul de mise à l'échelle: * * A/B = C/D * (T - (-25))/(75 - (-25)) = (I - 4)/(20 - 4) * (T + 25)/100 = (I - 4)/16 * * * T = (100/16)*(I - 4) - 25 <-- Fonction de transfert * * * Pour n'importe quelle valeur de courant mesurée entre * 4 et 20 mA, on peut évaluer la température à laquelle est * soumis le capteur du transmetteur de température. * * Paramètres: 1. Le courant du transmetteur. * 2. La portée minimale du transmetteur. * 3. La portée maximale du transmetteur. * * Valeur/retour: 1. La grandeur physique mesurée par le transmetteur. */

double mise_a_echelle(double courant, double portee_min, double portee_max){ double grandeur;

courant = (courant < 4.0) ? 4.0: courant; courant = (courant > 20.0) ? 20.0: courant; grandeur = ((portee_max - portee_min)*(courant - 4.0)/16.0) + portee_min;

return(grandeur);}

Le texte du programme FONCTION.C débute par les directives au préprocesseur #include et #define. Celles-ci sont immédiatement suivies des déclarations des fonctions de l'usager.

Par l'usage des fonctions de la bibliothèque et de l'usager, le programme principal, défini dans la fonction main(), ressemble davantage à un algorithme qu'à une suite d'instructions:

13. clrscr() ...................................effacer l'écran.14. menu() ...................................présenter le menu à l'usager.15. get_choix() .............................détecte le choix de l'usager.

Page 13: CH06_13T

16. switch(choix) .........................exécute la tâche choisie par l'usager· case CODE_ASCII:

- affi_ascii() ................ affichage des codes ASCII entre les limites déterminées par

l'usager.· case TX_4_20MA:

- mise_a_echelle() ........ simulation d'un transmetteur de température; calcul de la

température mesurée en fonction du courant choisi par l'usager.

· case ESC:- exit() .........................fin du programme et sortie au DOS.

17. clr_lines() ...............................effacer les résultats à la demande de l'usager.18. retour à l'étape 3.

La suite du programme contient les définitions des fonctions de l'usager. Consultez leur description respective pour en connaître le fonctionnement.

Pour obtenir des programmes structurés, faciles à comprendre et à dépanner, chaque fonction doit être simple: elle doit être conçue pour accomplir autant que possible une seule tâche. On doit bannir les fonctions dont les déclarations ressemblent à ceci:

void je_fais_tout_a_moi_tout_seul(void);

Exécutez le programme FONCTION.EXE. Vous devriez obtenir des résultats semblables à ceux de la Figure 6-1.

‚‚‚

…‚

‚‚‚

…‚

€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêë

Page 14: CH06_13T

‚‚‚

…‚

‚‚‚

…‚

‚‚‚

…‚

‚…

‚‚

Page 15: CH06_13T

‚‚‚

…‚

‚…

‚‚

F IGURE 6-1: EXÉCUTION DE FONCTION.EXE

6.3 Passage des paramètres par les valeurs

6.3.1 Variables locales

Éventuellement, on verra qu'il y a plusieurs façons de définir des variables. Jusqu'à maintenant, elles ont toujours été définies à l'intérieur des parenthèses des blocs de fonctions. Examinons les caractéristiques de ces variables.

Leur classe de stockage implicite est appelée automatique. Une classe de stockage détermine comment une variable est sauvée en mémoire. Une variable automatique est sauvée sur la pile du système. Sa durée de vie est limitée à la durée d'exécution de la fonction: elle est créée en début d'exécution et détruite à la fin.

Le mot-clé précisant cette classe de stockage est auto. Pour définir une variable automatique, on devrait utiliser une instruction comme auto int choix;. Toutefois auto représente la classe de stockage implicite; il n'est donc pas nécessaire de la préciser: int choix; est équivalent à auto int choix;.

La visibilité d'une variable automatique est dite locale: elle est accessible seulement par les instructions de la fonction où elle est définie. L'usager peut donc utiliser les identificateurs de son choix, sans se soucier s'il les a déjà utilisés ailleurs dans d'autres fonctions. Pour cette même raison, lorsqu'on met sur pied une équipe de programmeurs pour travailler à un gros projet, on peut leur demander de rédiger chacun leur groupe de fonctions, sans risque de confusion.

Pour vérifier ces notions, exécutez le programme FONCTION.C en respectant la procédure suivante.

1. Déplacez le curseur sur l'identificateur de la variable ascii_min définie dans la fonction main(). Utilisez la commande Debug/Watches/Add watch... pour ouvrir la fenêtre de surveillance sur la valeur de cette variable.

2. Ajoutez également les variables suivantes dans la fenêtre de surveillance: ascii_max définie dans main(), code_min et code_max définies dans affi_ascii(). Vous pouvez utiliser Ctrl-F7 pour y arriver plus rapidement.

3. Placez la curseur sur l'instruction affi_ascii(ascii_min, ascii_max); qui fait partie de case CODE_ASCII de l'instruction switch ... case de la fonction main().

Page 16: CH06_13T

4. Démarrez l'exécution du programme avec la commande Run/Go to cursor. Choisissez l'option Affichage des codes ASCII. Entrez le code initial 45 et le code final 67.

5. Jetez maintenant un coup d'oeil à la fenêtre de surveillance:

- ascii_min: 45- ascii_max: 67- code_min: Undefined symbol 'code_min'- code_max: Undefined symbol 'code_max'

Puisque le programme a été interrompu à l'intérieur de la fonction main(), seules les variables locales ascii_min et ascii_max sont visibles. code_min et code_max n'existent même pas encore.

6. Reprenez l'exécution du programme à l'aide de F7 (Run/Trace into). La fenêtre de surveillance change complètement d'aspect, car l'exécution de la fonction affi_ascii() vient de débuter:

- ascii_min: Undefined symbol 'code_min'- ascii_max: Undefined symbol 'code_min'- code_min: 47- code_max: 65

C'est maintenant au tour de code_min et code_max d'être visibles et définies. La fonction affi_ascii() n'est même pas au courant de l'existence de ascii_min et ascii_max.

7. Vous pouvez terminer l'exécution du programme avec Ctrl-F2 (Run/Program reset).

Cet exercice confirme qu'une variable locale de classe de stockage automatique n'est visible que de la fonction où elle est définie.

Mais une question se pose. Comment les fonctions peuvent-elles manipuler et s'échanger des données si leurs variables locales passent leur temps à apparaître et à disparaître continuellement? La réponse sera apportée par la technique du passage des paramètres par les valeurs.

Pour faciliter la compréhension et l'apprentissage de cette technique, nous introduirons d'abord le modèle des récipients pour représenter plus concrètement les variables.

6.3.2 Variables et modèle des récipients

Une variable peut être représentée par un récipient dont la dimension est proportionnelle à son type de donnée. Le récipient lui-même correspond à la région mémoire occupée par la variable et son emplacement sur la tablette de rangement est l'adresse de cette variable. L'identificateur et la valeur de la variable sont associés respectivement à l'étiquette collée sur le récipient et à son contenu.

La Figure 6-2 présente ce modèle pour des variables de type char, int et double.

‚‚‚

F IGURE 6-2: LE MODÈLE DES RÉCIPIENTS

Page 17: CH06_13T

6.3.3 Mécanisme de passage des paramètres par les valeurs

L'exécution d'une fonction s'effectue en un maximum de trois étapes:

19. le passage des paramètres à cette fonction par le programme d'appel;20. l'exécution des instructions de cette fonction;21. le transfert de la valeur retournée par cette fonction à une variable du programme

d'appel.

Comparons les prototypes de quelques fonctions du programme FONCTION.C:

void menu (void);int get_choix (void);void affi_ascii (int code_min, int code_max);double mise_a_echelle (double courant, double portee_min, double portee_max);

En examinant, pour chacune d'elles, les paramètres et le type de donnée de la valeur retournée, on peut déduire quelles sont les étapes nécessaires à leur exécution respective:

menu() ------------> étape 2 seulement,get_choix() -------> étapes 2 et 3,affi_ascii() ------> étapes 1 et 2,mise_a_echelle() --> étapes 1, 2 et 3.

Comme l'exécution de mise_a_echelle() implique les trois étapes, ce sera notre fonction cobaye pour illustrer le mécanisme de passage des paramètres par les valeurs.

Reportez-vous à la Figure 6-2. Les fonctions peuvent être considérées comme des compartiments séparés par un mur qui les rend invisibles les unes par rapport aux autres.

Dans main(), il y a deux récipients de type double, courant et temperature. Ce sont assurément des variables locales puisqu'elles sont définies à l'intérieur des accolades de main(). Cette fonction contient également deux constantes réelles représentant respectivement les portées minimale et maximale de l'étendue de mesure du transmetteur 4-20 mA: -25.0 C et 75.0 C.

Du côté de la fonction mise_a_echelle() se trouvent quatre récipients de type double: les trois paramètres de la fonction, courant, portee_min et portee_max, ainsi que la variable grandeur.

Comme toutes les variables du programme sont locales, elles sont invisibles d'une fonction à l'autre. Même si l'on retrouve un récipient courant dans chacune des deux fonctions, il n'y a pas de risque de confusion: ce sont deux régions mémoire bien distinctes.

L'appel de la fonction mise_a_echelle() s'effectue dans main() par l'instruction suivante:

temperature = mise_a_echelle(courant, -25.0, 75.0);

L'exécution de cette fonction débute par l'étape du passage des paramètres par le programme d'appel, ici la fonction main(). Remarquez qu'entre les parenthèses de mise_a_echelle() se trouve dans l'ordre courant, -25.0 et 75.0. Cette variable et ces deux constantes appartiennent à la fonction main(). Leurs positions respectives dans les parenthèses correspondent aux positions de courant, portee_min et portee_max, les trois paramètres de mise_a_echelle().

Selon notre modèle, le passage des paramètres se déroule comme ceci.

22. On prend le récipient courant de main(), dont le contenu est 16.0, et on l'approche de la fenêtre dédiée au passage des paramètres.

23. La fonction mise_a_echelle() présente automatiquement le récipient de son premier paramètre. Dans ce cas, l'étiquette sur chaque récipient est la même, mais personne

Page 18: CH06_13T

de chaque côté n'est au courant. Ce qui est primordial toutefois, c'est que les deux récipients soient de la même dimension; sinon, il y a risque de débordement!

24. On balance ensuite la constante réelle -25.0 par la même fenêtre. La fonction mise_a_echelle() l’attrape automatiquement avec le récipient de son deuxième paramètre, portee_min.

25. Enfin, c'est au tour de 75.0 de s'envoler à travers la fenêtre. Comme c'est la troisième valeur à franchir le mur, il tombe automatiquement dans le récipient du troisième paramètre, portee_max.

‚‚Š…

‚Š

‚‚ŠŠ

Š‚

F IGURE 6-3: LE MÉCANISME DE PASSAGE DES PARAMÈTRES PAR LES VALEURS

L'étape 1 est maintenant franchie. Passons à l'étape 2, l'exécution des instructions de cette fonction. L'instruction la plus importante de mise_a_echelle() effectue le calcul de la grandeur physique mesurée par le capteur du transmetteur en fonction de la valeur de son courant:

grandeur = ((portee_max - portee_min)*(courant - 4.0)/16.0) + portee_min;

Pour un courant de 16.0 mA, le contenu du récipient grandeur est 50.0 C.

La troisième étape de l'appel de mise_a_echelle() consiste à retourner cette valeur à la fonction d'appel, main(). L'instruction requise est:

return(grandeur);

Page 19: CH06_13T

Cette instruction approche le récipient grandeur près de la fenêtre dédiée au transfert de la valeur retournée. De son côté, main() présente le récipient temperature pour récupérer le contenu de grandeur; elle y parvient à l'aide de l'opérateur = dans l'instruction d'appel de mise_a_echelle():

temperature = mise_a_echelle(courant, -25.0, 75.0);

Précisons une dernière fois qu'aucune des deux fonctions ne connaît l'étiquette du récipient de l'autre. L'opération de transfert de donnée s'effectuera correctement à la seule condition que les dimensions des deux récipients soient identiques.

Un modèle est rarement parfait. Malheureusement, le nôtre n'échappe pas à cette règle. L'utilisation de récipients porte à croire que le contenu se déplacent lors des transferts de donnée. Hors, c'est complètement faux. En réalité, c'est une copie de la valeur de la variable qui est transférée de la fonction d'appel à la fonction appelée et non la valeur elle-même. Ainsi, la variable courant définie dans main() conserve toujours sa valeur 16.0. Même si courant est définie sur la pile parce qu'elle est locale, elle cessera d'exister seulement à la fin de l'exécution de la fonction main(), c'est-à-dire à la toute fin du programme lui-même.

Pour confirmer cette notion fondamentale, exécutez une dernière fois en pas-à-pas FONCTION.C, après avoir ouvert la fenêtre de surveillance (Watch) sur la variable courant de main(). Dès que l'exécution de mise_a_echelle() sera terminée, la reprise de main() s'effectuera avec

courant: 16.0

Les notions présentées dans ce chapitre sont d’une importance capitale pour rédiger des programmes structurés. Même si ce sujet n’est pas simple à maîtriser, apportez-y toute votre attention et n’hésitez pas à relire ces pages aussi souvent que nécessaire. Vous y gagnerez à la longue.