• Aucun résultat trouvé

qui contiennent la valeur de retour de la fonction). Pour cela, l’épilogue restaure les registres qu’il avait sauvegardés (correspondants aux registres demandés par les définitions de variables de type register), puis il restaure le contexte de pile en reprenant l’ancienne valeur dans la pile. Enfin, il replace le pointeur de sommet de pile à l’adresse qu’il avait avant le prologue. Finalement, il retourne à la fonction appelante en remettant dans le pointeur d’instruction la valeur qui avait été sauvegardée sur la pile lors de l’appel. L’exécution continue alors dans la fonction appelante.

Récupération du résultat et effacement des paramètres la fonction appelante dépile les paramètres qu’elle

avait empilé au début de l’appel et utilise la(es) valeur(s) de retour de la fonction pour calculer l’ex- pression courante dans laquelle la fonction a été appelée.

8.10

Exercices sur les fonctions

8.10.1

Exercice 1

Définir trois variablesi,j,kde type entier et de classe globale. Écrire une fonctionglobadd()qui fait l’addition deietjdansk.

Écrire la fonctionmain()qui réalise la saisie des variablesietj, fait appel à la fonction d’addition puis écrit le résultat contenu dansk.

8.10.2

Exercice 2

Même exercice que le précédent mais en utilisant le passage de paramètres et le retour de fonction. Les trois variables i, j, k de type entier sont déclarées localement dans la fonction main(). La fonction d’addition est une fonction retournant un entier. Elle accepte deux paramètres entiers (p1etp2) et retourne la somme de ces deux paramètres.

La fonctionmain()saisit les deux variables localesietjet appelle la fonction d’addition en ré- cupérant le résultat de cette fonction dans la variable localek. Elle écrit le contenu dekaprès l’appel de fonction.

8.10.3

Exercice 3

Même exercice que le précédent mais en utilisant le passage de paramètres et un pointeur pour modifier une variable dans la fonction appelante.

Les trois variables i, j, k de type entier sont declarées localement dans la fonction main(). La fonction d’additionptadd()est une fonction sans retour. Elle accepte trois paramètres entiers (p1etp2) et un paramètre de type pointeur vers un entier qui sert pour affecter la variable dont la fonction appelante passe l’adresse avec la somme de ces deux premiers paramètres.

La fonctionmain()saisit les deux variables localesietjet appelle la fonction d’addition en passant l’adresse de la variable localek. Elle écrit le contenu dekaprès l’appel de fonction.

CHAPITRE 8. FONCTIONS 107

PROGRAMME8.2 ARGUMENTS DEM A I N()CARACTÈRES UN-À-UN

1 #include <stdio.h> 2 int

3 main (int argc, char *argv[],char **envp) 4 {

5 int i;

6 register char *ptc;

7 printf("Nous avons %d arguments : \n",argc); 8 for(i=0;i<argc;i++){ 9 printf("argument %d == ",i); 10 for(ptc=argv[i];*ptc;ptc++) 11 printf("%c",*ptc); 12 printf("\n"); 13 }

14 printf("Nous affichons les 5 premières variables d’environnement \n"); 15 printf("mais nous les comptons toutes. \n");

16 for(i=0; envp[i] ;i++){ 17 if(i<5){ 18 printf("Environnement %d == ",i); 19 for(ptc=envp[i];*ptc;ptc++) 20 printf("%c",*ptc); 21 printf("\n"); 22 } 23 }

24 printf("Il y a %d variables d’environnement : \n",i); 25 return 0;

26 }

DONNÉES ÉCRITES SUR LE FICHIER STANDARD DE SORTIE

Nous avons 3 arguments : argument 0 == c08e02 argument 1 == c08e02 argument 2 == c08e02.c

Nous affichons les 5 premières variables d’environnement mais nous les comptons toutes.

Environnement 0 == BIBINPUTS=.:/home/chris/Bib:/usr/share/texmf/bibtex/bib Environnement 1 == NNTPSERVER=news.int-evry.fr Environnement 2 == MANPATH=/usr/X11R6/man:/usr/local/man:/usr/share/man Environnement 3 == COURSC=/home/chris/Enseignement/COURS/C Environnement 4 == SSH_AGENT_PID=495 Il y a 65 variables d’environnement :

108 8.10. EXERCICES SUR LES FONCTIONS

PROGRAMME8.3 SUGGESTION DE CORRIGÉ CHAPITRE8EXERCICE1

1 #include <stdio.h> 2

3 /* declaration des variables globales */ 4 int i; 5 int j; 6 int k; 7 8 9 void

10 globadd(){ /* fonction addition */ 11 k = i + j;

12 } 13 14 int

15 main(int argc, char *argv[], char **envp){ 16 /* saisie des valeurs */

17 printf("entrer 1 entier\n"); 18 scanf("%d", &i);

19 printf("entrer un autre entier\n"); 20 scanf("%d", &j);

21 /* appel de add et impression du resultat */ 22 globadd(); 23 printf(" i + j = %d\n", k); 24 return 0; 25 } DONNÉES EN ENTRÉE 24 67

DONNÉES ÉCRITES SUR LE FICHIER STANDARD DE SORTIE

entrer 1 entier

entrer un autre entier i + j = 91

CHAPITRE 8. FONCTIONS 109

PROGRAMME8.4 SUGGESTION DE CORRIGÉ CHAPITRE8EXERCICE2

1 #include <stdio.h> 2 3 /* fonction addition */ 4 5 int 6 add(int p1, int p2){ 7 return (p1 + p2); 8 } 9 10 int

11 main(int argc, char *argv[], char **envp){ 12 /* declarations des variables locales */ 13 int i, j, k;

14 /* saisie des valeurs */ 15 printf("entrer 1 entier :"); 16 scanf("%d", &i);

17 printf("entrer un autre entier :"); 18 scanf("%d", &j); 19 /* appel de add */ 20 k = add (i, j); 21 printf(" i + j = %d\n", k); 22 return 0; 23 } DONNÉES EN ENTRÉE 24 67

DONNÉES ÉCRITES SUR LE FICHIER STANDARD DE SORTIE

110 8.10. EXERCICES SUR LES FONCTIONS

PROGRAMME8.5 SUGGESTION DE CORRIGÉ CHAPITRE8EXERCICE3

1 #include <stdio.h> 2

3 /* fonction addition */ 4

5 void

6 ptadd(int p1, int p2, int *pti) 7 { 8 *pti = p1 + p2; 9 return; 10 } 11 12 int

13 main(int argc, char *argv[], char **envp) 14 {

15 /* declarations des variables locales */ 16 int i, j, k;

17 /* saisie des valeurs */ 18 printf("entrer 1 entier :"); 19 scanf("%d", &i);

20 printf("entrer un autre entier :"); 21 scanf("%d", &j); 22 /* appel de add */ 23 ptadd(i, j, &k); 24 printf(" i + j = %d\n", k); 25 return 0; 26 } DONNÉES EN ENTRÉE 24 67

DONNÉES ÉCRITES SUR LE FICHIER STANDARD DE SORTIE

Chapitre 9

Compilations séparées

La compilation séparée permet de fragmenter un grand programme en des parties qui peuvent être compilées indépendamment les unes des autres. Dans ce chapitre nous allons voir comment cette possibilité est exploitable en langage C.

À partir de maintenant, nous appellerons :

– déclaration : une association de type avec un nom de variable ou de fonction (dans ce cas la décla- ration contient aussi le type des arguments de la fonction),

– définition : une déclaration et si c’est une variable, une demande d’allocation d’espace pour cette variable, si c’est une fonction la définition du corps de fonction contenant les instructions associées à cette fonction.

9.1

Programme

Comme nous l’avons déjà dit dans le chapitre 1, un programme en langage C est un ensemble de

fichiers destinés à être compilés séparément.

La structure d’un programme, écrit en langage C, est résumée dans la figure 9.1.

9.2

Fichier source

Comme le montre schématiquement la figure 9.2, chaque fichier source contient les éléments suivants dans un ordre quelconque :

– des déclarations de variables et de fonctions externes,

– des définitions de types synonymes ou de modèles de structures (voir chapitre 14), – des définitions de variables1,

– des définitions de fonctions,

– des directives de pré-compilation et des commentaires.

Les directives de pré-compilation et les commentaires sont traités par le pré-processeur. Le compilateur ne voit que les quatre premiers types d’objets.

Les fichiers inclus par le pré-processeur ne doivent contenir que des déclarations externes ou des défi- nitions de types et de modèles de structures.

1Ces définitions de variables globales sont en fait transformées par l’éditeur de liens en des demandes de réservation mémoire à

112 9.2. FICHIER SOURCE

FIG. 9.1 – Du source à l’exécutable

CHAPITRE 9. COMPILATIONS SÉPARÉES 113

Règle fondamentale :

Toute variable ou fonction doit être déclarée avant d’être utilisée.

TAB. 9.1 – Règle fondamentale de visibilité

FIG. 9.3 – Exemple de visibilité

9.3

Visibilité

La compilation séparée des différentes parties d’un programme implique le respect de certaines règles que nous appellerons Règles de visibilité. Ces règles s’appliquent aux noms (de variables et de fonctions). Comme il a été écrit dans le chapitre 1, le compilateur ne lit le fichier source qu’une seule fois, et du début jusqu’à la fin. Lorsqu’il rencontre l’utilisation d’une variable ou d’une fonction, il doit connaître son type et sa classe d’adresse.

Par convention, dans le cas où une fonction n’est pas connue, le compilateur considère qu’elle retourne une valeur de type int et il essaye d’inférer le type des paramètres à partir de l’appel (les appels postérieurs devront se conformer à ce premier appel).

La fonction peut être définie plus loin dans le fichier. Si l’interface est conforme à ce que le compilateur a deviné, le compilateur n’émet pas de message d’erreur et utilise l’adresse de la fonction pour les appels suivants, mais il ne peut pas modifier ce qu’il a déjà généré. La liaison du premier appel avec la fonction est laissée à l’éditeur de liens.

Nous allons nous servir de l’exemple de la figure 9.3 pour continuer à expliquer la visibilité des noms. Pour cet exemple, les règles de visibilité de noms que nous venons d’énoncer nous permettent de dire :

1. la fonction f4 peut appeler les fonctions f4, f3, f2, et f1 ; 2. la fonction f3 peut appeler les fonctions f3, f2, et f1 ; 3. la fonction f2 peut appeler la fonction f2, et f1 ; 4. la fonction f1 ne peut que s’appeler elle-même ;

Documents relatifs