Vous êtes sur la page 1sur 49

Programmation modulaire en C

TSOPZE N.
Compétences et connaissances visées
 Décomposer un gros programme en fonctions,
 Programmer les fonctions et les utiliser dans un programme
principal
 Structurer un programme en modules.
 Faire la compilation séparée
 Manipuler les données dans les fichiers

Nombre de séances de CM : 5
Durée de la séance de CM : 02H00
Sous - programmes

Macro
fonctions
Principes
 Déconseiller d’utiliser des constantes
littérales
 utiliser la directive #define.
Exemple : fopen("mon_fichier", "r");
perimetre = 2 * 3.14 * rayon;
 La factorisation du code
 éviter les duplications de code.
 La fragmentation du code
 découper un programme en plusieurs fichiers.
 réutiliser facilement une partie du code pour
d'autres applications.
Sous – programmes (macro)
 Utiliser la directive #define
 Son appel est remplacé par son code à la
compilation
#define carre(a) a*a
carre(z) deviendra z*z
carre(valeur) deviendra valeur*valeur
carre(12) deviendra 12*12
Sous - programmes
 scinder en plusieurs parties et de regrouper dans
le programme principal les instructions en
décrivant les enchaînements
 éviter des séquences d’instructions répétitives
 partage d’outils (fonctions)
 Deux formes:
 Procédure : aucun retour
 Fonction: retour attendu (instruction return)

NB: en C, tout sous programme est une fonction


Sous – programmes (fonctions)
 Possibilité de ne pas utiliser la valeur de
retour
 Exemple: printf ou scanf
 Possibilité de ne fournir aucune valeur
(void);
 Le type de retour peut être quelconque
 Possibilité pour une fonction de modifier les
valeurs de certains de ses arguments
Sous – programmes (déclaration)
 Déclaration: juste donner l’entête de la
fonction (type, nom et liste des arguments)
 Optionnelle mais toujours avant toute utilisation
 Placée dans le fichier d’entête
float carre (float v)
 Définition: décrire la fonction (entête + corps)
 Peut être écrite avant ou après (si déclarée) l’appel
Float carre (float v){ return v*v;}
Sous – programmes (définition)
Aucun retour (procédure)
void nomprocedure (paramètres)
{
/*
Declaration des variables locales
*/
<bloc d’instructions>
}
Sous – programmes (définition)
Avec un retour
Type nomFonction (paramètres)
{
/*
Declaration des variables locales
*/
<bloc d’instructions>
Return <valeur de retour>
}
Sous – programmes (utilisation)
 Doit être au moins déclarée ou définie avant tout
appel
{
…..
Nomprocedure (paramètres)
type a=nomFonction(paramètres)
….
}
Sous – programmes (utilisation)
Exemple
int double(int a){
return 2*a;
}
int main()
{int x=5;
printf(‘’%d’’, double(x));

}
Passage de paramètres (par valeur)
 aucune modification sur l’argument après l’appel
 Tout se passe comme si l’appel avait fait une copie de la
valeur de l’argument et utiliser cette copie
void permuter (int a, int b)
main()
{ int c ;
{ void permuter (int a, int b) ;
printf ("début echange : %d int n=10, p=20 ;
%d\n", a, b) ; printf ("avant appel : %d %d\n", n, p)
c=a;a=b;b=c; ;
printf ("fin echange : %d permuter(n, p) ;
%d\n", a, b) ; printf ("après appel : %d %d", n, p)
}
}
Passage de paramètres (par adresse)
 Passer l’adresse du paramètre que son nom
 Modifier sa valeur à travers son adresse en mémoire
 Paramètre modifié après l’appel si la fonction l’a modifié

void permuter (int *a, int *b)


main()
{ int c ; { void permuter(int a, int b) ;
printf ("début echange : %d int n=10, p=20 ;
%d\n", *a, *b) ; printf ("avant appel : %d %d\n",
c = *a ; *a = *b ; *b = c ; n, p) ;
printf ("fin echange : %d %d\n", permuter(&n, &p) ;
*a, *b) ;
}
printf ("après appel : %d %d", n,
p)
}
Paramètres en nombre variable
 Fichier à incluire #include <stdarg.h>
 déclaration:
 type nom_fonction(paramètres fixes,…) les
« … » indiquent que la partie variable des
paramètres
 Cas des fonctions scanf et printf
Paramètres en nombre variable
 Deux éléments:
 va_list adpar :
 adpar est un identificateur de liste variable, servira
à récupérer les différents arguments variables
 fonction va_start permet de l’initialiser à l’adresse
du paramètre variable
 fonction va_arg :
 fournit la valeur trouvée à l’adresse courante fournie
par adpar;
 incrémente l’adresse contenue dans adpar
Paramètres en nombre variable
 macros va_start et va_end pour marquer le début et la fin
#include <stdio.h>
#include <stdarg.h>
void essai (int nbpar, ...)
{ va_list adpar ;
int parv, i ;
printf ("nombre de valeurs : %d\n", nbpar) ;
va_start (adpar, nbpar) ;
for (i=1 ; i<=nbpar ; i++)
{ parv = va_arg (adpar, int) ;
printf ("argument variable : %d\n", parv) ;
}
}
Variables globales
 Déclarées hors du bloc
 Peut-être utilisée dans n’importe quel sous programme
du fichier, à condition d’être déclarée avant
 Permet d’échanger les données entre les sous
programmes
int i ; main()
void test(void) { for (i=1 ; i<=5 ; i++)
{ printf ("%de appel\n", i) ;
test() ;
}
}
Variables locales
 Portée limitée au bloc où elles sont déclarées
 Aucun lien avec les variables globales de même nom
 Variable locale statique: conserve d’un appel à l’autre;
initialisé à 0 par défaut
static type nom_variable;

void test_static(void)
{ static int i ;
i++ ;
printf ("appel numéro : %d\n", i) ;
}
Passage des tableaux en paramètres
 Nom du tableau comme paramètre est considéré
comme une adresse
 Possibilité d’effectuer toutes les manipulations
voulues sur ses éléments

void init_t(int t[10])


void init_t(int t[10])
{
void init_t(int * t)
int i ;
void init_t(int t[])
for (i=0 ; i<10 ; i++) t[i] =1 ;
}
Passage des structures en paramètres
 Passage par valeur
void affiche(struct etudiant e)
{
printf ("\n etudiant : %s %d", e.matricule, e.age);
}
 Appel
etudiant e1;
Affiche(e1);
Passage des structures en paramètres
 Passage par adresse
void init(struct etudiant * e){
e->matricule=‘’16Z002’’;
e->age=17;
}
 Appel
etudiant e1;
init(&e1);
 Accès aux élément: * ou ->
(*e1).matricule ou e1->age
Gestion dynamique de la mémoire
Gestion dynamique de la mémoire
 mise en oeuvre de listes chaînées, d’arbres
binaires,…
 Créer l’espace lorsque le besoin est
 Éviter de perdre l’espace comme dans le cas des
tableaux
 Libérer l’espace dès que possible
Fonction de création de l’espace
 En-tête: #include <stdlib.h>
 Création dynamique de l’espace: malloc()
char * adr ;
adr = malloc (N) ;
for (i=0 ; i<N ; i++) *(adr+i) = 'x' ;
malloc(N) crée un espace de N octets et renvoie
l’adresse en retour dans un pointeur
Fonction de création de l’espace
 Syntaxe: void * malloc (size_t taille)
 Précision de la taille des éléments
Fonction sizeof()
long * adr ;
adr = malloc (100 * sizeof(long)) ;
Libération de l’espace
 pouvoir récupérer des emplacements dont on n’a plus
besoin.
 libérer un emplacement préalablement alloué.
 Fonction : free
free(adr)
Libère l’espace réservé à adr
Autres fonctions
 void * calloc ( size_t nb_blocs, size_t taille )
allouer l’emplacement nécessaire à nb_blocs consécutifs,
ayant chacun une taille de taille octets.

 void * realloc (void * pointeur, size_t taille ) (stdlib.h)


permet de modifier la taille d’une zone préalablement
allouée (par malloc, calloc ou realloc).
Liste chainée
#include <stdio.h> void creation (struct element * * adeb)
#include <stdlib.h> {int num ;
struct element { int num ; float x, y ;
struct element * courant ;
float x ;
* adeb = NULL ;
float y ;
while ( printf("numéro x y : "),
struct element * suivant ; scanf ("%d %f %f", &num, &x, &y), num)
}; { courant = (struct element *) malloc
void creation (struct element * * (sizeof(struct element)) ;
adeb) ; courant -> num = num ;
courant -> x = x ;
main() {
courant -> y = y ;
struct element * debut ; courant -> suivant = * adeb ;
creation (&debut) ;} * adeb = courant ;
}}
Fichiers
Fichiers
 Déclaration
FILE * nom_fichier;
 Ouverture  lier le nom logique (utilisé dans le
programme au nom physique (vu dans l’explorateur)
nom_fichier = fopen (chemin, « mode") ;
 chemin: emplacement du fichier
 mode: comment ouvrir le fichier
 En lecture: r
 En écriture : w
 En ajout: a
…
Fichiers
Mode Description
"r" ouverture d'un fichier texte en lecture
"w" ouverture d'un fichier texte en écriture
"a" ouverture d'un fichier texte en écriture à la fin
"rb" ouverture d'un fichier binaire en lecture
"wb" ouverture d'un fichier binaire en écriture
"ab" ouverture d'un fichier binaire en écriture à la fin
"r+" ouverture d'un fichier texte en lecture/écriture
"w+" ouverture d'un fichier texte en lecture/écriture
"a+" ouverture d'un fichier texte en lecture/écriture à la fin
"r+b" ouverture d'un fichier binaire en lecture/écriture
"w+b" ouverture d'un fichier binaire en lecture/écriture
"a+b" ouverture d'un fichier binaire en lecture/écriture à la fin
Fichiers (écriture)
fwrite (&var, sizeof(type de var), nb_blocs, nom_fichier) ;
 &var : adresse d’un bloc d’informations à écrire ;
 sizeof(typede var) : taille d’un bloc, en octets
 Nb_blocs : nombre de blocs de cette taille que l’on souhaite
transférer dans le fichier;
 nom_fichier : adresse de la structure décrivant le fichier.
fprintf(flot,"descripteur",expression-1, ..., expression-n)
 semblable à printf.
 flot: est l’objet retourné par fopen
Fichiers (écriture)
char nomfich[21] ;
int n ;
FILE * F;//………………………………………………………
printf ("nom du fichier à créer : ") ;
scanf ("%20s", nomfich) ;
F= fopen (nomfich, "w") ;//…………………………………
do { printf ("donnez un entier : ") ;
scanf ("%d", &n) ;
if (n) fwrite (&n, sizeof(int), 1, F)
;………………………………………
}
while (n) ;
fclose (F) ;
Fichiers (lecture)
fread (&var, sizeof(type de var), nb_blocs, nom_fichier) ;
 &var : adresse d’un bloc d’informations à écrire ;
 sizeof(typede var) : taille d’un bloc, en octets
 Nb_blocs : nombre de blocs de cette taille que l’on souhaite
transférer dans le fichier;
 nom_fichier : adresse de la structure décrivant le fichier.
fscanf(flot,"descripteur",argument-1,...,argument-n)
 flot retourné par fopen
 Semblable à scanf
 feof (F) : fin du fichier F, plus de données à lire
Fichiers (lecture)
Lecture et affichage du contenu du fichier F
do
{ fread (&n, sizeof(int), 1, F) ;
if ( !feof(F) ) printf ("\n%d", n) ;
}
while ( !feof(F) ) ;
Fichiers (prédifinis)
 stdin : unité d’entrée (par défaut, le clavier) ;
 stdout : unité de sortie (par défaut, l’écran) ;
 stderr : unité d’affichage des messages
d’erreurs (par défaut, l’écran).
 stdaux : unité auxiliaire ;
 stdprt : imprimante
Programmation modulaire
Programmation modulaire
 découper un programme en plusieurs fichiers.
 réutiliser facilement une partie du code pour
d'autres applications.
 placer une partie du code dans un fichier en-
tête (ayant l'extension .h)
 inclure le fichier .h dans le fichier contenant le
programme principal à l'aide de la directive
#include .
Programmation modulaire
Compilation séparée
 Préprocesseur : traduire les directives figurant
dans les fichiers sources.
 Directives : ordres destinés au préprocesseur,
commencent toujours par # :
 #define
 #include ou #.
 Résultat : fichiers qui sont encore écrits en Langage
C.
Compilation séparée
 compilation : traduction de chaque texte C
en langage machine
 Résultat : un fichier objet d'extension .obj (sur PC).
 à présent autant de fichiers objets qu'il avait de fichiers
sources.
 l'éditeur de liens (Linker) : réunion des différents
modules objets en un seul module exécutable
 Entrée : liste des fichiers objets à assembler (projet).
 Résultat : programme exécutable, écrit en langage machine
et autonome,.exe.
Compilation séparée
 programme divisé en fichiers : main.c et
module1.c…. disjoints.
 Compiler ces fichiers séparément et obtenir les
fichiers objets qui seront liés lors l'édition de liens.
 Exemple :
gcc -c module1.c
gcc -c module2.c
gcc -c main.c
gcc main.o module1.o module2.o
Compilation séparée - Makefile
 composé de règles ayant l’aspect suivant:
CIBLE: DEPENDANCES
<tabulation> COMMANDE
< tabulation > COMMANDE
< tabulation > COMMANDE
 cible : nom du fichier que l’on veut générer.
 dépendances : fichiers utilisés pour générer cette
cible.
 commandes : actions à mener pour générer la cible
à partir des dépendances
Fichier en-tête d'un fichier source
 règle d'écriture : associer à chaque fichier source
nom.c un fichier en-tête nom.h
 Manipulation de nom.h :
 déclarations des fonctions non locales au fichier nom.c
(précédées du mot-clef extern)
 définitions des constantes symboliques
 Définitions des macros partagées par les autres fichiers.
 doit être inclus par la directive #include dans tous les fichiers
sources qui utilisent une des fonctions définies dans nom.c,
ainsi que dans le fichier nom.c.
 Manipulation de nom.c
 #include nom.h
 Définitions de toutes les fonctions déclarées dans nom.h
Fichier en-tête d'un fichier source
/** fichier: calcul.h : en-tete de calcul.c
*/ #include <stdlib.h>
extern int puissance(int, int); #include <stdio.h>
extern int factoriel(int); #include "calcul.h"
int main(void)
/******* fichier: calcul.c ********/ {
#include "calcul.h"
int puissance(int a, int b)
int a, b, c;
{int p=1; scanf("%d",&a);
…. scanf("%d",&b);
return p; c = puissance(a,b);
} printf("\n a^b= %d\n",c);
int factoriel (int a, int b)
{int f=1;
return EXIT_SUCCESS;
…. }
return f;
}
Fichier en-tête d'un fichier source
 éviter les possibilités de double inclusion de fichiers en-tête.
 Recommandation :
 définir une constante symbolique, habituellement appelée NOM_H,
au début du fichier nom.h dont l'existence est précédemment testée.
 Si cette constante est définie alors le fichier nom.h a déjà été inclus le
préprocesseur ne le prend pas en compte.
 Sinon, le préprocesseur définit la constante et prend en compte le
contenu de nom.h.

/******* fichier: calcul.h ***/


#ifndef CALCUL_H
#define CALCUL_H
extern int puissance(int, int);
extern int factoriel(int);
#endif /* CALCUL_H */
Variables partagées
 utiliser une variable commune à plusieurs fichiers sources.
 déclarer la variable une seule fois de manière classique dans un
seul fichier.
 Dans les autres fichiers qui l'utilisent, faire une référence à cette
variable, sous forme d'une déclaration précédée du mot-clef
extern.
 Exemple : deux fichiers sources main.c et calcul.c partagent une
variable entière x,
 définir x dans calcul.c sous la forme
int x;
 faire référence dans main.c par
extern int x;
Variables privées
 restreindre la visibilité de la variable ou de la fonction au fichier
source courant
 Préciser lors de la déclaration
 static int id(int x);
 static int i;

Vous aimerez peut-être aussi