Previous Next Up Index Contents

10.1.2. Exemples de modularisation en C

Les deux programmes présentés ci-dessous vous donnent un petit aperçu sur les propriétés principales des fonctions en C. Les détails seront discutés plus loin dans ce chapitre. (Vous pouvez trouver les solutions des mêmes problèmes en langage algorithmique et en Pascal dans votre manuel d'algorithmique de 13e [Chapitre I.2. procédures et fonctions]).

a) Exemple 1: Afficher un rectangle d'étoiles

Commençons par un petit programme que nous vous proposons d'examiner vous-mêmes sans autres explications:

Le programme suivant permet d'afficher à l'écran un rectangle de longueur L et de hauteur H, formé d'astérisques '*' :

Implémentation en C

#include <stdio.h>

main()
{
 /* Prototypes des fonctions appelées par main */
 void RECTANGLE(int L, int H);
 /* Déclaration des variables locales de main */
 int L, H;
 /* Traitements */
 printf("Entrer la longueur (>= 1): ");
 scanf("%d", &L);
 printf("Entrer la hauteur  (>= 1): ");
 scanf("%d", &H);
 /* Afficher un rectangle d'étoiles */
 RECTANGLE(L,H);
 return 0;
}

Pour que la fonction soit exécutable par la machine, il faut encore spécifier la fonction RECTANGLE:

void RECTANGLE(int L, int H)
{
 /* Prototypes des fonctions appelées */
 void LIGNE(int L);
 /* Déclaration des variables locales */
 int I;
 /* Traitements */
 /* Afficher H lignes avec L étoiles  */
 for (I=0; I<H; I++)
      LIGNE(L);
}

Pour que la fonction RECTANGLE soit exécutable par la machine, il faut spécifier la fonction LIGNE:

void LIGNE(int L)
{
 /* Affiche à l'écran une ligne avec L étoiles */
 /* Déclaration des variables locales */
 int I;
 /* Traitements */
 for (I=0; I<L; I++)
      printf("*");
 printf("\n");
}

Schématiquement, nous pouvons représenter la hiérarchie des fonctions du programme comme suit:

b) Exemple 2: Tableau de valeurs d'une fonction

Soit F la fonction numérique définie par F(X) = X3-2X+1. On désire construire un tableau de valeurs de cette fonction. Le nombre N de valeurs ainsi que les valeurs de X sont entrés au clavier par l'utilisateur.

Exemple

Entrez un entier entre 1 et 100 : 9
Entrez 9 nombres réels :
 -4 -3 -2 -1 0 1 2 3 4

X    -4.0  -3.0  -2.0  -1.0  0.0  1.0  2.0  3.0  4.0
F(X) -55.0 -20.0 -3.0  2.0   1.0  0.0  5.0  22.0 57.0 

En modularisant ce problème, nous obtenons un programme principal très court et bien 'lisible'. La fonction main joue le rôle du programme principal:

main()
{
 float X[100];   /* valeurs de X */
 float V[100];   /* valeurs de F(X) */
 int N;
 ACQUERIR(&N);   /* 1 <= N <= 100 */
 LIRE_VECTEUR(X, N);
 CALCULER_VALEURS(X, V, N);
 AFFICHER_TABLE(X, V, N);
 return 0;
}

Pour que la machine puisse exécuter ce programme, il faut encore implémenter les modules ACQUERIR, LIRE_VECTEUR, CALCULER_VALEURS et AFFICHER_TABLE. Ces spécifications se font en C sous forme de fonctions qui remplacent les fonctions et les procédures que nous connaissons en langage algorithmique et en Pascal. Une 'procédure' est réalisée en C par une fonction qui fournit le résultat void (vide). Les fonctions sont ajoutées dans le texte du programme au-dessus ou en-dessous de la fonction main.

Si dans le texte du programme une fonction est défine après la fonction appelante, il faut la déclarer ou bien localement à l'intérieur de la fonction appelante ou bien globalement au début du programme. La déclaration d'une fonction se fait à l'aide d'un 'prototype' de la fonction qui correspond en général à la première ligne (la ligne déclarative) de la fonction.

Par convention, nous allons définir la fonction main en premier lieu. Ainsi nous obtenons le programme suivant:

Implémentation en C

#include <stdio.h>

main()
{
 /* Prototypes des fonctions appelées par main */
 void ACQUERIR(int *N);
 void LIRE_VECTEUR(float T[], int N);
 void CALCULER_VALEURS(float X[], float V[], int N);
 void AFFICHER_TABLE(float X[], float V[], int N);
 /* Déclaration des variables locales de main */
 float X[100];   /* valeurs de X    */
 float V[100];   /* valeurs de F(X) */
 int N;
 /* Traitements */
 ACQUERIR(&N);   /* 1 <= N <= 100   */
 LIRE_VECTEUR(X, N);
 CALCULER_VALEURS(X, V, N);
 AFFICHER_TABLE(X, V, N);
 return 0;
}

void ACQUERIR(int *N)
{
 do
   {
    printf("Entrez un entier entre 1 et 100 : ");
    scanf("%d", N);
   }
 while (*N<1 || *N>100);
}

void LIRE_VECTEUR(float T[], int N)
{
 /* Remplit un tableau T d'ordre N avec des nombres 
    réels entrés au clavier */
 /* Déclaration des variables locales */
 int I;
 /* Remplir le tableau */
 printf("Entrez %d nombres réels :\n", N);
 for (I=0; I<N; I++)
     scanf("%f", &T[I]);
}

void CALCULER_VALEURS(float X[], float V[], int N)
{
 /* Remplit le tableau V avec les valeurs de */
 /* F(X[I]) pour les N premières composantes */
 /* X[I] du tableau X */
 /* Prototype de la fonction F */
 float F(float X);
 /* Déclaration des variables locales */
 int I;
 /* Calculer les N valeurs */
 for (I=0; I<N; I++)
     V[I] = F(X[I]);
}

float F(float X)
{
 /* Retourne la valeur numérique du polynôme défini 
    par F(X) = X^3-2X+1 */
 return (X*X*X - 2*X + 1);
}

void AFFICHER_TABLE(float X[], float V[], int N)
{
 /* Affiche une table de N valeurs : 
    X contient les valeurs données et 
    V contient les valeurs calculées. */
 /* Déclaration des variables locales */
 int I;
 /* Afficher le tableau */
 printf("\n X   : ");
 for (I=0; I<N; I++)
     printf("%.1f", X[I]);
 printf("\n F(X): ");
 for (I=0; I<N; I++)
     printf("%.1f", V[I]);
 printf("\n");
}

Le programme est composé de six fonctions dont quatre ne fournissent pas de résultat. La fonction F retourne la valeur de F(X) comme résultat. Le résultat de F est donc du type float; nous disons alors que 'F est du type float' ou 'F a le type float'.

Les fonctions fournissent leurs résultats à l'aide de la commande return. La valeur rendue à l'aide de return doit correspondre au type de la fonction, sinon elle est automatiquement convertie dans ce type.

A la fin de l'exécution du programme, la fonction main fournit par défaut une valeur comme code d'erreur à l'environnement. Le retour de la valeur zéro veut dire que le programme s'est terminé normalement et sans erreurs fatales.

Le passage des paramètres en C se fait toujours par la valeur. Pour pouvoir modifier une variable déclarée dans la procédure appelante, la fonction appelée a besoin de l'adresse de cette variable. Le paramètre correspondant doit donc être un pointeur et lors d'un appel de la fonction, il faut veiller à envoyer l'adresse et non la valeur de la variable.

Dans notre exemple, la fonction ACQUERIR a besoin de l'adresse de la variable N pour pouvoir affecter une nouvelle valeur à N. Le paramètre N doit donc être défini comme pointeur (sur int). Lors de l'appel, il faut transmettre l'adresse de N par &N. A l'intérieur de la fonction il faut utiliser l'opérateur 'contenu de' pour accéder à la valeur de N. Les autres fonctions ne changent pas le contenu de N et ont seulement besoin de sa valeur. Dans les en-têtes de ces fonctions, N est simplement déclaré comme int.

Lorsque nous passons un tableau comme paramètre à une fonction, il ne faut pas utiliser l'opérateur adresse & lors de l'appel, parce que le nom du tableau représente déjà l'adresse du tableau.

Dans notre exemple, la fonction LIRE_VECTEUR modifie le contenu de la variable X, mais lors de l'appel, il suffit d'envoyer le nom du tableau comme paramètre.

Schématiquement, nous pouvons représenter la hiérarchie des fonctions comme suit:


Previous Next Up Index Contents

Feedback - Copyright © 1993,1996,1997 F.Faber