Académique Documents
Professionnel Documents
Culture Documents
Département d’Informatique
Séquence 02 :
La modularité d’un
programme en
Langage C
Janvier 2016
Guy MBATCHOU
Préliminaires
Préliminaires
1.1 - Durée :
1.3 - Ressources
1.3.1 - Problématique
Durant le cours d’Algorithmique et Programmation 1, les algorithmes et programmes
ont été toujours écrits en un seul bloc. Dans certains algorithmes et programmes, certaines
parties se répètent ou sont très proches à l’exception généralement des données manipulées.
Ces parties peuvent être regroupées en un bloc clairement identifié pour faciliter d’une part la
visibilité de l’algorithme et d’autre part pour faciliter sa maintenance (correction des bugs ou
amélioration de la performance). L’idée est de n’écrire ce bloc qu’une seule fois dans
l’algorithme ou programme et chaque fois qu’on en a besoin, on l’appelle seulement.
𝑵!
En exemple, pour calculer la combinaison de P dans N (∁ 𝑷𝑵 = ) il faut écrire un
𝑷!(𝑵−𝑷)!
ensemble d’instructions pour calculer le factoriel d’un nombre. Ces instructions devront être
reprises trois fois pour calculer le factoriel de N (N !), le factoriel de P (P !) et le factoriel de N-
P ((N-P) !). Lors des différentes reprises des instructions, seules les valeurs manipulées
changent à savoir N lorsqu’on calcule le factoriel de N, P lorsqu’on calcule le factoriel de P et
(N-P) lorsqu’on calcule le factoriel de (N-P).
#include<stdio.h>
int main(){
unsigned short int N, P, i;
unsigned long int FacP, FacN, FacNmoinsP, Comb;
//Calcul du factoriel de N
FacN = 1;
for(i=1; i<=N; i++)
FacN *= i;
//Calcul du factoriel de P
FacP = 1;
for(i=1; i<=P; i++)
FacP *= i;
return 0;
}
Or, il serait intéressant de regrouper les instructions de du calcul du factoriel et d’en
faire appel chaque fois lorsqu’on en a besoin. Ce regroupement permettra de réduire le nombre
d’instructions et de rendre le programme facilement lisible et modifiable (maintenance).
Un ensemble d’instructions est appelé bloc d’instructions et non un module. Pour être
appelé module, l’ensemble d’instructions doit avoir un nom et si nécessaire une liste finie de
variables (paramètres) dont on décidera de la valeur lors de l’appelle du module.
1.3.2 - Définition
Un module est une suite finie d’instructions ordonnées à laquelle on attribue un nom et
une liste de paramètres qui peut éventuellement être vide.
Un algorithme modulaire est un algorithme dans lequel certaines instructions sont
regroupées en module.
La réutilisabilité : un module peut être réutilisé dans un autre algorithme car il permet
de résoudre un sous-problème précis. Donc un module peut être utilisé dans tous les
algorithmes où ce sous-problème est identifié.
une partie de déclaration des variables qui contient la liste des variables et leur type.
Ces variables sont dites locales au module. En général, il n’est pas conseiller de déclarer
de nouveau type dans le module sauf si dans l’algorithme, ce type ne sera utilisé que par
ce module. Cette partie peut être vide et au cas où elle ne l’est pas, elle est introduite par
le mot clé « type » pour les nouveaux types et le mot clé « variable » pour les variables
locales.
Module permettant de calculer le salaire d’un employé est une fonction car après le
calcul, il faut retourner la valeur du salaire pour une utilisation ultérieure. Par contre si
on demande un module de calcul et d’affiche du salaire d’un employé, il s’agira d’une
procédure car le résultat (salaire) est affiché et non retourné pour une quelconque
utilisation.
Module pour le tri des données est une procédure car après le tri, on ne revoit pas les
données triées et le commanditaire du tri verra que le travail est bel et bien fait.
Module de calcul de la moyenne ou somme des valeurs d’un ensemble est une fonction
car il faudra retourner la valeur de la moyenne ou de la somme.
Pour renvoyer un résultat, on doit affecter le résultat au nom de la fonction. Cela veut
dire que dans la liste des variables locales, il faut absolument une variable qui doit contenir le
résultat ou plusieurs variables dont la combinaison par des opérateurs arithmétiques ou logiques
donnera le résultat.
Function CalculAge : integer ; Function CalculAge : integer ;
Var Var
annee_en_cours : integer ; annee_en_cours : integer ;
annee_naissance : integer ; annee_naissance : integer ;
Begin age : integer ;
writeln(‘Programme de calcul d’âge’) ; Begin
write(‘Donner l’année courante :’) ; writeln(‘Programme de calcul d’âge’) ;
read(annee_en_cours) ; write(‘Donner l’année courante :’) ;
write(‘Donner votre année de naissance’) ; read(annee_en_cours) ;
read(annee_naissance) ; write(‘Donner votre année de naissance’) ;
read(annee_naissance) ;
CalculAge := annee_en_cours – annee_naisance ; age := annee_en_cours – annee_naisance ;
End ;
CalculAge := age ;
End ;
Fonction sans variable contenant le résultat. Fonction avec variable contenant le
Ici, le résultat est renvoyé sans besoin d’être résultat. Ici, le résultat est d’abord stocké
stocké dans une variable résultat dans la variable age avant d’être renvoyé
Program Salutation_et_Calcul_Age;
CalculAge := age ;
End;
1.4 - Activités
Exercice 201 : Poids : 100% - Seuil de validation : 80% - Titre : QCM
2.1 - Durée :
2.3 - Ressources
Jusqu’à présent, tous les modules écrits ne sont pas paramétrés ! Cela veut dire que ces
modules ont tout le nécessaire pour réaliser leur tâche sans intervention de celui qui exécute le
module. Or si nous prenons la fonction CalculAge écrite précédemment, celui qui vous
demande de calculer son âge ne sera pas présent lorsque vous le ferez or dans la fonction
CalculAge, il y a une instruction qui lui demande de saisir son année de naissance. Que faire ?
Avant de commencer l’exécution de la fonction ou de la procédure, le programmeur doit
s’assurer qu’il dispose tout le nécessaire pour le faire. La question qu’il faut se poser pour
trouver les paramètres d’une fonction ou procédure est la suivante : De quoi ai-je besoin de
celui qui passe la commande de la fonction ou procédure pour la réaliser ?
Dans notre exemple, si vous voulez que je calcule votre âge, j’ai absolument besoin
que vous me donnez votre date de naissance (pour simplifier dans l’exemple, nous avons
demandé seulement l’année de naissance). Cette date de naissance est appelée paramètre pour
la fonction CalculAge car la valeur de retour de la fonction dépend de la valeur du paramètre.
Dans le cas d’une procédure le résultat produit dépendra de la valeur du paramètre.
CalculAge := age ;
End;
{Déclaration des variables du programme principal}
VAR
MonNom : string;
MonAge : integer;
MonAnneeNaissance : integer;
BEGIN
write('Veuillez saisir votre nom : ');
read(MonNom);
Salutation(MonNom); {appel de la procédure Salutation avec le paramètre MonNom}
write('Donner votre année de naissance') ;
read(MonAnneeNaissance) ;
MonAge := CalculAge(MonAnneeNaissance); {appel de la fonction CalculAge avec le paramètre
MonAnneeNaissance et retour du résultat dans la variable MonAge}
write('Votre age est de : ', MonAge);
END.
Le rôle des déclarations est donc de signaler l'existence des fonctions aux compilateurs
afin de les utiliser, tout en reportant leur définition plus loin ou dans un autre fichier.
La syntaxe de la déclaration d'une fonction est la suivante :
typeValeurDeRetour nomFonction(typeParametres) ;
typeParamètres est la liste des types des paramètres que la fonction admet séparés par
des virgules.
De façon naturelle, la déclaration d’une fonction est placée à l’intérieur des déclarations
de toute fonction l’utilisant. Dans ce cas, nous avons à faire une déclaration dont la portée est
locale et limitée à la fonction où elle est déclarée.
Il est également possible de la déclarer avec une portée globale à toutes les fonctions
tout comme les variables globales. Il suffit de la déclarer à l’extérieur de toute fonction et qu’elle
soit avant la première définition d’une fonction.
Toute fonction devrait être déclarée avant d'être appelée pour la première fois mais la
définition d'une fonction peut faire office de déclaration si elle est définie avant son appel.
Entête de la fonction Factoriel
Entête de la fonction PPMC
Entête de la fonction PGCD
Entête de la fonction Puissance (puissance entière)
Entête de la fonction calculant le nième terme de la suite de Fibonacci
Elle est déclarée dans un module (fonction ou procédure) : variable locale au module.
Elle est déclarée juste avant le début du programme principal : variable locale au
programme principal. En langage C, le programme principal n’existe pas puisque tout
est fonction !
Elle est un paramètre d’un module : un paramètre d’un module est une variable locale
à ce module.
Exemple en Pascal
Program Portée_des_variables;
VAR A : integer;
Procedure Procedure1;
Var
x : integer;
Begin
...
End;
VAR B : integer;
VAR C : integer;
VAR D : integer;
Procedure Procedure2;
Begin
...
End;
VAR E : integer;
2.4 - Activités
Exercice 202 : Poids : 10% - Seuil de validation : 80% - Titre : Module qui
affiche par ordre croissant 3 entiers.
Exercice 203 : Poids : 10% - Seuil de validation : 80% - Titre : Module qui
affiche la table de multiplication d’une valeur.
Exercice 204 : Poids : 20% - Seuil de validation : 80% - Titre : Calcul itératif
du factoriel
Exercice 205 : Poids : 20% - Seuil de validation : 70% - Titre : PGCD itératif
Ecrire une fonction itérative qui calcule le PGCD de deux entiers.
Exercice 206 : Poids : 20% - Seuil de validation : 70% - Titre : PPMC
itératif
Ecrire une fonction itérative qui calcule le PPMC de deux entiers.
3.1 - Durée :
3.3 - Ressources
Lorsqu’on utilise un paramètre dans un module, on dit qu’on passe le paramètre au
module. Ce passage peut se faire de façons différentes en fonction du comportement du module
sur le contenu de la variable.
int main(){
void valeurAbsolue(int);
int n = -8;
valeurAbsolue(n);
printf("Valeur après l'appel de la fonction : %d \n", n);
return 0;
}
int n = -8;
int main()
{
void valeurAbsolue(void);
return 0;
void valeurAbsolue(void){
printf("Valeur au début de la fonction : %d \n",n);
if (n<0) n = -n;
printf("Valeur à la fin de la fonction : %d \n",n);
}
Résultat d’exécution
Valeur avant l'appel de la fonction : -8
Valeur au début de la fonction : -8
Valeur à la fin de la fonction : 8
Valeur après l'appel de la fonction : 8
La variable « n » n’est plus déclarée dans la fonction main() mais comme une variable
globale à toutes les fonctions car elle est déclarée avant la définition des fonctions. De ce fait,
elle est accessible par toutes les fonctions nous permettant de réaliser notre solution de calcul
de la valeur absolue.
L’utilisation des variables globales doit être exceptionnelle car il peut se produire des
effets de bord ; une fonction peut malencontreusement modifier une valeur globale et cette
modification affectera la suite de vos résultats.
#include <stdio.h>
int main()
{
void valeurAbsolue(int *);
int n = -8;
return 0;
}
Exemple :
int main()
{
int a = 5;
printf("Valeur avant = %d", a);
doubler(&a); //Passage en paramètre l’adresse de a
printf("Valeur après = %d", a);
return 0;
}
Entête de la fonction de tri dans l’ordre croissant de 3 valeurs
flottantes
Entête de la fonction qui prend en entrée 4 valeurs entières et
retourne le minorant et le majorant sans modifier les valeurs initiales
3.4 - Activités
Exercice 207 : Poids : 30% - Seuil de validation : 80% - Titre : Tri des
nombres
Ecrire la fonction de tri dans l’ordre croissant de 3 valeurs flottantes
Exercice 208 : Poids : 70% - Seuil de validation : 70% - Titre : Minimun et
Maximum des nombres
Ecrire la fonction qui prend en entrée 4 valeurs entières et retourne le minorant et le
majorant sans modifier les valeurs initiales
4.1 - Durée :
4.3 - Ressources
Un algorithme est dit récursif si dans son exécution, il s’appelle lui-même (récursivité
directe) ou s’il appelle un autre algorithme qui l’appelle (récursivité indirecte).
Un algorithme récursif doit obéit aux règles suivantes :
il doit exister des critères pour lesquels la récursivité (les auto-appels) doit
s’arrêter
chaque fois que l’algorithme s’appelle (directement ou indirectement), il doit
être plus proche de ses critères d’arrêt.
Le langage C autorise la récursivité des appels de fonctions.
Une fonction est dite récursive si elle contient dans sa définition un appel à elle-même
(récursivité directe) ou si elle contient dans sa définition un appel de fonction qui appelle à son
tour (ou à plus tard) la fonction initiale (récursivité indirecte).
Chaque appel de fonction entraîne une allocation d’espaces pour les variables locales et
les paramètres. La récursivité entraine donc un empilement d’espaces mémoires et ce n’est que
lors de la première instruction de return que les appels et les emplacements mémoires
commenceront à être dépilés et libérés.
4.4 - Activités
Exercice 209 : Poids : 20% - Seuil de validation : 80% - Titre : Factoriel
récursif
Ecrire une fonction récursive qui calcule le factoriel d’un entier.
5.1 - Durée :
5.3 - Ressources
En général, les fonctions ont un nombre constant de paramètres (arguments) mais le
langage C offre aux fonctions la possibilité d’avoir des arguments variables en nombre. C’est
le cas des fonctions d'entrée / sortie du C dont la liste des arguments n'est pas fixée afin de
pouvoir réaliser un nombre arbitraire d'entrées / sorties.
Pour déterminer le nombre de paramètres, on peut soit :
mettre un premier argument en paramètre désignant le nombre de paramètres
définir une valeur particulière de paramètre qui détermine la fin de la liste des
paramètres
Pour récupérer les différents paramètres, le langage C dispose du type de données
va_list et des fonctions va_start(), va_arg() et va_end() contenues dans la bibliothèque
stdarg.h. Pour réaliser une fonction à nombre d’arguments variables, il faut qu’un certain
nombre d’arguments (au moins 1) soit fixe c’est-à-dire obligatoire.
Pour indiquer au compilateur qu'une fonction peut accepter une liste de paramètres à
nombre variable, il faut simplement utiliser des points de suspension dans la liste des
paramètres.
typedeRetour nomFonction(parametresFixes, …)
Dans la fonction, il faut déclarer une variable de type va_list permettant de stocker un
à un les différents paramètres de la fonction.
Pour positionner sur les arguments variables, il faut initialiser la variable de type va_list
par le dernier argument à nombre variable de la fonction. Cela se fait à travers la fonction
va_start(variable, paramètre) où « variable » est le nom de la variable de type va_list, et
paramètre est le dernier paramètre fixe de la fonction. Dès que « variable » est initialisée, vous
pouvez récupérer un à un les paramètres à l'aide de la fonction va_arg().
return Resultat;
}
double Produit(double Valeur_Fin, ...){// Valeur_Fin = la valeur de fin de la liste des valeurs en paramètre
va_list parametre;
unsigned int i;
double Resultat = 1;
float Valeur_Courante;
return Resultat;
}
int main(){
printf("\n\nLa somme des 5 premiers entiers non nuls = %d\n\n", Somme(5, 1,2,3,4,5));
printf("\n\nLe produit des réels = %0.2lf\n\n", Produit(-1.0, 1.0, 2.0, 3.0, 4.0, 5.0, -1.0));
return 0;
}
5.4 - Activités
Exercice 214 : Poids : 30% - Seuil de validation : 80% - Titre : QCM
Exercice 215 : Poids : 70% - Seuil de validation : 50% - Titre : Comptage du
nombre de voyelles dans une liste de caractères (pas une chaîne) en paramètre
6.1 - Durée :
6.3 - Ressources
6.4 - Activités
Exercice 216 : Poids : 20% - Seuil de validation : 80% - Titre : Traduction
en langage C d’un programme modulaire
Traduire le programme (Salutation_et_Calcul_Age) ci-dessus en langage C en
remplaçant la chaîne de caractères par un caractère.
Objectif 27 : Préprocesseur et
compilation séparée
7.1 - Durée :
7.3 - Ressources
Jusqu’à présent, nous avons toujours écrit tout notre programme (main + autres
fonctions) dans un seul fichier d’extension « .c ». Or il arrive que le programme contienne un
nombre grand de fonctions ; ce qui rend le fichier long et difficile à lire. Précédemment, nous
avons vu la modularité du programme en plusieurs fonctions. A présent, nous allons découper
notre programme en plusieurs fichiers ce qui facilitera son organisation, sa lecture, sa
compréhension, la réutilisation de certaines fonctions dans d’autres projets et sa maintenance.
Ces fichiers doivent être inclus dans les fichiers sources nécessaires avec l’instruction
suivante :
include "fichier_entete.h"
L’inclusion du fichier d’entête se fait avec les guillemets (") contrairement aux fichiers
des bibliothèques qui se fait avec des crochets (< et >).
représentée par la constante du préprocesseur doit être modifiée, on la modifie juste dans la
directive du processeur sans se soucier du reste du programme Une utilité de cette constante du
préprocesseur se verra lors de la déclaration des tableaux puisque la taille d’un tableau ne peut
être ni une variable, ni une constante.
Il est possible de définir des constantes du préprocesseur contenant des calculs à base
des opérations de base telle que l’addition, la soustraction, la multiplication, la division et le
modulo. Dans ce cas, il faut mettre vos expressions entre parenthèses.
Exemple :
#define TAILLE_MOYENNE 175 //taille exprimée en centimètre
#define MASSE_MOYEN 80 //masse exprimée en kilogramme
#define IMC_MOYEN (MASSE_MOYEN / (TAILLE_MOYENNE * TAILLE_MOYENNE)) //IMC = Indice
de Masse Corporelle
Il est fortement recommandé que les constantes soit en lettres majuscules.
Il existe aussi des constantes de préprocesseur sans valeur :
#define Nom_Constante
Elle permet juste de savoir que la constante est tout simplement définie. Son intérêt sera
mis en exergue lorsque nous définirons plus loin les conditions dans les directives du
préprocesseur.
#ifndef ENTETE
#define ENTETE
#include <stdio.h>
#include "Entete.h"
int main()
{
double A, B, R;
7.4 - Activités
Exercice 218 : Poids : 30% - Seuil de validation : 80% Titre : QCM
Objectifs
Seuil 60% 70% 70% 60% 50% 80% 50%
Seuil N° O21 O22 O23 O24 O25 O26 O27
80% 201 100%
80% 202 10%
80% 203 10%
80% 204 20%
70% 205 20%
70% 206 20%
80% 207 50% 30%
70% 208 50% 70%
8% 209 10% 20%
Activités