Sunteți pe pagina 1din 22

Chapter 1

Introduction à la programmation
procédurale

1.1 Le paradigme

La programmation procédurale est un paradigme de programmation s'appuyant sur le


mécanisme d'appel de procédure. Un programme procédural est constitué d'un ensemble
de procédures, auxquelles il peut faire appel à n'importe quelle étape de son exécution.
Une procédure est une suite d'instructions dédiées à eectuer une tâche bien déterminée.
Cette suite d'instructions indique à la machine comment procéder an d'eectuer la tâche
pour laquelle la procédure a été conçue.
Les programmes se conformant au paradigme de la programmation procédurale sont
plus modulaires et plus structurés que les programmes séquentiels qui se résument à de
simples suites d'instructions exécutées l'une après l'autre, à la manière d'un programme
en langage assembleur 1 . Toutefois, pour concevoir un programme procédural, le pro-
grammeur doit se conformer à une stratégie particulière. Ainsi, le problème à résoudre
est souvent découpé en des sous-problèmes, chacun pouvant être résolu séparément par
une des procédures du programme. Une procédure particulière du programme est alors
dédiée à combiner les solutions des diérents sous-problèmes pour construire la solution
du problème global.
La programmation procédurale est le paradigme suivant lequel opèrent beaucoup de
langages de programmation traditionnels, tels que Pascal, C, Python etc... Soulignons
aussi le fait que ces langages sont les plus proche du langage algorithmique. En eet,
passer d'un algorithme à un programme C, par exemple, revient pratiquement à eectuer
une simple traduction.
Le langage qui sera utilisé dans ce cours pour illustrer les diérents préceptes de la pro-
grammation procédurale est le langage C. Ce choix à été motivé par le fait que ce langage
autorise les constructions algorithmiques usuelles, telles que les conditions, les boucles, les
fonctions, et est de ce fait considéré comme un langage de haut niveau (assez facile à lire
pour un être humain). Le langage C autorise, néanmoins, un accès ecace aux ressources
de bas niveau des machines cibles (les périphériques d'entrées/sorties et la mémoire no-
tamment). Une conséquence de ceci c'est la possibilité de développer assez rapidement
1 Lelangage assembleur, ou langage d'assemblage, est un langage de programmation de bas niveau se
résumant à une séquence d'instructions proche des instructions machine.

1
2 CHAPTER 1. INTRODUCTION À LA PROGRAMMATION PROCÉDURALE

suite_de_directives
/* Un ensemble de fonctions */
type_1 fonction_1(. . .)
{
..
.
}
..
.
type_n fonction_n(. . .)
{
..
.
}
/* Le programme principal */
int main()
{
..
.
return 0;
}
Programme 1: Structure globale d'un programme C.

du code ecace, ce qui justie l'utilisation du C dans de nombreuses applications.

1.2 Structure d'un programme procédural

Selon les préceptes de la programmation procédurale, un programme est une suite non
vide de procédures. Pour le langage C, on parle plutôt de fonction au lieu de procédure,
car chaque procédure C retourne une valeur, à la manière d'une fonction mathématique.
Un programme procédural contient, au moins, la fonction programme principal, dont le
prototype simplié est int main() dans le cas du langage C (voir Programme 1). Cette
dernière est une fonction qui peut être appelée sans paramètre et qui retourne un entier,
d'où l'instruction return 0; qui se trouve à la n de la fonction main() du Programme 1.
La valeur retournée par la fonction main() a pour rôle de faire savoir au système si une
erreur a été détectée. Par convention, une valeur nulle indique que le programme s'est
bien déroulé alors qu'une valeur non nulle indique une erreur.
La fonction main() est automatiquement appelée dès que le programme démarre et
lorsqu'elle termine, le programme termine aussi.
Par ailleurs, il est possible de paramétrer la fonction main(), car cette dernière admet,
en réalité, deux paramètres. Le prototype complet de la fonction main() est le suivant:
int main(int argc, char *argv[]);


• argc est le nombre de paramètres passés au programme, lors de son exécution, plus
1. Donc, ce nombre est toujours supérieur ou égal à 1 car le premier paramètre est,
automatiquement, le nom de l'exécutable.
1.3. LES DIRECTIVES 3

1#include <s t d i o . h>


2
3int main ( int char
argc , ∗ argv [ ] )
4 {
5 int i;
6 // a f f i c h a g e d e s arguments
7 p r i n t f ( "Nombre d ' ' arguments p a s s e s au programme : %d\n" , a r g c ) ;
8 for ( i = 0 ; i < a r g c ; i ++) {
9 p r i n t f ( " argv [%d ] : %s \n" , i , argv [ i ] ) ;
10 }
11 return 0 ;
12 }

Figure 1.1: Programme C achant le nombre et les arguments reçus lors de son exécution.

• argv est un tableau de chaînes de caractères contenant les paramètres passés au


programme, y compris le nom de l'exécutable. Ainsi argv[0] contient le nom de
l'exécutable (éventuellement avec le chemin d'accès). Le ième élément argv[i] con-
tient la chaine de caractère qui correspond au ième paramètre passé au programme.
Le programme de la Figure 1.1 montre une utilisation possible des arguments de la
fonction main(). Il s'agit d'une fonction main() qui ache le nombre et les arguments
reçus lors de l'exécution du programme. Ainsi, en exécutant le programme avec la com-
mande argmain.exe mohamed ali, on obtient l'achage suivant:

Nombre darguments passes au programme : 3


argv[0] : argmain.exe
argv[1] : mohamed
argv[2] : ali

1.3 Les directives

Un programme C commence, le plus souvent, par une suite de directives (voir Pro-
gramme 1). Contrairement aux instructions, qui seront étudiées dans la Section 1.4,
les directives peuvent se trouver à l'extérieur de toute fonction. Les directives sont facile-
ment reconnaissables dans un programme C car elles commencent toujours par le symbole
#.
Les directives sont traitées par un sous-programme du compilateur C qui s'appelle
le préprocesseur. Ce dernier eectue des modications textuelles sur le chier source au
niveau des directives, justement. Ces modications consistent à remplacer, dans le code
source, des bouts de code apparaissant dans les directives par d'autres bouts de code,
(plus volumineux en général), qui se trouvent aussi dans les directives.
Parmi les directives les plus utilisées, on retrouve:

• #include qui permet l'inclusion de chiers entête ou source, notamment des biblio-
thèques contenant des fonctions prédénies par le langage, comme car exemple la
4 CHAPTER 1. INTRODUCTION À LA PROGRAMMATION PROCÉDURALE

directive #include <stdio.h> qui permet d'inclure la bibliothèque standard du


langage C.
• #define qui permet la déclaration des constantes et des macros. Pour la dénition
des constantes, la syntaxe C est la suivante:

#define nom_constante expression

Des exemples de dénition de constante se trouve dans le programme de la Fig-


ure 1.2.
Pour la dénition des macros, la syntaxe C est la suivante:

#define entête_macro corps_macro

L'entête d'une marco comporte le nom de la marco ainsi que ses paramètres, sans
précision de type. Le corps d'une macro est une expression. Par exemple, la directive
suivante dénit une macro qui retourne le cube d'un nombre:
#define Cube(x) ((x)*(x)*(x))

Le préprocesseur remplacera, dans la suite du code, toutes les occurrences de Cube(expr ),


où expr est supposée être une expression à valeur réelle, par le corps de la macro en
substituant expr à x, c'est-à-dire, que Cube(expr ) sera remplacé par ((expr )*(expr )*(expr )).
Il faut noter que les parenthèses employés dans le corps de la marco Cube sont néces-
saires pour ne pas avoir des problèmes de priorité, dans le cas où expr vaut a+1, par
exemple.
L'emploi d'une marco permet, en général, d'obtenir de meilleures performances en
temps d'exécution que l'emploi d'une fonction.
• Les directives de compilation conditionnelle, qui sont en nombre de six: #if, #ifdef,
#ifndef, #elif, #else et #endif. La directive #if permet d'exprimer des condi-
tions liées à la valeur d'une constante. La syntaxe générale d'un schéma conditionnel
utilisant cette directive est la suivante:
#if condition _1
bloc _1
#elif condition _2
bloc _2
..
.
#elif condition _n
bloc _n
#else
bloc_par_defaut
#endif
Dans ce schéma, les directives #elif et #else ne sont que optionnelles.
#ifdef, respectivement #ifndef sont des directives qui permettent d'exprimer des
conditions liées à l'existence, respectivement, l'absence d'un objet. Un exemple de
1.4. LES INSTRUCTIONS 5

l'utilisation de #ifndef est le suivant:


#ifndef true
# define true 1
# define false 0
#endif
Cette dernière directive dénit les deux constantes logiques, dans le cas où elles ne
sont pas déjà dénies.

1.4 Les instructions

Comme il a été précisé plus haut, un programme procédural se compose d'un ensemble
non vide de procédures. Une procédure est, à son tour, composée de briques de base
appelées instructions. Ceci sous-entend que toute instruction du programme doit gurer
à l'intérieur d'une procédure. Une instruction peut être élémentaire ou composée. Dans ce
qui suit, nous présentons, tout d'abord, les instructions élémentaires puis les instructions
composées les plus courantes du langage C.

1.4.1 Variables et aectations


Comme pour les algorithmes, un programme procédural à besoin de variables pour calculer
le résultat qu'il est sensé produire. Pour certains langages, comme Python, il susait
d'aecter une valeur à une variable pour que cette dernière existe et soit utilisable dans
la suite du programme. Avec le langage C, il en est tout autrement. En eet, chaque
variable a un type qui lui est attribué une fois pour toute lors de sa déclaration. Ceci
sous-entend que, avec le langage C, chaque variable doit être déclarée explicitement par
le programmeur. Cette déclaration comporte alors deux informations essentielles: le type
et le nom de la variable déclarée. La forme générale d'une telle déclaration est:

type liste_d'identicateurs ;
où la liste des identicateurs contient des identicateurs séparés par des virgules. Un
exemple de déclaration de variables se trouve à la Ligne 9 du programme de la Figure 1.2.
Une variable ne peut avoir que des valeurs ayant le type qui lui est aecté lors de sa
déclaration. C'est sur ce type que le compilateur du langage s'appuie pour vérier la
cohérence des opérations eectuées sur la variable en question.
Le langage C ore un ensemble de types de base qui sont résumés dans le Tableau 1.1.

Le moyen le plus simple permettant d'aecter une valeur à une variable est d'exécuter
une aectation. L'aectation est sans doute l'instruction élémentaire la plus utilisée lors
de l'écriture d'un programme procédural. Dans le fragment de Programme 2, les deux
variables ville et annee ont étés initialisées à la chaîne de caractère La Marsa et à la
valeur entière 1967, en utilisant deux aectations. Le symbole = est celui de l'aectation
en langage C. À gauche du =, on trouve toujours le nom d'une variable et à droite on
trouve une expression qui va donner sa valeur à la variable. La variable et l'expression
doivent être de même type ou, du moins, de types compatibles.
6 CHAPTER 1. INTRODUCTION À LA PROGRAMMATION PROCÉDURALE

1 #include <s t d i o . h>


2 #include <math . h>
3 #define e p s i l o n 1E−6
4 #define a i g u 130
5 #define c i r c 136
6
7 int main ( ) // Procéd ure programme p r i n c i p a l
8 {
9 float a,b,c ,d,x,y;
10
11 p r i n t f ( "Donnez l e s t r o i s c o e f f i c i e n t s du l '% c q u a t i o n : " , a i g u ) ;
12 s c a n f ( "%f%f%f " ,&a ,&b,& c ) ;
13
14 while ( fabs (a) < epsilon )
15 {
16 p r i n t f ( " l e p r e m i e r c o e f f i e n t ne d o i t pas %c t r e nul , r e s a i s i s s e z
le : " , circ ) ;
17 s c a n f ( "%f " ,&a ) ;
18 }
19
20 d = b∗b − 4∗ a ∗ c ;
21
22 if( d<0)
23 {
24 p r i n t f ( " Pas de s o l u t i o n \n" ) ;
25 }
26 else
27 if ( d == 0 )
28 {
29 x = −b / ( 2 ∗ a ) ;
30 p r i n t f ( " Racine d o u b l e : %f " , x ) ;
31 }
32 else
33 {
34 d = sqrt (d) ;
35 x = (−b+d ) / ( 2 ∗ a ) ;
36 y = (−b−d ) / ( 2 ∗ a ) ;
37 p r i n t f ( "Deux r a c i n e s : %f %f " , x , y ) ;
38 }
39
40 return 0; // Valeur de r e t o u r ( f i c t i v e dans l e c a s p r é s e n t )
41 }

Figure 1.2: Programme C pour la résolution des équations du second degré.


1.4. LES INSTRUCTIONS 7

type Désignation Format


char caractère %c
char [] chaine de caractère %s
unsigned short entier sans signe %d
unsigned int entier sans signe %d
short entier court %d
int entier %d
long int entier long %ld
float réel simple précision %f ou %e
double réel double précision %lf ou %le
void ensemble vide
Table 1.1: Types de base du langage C et leurs formats de saisie/achage.

..
.
// Déclaration d'une variable de type entier
int annee;
// Déclaration et initialisation d'une variable de type chaîne de caractères
char ville[64] = "La Marsa";
..
.
// Aectation de valeur aux deux variables déjà déclarées
annee = 1967;
ville = La Goulette // affectation incorrecte;
..
.
Programme 2: Déclaration et aectation de valeurs aux variables.

Règle 1. Avant d'utiliser une variable dans un calcul, il faut la déclarer et lui
aecter une valeur.

Par ailleurs, le langage C ore des aectations dites combinées. Il s'agit d'aectations
combinées avec des opérateurs binaires de l'arithmétique usuelle ou booléenne. La liste
de ces aectations combinées est la suivante: += -= *= /= %= &= |=. D'une manière
générale, une instruction de la forme: var op = expression ; est équivalente à
var = var op expression ;
Le langage C se distingue aussi par deux opérateurs à eet de bord2 . Ce sont les
opérateurs d'auto-incrémentation (++) et d'auto-décrémentation (--). Ces opérateurs
permettent d'ajouter ou de retrancher 1 au contenu d'une variable. Chacun de ces opéra-
teur peut être utilisée de manière préxée ou postxée. Ainsi, on peut écrire ++var ou
var ++ pour l'incrémentation et --var ou var -- pour la décrémentation. La diérent en-
tre la version préxé et la version postxée est très subtile et se résume dans la valeur
renvoyée par l'expression composée. Ainsi, la valeur renvoyée par ++var vaut var +1, alors
celle renvoyée par var ++ vaut var. La valeur renvoyée par une auto-décrémentation suit
la même logique.
2 Nous appelons eet de bord le fait de modier le contenu d'une variable.
8 CHAPTER 1. INTRODUCTION À LA PROGRAMMATION PROCÉDURALE

1#include <s t d i o . h>


2#include <math . h>
3int main ( )
4 {
5 double p i = M_PI; // l a c o n s t a n t e M_PI e s t d é f i n i e dans math . h
6 p r i n t f ( "%f %9.8 f %12.11 f " , pi , pi , p i ) ;
7 return 0;
8 }

Figure 1.3: Trois approximations possibles de pi.

1.4.2 Lecture et écriture


Tout programme utile fonctionne avec des données qu'il reçoit comme paramètres ou qu'il
saisit à partir d'un dispositif d'entrée/sortie. Avec le langage C, la saisie des données peut
être eectuer par la fonction
scanf("f ormat_de_lecture",liste_d0 adresses)

dont l'entête est dénie dans la bibliothèque standard du C stdio.h, qu'on doit inclure
avec la directive #include <stdio.h> . En tant que fonction, scanf() retourne un
entier, qui est non nul si la fonction s'est bien déroulée. La fonction scanf() permet la
lecture des valeurs tapées au clavier et, par la même, aecte ces valeurs aux variables dont
les adresses lui sont passées en paramètre (voir le programme de la Figure 1.2).
Le langage C ore deux autres fonctions de saisie de donnée, la première getchar() re-
tourne comme résultat un caractère saisi au clavier et la seconde, gets(chaine ), récupère
dans son paramètre une chaîne de caractère (voir le programme de la Figure 1.4).

An de communiquer ses résultat, une fois les calculs terminés, un programme C doit
avoir accès à un dispositif de sortie. Le moyen le plus simple pour acher des résultats
est d'utiliser la fonction:
printf("f ormats_d0 af f ichage",liste_d0 expressions)

A titre d'exemple, le programme C de la Figure 1.3 ache les trois approximations de pi


suivantes:
3.141593 3.14159265 3.14159265359
Le langage C ore deux autres fonctions d'achage de donnée, la première putchar(c)
ache un caractère passé en paramètre et la seconde, puts(chaine ), ache une chaine
de caractères passée en paramètre (voir le programme de la Figure 1.4).

1.4.3 Les schémas conditionnels


Lors de l'exécution d'un programme, les instructions qui composent le programme sont
exécutées suivant l'ordre de leur apparition dans le texte du programme. On peut avoir
besoin d'exécuter certaines instructions uniquement quand une condition est vraie. Par
exemple, en exécutant une fois le programme de la Figure 1.2, les instructions ne sont
jamais exécutées toutes à la fois. Dans de pareilles situations, on est amené a utiliser des
1.4. LES INSTRUCTIONS 9

1 #include <s t d i o . h>


2 #define MAX_CHAINE 64
3
4 int main ( )
5 {
6 char c , ch [MAX_CHAINE ] ;
7
8 p u t s ( "Donnez un c a r a c t è r e : " ) ;
9 c = getchar () ;
10 putchar ( c ) ;
11 f f l u s h ( s t d i n ) ; // v i d e r l e b u f f e r
12
13 p u t s ( " \nDonnez une c h a i n e de c a r a c t è r e : " ) ;
14 g e t s ( ch ) ;
15 p u t s ( ch ) ;
16
17 return 0;
18 }

Figure 1.4: Test de quelques fonctions d'entrée/sortie pour caractère et chaîne de carac-
tère.

structures dites de contrôle, qui modient l'ordre d'exécution linéaire. Pour ce faire, on
fait principalement recours aux structures de contrôle décrites dans les sous-paragraphes
suivants.
Le langage C ore les trois schéma conditionnels classiques, qui sont les schémas à
une, deux ou plusieurs alternatives. La syntaxe de ces schémas est montrée dans le
Programme 3.
L'instruction if : c'est le schéma conditionnel le plus simple, qui implique un seul
traitement dont l'exécution est contrôlé par une condition. Cette dernière est, le plus
souvent, une expression logique.
L'instruction if-else : ce schéma impliques deux traitements, dont un et un seule-
ment est exécuté selon la valeur logique de l'expression de contrôle.
L'instruction switch : Ce schéma, appelé aiguillage, est utilisé quand il y a plus que
deux traitements diérents à exécuter selon les valeurs constantes d'une expression dite
de contrôle. La syntaxe générale de l'aiguillage se trouve dans la colonne de droite du
Programme 3. Si aucune des constantes listées n'est égale à la valeur de l'expression de
contrôle alors un traitement par défaut est exécuté.
Exercice 1. Écrivez un programme C qui saisit trois nombres réels, supposés
correspondre aux longueurs des trois cotés d'un triangle, puis détermine si le triangle
en question est rectangle ou pas.

1.4.4 Les schémas répétitifs (ou boucles)


La plupart des langages procéduraux permettent de coder les trois boucles standards de
l'algorithmique, en particulier le langage C, qui utilise la syntaxe décrite dans le Pro-
10 CHAPTER 1. INTRODUCTION À LA PROGRAMMATION PROCÉDURALE

1 #include <s t d i o . h>


2 int main ( )
3 {
4 unsigned int n;
5
6 p r i n t f ( "Donnez un e n t i e r de 1 à 9 : " ) ;
7 s c a n f ( "%d" ,&n ) ;
8
9 switch (n)
10 {
11 case 1:
12 p r i n t f ( " 1\n" ) ;
13 break ;
14 case 2:
15 p r i n t f ( " 2\n" ) ;
16 break ;
17 case 3:
18 p r i n t f ( "1+2\n" ) ;
19 break ;
20 case 4:
21 p r i n t f ( "2+2\n" ) ;
22 break ;
23 case 5:
24 p r i n t f ( " 5\n" ) ;
25 break ;
26 case 6:
27 p r i n t f ( "1+5" ) ;
28 break ;
29 case 7:
30 p r i n t f ( "2+5\n" ) ;
31 break ;
32 case 8:
33 p r i n t f ( "1+2+5\n" ) ;
34 break ;
35 case 9:
36 p r i n t f ( "2+2+5" ) ;
37 break ;
38 default :
39 p r i n t f ( "L ' e n t i e r s a i s i n ' e s t pas e n t r e 1 e t 9\n" ) ;
40 }
41 }

Figure 1.5: Programme C pour la résolution des équations du second degré.


1.4. LES INSTRUCTIONS 11

if (condition )
{ switch (expression )
bloc {
} case val _1 :
bloc _1
break;
if (condition ) case val _2 :
{ bloc _2
bloc_alors break;
} ..
.
else
case val _n :
{
bloc_sinon bloc _n
break;
}
default :
bloc_par_defaut
}

Programme 3: (Gauche) Schéma conditionnel à une ou deux alternatives.


(Droite) Schéma conditionnel à plusieurs alternatives.

gramme 4.

La boucle for : cette boucle à une structure bien précisée. Le plus souvent, elle est
utilisée pour répéter un traitement qui dépend de la valeur d'un compteur. Le programme
de la Figure 1.6 montre un cas typique de l'utilisation de la boucle for.

La boucle while :qui correspond à la boucle tant-que standard du langage algorith-


mique. La condition est évaluée en premier et si elle est vraie, le traitement qui se trouve
à l'intérieur de la boucle est exécuté. Ensuite, la condition est évaluée de nouveau et ainsi
de suite.

La boucle do-while : La particularité de cette boucle est que le bloc d'instructions


qui se trouve à l'intérieur de la boucle est amené à s'exécuter, au moins, une fois. Cette
boucle est souvent utilisée pour forcer l'utilisateur à saisir des données vériant certaines
propriétés, comme c'est le cas avec le coecient du monôme de degré 2, (a), du programme
de la Figure 1.2.

Exercice 2. Codez, en langage C, la boucle itérer (voir Chapitre 1 du cours


d'algorithmique).
12 CHAPTER 1. INTRODUCTION À LA PROGRAMMATION PROCÉDURALE

1 #include <s t d i o . h>


2
3 int main ( )
4 {
5 unsigned int i , n , f , f0 , f 1 ;
6
7 p r i n t f ( "n? " ) ;
8 s c a n f ( "%d" ,&n ) ;
9
10 switch (n)
11 {
12 case 0:
13 p r i n t f ( " 0\n" ) ;
14 break ;
15 case 1:
16 p r i n t f ( "0 1\n" ) ;
17 break ;
18 default :
19 p r i n t f ( "0 1 " ) ;
20 f0 = 0;
21 f1 = 1;
22
23
24 for ( i = 2 ; i <= n ; i++ )
25 {
26 f = f1 ;
27 f1 = f1 + f0 ;
28 f0 = f ;
29 p r i n t f ( "%d " , f 1 ) ;
30 }
31 }
32
33 return 0;
34 }

Figure 1.6: Programme C pour le calcul de la suite de Fibonacci pour un nombre n donné.
1.4. LES INSTRUCTIONS 13

for (instruction_initiale ; condition ; instruction_itérative )


{
instructions_à_répéter
}

while (condition )
{
instructions_à_répéter
}

do
{
instructions_à_répéter
}
while (condition );
Programme 4: Les boucles du langage C.

Exercice 3.

• Écrivez un programme C qui saisit trois suites de nombres entier se terminant


par zéro, et ceci en utilisant, à chaque fois, une boucle diérente parmi celle
décrites dans le Programme 4.
• Ajoutez, à présent, la saisie d'une quatrième suite, se terminant toujours par
zéro, avec la boucle itérer codée dans l'Exercice 2.
• Quelle est, parmi les quatre boucles, celle qui vous semble la plus adéquate
pour saisir une suite d'élements se terminant par un élément particulier.

1.4.5 Les branchements inconditionnels


Une autre façon de couper avec le déroulement linéaire et ordonné des instructions d'un
programme consiste à utiliser des instructions de branchement inconditionnels. Ces
dernières permettent de se brancher dans un endroit du programme qui n'est pas force-
ment l'instruction suivante, et ceci sans être lié par une condition.
Le langage C ore des instructions de branchement qui sont en nombre de quatre

• return: cette instruction a été déjà rencontrée lors de la présentation de la fonction


main. En fait, c'est à l'aide de l'instruction return qu'une fonction peut commu-
niquer son résultat à la fontion appelante. Rappelant que la syntaxe C est: return
expression ; où expression peut être omise, mais dans ce cas, la fonction exécutant
le return doit avoir comme type de retour void.
L'usage de l'instruction return, avec ou sans expression de retour, met immédiate-
ment n à l'exécution de la fonction courante et retourne à la fonction appelante
sans que les instructions qui suivent l'instruction return ne soient exécutées. C'est
14 CHAPTER 1. INTRODUCTION À LA PROGRAMMATION PROCÉDURALE

également le cas si l'instruction return est exécutée à l'intérieur d'un aiguillage ou


d'une boucle.
• break: cette instruction, qui s'exécute sans argument, permet de quitter le corps
d'une boucle ou d'un aiguillage (voir le programme de la Figure 1.5 pour ce dernier
cas). Si l'instruction break se trouve à l'intérieur de plusieurs boucles imbriquées
alors son exécution permet uniquement de quitter la boucle la plus interne.
• continue: l'exécution de cette instruction, qu'on place dans le corps des boucles,
provoque un branchement inconditionnel vers la n du corps de la boucle, sans
quitter cette dernière. S'il s'agit d'une boucle while ou do-while, cela provoque
directement l'évaluation de la condition de la boucle. Dans le cas d'une boucle for,
l'instruction itérative, (voir Programme 4), est exécutée avant la nouvelle évaluation
de la condition. L'instruction continue trouve son principal intérêt pratique dans
une boucle dont le corps est constitué d'une succession d'alternatives exclusives les
unes des autres.
• goto: qui est utile au développement de compilateur C, mais pour ce qui est des
programmes communs, il vaut mieux ne pas l'utiliser.

1.5 Expressions arithmétiques et logiques

Parmi les constituant d'un programme C, on peut trouver des expressions arithmétiques et
logiques. Ces dernières ne constituent pas des instructions à part entière, car une expres-
sion arithmétique ou logique ne constitue pas, à elle seule, une instruction du programme.
Cependant, le membre droit d'une aectation est, le plus souvent, une expression arith-
métique ou logique.

Une expression arithmétique bien formée peut contenir des constantes et/ou variables
numériques, (entières ou réelles), combinées à l'aide des opérateurs arithmétiques (voir le
premier point de la liste ci-après), avec éventuellement des parenthèses ouvrantes et fer-
mantes3 . Quelques exemple d'expression arithmétiques apparaissent dans le programme
de la Figure 1.2. La valeur d'une expression arithmétique bien formée est un nombre
(un entier ou un réel). Pour calculer correctement une telle valeur, on doit tenir compte
des priorité des opérateurs arithmétique qui y gurent. Rappelons que * et / sont plus
prioritaires que + et -. Si l'expression arithmétique désirée ne suit pas ces priorités, on
doit recourir à l'utilisation de parenthèses.
Pour les expressions logiques, on peut les obtenir à l'aide d'opérateurs de comparaison
(voir le deuxième point de la liste ci-après), plus les connecteurs logiques usuels (voir le
troisième point de la liste qui suit).

• + - * / % : oprérations arithmétiques usuelles et reste de la division euclidienne;

• > >= < <= == != : comparaisons > ≥ < ≤ = 6=;

• ! && || : connecteurs logiques non et ou .


3 Voir le Chapitre 1 du cours d'algorithmiqueet structures de données.
1.6. CONVERSION DE TYPE 15

En plus des variables et constantes, les expressions arithmétiques peuvent faire inter-
venir des appels de fonctions mathématiques usuelles telles que les fonctions trigonométriques,
la racine carrée ou la fonction logarithme. Toutes ces fonctions sont disponibles dans la
bibliothèque standard math.h qu'on peut inclure avec la directive #include <math.h> .
Dans une expression arithmétique ou logique, on peut aussi trouver des appels de fonctions
dénies par le programmeur.
Un exemple d'une expression en langage C est la formule de Gauss:

1/(sig*sqrt(2*pi))*exp(pow(x-mu,2)/(2*sig*sig))

Nous terminons le paragraphe avec les expressions dites conditionnelles, qui sont spé-
ciques au langage C. Il s'agit d'expressions dont la forme générale est la suivante:

expression _1 ? expression _2 : expression _3

où expression _1 est interprétée comme une expression booléenne de sorte que si sa valeur
est non nulle alors la valeur de l'expression conditionnelle vaut expression _2, sinon elle
vaut expression _3.
Les expressions conditionnelles sont notamment utilisées lors de la dénition de macros
(voir Section 1.3). En voici un exemple dénissant une marco qui retourne le maximum
de deux nombres:
#define Max(a,b) (a > b ? a : b)

1.6 Conversion de type

Dans les exemples qui ont précédés, dans un même calcul nous n'utilisions que des vari-
ables ou fonctions de même type ou, du moins, de types cohérents. Il arrive néanmoins
qu'il soit nécessaire de faire intervenir des types diérents dans un même calcul. On peut,
par exemple, trouver dans un même calcul, des nombres réels et des nombres entiers.
Deux cas de gure sont possibles:

• si le compilateur sait que la transmission d'une valeur d'un type à un autre peut
se faire sans perte d'information, (d'un entier vers un réel, par exemple), alors la
conversion aura lieu implicitement,

• si, au contraire, le compilateur soupçonne un risque de perte d'information, (en


aectant une variable réelle à une variable entière, par exemple), alors il pourra
générer un message d'avertissement, (warning en anglais), lors de la compilation.
Dans ce second cas, le programmeur peut indiquer au compilateur qu'une telle con-
version est intentionnelle. Pour ce faire, le programmeur peut utiliser une conversion
explicite de type, qui opère selon le schéma général décrit dans le Programme 5.

Néanmoins, une conversion ne modie pas la valeur d'origine mais en produit une
copie dont le type est changé (la variable d'origine garde bien évidemment sa valeur et
son type).
16 CHAPTER 1. INTRODUCTION À LA PROGRAMMATION PROCÉDURALE

type _1 x; /* déclaration d'une variable du type _1 */

type _2 y; /* déclaration d'une variable du type _2 */

..
.
y = (type _2) x; /* conversion du type _1 vers le type _2 */

Programme 5: Une opération générique de conversion de type.

1 #include <s t d i o . h>


2 #include <math . h>
3
4 int main ( )
5 {
6 unsigned int i;
7 int d;
8 double x;
9 float y;
10
11 x = −M_PI;
12 y = ( float ) x; // C o n v e r s i o n de d o u b l e v e r s f l o a t
13 d = ( int ) floor (y) ; // C o n v e r s i o n de f l o a t v e r s i n t
14 i = ( unsigned int ) abs ( d ) ; // C o n v e r s i o n de i n t v e r s u n s i g n e d i n t
15 p r i n t f ( " v a l e u r s de x , y , d e t i : %l f %f %d %d" , x , y , d , i ) ;
16
17 return 0;
18 }

Figure 1.7: Programme C eectuant des conversions de type.


1.7. LES TABLEAUX 17

1.7 Les tableaux

Les données qu'on peut avoir à manipuler par un programme sont, parfois, composées d'un
grand nombre de données élémentaires similaires, en l'occurrence, de même type. C'est
le cas, par exemple, si on veut saisir et mémoriser les notes des étudiants d'une classe.
Pour représenter de telles données au niveau d'un programme, on a besoin d'un grand
nombre de variables de même type, des variables réelles, par exemple. Si on se limitait
aux variables simples utilisées dans le début du chapitre, on serait obligé de déclarer, dans
le programme, autant de variables que de notes à saisir. Ceci n'est pas pratique, car le
nombre d'étudiants d'une classe peut être assez grand et, surtout, peut varier d'une classe
à une autre. On serait donc obligé de modier le programme pour chaque classe!
Dans des cas pareils, la solution se trouve dans l'utilisation des tableaux. Ces derniers
sont des structures de données composées de plusieurs variables simples de même type.
L'accès à ces variables se fait à l'aide d'indices, qui sont des entiers dans le cas du lan-
gage C. Selon le besoin, ces tableaux peuvent être uni- ou multi-dimensionnels et on y
accédera en utilisant un ou plusieurs indices, selon la dimension (voir la Figure 1.8 qui
illustre un tableau uni-dimensionnel). Néanmoins, quelque soit sa dimension, les cases
d'un même tableau se trouveront lors de l'exécution dans des zones contiguës de la RAM
de l'ordinateur et c'est d'ailleurs cette particularité qui rend l'accès via des indices possible.

Pour pouvoir utiliser un tableau dans un programme C, il faut tout d'abord le déclarer,
comme toute autre variable (Règle 1). Cette déclaration se fait au début des fonctions
du programme, comme pour n'importe quelle variable. Il est aussi possible de déclarer
des tableaux globaux, et ceci à l'extérieur de toutes les fonctions, d'habitude juste après
la suite des directives du programme.
Lors de la déclaration d'un tableau, on doit préciser trois informations:
• le type de chaque élément du tableau.

• le nom du tableau, qui est un identicateur composé de lettres, de chires, ou


symbole _ et ne commençant pas par un chire.
• la taille de chaque dimension du tableau, qui doit être une constante entière positive
placée entre deux crochets.
Ces informations sont données selon la syntaxe générale suivante:
type nom_tableau [taille_dim_1 ]. . .[taille_dim_d ];
où d est la dimension du tableau. L'instruction oat matrice[128][128]; , par ex-
emple, déclare un tableau bi-dimensionnel de réels simple précision de taille 128 × 128. Il
est, toutefois possible de ne pas préciser la taille d'un tableau lors de sa déclaration, mais
dans ce cas, il faut l'initialiser dès sa déclaration, comme dans la déclaration suivante:
int premiers[] = {2,3,5,7,11,13,17,19};
En langage C, l'accès aux diérentes cases d'un tableau d-dimensionnel ce fait à l'aide
de d indices. Ces derniers sont des entiers compris entre zéro et la taille de la dimen-
sion correspondante moins 1. Chaque indice est placé entre deux crochets comme dans
18 CHAPTER 1. INTRODUCTION À LA PROGRAMMATION PROCÉDURALE

un indice élément de la case 8


0 1 2 3 4 5 6 7 8 9 Indices

Taille du tableau = 10

Figure 1.8: Un tableau uni-dimensionnel contenant 10 cases indexées par les entiers de 0
à 9.
n/p 0 1 2 3 4 5
0 1
1 1 1
2 1 2 1
3 1 3 3 1
4 1 4 6 4 1
5 1 5 10 10 5 1

Table 1.2: Triangle de Pascal pour n=5.

l'aectation suivante tab[i] = 2018; où tab est le nom du tableau uni-dimensionnel et


i est un indice. Grâce aux indices, chaque case d'un tableau peut être traitée comme si
c'était une variable simple.

Exemple 1. Le programme C de la Figure 1.9 saisit un ensemble de notes dans un tableau


uni-dimensionnel et majore ces notes de telle sorte que la note maximale soit égale à 20.

Dans l'Exemple 1, il n'était pas nécessaire de faire recours à une structure de donnée
plus complexe qu'une suite linéaire d'éléments. Une fois cette suite stockée dans un
tableau, ce dernier permet un accès direct à n'importe quel élément de la suite, à l'aide
d'un indice unique.
Dans d'autres situations, la collection d'éléments de même type peut avoir une struc-
ture plus complexe. Elle peut, par exemple, être sous-forme d'un tableau bi-dimensionnel.
Une utilisation très fréquente des tableaux bi-dimensionnels a lieu avec la manipulation
des images numériques dites image à niveau de gris. Avec de telles images, chaque entrée
du tableau sert à mémoriser une valeur numérique, le plus souvent un entier comprit entre
0 et 255. l'accès à un tel tableau se fait donc, à l'aide de deux indices qui varient dans les
deux intervalles spéciés dans la déclaration du tableau. Ainsi, l'entrée (i, j) d'un tableau
tab bi-dimensionnel sera référencé par tab[i][j].

Exemple 2. On voudrait acher le triangle


 de n−1
Pascal sous la forme illustrée dans le
Tableau 1.2 en se servant de l'identité p = p + n−1
n
. Le programme C de la

p−1
Figure 1.10 eectue l'achage du triangle de Pascal en utilisant un tableau à deux di-
mensions.
1.7. LES TABLEAUX 19

1 #include <s t d i o . h>


2 #define TAILMAX 128
3
4 int main ( )
5 {
6 unsigned int i , n;
7 float max , tabNote [TAILMAX ] ;
8
9 p r i n t f ( "Combien de n o t e %c s a i s i r ? : " , 1 3 3 ) ;
10 s c a n f ( "%d" ,&n ) ;
11
12 if ( n==0) return 0;
13
14 for ( i =0; i <n ; i ++)
15 {
16 do {
17 p r i n t f ( "%d : S a i s i s s e z une n o t e ( e n t r e 0 e t 2 0 ) : " , i +1) ;
18 s c a n f ( "%f " ,& tabNote [ i ] ) ;
19 } while ( tabNote [ i ] < 0 | | tabNote [ i ] > 2 0 ) ;
20 }
21
22 max = tabNote [ 0 ] ;
23
24 for ( i =1; i <n ; i ++)
25 if ( tabNote [ i ] > max)
26 max = tabNote [ i ] ;
27
28 for ( i =0; i <n ; i ++)
29 p r i n t f ( "%d : n o t e major%c e : %4.2 f \n" , i +1 ,130 , tabNote [ i ] −max+20) ;
30
31 return 0;
32 }

Figure 1.9: Programme de majoration de note utilisant un tableau à une dimension.


20 CHAPTER 1. INTRODUCTION À LA PROGRAMMATION PROCÉDURALE

1 #include <s t d i o . h>


2 #define TAILMAX 64
3
4 int main ( )
5 {
6 int N, n , p , t r i a n g l e [TAILMAX ] [ TAILMAX ] ;
7
8 p r i n t f ( " S a i s i s s e z l a d i m e n s i o n du t r i a n g l e : " ) ;
9 s c a n f ( "%d" ,&N) ;
10
11 triangle [ 0 ] [ 0 ] = 1;
12 p r i n t f ( " 1\n" ) ;
13 for( n=1;n<=N; n++)
14 {
15 triangle [n ] [ 0 ] = 1;
16 p r i n t f ( "1 " ) ;
17 for ( p=1;p<n ; p++)
18 {
19 t r i a n g l e [ n ] [ p ] = t r i a n g l e [ n−1][p ] + t r i a n g l e [ n−1][p−1];
20 p r i n t f ( " %d" , t r i a n g l e [ n ] [ p ] ) ;
21 }
22 triangle [n ] [ n] = 1;
23 p r i n t f ( " 1\n" ) ;
24 }
25
26 return 0;
27 }

Figure 1.10: Programme d'achage du triangle de Pascal.


1.8. CONCLUSION 21

Exercice 4. Revenons de nouveau aux coecients binomiaux. Cette  fois, on


voudrait calculer un seul des coecients en se servant de l'identité p = p +
n n−1

n−1
.

p−1

• Ecrire un programme C qui calcule le coecient n


, pour deux entiers n et p

p
donnés en se servant d'un tableau bi-dimensionnel, (on cherchera à n'eectuer
que les calculs nécessaires).
• Proposez, à présent, une solution qui utilise un tableau uni-dimensionnel.

Exercice 5. Un mot v est une anagramme d'un mot w s'il existe une permutation
des lettres qui transforme v en w. Étant donné un ensemble de n mots de longueur
k , on veut écrire un programme C qui détermine toutes les classes d'anagrammes.

1.8 Conclusion

En guise de conclusion du chapitre, nous listons quelques avantages et inconvénients de la


programmation procédurale. Certains des points soulevés ci-dessous ne sont valables qu'en
comparaison avec d'autres paradigmes de programmation, notamment, la programmation
orientée objet.
Parmi les avantages de la programmation procédurale, on peut citer:

• la simplicité relative à concevoir des programme procéduraux, car l'approche procé-


durale ressemble à la façon de procéder de l'être humain dans la vie de tout les jours
pour résoudre des problèmes.
• La possibilité de concevoir des programmes fortement modulaires et structurés, et
par conséquent, la possibilité d'utiliser le même bout de code (la procédure) à dif-
férents endroits du programme sans avoir à le réécrire.
• Une facilité relative à suivre le déroulement du programme, très utile pendant le
débogage.
• Les programmes procéduraux sont peu gourmand en mémoire, en comparaison avec
des programmes issus d'autres paradigmes.

Voici, à présent, quelques désavantages de la programmation procédurale:


• Le programmeur doit indiquer, avec précision, comment la machine doit procéder
pour résoudre un problème donné.
• Les données peuvent être disponible dans tout le programme, ce qui va contre la
sécurité de ces données.
• Une plus grande importance est donnée aux opérations que le programme exécute
sur les données et non pas aux données.
22 CHAPTER 1. INTRODUCTION À LA PROGRAMMATION PROCÉDURALE

• Les données sont dissociées des procédures qui les manipulent.

• Pour de grandes applications, on peu se retrouvé avec du code Spaghetti. Ceci se


traduit par des programmes qui font trop d'appels imbriqués de procédures, qui sont
diciles à suivre lors de la modication du code.

S-ar putea să vă placă și