Vous êtes sur la page 1sur 61

Programmation II

Programmation modulaire
Pr Rachid DEHBI
Département Mathématique et
informatique
Faculté des sciences Ain chock

Année Universitaire 2017/2018


Plan de la formation
• Génie logiciel
• Principe de la programmation modulaire
avec le langage C
• la programmation modulaire et les pointeurs
• la programmation modulaire et les structures
• L’allocation dynamique de la mémoire
• Gestion des fichiers

dehbirac@yahoo.fr
PRÉ-REQUIS
• Programmation 1.
• La règle de 3R.

dehbirac@yahoo.fr
• La crise du génie logiciel
• Critères de qualité logiciel
• Principes de génie logiciel
• Historique des langages
• Historique des méthodologies de programmation

dehbirac@yahoo.fr
(anglais software engineering) est une science
de génie industriel qui étudie les méthodes de travail et les
bonnes pratiques des ingénieurs en développement logiciels.

dehbirac@yahoo.fr
La crise du logiciel

dehbirac@yahoo.fr
Critères de qualité du logiciel
• Utilité : le logiciel doit correspondre aux besoins des
utilisateurs
• Utilisabilité : ergonomie et facilité d'apprentissage et
d'utilisation
• Fiabilité : correction et conformité, robustesse et sureté
• Efficacité : temps d'exécution faible et utilisation réduite
des ressources
• Interopérabilité : interactions possibles entre logiciels
• Portabilité : le logiciel doit pouvoir tourner sur le plus
possible de systèmes
• Maintenabilité : facilité de test, brièveté et lisibilité,
extensibilité
• Réutilisabilité : le code doit pouvoir être réutilisé au
maximum
dehbirac@yahoo.fr
Principes du génie logiciel
• Généralisation : regrouper du code commun à
plusieurs programmes dans un seul programme et
regrouper un ensemble de fonctionnalités semblables en
une fonctionnalité paramétrable.
• Abstraction : décrire des entités à différents niveaux
d'abstraction, ne donner que les éléments pertinents et
omettre ceux qui ne le sont pas.
• Modularité : décomposer les logiciels en composants
aussi petits et indépendants que possible.
• Documentation : documenter le code et produire des
notices permettant la réutilisation.
• Vérification : respecter des spécifications initiales,
permettre des tests unitaires.

dehbirac@yahoo.fr
Historique des langages

dehbirac@yahoo.fr
dehbirac@yahoo.fr
Classement Tiobe 2015

dehbirac@yahoo.fr
Historique sur les méthodes de
programmation

dehbirac@yahoo.fr
Synthèse

• Identifier les critères qui relèvent de chaque


principe en génie logiciel
Plan
• Principe de la programmation modulaire
• Implémentation à l’aide des fonctions:
▫ Déclaration
▫ Utilisation
▫ Portés des variables et les modes de
transmission
▫ La classe d’allocation des variables
▫ Principe de la compilation séparée et ses
conséquences

dehbirac@yahoo.fr
Principe de la programmation
modulaire
 Un long programme est difficile à appréhender
globalement.
 Il vaut donc mieux le scinder en petits programmes
un programme principal fait appel à ses sous-
programmes , qui peuvent eux-mêmes faire appel à
d’autres sous-programmes, du programme principal, et
ainsi de suite.
C’est le principe du raffinement successif.
 De plus certains sous-programmes peuvent servir dans
d’autres programmes,
C’est le principe de la modularité.
Ces principes sont mis en œuvre en langage C grâce
aux fonctions.

dehbirac@yahoo.fr
Principe de la modularité
• Plutôt que de placer tout le code de notre
programme dans un seul fichier: main.c
• Nous le découpons en plusieurs action
Raffinement successif
• Puis nous regroupons les actions dans de petits
fichiers
modularité

dehbirac@yahoo.fr
Avantage de la programmation
modulaire

• Réutilisation du code
• Notion de librairie
• Augmente la lisibilité d’un programme
• Réalise des objectifs précis
• Améliore le débogage (Compilation séparée)
• Améliore la maintenance d’un programme

dehbirac@yahoo.fr
Implémentation de la modularité
• Plusieurs fichiers par projet

Les .h, appelés fichiers headers. Ces


fichiers contiennent les prototypes des
fonctions.
Les .c : les fichiers source. Ces fichiers
contiennent les fonctions elles-mêmes.

dehbirac@yahoo.fr
La compilation séparée

dehbirac@yahoo.fr
Synthèse
• Avantage de la modularité en C
• Expliquer le principe de la compilation séparée
• Lister et trier les étapes de la compilation en C
Fonctions définies par les
utilisateurs
Exemple
Sans Fonction: Avec Fonction:
#include <stdio.h>
#include <stdio.h> #include <math.h>
#include <math.h> double f(double x)
void main(void) {
{ return((sin(x) + log(x))/(exp(x)
float x, y; + 2));
printf("x = "); }
scanf("%f",&x); void main(void)
while (x != 1000) {
{ float x, y;
y = (sin(x) + log(x))/(exp(x) + 2); printf("x = ");
printf("f(%f) = %f\n", x, y); scanf("%f",&x);
printf("x = "); while (x != 1000)
scanf("%f",&x); {
} y = f(x);
} printf("f(%f) = %f\n", x, y);
printf("x = ");
scanf("%f",&x);
}
}

dehbirac@yahoo.fr
Fonction?
• Une suite d'instructions élémentaires
• Représente une seule action.
• La base de la programmation modulaire:
La réalisation d'un algorithme c'est la
décomposition en une suite de fonctions simples
pouvant réaliser une ou plusieurs fonctions
beaucoup plus compliquées.

dehbirac@yahoo.fr
Caractéristiques des fonctions: appel,
retour

dehbirac@yahoo.fr
Utilisation des fonctions
• Etape1- La déclaration des prototypes: Elle
permet de définir au compilateur les paramètres
d'entrée et de sortie afin de pouvoir vérifier lors
d'appel de fonction l'ordre de passage des
paramètres.
• Etape2- La déclaration de fonction: Elle décrit
l'enchaînement de toutes les instructions permettant
de réaliser la fonction.
• Etape3- L'appel de fonction: elle dirige le
programme principal ou une autre fonction sur la
fonction à exécuter en donnant les variables
d'entrées (et/ou)l'affectation de la variable de sortie.

dehbirac@yahoo.fr
Syntaxe d’utilisation
• La déclaration des prototypes:
<type_iden_sortie> iden_fonc(<type> iden_1,..., <type> iden_n);

• La déclaration de fonction:
<type_iden_sortie> iden_fonc(<type> iden_1,..., <type> iden_n)
{
/* Déclaration de variable(s) locale(s) */
<type> iden_2,...,iden_m;
.
.
.
/* renvoie dans le paramètre de sortie */
return(valeur);
}

• L'appel de fonction:
iden_var_sortie = iden_fonc(iden_1_var,...,iden_n_var) ;
ou
iden_fonc(iden_1_var,...,iden_n_var) ;

dehbirac@yahoo.fr
Scénario Exemple:
#include <stdio.h>
/* Déclaration des variables globales */
int a,b,res;
/* Programme Principal */
void main()
{
printf("Donnez les valeurs de a et b ");
scanf("%d %d",&a,&b);

res=a+b;
printf("\t\tJe traite\n");

printf("L'addition de a=%d et de b=%d est égale à


%d\n",a,b,res);
}

dehbirac@yahoo.fr
Solution1: sans valeur de retour ni
paramètre
#include <stdio.h> void affichage(void)
/* Déclaration des variables globales */ {
int a,b,res; printf("L'addition de a=%d et de
/* Déclaration des prototypes */ b=%d est égale à %d\n",a,b,res);
void saisie(void); }
void traitement(void); /* Programme Principal */
void affichage(void); void main()
/* Déclaration des fonctions */ {
void saisie(void) saisie();
{ traitement();
printf("Donnez les valeurs de a et b "); affichage();
scanf("%d %d",&a,&b); }
}
void traitement(void)
{
res=a+b;
printf("\t\tJe traite\n");
}

dehbirac@yahoo.fr
solution2: Exemple sans valeur de retour
#include <stdio.h> void traitement(int val1,int val2)
/* Déclaration des variables {
globales */ printf("\t\tJe traite\n");
/* traitement */
int a,b,res;
res=val1+val2;
/* Déclaration des prototypes */ }
void saisie(void); void affichage(void)
void traitement(int val1,int val2); {
void affichage(void); printf("L'addition de a=%d et de
/* Déclaration des fonctions */ b=%d est égale à %d\n",a,b,res);
void saisie(void) }
{ /* Programme Principal */
void main()
printf("Donnez les valeurs de a et b
{
"); saisie();
scanf("%d %d",&a,&b); traitement(a,b);
} affichage();
}

dehbirac@yahoo.fr
solution3: Exemple avec valeur de retour
#include <stdio.h> printf("\t\tJe traite\n");
/* Déclaration des variables globales /* traitement */
*/ val3=val1+val2;
int a, b, res; /* Renvoie la valeur de val3 */
/* Déclaration des prototypes */ return(val3);
void saisie(void); }
int traitement(int val1,int val2); void affichage(void)
void affichage(void); {
/* Déclaration des fonctions */ printf("L'addition de a=%d et de
void saisie(void) b=%d est égale à %d\n",a,b,res);
{ }
printf("Donnez les valeurs de a et b /* Programme Principal */
"); void main()
scanf("%d %d",&a,&b); {
} saisie();
int traitement(int val1,int val2) res=traitement(a,b);
{ affichage();
/* Déclaration de variable locale */ }
int val3;

dehbirac@yahoo.fr
Fonction globale ou locale?
#include <stdio.h> #include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h> #include <math.h>

double f(double x) double f(double x); void main(void)


{ void main(void) {
return((sin(x) + { double f(double x);
log(x))/(exp(x) + 2)); float x, y; float x, y;
} printf("x = "); printf("x = ");
void main(void) scanf("%f",&x); scanf("%f",&x);
{ while (x != 1000) while (x != 1000)
float x, y; { {
printf("x = "); y = f(x); y = f(x);
scanf("%f",&x); printf("f(%f) = %f\n", x, y); printf("f(%f) = %f\n", x, y);
while (x != 1000) printf("x = "); printf("x = ");
{ scanf("%f",&x); scanf("%f",&x);
y = f(x); } }
printf("f(%f) = %f\n", x, y); } }
printf("x = "); double f(double x) double f(double x)
scanf("%f",&x); { {
} return((sin(x) + return((sin(x) +
} log(x))/(exp(x) + 2)); log(x))/(exp(x) + 2));
} }

dehbirac@yahoo.fr
Pragmatique
• Pour mettre un peu d’ordre dans un programme
faisant intervenir beaucoup de fonctions:
▫ On peut commencer par la définition de la fonction
principale, précédée de la déclaration des fonctions
dont elle a besoin.
▫ On définit ensuite ces fonctions auxiliaires, chacune
d’elles précédée des déclarations des fonctions
auxiliaires de second niveau dont elles ont besoin, et
ainsi de suite.

dehbirac@yahoo.fr
Problème 1
• Ecrire une fonction SOMME de type float qui
calcule la somme de deux nombres réels.
• Ecrire une fonction MOYENNE de type float qui
calcule la moyenne arithmétique de deux
nombres réels.
• Ecrire un programme se servant de ces deux
fonctions pour afficher la somme et la moyenne
de deux nombres réels saisis au clavier.
Problème

• Ecrire une fonction MIN et une fonction MAX


qui déterminent le minimum et le maximum de
deux nombres réels.
• Ecrire un programme se servant des fonctions
MIN et MAX pour déterminer le minimum et le
maximum de :
▫ deux nombres réels entrés au clavier.
▫ Quatre nombre réels entrés au clavier.
Déclaration de fonctions dans des
fichiers en-tête
• La déclaration d’une fonction ne doit pas
nécessairement être incluse explicitement dans
le même fichier du programme source que la
fonction principale (main).
• Elle peut aussi se trouver dans un fichier en-tête,
comme pour les fonctions prédéfinies.
• Ce dernier est ensuite inséré dans le programme
grâce à une commande du préprocesseur
(include ).

dehbirac@yahoo.fr
La compilation séparée

dehbirac@yahoo.fr
Exemple de déclaration avec les
fichiers en-tête
/* calcul.c */ /* defini.h */
#include <stdio.h> double f(double x)
#include <math.h> {
#include "defini.h" return((sin(x) + log(x))/(exp(x) +
void main(void) 2));
{ }
float x, y;
printf("x = ");
scanf("%f",&x);
while (x != 1000)
{
y = f(x);
printf("f(%f) = %f\n", x, y);
printf("x = ");
scanf("%f",&x);
}
}
dehbirac@yahoo.fr
Exemple de déclaration avec les
fichiers en-tête et les fichiers source
/* calcul.c */ /* defini.h */ /* defini.c */
#include <stdio.h> double f(double x); #include <math.h>
#include <math.h> #include "defini.h"
#include "defini.h"
void main(void) double f(double x)
{ {
float x, y; return((sin(x) +
printf("x = "); log(x))/(exp(x) + 2));
scanf("%f",&x); }
while (x != 1000)
{
y = f(x);
printf("f(%f) =
%f\n", x, y);
printf("x = ");
scanf("%f",&x);
}
}
dehbirac@yahoo.fr
Mode de transmission des paramètres
• Passage par valeur: Lorsqu’on ne veut pas
changer la valeur d’un paramètre on parle de
transmission par valeur (call by value) (mode par
défaut)
 le Sous-programme ne reçoit qu’une copie de la
valeur.
• Passage par référence: Lorsqu’on veut se
permettre la possibilité de changer la valeur on parle
de transmission par référence (call by reference)
 Technique d’ utilisation des pointeurs

dehbirac@yahoo.fr
Passage par valeur
/* valeur_1.c */ /* valeur_2.c */
#include <stdio.h> #include <stdio.h>
void affiche(int n) void affiche(int n)
{ {
printf("%d \n", n); n = 4;
} printf("%d \n", n);
void main(void) }
{ void main(void)
int n = 3; {
affiche(n); int n = 3;
} affiche(n);
printf("%d \n", n);
}
Résultat: Résultat:
3 4
3

La fonction affiche() n’a donc pas changé la valeur de n dans la fonction principale.
dehbirac@yahoo.fr
Problème3
• Implémenter la modularité sur le problème 1
Porté des variables: variable globale
• Une variable globale est une variable déclarée
avant toute définition de fonction.
• Elle est connue de toutes les fonctions,

On peut changer sa valeur dans chacune d’elle.


L’inconvénient: changer sa valeur par
inattention.
Il vaut donc mieux utiliser le passage par
référence qu’une variable globale.

dehbirac@yahoo.fr
Voyons cependant comment cela
fonctionne
/* global_1.c */
#include <stdio.h>
int n = 3; L’exécution de ce
void affiche(void) programme fait afficher
{ printf("%d \n", n);
n = 4; La fonction affiche() a
printf("%d \n", n); donc changé la valeur de n
} dans la fonction principale.
void main(void)
{
printf("%d \n", n);
n = 2;
affiche();
printf("%d \n", n);
}

dehbirac@yahoo.fr
Par opposition • Quelle
considérée
est la
lors
variable
de son
/* global_2.c */ utilisation ?
#include <stdio.h> • La réponse est simple : celle
int n = 3; qui a été déclarée le plus
void affiche(int n) récemment, c’est à-dire la plus
{ locale à l’intérieur du sous-
n = 4; programme (et, plus
printf("%d \n", n); généralement, à l’intérieur du
} bloc).
void main(void) Explication: La déclaration de l’argument
{ n dans la fonction affiche() le fait
affiche(n); considérer comme une variable locale. La
printf("%d \n", n); variable n du corps de cette fonction
} correspond à la dernière variable déclarée,
donc à la variable locale, et non à la
Son exécution fait afficher 4 variable globale ; ceci explique pourquoi la
puis 3. valeur de la variable globale, elle, n’a pas
changée.
POURQUOI???
dehbirac@yahoo.fr
La classe d’allocation des variables
globales
• Les variables globales existent pendant toute
l’exécution du programme dans lequel elles
apparaissent.
• Leurs emplacements en mémoire sont parfaitement
définis lors de l’édition de liens.
Elles font partie de la classe d’allocation
statique.
• RQ: ces variables se voient initialisées à zéro,
avant le début de l’exécution du programme, sauf,
bien sûr, si vous leur attribuez explicitement une
valeur initiale au moment de leur déclaration.

dehbirac@yahoo.fr
Variable locale automatique
double puiss(double x, int n) Par défaut, les variables locales ont
{ une durée de vie limitée à celle
double y; d’une exécution de la fonction dans
int i; laquelle elles figurent.
y = 1; Plus précisément, leurs
for (i=1; i <= n; i++) y = y*x; emplacements ne sont pas
return(y); définis de manière permanente
} comme ceux des variables globales.
Un nouvel espace mémoire leur
est alloué à chaque entrée dans
la fonction et libéré à chaque
sortie.
Il sera donc généralement différent
d’un appel au suivant.
On traduit cela en disant que la
classe d’allocation de ces variables
est automatique.

dehbirac@yahoo.fr
Les variables locales statiques
#include <stdio.h> Le besoin d’attribuer un
main() emplacement permanent à une
{ variable locale et qu’ainsi sa
void fct(void) ; valeur se conserve d’un appel
int n ;
au suivant
for ( n=1 ; n<=5 ; n++)
fct() ;  mise en œuvre: la déclarer à
} l’aide du mot-clé static
void fct(void) le mot static employé sans
{ indication de type est équivalent à
static int i ; static int
i++ ;
printf ("appel numéro :
%d\n", i) ; Remarque: Prenez garde à ne pas
} confondre une variable locale de
Résultat : classe statique avec une variable
appel numéro : 1 globale. En effet, la portée d’une
appel numéro : 2 telle variable reste toujours limitée
appel numéro : 3 à la fonction dans laquelle elle est
appel numéro : 4 définie.
appel numéro : 5
dehbirac@yahoo.fr
Le cas des fonctions récursives
Le langage C autorise la récursivité des appels de
fonctions selon deux aspects :

• Récursivité directe : une fonction comporte, dans


sa définition, au moins un appel à elle-même,
• Récursivité croisée : l’appel d’une fonction
entraîne celui d’une autre fonction qui, à son tour,
appelle la fonction initiale (le cycle pouvant
d’ailleurs faire intervenir plus de deux fonctions).

dehbirac@yahoo.fr
Exemple fort classique
long fac (int n)
{
if (n>1) return (fac(n-1)*n) ;
else return(1) ;
}

Inefficace sur le plan du temps d’exécution!!!!!!!

chaque nouvel appel de fac, provoque une allocation mémoire pour les
paramètres, les variables locales et le retour, sans que les emplacements
précédents soient libérés.
Il y a donc un empilement des espaces alloués aux variables locales,
parallèlement à un empilement des appels de la fonction.
Ce n’est que lors de l’exécution de la première instruction return que l’on
commencera à « dépiler » les appels et les emplacements et donc à libérer de
l’espace mémoire.
dehbirac@yahoo.fr
dehbirac@yahoo.fr
La portée d’une variable globale - la
déclaration extern
A priori, la portée d’une variable globale semble limitée au fichier source
dans lequel elle a été définie.

source 1 source 2
int x ; fct2()
main() {
{ .....
..... }
} fct3()
fct1() {
{ .....
.....} }

Impossible de faire référence la variable globale x déclarée dans


le premier fichier source à partir des fonctions fct2 et fct3
dehbirac@yahoo.fr
Solution
source 1 source 2

int x ; fct2()
main() {extern int x ;
{ .....
..... }
} fct3()
fct1() {
{ .....
.....} }

RQ: Cette déclaration extern n’effectue pas de


réservation d’emplacement de variable.
Elle ne fait que préciser que la variable globale x
est définie par ailleurs.
dehbirac@yahoo.fr
Les variables globales et l’édition de
liens
• Voir commentaire

dehbirac@yahoo.fr
Les variables globales cachées- la
déclaration static
Il est possible de « cacher » une variable globale: la rendre
inaccessible à l’extérieur du fichier source où elle a été définie (on
dit aussi « rendre confidentielle » au lieu de « cacher » ; on parle alors
de « variables globales confidentielles »).
Il suffit d’utiliser la déclaration static:

static int a ;
main()
{
.....
}
fct()
{
.....
}

dehbirac@yahoo.fr
Le cas des fonctions
• La fonction est considérée par le langage C comme
un objet global.
• C’est ce qui permet d’ailleurs à l’éditeur de liens
d’effectuer correctement son travail.
Note: il n’est pas nécessaire d’utiliser une
déclaration extern pour les fonctions définies dans
un fichier source différent de celui où elles sont
appelées (mais le faire ne constitue pas une erreur).
• En tant qu’objet global, la fonction peut voir sa
portée limitée au fichier source dans lequel
elle est définie à l’aide d’une déclaration static
comme dans : static int fct (...)

dehbirac@yahoo.fr
Synthèse

dehbirac@yahoo.fr
Le besoin d’interactivité
#include <stdio.h> int main(int argc, char *argv[]) {
#include <stdlib.h>
switch (menu())
{
int menu() case 1:
{ printf("Addition:\n");
int choix = 0; break;
while (choix <1 || choix > 5) case 2:
{ printf("Multiplication: \n");
printf("Menu :\n"); break;
printf("1 : Addition \n") ; case 3:
printf("2 : Multiplication \n"); printf("Soustraction: \n");
printf("3 : Soustraction \n"); break;
printf("4 : division \n"); case 4:
printf("5 : Quitter \n"); printf("Division: \n");
printf("Votre choix ? "); break;
scanf("%d", &choix); }
} return 0;
return choix; }
}
Atelier: Compléter le programme suivant en implémentant les
différentes fonctions possibles

Vous aimerez peut-être aussi