Vous êtes sur la page 1sur 17

1.

1 Introduction
Imaginer les morceaux d’algorithmes suivants :

Programme Principal Algo1

Début

Pour i= 1 à 20

Ecrire ("************") ;

Fait ;
Fin

Programme Principal Algo2

Début

Pour i = 1 à 15

Ecrire (‘*’) ;

Fpr
Fin

Programme Principal Algo3

Début

Pour i =1 à 20

Ecrire (‘+’) ;

Fpr
Fin

Il est clair que l’on veut à chaque fois écrire un certain nombre de lignes de
caractères. Il est plus astucieux d’écrire une procédure qui fait ce travail et
paramétrée par le nombre de lignes et le caractère à imprimer
Procédure imprimer ( n : entier, c : caractère)

Début

Variable i : entier ;

Pour i= 1 à n faire

Ecrire (c) ;

Fpr
Fin

Dans le programme principal, on peut appeller cette procédure trois fois.


Imprimer (15, ‘*’) ;
Imprimer (20, ‘+’) ;
Imprimer (20, ‘*’) ;
Nous avons une meilleure lisibilité du programme, même si on n’a pas écrit le
code de la fonction « imprimer », on sait au moins ce qu’on veut faire en divisant
les tâches.
Une fonction ou procédure devient alors un sous algorithme. Le sous-
algorithme est écrit séparément du corps de l'algorithme principal et sera appelé
par celui-ci quand ceci sera nécessaire. De la même façon, nous faisons appel à
une fonction mathématique (sinx (x) par exemple) sans avoir écrit cette fonction.
Elle se trouve déjà dans la bibliothèque math.h que nous intégrons au programme.
Cette façon de programmer en divisant le programme en tâches est la modularité
du programme. Elle a plusieurs avantages :

* Meilleure lisibilité, comme nous venons de le voir avec l’exemple précédant, la


fonction est alors une mise en facteur commun d’un morceau de programme.

* Diminution du risque d'erreurs : on ne modifie que la fonction ou les appels en


cas de changement.

* Possibilité de tests sélectifs, ou même dans certains cas de compilation séparée,


pour indiquer qu’une fonction testée et compilée ne peut être source d’erreur (dans
les grands programmes, on peut avoir jusqu’à 10 000 lignes).
* Dissimulation des méthodes : Lors de l'utilisation d'un module il faut seulement
connaître son effet, sans devoir s'occuper des détails de sa réalisation. Plus connue
en orienté objet par l’encapsulation des méthodes (les rendre sous forme de boites
noires accessibles via des interfaces). On peut également enrichir sa propre
bibliothèque de fonctions (packaging).

* Réutilisation de modules déjà existants : Il est facile d'utiliser des modules qu'on
a écrits soi-même ou qui ont été développés par d'autres personnes. Le réusing est
un concept très important en programmation. Comme votre chemise, une fonction
n’est pratiquement jamais utilisée une seule fois.

* Simplicité de l'entretien ou debugging du programme : Un module peut être


changé ou remplacé sans devoir toucher aux autres modules du programme.

* Favorisation du travail en équipe : Un programme peut être développé en équipe


par délégation de la programmation des modules à différentes personnes ou
groupes de personnes. Une fois développés, les modules peuvent constituer une
base de travail commune. Ainsi un développeur n’est pas sensé savoir à quoi
servira la méthode qu’il a écrite.

* Hiérarchisation des modules : Un programme peut d'abord être résolu


globalement au niveau du module principal. Les détails peuvent être reportés à
des modules sous-ordonnés qui peuvent eux aussi être subdivisés en sous-modules
et ainsi de suite.

1.2 Les Fonctions


1.1 Définition de la fonction
Une fonction fait une seule chose. C’est un sous algorithme qui est exécuté dans
un autre algorithme, elle est définie par :
 L’identificateur qui représente le nom de la fonction.
 Le mot clé Fonction définit dans l’algorithme.
 Les paramètres de la fonction, chaque paramètre est identifié par son nom
et son type. La liste des paramètres est fournie entre parenthèses ( ).
 Le type de retour de la fonction.
 La Description du code ou de l’expression qui fournit la valeur de la
fonction.
Dans le corps d’une fonction, on trouve des déclarations de variables. Ces
variables sont dites locales, utilisables uniquement à l’intérieur de cette fonction.
Une fois terminée l’exécution de la fonction, sa valeur n’est plus connue. Si l’on
trouve une variable de même nom ailleurs dans le programme, il ne s’agit pas de
la même variable, mais d’une variable homonyme.
Syntaxe algorithmique 1 :

Fonction identificateur (p1: type1, p2: type2,…., pn:type n):


type de retour
Déclaration des variables locales ;
début

Instructions ;
Corps de la fonction
Identificateur ←résultat ;

Fin

Même si certains langages le permettent, la déclaration et le corps d’une


fonction ou procédure doit en général précéder celui du programme principal qui
les appelle.
Astuce : dans certains langages comme le C, le nom de la fonction est suivi de
parenthèses ( ), même si cette fonction n’a pas besoin de paramètres- par exemple
nettoyer_ecran( ) -. C’est une manière de distinguer les noms de fonctions (ou
procédures) des noms de variables.

Syntaxe algorithmique 2 En C
Fonction identificateur Type retour identificateur
(p1:type1,…,pn:type n): Type
Retour (type1 : p1,..., type n :pn)

Déclaration des variables Déclaration des variables


locales ; locales ;

debut {

Instructions;
Instructions ;
return valeur de retour ;
Retourner valeur de retour ; }

Fin

Dans la syntaxe 1 (de type Pascal), le nom de la fonction sera considéré comme
variable qui contiendra le résultat. Dans la syntaxe 2 le mot réservé retourner (en
C « return ») sera utilisé dans le corps de la fonction (ou routine) pour retourner
le résultat au programme principal.

Remarque : Pour des raisons évidentes, nous préférons la syntaxe 2 qui est
proche du langage C (plus souple), et plus facile à retenir lorsqu’on passe au
codage. La syntaxe 1, l’identificateur de la fonction a plusieurs rôles (déclaration
de la fonction et retourner sa valeur).
Exemple 1 : Maximum de deux valeurs de type réel
Dans cet exemple, a et b représentent les paramètres de la fonction max qui est
prête à, l’emploi, telle quelle est, la fonction max attend que le programme
principal l’appelle et lui transmets des valeurs qui seront traitées à l’intérieur de
la fonction.

fonction max (a: reel , b: reel) : reel


variable m: reel ; /* variable locale */
debut
si (a > b)
alors m ← a ;
sinon m ←b ;
fin si
retourner m ; /* retour du résultat */
fin

Dans l’exemple précédent, on pouvait mettre max←m (syntaxe1) à la place de


« retourner m ».
Exemple 2: dans cet exemple la fonction pi n’a pas besoin de paramètres donc on
met void entre parenthèses, qui signifie qu’il n’y a rien. On peut même laisser
comme dans Java, juste les parenthèses. C’est un moyen efficace de savoir
syntaxiquement que c’est une fonction et non une variable.
Attention ce n’est pas une procédure car il y a un retour.
float pi (void)
{
return 3.1415927;
}

1.2 Appel de fonction


Le programme ou algorithme principal, ou même une autre fonction qui appelle
une fonction est dit algorithme appelant; la fonction qui est utilisée est dite
fonction appelée.
Pour appeler une fonction, il suffit de mentionner son nom dans l’algorithme
appelant. Pour une fonction qui a un type, nous devons l’affecter à une variable,
exactement comme pour une fonction mathématique, Y=sin(x) par exemple.
La syntaxe est la suivante :
Nom de la fonction (p1 , p2 , ……..pn )

Une fonction est appelée par l’algorithme principal ou une autre fonction.
1. La fonction récupère en entrée des informations plus précisément des valeurs
par l’intermédiaire de ses paramètres.
2. Les valeurs récupérées par la fonction provenant du programme appelant sont
traitées à l’intérieur de la fonction.
3. Grace à l’instruction retourner ou return on revient à l’algorithme appelant
et on reprend l’exécution des instructions qui suivent l’instruction d’appel.
4. Si le nombre de paramètres ne correspond pas, on peut palier à ce problème en
forçant dans la déclaration de la fonction une affectation de valeur par défaut
du paramètre correspondant nom_fun (type var1, ..type varn = value, …)

Exemple 3 :

Algorithme principal Fonction addition (a :entier,


b :entier) :entier
Variables Debut
R,x,y : entier ; Variable S :entier ;
S←a+b ;
Debut Retourner S ;
// valeur retournée ici 9+10
x←9 ; y←10 ;

R← addition (x,y)/* Fin


appel */
Ecrire ("le résultat Les paramètres a et b définis entre parenthèses
est", :R) ; sont dits des paramètres formels ou
Fin arguments. Les paramètres x et y utilisées au
moment de l’appel sont dits des paramètres
effectifs, car c’est leurs valeurs qui iront
remplacer les variables formelles pendant
l’exécution de la procédure.

1.3 Procédures

Définition de la procédure
Le mot procédure est très courant aujourd’hui. En voici une série : Procédure
d’inscription : Procédure d’installation, Procédure de sauvegarde, Procédure de
tri, Procédure de découpage, Procédure administrative, Procédure judiciaire,
Procédure de sauvetage, Procédure d’intégration, Procédure de contrôle ou
d’Analyse, ……etc. Pour signifier d’abord que l’exécution d’une procédure
nécessite du temps, mais surtout que l’autorité qui l’exécute est indépendante.
Une procédure est similaire à une fonction, mais elle ne retourne aucune valeur,
elle aura la forme suivante. En fait, s’il y des valeurs à retourner, ce sera fait à
travers les paramètres.

Procédure identificateur (p1:type1, p2:type2,…..,pn: type n)


Début
Déclaration des variables locales ;
Instructions ;
Fin.

Après le nom de la procédure, il faut donner la liste des paramètres (s'il y en a)


avec leur type respectif. Pour déclencher l'exécution d'une procédure dans un
programme, il suffit de l'appeler, l’appel d’une procédure peut être effectué en
spécifiant, au moment souhaité, son nom et éventuellement ses paramètres, cela
déclenche l’exécution des instructions de la procédure.

A l'appel d'une procédure, le programme interrompt son déroulement normal,


exécute les instructions de la procédure, puis retourne au programme appelant et
exécute l'instruction suivante. C’est le principe de la pile.
Cette notion de pile est importante en informatique, car elle a une gestion dite
LIFO (last in first out), pour dire que le dernier sous programme appelé est celui
exécuté en premier, il est alors en sommet de pile. Une fois terminé, le sous
programme est alors dépilé (libérer l’espace mémoire utilisé –et les variables
locales-, et recharger l’environnement où on s’est arrêté avant l’appel)
Sachez que cela revient au programmeur d’écrire soit une procédure ou
fonction, mais comme il n’y a pas de retour dans la procédure, l’affichage ne se
fera pas dans le programme principal mais dans la procédure elle-même. Voir
dans l’exemple 4, une fonction min et sa version procédure
Exemple 4
Fonction minimum(a :entier,b :entier ) :entier

Variable min : entier ;


Debut

si a <b ;
alors min←a ;
sinon min←b ;
fin si
retourner min ;
Fin

Programme principal

Variables x,y ;entier ;


Debut

Ecrire ("donnez 2 valeurs :") ;


Lire (x,y) ;
/* appel de fonction*/
Ecrire ("le minimum des 2 valeurs est :", minimum (x,y)); ;
Fin

Procédure minimum(a :entier,

b :entier )
Variable min : entier ;
Debut
si a <b ;
alors min←a ;
sinon min←b ;
fin si
Ecrire ("le minimum des 2 valeurs est :", min) ;
Fin

Programme_principal
Variables x,y ;entier ;
Debut
Ecrire ("donnez 2 valeurs :") ;
Lire (x,y) ;
minimum (x,y) ; /* appel de procédure*/
Fin

Exemple 5 : Procédure qui affiche un article, les informations sur l’article sont le
nom de l’article, le code de l’article et le prix de l’article.
Type article =structure
{
nom[20] : caractère ;
code :entier ;
prix :réel ;
}

Procédure Afficher (A : article)


début
Ecrire (A.code, A.nom, A.prix);
Fin

Programme_principal
début
A : article ;
Ecrire("Entrez le code, le nom et le prix :");
Lire (A.code, A.nom, A.prix);
Afficher(A); /*appel de la procédure */
Fin.

Exemple 6 : procédure en C qui calcule la puissance d’un entier.

# include<stdio.h>
void puissance(int a)
{
int p;
p=a*a;
printf (" la puissance de %d est = %d\n", a,p);
}
int main() {
int a;
Printf("saisir le nombre: ");
scanf("%d", &a);
puissance( a); /* appel de la procédure*/
return 0;
}

Remarques

 En C, tout programme doit avoir au moins une fonction principale


appelée main ( ) qui est donc la première fonction à exécuter. En java
aussi la méthode principale s’écrit public static void main
(String[] args)
 En langage C, une procédure s'appelle fonction, et une fonction s'appelle
aussi une fonction. Donc, tout sous algorithme en C, est une fonction !
Toutefois, pour la différencier d’une fonction, une procédure est donc
de type «void».
 Selon son utilisation, le programmeur doit faire le choix d’écrire une
fonction ou une procédure, car toute fonction peut être écrite sous
forme de procédure, mais on préfère souvent écrire des fonctions c’est
plus facile quand c’est possible.
 Toute instruction après l’instruction retourner ou return est zappée.
Pour cela on la met en dernier dans l’exécution de la fonction.
 En général, le nombre de paramètres de la fonction est égal au nombre
de paramètres lors de l’appel. Les paramètres effectifs doivent être de
même type que les paramètres formels correspondants, ainsi donc
l’ordre des paramètres doit être respecté au moment de l’appel.
 Beaucoup de sous algorithmes peuvent être écrits sous forme de
fonctions ou procédures pour un même résultat.
 Les informations sont fournies en général par le programme appelant.
Python propose une méthode de transmettre des arguments par mot clé,
en utilisant value, c’est-à-dire donner la valeur dans la liste entre ( ).
Dans ce langage les fonctions sont introduites par le mot clé def
Def nom_fonction( args ) : ……code de la fonction.

1.4 Variables Locales et Globales


3.1 Définition
 une variable locale est une variable qui ne peut être utilisée que dans la
fonction ou le bloc où elle est définie.
 une variable globale est une variable déclarée à l'extérieur du corps de
toute fonction et pouvant donc être utilisée n'importe où dans l’algorithme.
On parle également de variable de portée globale. En C, on déclare les
variables globales juste en dessous des « includes ».

Exemple 7: programme qui affiche via une fonction le contenu d’une variable
globale.

#include<stdio.h>
int C=90; /* variable globale */
int nombre (int)
main()
{
float B=10;
int nombre(int)
{
int A=20; /* variable locale dans la fonction */
printf("%d",C);
}
}

Explication :

 La valeur de C apparait à l’écran parce que C est globale.


 B est locale par rapport au main () mais englobe la fonction nombre ().
 A est locale par rapport la fonction nombre (), la variable A sera détruite
une fois la fonction nombre() exécutée.
Exemple 8 : On modifie l’exemple précédent pour montrer que les variables
locales sont détruites à la fin de l’exécution de la fonction.

#include<stdio.h>
int C=90; /* variable globale*/
int nombre (int)
main()
{ float B=10;
int nombre (int)
{ int A=20; /* variable locale*/
}
printf("%d",A);
}

A l’exécution, nous aurons erreur de compilation parce que la variable A est une
variable locale, elle existe seulement dans le contexte de la fonction nombre(), à
la fin de l’exécution de la fonction, elle est détruite.

3.2 Avantages et inconvénients des variables globales


 Les variables globales peuvent être utilisées partout dans un programme

 Les variables globales rendent plus difficile la compréhension d'un


programme lors de la recherche d'erreurs de programmation (debugging),
on ne sait pas quel sous-programme est à l’origine de l’erreur.

1.5 . La programmation modulaire :


Lorsque le nombre de fonctions et/ou procédures est important, il devient alors
possible au programmeur de classer ses sous-programmes dans différentes
bibliothèques. Ce classement permet de découper le programme en plusieurs
modules améliorant ainsi sa lisibilité et son débogage

Le principe consiste d'une part, à écrire le code des sous-programmes dans des
fichiers .C séparés. Ainsi dans chaque fichier .C, nous retrouvons les codes des
fonctions/procédure de même catégorie.
D'autre part, nous associons à chaque fichier .C, un fichier d'entête (Header) .H.
Ce dernier, regroupe les entêtes (prototypes) des fonctions/procédures contenues
dans le fichier .C.

Enfin, nous ajoutons au niveau du programme principal (main.c), les inclusions


des fichiers d'entête via la directive :

#include "NomFichier.h"
//Nous utilisons les guillemets pour les inclusions externes,
les //chevrons "<...>" étant utilisés pour les inclusions
internes (stdio.h, math.h,…etc)

Exemple : Supposons que nous devons écrire un gros programme avec un


ensemble de fonctions/procédure relatives à la "cryptographie" et aux
"statistiques".
Au lieu d'écrire toutes les fonctions dans un seul et unique programme main.c,
nous allons répartir les codes des sous-programmes dans des bibliothèques .C.
Puis à chacune de ses bibliothèques nous associons un fichier d'entête.H. de la
manière suivante :
Fichier Cryptographie.C Fichier Cryptographie.H
Code des Fonctions et Entêtes des fonctions et
procédure procédures
void EncodeBase64(…)
{ void EncodeBase64(…)
//Code de la fonction/procédure

}
void HashChaineMD5(…)
void HashChaineMD5(…)
{
void CrypteCesar(…)
//Code de la fonction/procédure

}
void CrypteCesar(…) void DecrypteCesar(…)
{
//Code de la fonction/procédure //… autres prototypes

}
void DecrypteCesar(…)
{
//Code de la fonction/procédure

Fichier Statistiques.C Fichier Statistiques.H


Code des Fonctions et Entêtes des fonctions et
procédure procédures
void EcartType(…)

{ void EcartType(…)
//Code de la fonction/procédure

} void Moyenne(…)
void Moyenne(…)

{ void MoyenneQuadratique(…)
//Code de la fonction/procédure

}
void Variance(…)
void MoyenneQuadratique(…)

//Code de la fonction/procédure

void Variance(…)

//Code de la fonction/procédure

Ainsi, nous aurons un programme principal (main.c) allégé sur lequel nous
pouvons appeler chacune des fonctions définies dans les fichiers .H et.C.
1.7. Allocation dynamique de la mémoire
1.7.1 Introduction
Toute déclaration signifie réservation ou allocation c'est-à-dire demander au
système d’exploitation la permission d'utiliser une taille donnée de mémoire.
C’est une réservation statique. Un des principaux intérêts de l'allocation
dynamique est de permettre à un programme de réserver la place nécessaire et
suffisante au stockage des données en mémoire selon le besoin, on peut ne pas
connaitre à l’avance, l’espace à réserver, c’est pour cela que dans une réservation
statique, on réserve plus qu’il n’en faut.

Rappelons que quand on déclare une variable, le système lui donne une adresse et
lui réserve une place. Celle-ci est automatiquement supprimée de la mémoire à la
fin de l’exécution (ou lorsque la fonction est dépilée).

1.7.2 Taille des variables


Selon le type de variable que vous demandez de créer (char, int, double, float…),
vous avez besoin de plus ou moins de mémoire (Un int occupe généralement 4
octets en mémoire).
sizeof() : fournit la taille d’un type à la compilation, cela sera remplacé par le
nombre d'octets que prend int ou autre type en mémoire .
Exemple : Affichage des tailles de différents types
printf("char : %d octets\n", sizeof(char));

printf("int : %d octets\n", sizeof(int));

printf("long : %d octets\n", sizeof(long));

1.7.3 Les fonctions de l’Allocation dynamique


La bibliothèque <stdlib.h> contient deux fonctions :
a) La fonction malloc()
Signifie Memory Allocation c'est-à-dire « Allocation de mémoire »: permet de
réserver de la place en mémoire, quand on fait une allocation de mémoire, on
vérifie la valeur retournée par malloc. Une fois qu'on a fini d'utiliser la mémoire,
on doit la libérer avec free(mais on peut oublier de la libérer et ce n’est pas
grave).
int* place = NULL;/* On crée un pointeur sur un entier nommé place */
place= malloc(sizeof(int));
La fonction malloc renvoie l’adresse de place qui a une taille de 4 octets.

Deux possibilités sont à envisager :


- si l'allocation a marché, le pointeur contient une adresse ;
- si l'allocation a échoué, le pointeur contient l'adresse NULL.
Remarque : la valeur Nil (ou null en langage C) d’un pointeur signifie qu’il
n’y a pas d’adresse. En informatique, on doit représenter explicitement aussi bien
l’existence que l’inexistence d’une chose. Ainsi nous avons déjà vu le mot clé
‘Void’, souvent appelé le type sans type, pour représenter qu’une fonction n’a pas
de type. C’est le principe du zéro en arithmétique. Zéro n’est pas rien. Nuance !

b) La fonction free ()
La fonction free permet de libérer la mémoire, free(place ).

Exemple 1 : allocation dynamique d’un tableau contenant un certain nombre


d’étudiants.
Le tableau « tableau_etudiant « contiendra exactement le nombre d’étudiants
saisi par l’utilisateur, le compilateur fournira l’adresse du tableau (lui réservera
un emplacement en mémoire), après avoir pris connaissance du nombre
d’étudiants, si le nombre d’étudiants saisis tableau_etudiant= 400 alors la taille
sera égale à 4*400=1600 octets.

#include<stdio.h>
#include<stdlib.h>
int main(int)

{
int i, N;
int *tableau_etudiant;
tableau_etudiant=NULL;//initialisation d'un tableau
dynamique
N=0;
printf(" donnez le nombre d’étudiants : ");
scanf("%d",&N);
tableau_etudiant = (int*) malloc(sizeof(int) * N);
/* 4 octets multiplié par le nombre des étudiants
introduit par
l'utilisateur (400)*/

if (tableau_etudiant == NULL)
exit(1);
for (i=0;i<N;i++)
{
printf("l’étudiant %d a pour code %d
\n",i+1,i*10);
tableau_etudiant[i]=i*10;
}
for (i=0;i<N;i++)
{
printf(" [%d] ",tableau_etudiant[i]);
}
free(tableau_etudiant); /* l’espace est libéré*/
return 0;
}

Imaginons que nous voulions représenter les prénoms des enfants dans un
enregistrement relatif à une personne. Une réservation statique réservera disons
trois chaines de caractères, ce qui suppose que pour une personne donnée, nous
ne pouvons saisir que 3 prénoms. S’il a moins, c’est de l’espace perdu, et s’il a
plus on ne peut représenter que les trois premiers. Si le seul habitant du village a
cinq enfants, et qu’on veuille bien les mettre tous dans le fichier, on peut estimer
l’espace perdu d’une telle solution.

Vous aimerez peut-être aussi