Vous êtes sur la page 1sur 56

ROYAUME DU MAROC

FORCES ARMEES ROYALES


MARINE ROYALE
ECOLE ROYALE NAVALE

1ère Année Cycle Ingénieur

SUPPORT DE COURS

Algorithmique et Structures de données

PROFESSEUR
ADIL SAYOUTI
ANNEE UNIVERSITAIRE
2017 / 2018
Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Tables des matières


I. Algorithmique et programmation ............................................................... 1
1. Notion de programme .......................................................................................................................................1
2. Algorithme, programmation et langage de programmation .............................................................1
3. Présentation du langage C ...............................................................................................................................2

II. Variables ............................................................................................................... 2


1. Données et variables ..........................................................................................................................................2
1.1. Notion de variable .....................................................................................................................................2
1.2. Déclaration des variables .......................................................................................................................2
2. Types de variables...............................................................................................................................................3
2.1. Type numérique .........................................................................................................................................3
2.2. Type caractère.............................................................................................................................................3
2.3. Type booléen................................................................................................................................................3
3. Opérateurs et expressions ...............................................................................................................................3
3.1. Affectation .....................................................................................................................................................3
3.2. Opérateurs arithmétiques ......................................................................................................................4
3.3. Opérateurs relationnels ..........................................................................................................................5
3.4. Opérateurs logiques ..................................................................................................................................5
3.5. Opérateurs d’incrémentation et de décrémentation...................................................................5
4. Entrées/Sorties ....................................................................................................................................................6
4.1. Fonction printf ............................................................................................................................................6
4.2. Fonction scanf .............................................................................................................................................7
5. Présentation du langage C par l’exemple...................................................................................................7
5.1. Exemple d’un programme ......................................................................................................................7
5.2. Structure d'un programme en langage C .........................................................................................7
5.3. Création d’un programme en langage C ...........................................................................................8

III. Sélection ............................................................................................................... 8


1. Introduction ...........................................................................................................................................................8
2. Sélection simple ...................................................................................................................................................9
3. Sélection imbriquée ............................................................................................................................................9
4. Choix multiples .................................................................................................................................................. 10

IV. Itération ............................................................................................................. 10


1. Introduction ........................................................................................................................................................ 10
2. Boucle déterministe – boucle POUR ......................................................................................................... 11
3. Boucle indéterministe .................................................................................................................................... 11
3.1. Boucle TANT QUE ................................................................................................................................... 12
3.2. Boucle REPETER JUSQU’A ................................................................................................................ 12
4. Boucle imbriquée.............................................................................................................................................. 13

V. Organigramme ................................................................................................. 13
1. Définition ............................................................................................................................................................. 13
2. Symboles .............................................................................................................................................................. 13
3. Sélection ............................................................................................................................................................... 14
4. Itération ................................................................................................................................................................ 14

Professeur : Adil SAYOUTI Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

1.3. Boucle POUR ............................................................................................................................................. 14


1.4. Boucle TANT QUE ................................................................................................................................... 15
1.5. Boucle REPETER…JUSQU'A ................................................................................................................ 15

VI. Tableaux et chaînes de caractères ............................................................ 16


1. Tableau à une dimension .............................................................................................................................. 16
2. Tableau à deux dimensions .......................................................................................................................... 17
3. Chaînes de caractères ..................................................................................................................................... 18

VII. Programmation modulaire ....................................................................... 19


1. Procédure et fonction ..................................................................................................................................... 19
1.1. Déclaration et définition de fonctions ............................................................................................ 20
1.2. Fonctions sans valeur de retour ou sans arguments................................................................ 22
2. Variable locale et variable globale ............................................................................................................. 22
2.1. Variable locale .......................................................................................................................................... 22
2.2. Variable globale ....................................................................................................................................... 22
3. Passage par valeur et passage par adresse ............................................................................................ 22
3.1. Passage par valeur .................................................................................................................................. 22
3.2. Passage par adresse ............................................................................................................................... 23

VIII. Récursivité ..................................................................................................... 24


1. Récursivité simple ............................................................................................................................................ 24
2. Terminaison d’une fonction récursive ..................................................................................................... 25
3. Récursivité et itération................................................................................................................................... 25
4. Occupation de la mémoire ............................................................................................................................ 26

IX. Les Structures .................................................................................................. 26


1. Introduction ........................................................................................................................................................ 26
2. Déclaration d’un enregistrement ............................................................................................................... 26
3. Manipulation d’un enregistrement ........................................................................................................... 27
3.1. Manipulation des champs d’un enregistrement......................................................................... 27
3.2. Manipulation globale d’un enregistrement .................................................................................. 27
3.3. Initialisation de structures .................................................................................................................. 28
3.4. Synonymes de types - typedef ........................................................................................................... 28
3.5. Imbrication de structures .................................................................................................................... 29

X. Allocation dynamique de la mémoire ...................................................... 30


1. Introduction ........................................................................................................................................................ 30
2. Pointeurs .............................................................................................................................................................. 31
2.1. Introduction .............................................................................................................................................. 31
2.2. Adresse mémoire et valeur d’un objet ........................................................................................... 31
2.3. Notion de pointeur ................................................................................................................................. 31
2.4. Arithmétique des pointeurs................................................................................................................ 33
3. Allocation dynamique et libération de la mémoire ............................................................................ 34
3.1. Introduction .............................................................................................................................................. 34
3.2. La fonction malloc................................................................................................................................... 34
3.3. La fonction calloc .................................................................................................................................... 36
3.4. La fonction free ........................................................................................................................................ 36

Professeur : Adil SAYOUTI Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

XI. Les listes chainées.......................................................................................... 36


1. Introduction ........................................................................................................................................................ 36
2. Création d’une liste chaînée ......................................................................................................................... 37
3. Déclaration d’une liste chaînée ................................................................................................................... 38
4. Manipulation d’une liste chaînée ............................................................................................................... 38
4.1. Ajouter un élément à la liste chaînée .............................................................................................. 38
4.2. Afficher les valeurs de la liste chaînée ........................................................................................... 40
4.3. Supprimer un élément de la liste chaînée..................................................................................... 40

XII. Les piles ............................................................................................................ 42


1. Définition ............................................................................................................................................................. 42
2. Création d’une pile ........................................................................................................................................... 42
3. Manipulation d’une pile ................................................................................................................................. 43
3.1. Ajouter un élément à la pile ................................................................................................................ 43
3.2. Enlever un élément de la pile............................................................................................................. 43
3.3. Afficher les éléments de la pile .......................................................................................................... 44

XIII. Les fichiers ..................................................................................................... 46


1. Introduction ........................................................................................................................................................ 46
2. Manipulation des fichiers .............................................................................................................................. 46
2.1. Ouvrir un ficher - fopen ........................................................................................................................ 46
2.2. Fermer un ficher - fclose ...................................................................................................................... 47
2.3. Ecrire dans un fichier ............................................................................................................................ 47
2.4. Lire le contenu d’un fichier ................................................................................................................. 49
2.5. Se déplacer dans un fichier ................................................................................................................. 51
2.6. Renommer et supprimer un fichier ................................................................................................. 51

Professeur : Adil SAYOUTI Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

I. Algorithmique et programmation
1. Notion de programme
Si l’on s’intéresse aux applications de l’ordinateur, on s’aperçoit qu’elles sont très nombreuses.
En voici quelques exemples : gestion d’une école, établir le bulletin de paie des employés,
gestion de stocks…
Un ordinateur pour qu’il puisse effectuer des tâches aussi variées il suffit de le programmer.
Effectivement l’ordinateur est capable de mettre en mémoire un programme qu’on lui fournit
puis l’exécuter. Plus précisément, l’ordinateur possède un ensemble limité d’opérations
élémentaires qu’il sait exécuter. Un programme est constitué d’un ensemble de directives,
nommées instructions, qui spécifient les opérations élémentaires à exécuter et la façon dont
elles s’enchaînent.
Pour s’exécuter, un programme nécessite qu’on lui fournisse ce qu’on peut appeler « données ».
En retour, le programme va fournir des « résultats ».
Par exemple un programme de calcul de la moyenne générale d’un étudiant nécessite des
informations suivantes : nom de l’étudiant, la note et le coefficient de chaque matière. Les
résultats seront imprimés sur les bulletins de notes.

2. Algorithme, programmation et langage de programmation


La programmation consiste, avant tout, à déterminer la démarche permettant d’obtenir, à l’aide
d’un ordinateur, la solution d’un problème donné.
Le processus de la programmation se déroule en deux phases :
o Dans un premier temps, on procède à ce qu’on appelle l’analyse du problème posé ou
encore la recherche d’un algorithme qui consiste à définir les différentes étapes de la résolution
du problème. Un algorithme est une suite d’actions que devra effectuer un ordinateur pour
arriver à un résultat, à partir d’une situation donnée. Cette première partie est essentielle dans
le processus de programmation. Elle permet de définir le contenu d’un programme en termes de
données et d’actions.
o Dans un deuxième temps, on exprime dans un langage de programmation donné, le
résultat de l’étape précédente. Ce travail, quoi qu’il soit facile, exige le respect strict de la syntaxe
du langage de programmation.
Le langage de programmation est l'intermédiaire entre l'humain et la machine, il permet d'écrire
dans un langage proche de la machine mais intelligible par l'humain les opérations que
l'ordinateur doit effectuer. Ainsi, étant donné que le langage de programmation est destiné à
l'ordinateur, il doit donc respecter une syntaxe stricte. Un algorithme peut toutefois aboutir à
plusieurs programmes. Le programme est ensuite transformé en langage machine lors d'une
étape appelée compilation. La compilation est une phase réalisée par l'ordinateur lui-même
grâce à un autre programme appelé compilateur. La figure ci-dessous montre Les différentes
étapes du processus de programmation.

Professeur : Adil SAYOUTI 1 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Voici quelques exemples de langages de programmation : pascal, Visual Basic, C, C++, java, etc.
L’objectif de ce cours est d’étudier les éléments de base intervenant dans un algorithme
(programme): variables, affectation, lecture, écriture, sélection, itération, structure de données…

3. Présentation du langage C
Le langage C a été créé en 1972 par Denis Ritchie afin de développer un système d’exploitation
(UNIX). Mais ses qualités opérationnelles l’ont vite fait adopter par une large communauté de
programmeurs. Une première définition de C est apparue en 1978 avec l’ouvrage de Kernighan
et Ritchie « The C programming language ».
Le langage C est normalisé par l’ANSI (American National Standards Institute), puis par l’ISO
(International Standards Organization) en 1990 et en 1993 par le CEN (comité européen de
normalisation)  « C ANSI » ou « C norme ANSI».

II. Variables
1. Données et variables
1.1. Notion de variable
Dans un programme informatique, on va avoir en permanence besoin de stocker provisoirement
en mémoire des valeurs. Il peut s’agir de données issues du disque dur ou fournies par
l’utilisateur (frappées au clavier). Il peut aussi s’agir de résultats obtenus par le programme,
intermédiaires ou définitifs. Ces données peuvent être de plusieurs types : elles peuvent être des
nombres, du texte, etc. Dès que l’on a besoin de stocker une information (donnée) au cours
d’un programme, on utilise une variable. Une variable est un nom qui sert à repérer un
emplacement donné de la mémoire, c’est à dire que la variable ce n’est qu’une adresse de
mémoire. Cette notion contribue considérablement à faciliter la réalisation des programmes. Elle
permet de manipuler des données sans avoir à se préoccuper de l’emplacement qu’elles
occupent effectivement en mémoire. Pour cela, il vous suffit tout simplement de leur choisir un
nom (identificateur).
Les identificateurs servent à désigner les différents "objets" manipulés par le programme :
variables, fonctions, etc. les identificateurs sont composés d’une suite de caractères
alphanumériques. Le premier d’entre eux étant nécessairement une lettre. La norme ANSI
(American National Standards Institute) prévoit qu’au moins les 31 premières lettres soient
significatives.
Remarque : Les mots-clefs, sont réservés pour le langage lui-même et ne peuvent pas être
utilisés comme identificateurs. Le langage C compte 32 mots-clés :

1.2. Déclaration des variables


La première chose à faire tout au début de l’algorithme, avant de pouvoir utiliser n’importe
quelle variable, c’est de faire la déclaration des variables. Lorsqu’on déclare une variable, on lui
attribue un nom et on lui réserve un emplacement mémoire. La taille de cet emplacement
mémoire dépend du type de variable.

La syntaxe d’une déclaration de variable est la suivante :


VARIABLE nom : TYPE ou VARIABLES nom1, nom2,… : TYPE
En langage C, la syntaxe d’une déclaration de variable est la suivante : TYPE VARIABLE ;

Professeur : Adil SAYOUTI 2 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

2. Types de variables
2.1. Type numérique
A. ENTIER
Le type entier désigne l’ensemble des nombres entiers négatifs ou positifs dont les valeurs
varient entre -32 768 à 32 767. On écrit alors : VARIABLES i, j, k : ENTIER
En langage C, la syntaxe de déclaration d’une variable de type entier : int i, j, k ;
La taille d’un entier est égale à 2 octets (processeur 16 bits) et à 4 Octets (processeur 32 bits).

B. REEL
Le type réel comprend les variables numériques qui ont des valeurs réelles. La plage des valeurs
du type réel est : de -3,40x1038 à -1,40x10-45 pour les valeurs négatives et de 1,40x10-45 à
3,40x1038 pour les valeurs positives.
On écrit alors : VARIABLES x, y : REEL
En langage C, la syntaxe de déclaration d’une variable de type réel (taille = 4 octets): float x, y ;

2.2. Type caractère


En plus, du type numérique on dispose également du type caractère (ou alphanumérique). Dans
une variable de ce type, on stocke des caractères, qu’il s’agisse de lettres, de signes de
ponctuation, d’espaces, ou même de chiffres.
On écrit alors : VARIABLE A : CARACTERE
VARIABLE nom, prenom, adresse : CHAINE DE CARACTERES
C permet de manipuler des caractères codés en mémoire sur 1 octet. En C, Les constantes de
type ‘caractère’, se notent de façon classique, en écrivant ente apostrophes (quottes) le caractère
(exemple: ‘a’, ‘f’, ‘+’). Une chaîne de caractères est une suite de caractères entourés par des
guillemets (exemple: ‘’Ceci est une chaîne de caractères’’).
En langage C, la syntaxe de déclaration d’une variable de type chaîne de caractères pouvant
contenir par exemple jusqu’à 20 caractères est : char NOM [20];

2.3. Type booléen


Le langage C ne possède pas de type booléen dédié. Dans ce type de variables on y stocke
uniquement des valeurs logiques VRAI ou FAUX, TRUE ou FALSE, 0 ou 1.
On écrit alors : VARIABLE etat : BOOLEEN

3. Opérateurs et expressions
Un opérateur est un signe qui relie deux variables pour produire un résultat. Une expression est
un ensemble de variables (ou valeurs) reliées par des opérateurs et dont la valeur du résultat de
cette combinaison est unique. Exemple : 6+8 , x+15-y
Le langage C est l'un des langages les plus fournis en opérateurs. Cette richesse se manifeste au
niveau des opérateurs classiques (arithmétiques, relationnels et logiques) et les opérateurs
originaux d'affectation et d'incrémentation.

3.1. Affectation
L’affection est opération qui consiste à attribuer une valeur à une variable. On la notera avec le
signe←. Cette instruction s’écrit : VARIABLE ← valeur
Exemple : Note←14.5
On peut aussi attribuer à une variable la valeur d’une variable ou d’une expression de façon
générale.
On écrit : VARIABLE ← EXPRESSION

Professeur : Adil SAYOUTI 3 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Exemple : A←B*2+5
Dans ce cas, l’instruction d’affectation sera exécutée en deux temps :
o D’abord, on calcule la valeur de l’expression
o On affecte la valeur obtenue à la variable à gauche.
On peut même avoir des cas où la variable de gauche qui figure dans l’expression à droite.
Par exemple : A ← A + 5
Dans cet exemple, après l’exécution de l’instruction d’affectation la valeur de la variable A sera
augmentée de 5.
En C, l’affectation est symbolisée par le signe =. Sa syntaxe est la suivante :
variable = expression ;
Le terme de gauche de l’affectation peut être une variable simple, un élément de tableau mais
pas une constante. Cette expression a pour effet d’évaluer expression et d’affecter la valeur
obtenue à variable.
Remarque :
o Il est possible d'initialiser une variable lors de sa déclaration comme dans : int n = 10 ;
o Il est possible de déclarer que la valeur d'une variable ne doit pas changer lors de l'exécution
du programme. Par exemple, avec : Const int n = 20 ;

3.2. Opérateurs arithmétiques


3.2.1. Présentation des opérateurs arithmétiques
Les opérateurs arithmétiques classiques sont l’opérateur unaire - (changement de signe) ainsi
que les opérateurs binaires (c.à.d. portant sur deux opérandes):
+ addition - soustraction * multiplication / division % reste de la division (modulo)
Ces opérateurs agissent de la façon attendue sur les entiers comme sur les flottants. Leurs seules
spécificités sont les suivantes :
o Le C ne dispose que de la notation / pour désigner à la fois la division entière et la division
entre flottants. Si les deux opérandes sont de type entier, l’opérateur / produira une division
entière (quotient de la division). Par contre, il délivrera une valeur flottante dès que l’un des
opérandes est un flottant.
o L’opérateur % ne s’applique qu’à des opérandes de type entier.
Remarque : Il n’y a pas en C d’opérateur effectuant l’élévation à la puissance. De façon générale,
il faut utiliser la fonction pow(x,y) de la librairie math.h pour calculer xy.

3.2.2. Priorités des opérateurs arithmétiques


Les opérateurs unaires + et - ont la priorité la plus élevée. On trouve ensuite, à un même niveau,
les opérateurs *, / et %. Enfin, sur un dernier niveau, apparaissent les opérateurs binaires + et -.
En cas de priorités identiques, les calculs s'effectuent de "gauche à droite". On dit que l'on a
affaire à une "associativité de gauche à droite".
Les parenthèses permettent d'outrepasser ces règles de priorité, en forçant le calcul préalable de
l'expression qu'elles contiennent.

Exercice. Evaluer les expressions suivantes :


A. 5+7*3-14/2 B. 15/3-2*6+14-2*5+1 C. 4 * 8 + 7 % 3
Solution : A=…………….. B=…………….. C=……………….

Exercice.
Quelles seront les valeurs des variables A et B après exécution des instructions ci-après ?

Professeur : Adil SAYOUTI 4 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

3.3. Opérateurs relationnels

Leur syntaxe est


expression-1 opérateur expression-2

Les deux expressions sont évaluées puis comparées. La valeur rendue est de type int (il n’y a pas
de type booléen en C); elle vaut 1 si la condition est vraie, et 0 sinon.
Attention : à ne pas confondre l’opérateur de test d’égalité == avec l’opérateur d’affection = .

3.4. Opérateurs logiques


Les opérateurs logiques sont : ET, OU et NON.
o Pour que la condition logique : condition1 ET condition2
Soit VRAI, il faut impérativement que la condition1 soit VRAI et que la condition2 soit VRAI.
o Pour que la condition logique : condition1 OU condition2
Soit VRAI, il suffit que condition1 soit VRAI ou condition2 soit VRAI. Il est à noter que cette
condition logique sera VRAI si condition1 et condition2 sont VRAI.
o Le NON inverse une condition : NON(condition)
Est VRAI si condition est FAUX, et il sera FAUX si condition est VRAI.
En langage C :
&& et logique
|| ou logique
! négation logique
Dans une expression de type: expression-1 op-1 expression-2 op-2 ...expression-n
L’évaluation se fait de gauche à droite et s’arrête dès que le résultat final est déterminé.
Remarque: l’opérateur || est moins prioritaire que &&.

3.5. Opérateurs d’incrémentation et de décrémentation


Les opérateurs d’incrémentation ++ et de décrémentation -- s’utilisent aussi bien en suffixe
(i++) qu’en préfixe (++i).
Dans les deux cas la variable i sera incrémentée, toutefois dans la notation suffixe la valeur
retournée sera l’ancienne valeur de i alors que dans la notation préfixe se sera la nouvelle. Par
exemple,

Professeur : Adil SAYOUTI 5 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

int a = 3, b, c;
b = ++a; /* a et b valent 4 */
c = b++; /* c vaut 4 et b vaut 5 */

4. Entrées/Sorties
Considérons l’algorithme suivant :
VARIABLE A : ENTIER
Début
A ← 12 ^ 2 ;
Fin
Il permet de calculer le carré de 12. Le problème de ce programme, c’est que, si l’on veut calculer
le carré d’un autre nombre que 12, il faut réécrire le programme. C’est pour cela qu’il faut
introduire des instructions qui permettent le dialogue avec l’utilisateur.
En effet, il existe une instruction qui permet à l’utilisateur de faire entrer des valeurs au clavier
pour qu’elles soient utilisées par le programme.

La syntaxe de cette instruction de lecture est : LIRE NomVariable ;

Lorsque le programme rencontre une instruction LIRE, l’exécution du programme s’interrompt,


attendant la saisie d’une valeur au clavier. Dès que l’on frappe sur la touche ENTER,
l’exécution reprend.
Une autre instruction permet au programme de communiquer des valeurs à l’utilisateur en les
affichant à l’écran. La syntaxe de cette instruction d’écriture est :
ECRIRE NomVariable ;
Ou de façon générale ECRIRE Expression ;
Remarque : Avant de lire une variable, il est fortement conseillé d’écrire des libellés à l’écran,
afin de prévenir l’utilisateur de ce qu’il doit frapper. La même chose pour l’instruction d’écriture.
Exemple : Algorithme CARRE
Variables A, CARRE : Réels ;
DEBUT
Ecrire (‘Entrez un nombre’) ;
Lire A ;
CARRE ← A * A ;
Ecrire (‘Le carré de ce nombre est : ’) ;
Ecrire CARRE ;
FIN

En langage C, les fonctions d’entrées/sorties (scanf/printf) sont des fonctions de la librairie


standard stdio.h utilisées avec les unités classiques d’entrées-sorties, qui sont respectivement le
clavier et l’écran. L’appel à la librairie stdio.h se fait par la directive au préprocesseur #include
<stdio.h> .

4.1. Fonction printf


La fonction printf est une fonction d’impression formatée (affichage sur l’écran), ce qui signifie
que les données sont converties selon le format particulier choisi. Sa syntaxe est
printf("chaîne de contrôle ",expression-1, ..., expression-n);
La chaîne de contrôle contient le texte à afficher et les spécifications de format
correspondant à chaque expression de la liste. Les spécifications de format sont introduites par
le caractère %, suivi d’un caractère désignant le format d’impression (code de conversion).

Les principaux formats sont :


c char : caractère affiché "en clair"
d int

Professeur : Adil SAYOUTI 6 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

f float écrit en notation "décimale" avec six chiffres après le point (123.456789)
s chaîne de caractères

Exemple: printf(‘’la somme est égale à : %d ‘’, Som) ;


En plus du caractère donnant le type des données, on peut éventuellement préciser certains
paramètres du format d’affichage, qui sont spécifiés entre le % et le caractère de conversion
dans l’ordre suivant :
o Largeur minimale du champ d’affichage : %10d spécifie qu’au moins 10 caractères seront
réservés pour afficher l’entier.
o Précision : %4.2f signifie que l’on réserve 6 caractères pour afficher le flottant et que 2
d’entre eux sont destinés aux chiffres après la virgule.

Remarque : Les caractères non imprimables possèdent une représentation conventionnelle


utilisant le caractère "\". Les caractères non-imprimables les plus fréquents sont : \a bip ; \n
saut de ligne ; \t tabulation horizontal.

4.2. Fonction scanf


La fonction scanf permet de faire une lecture formatée du flux standard d'entrée (le clavier par
défaut). Sa syntaxe est : scanf("<format>",<AdrVar1>,<AdrVar2>, ...)
o "<format>" : format de lecture des données.
o <AdrVar1>,... : adresses des variables auxquelles les données seront attribuées.
La chaîne de format détermine comment les données reçues doivent être interprétées. Les
données reçues correctement sont mémorisées successivement aux adresses indiquées par
<AdrVar1>,.... L'adresse d'une variable est indiquée par le nom de la variable précédé du signe &.

Exemple: printf ("donnez une valeur pour n : ") ;


scanf ("%d", &n) ;
Les principaux formats sont : c pour char, d pour int, f pour float et s pour une chaîne de
caractères.
L'expression : c = getchar ( ) joue le même rôle que : scanf ("%c", &c)

5. Présentation du langage C par l’exemple


5.1. Exemple d’un programme
#include <stdio.h>
#include <math.h>
main ( )
{
float x ;
float racx ;
printf ("Donner un nombre positif : ") ;
scanf ("%f", &x) ;
racx = sqrt (x) ;
printf ("Le nombre %f a pour racine carrée : %f\n", x, racx) ;
}
Exemple d’exécution
Donnez un nombre : 4
Le nombre 4.000000 a pour racine carrée : 2.000000

5.2. Structure d'un programme en langage C


o La ligne : main ( ) se nomme un "en-tête". Elle précise que ce qui sera décrit à sa suite est le
"programme principal" (main). Le programme principal est délimité par les accolades "{" et
"}". Les instructions situées entre ces accolades forment un "bloc".

Professeur : Adil SAYOUTI 7 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

o Les deux instructions ci-dessous sont des "déclarations".


float x ;  x et racx de type float (destinée à contenir des nombres réels).
float racx ;
Remarque: les déclarations des types des variables sont obligatoires et doivent être regroupées
au début du programme.
o L’instruction printf ("Donner un nombre positif ") ; appelle une fonction "prédéfinie"
(fournie avec le langage C) nommée printf. Ici, cette fonction reçoit un argument : "Donner un
nombre positif " Les guillemets servent à délimiter une "chaîne de caractères".
o L’instruction : scanf ("%f", &x) ; est un appel de la fonction prédéfinie scanf dont le rôle est
de lire une information au clavier. Scanf possède en premier argument un format, ici: "%f".
La fonction scanf range la valeur lue dans l'emplacement correspondant à la variable x, c'est-
à-dire à son adresse. L’opérateur & signifie "adresse de".
o La fonction sqrt fournit la valeur de la racine carrée d'une valeur flottante qu'on lui transmet
en argument.
o Les deux premières lignes :
#include <stdio.h>
#include <math.h>

Il s'agit de "directives" qui seront prises en compte avant la traduction (compilation) du


programme. Ces directives doivent être écrites à raison d'une par ligne et elles doivent
obligatoirement commencer en début de ligne.
Les deux premières directives demandent d'introduire des instructions situées dans les fichiers
stdio.h et math.h. Lorsque vous faites appel à une fonction prédéfinie, il est nécessaire
d'incorporer de tels fichiers, nommés "fichiers en-têtes", qui contiennent des déclarations
appropriées concernant cette fonction : stdio.h pour printf et scanf et math.h pour sqrt.

5.3. Création d’un programme en langage C


La création d’un programme comporte trois étapes: l’édition du programme, la compilation et
l’édition des liens.
o L’édition du programme
Consiste à créer, à partir du clavier, tout ou partie du texte d’un programme « programme
source». Le fichier source porte l’extension C.
o La compilation
Consiste à traduire le programme source en langage machine, en faisant appel à un programme
nommé compilateur. En langage C, cette opération comporte deux étapes:
 Traitement par le préprocesseur: exécute les directives. Il produit, en résultat, un
programme source en langage C pur.
 Compilation: traduction en langage machine du texte en langage C fourni par le
préprocesseur. Le résultat de la compilation porte le nom « module objet ».
o L’édition de liens
Le module objet créé par le compilateur n'est pas directement exécutable. Il lui manque les
différents modules objet correspondant aux fonctions prédéfinies (printf, scanf, sqrt…). L’éditeur
de liens recherche dans la bibliothèque standard (collection de modules objet) les modules objet
nécessaires. Le résultat de l’édition de liens est un programme exécutable: un ensemble
autonome d’instructions en langage machine.

III. Sélection
1. Introduction
Dans un programme, les instructions sont exécutées séquentiellement, c.à.d. dans l'ordre où elles
apparaissent. Or la puissance et le "comportement intelligent" d'un programme proviennent
essentiellement de la possibilité d'effectuer des sélections (choix) et de se comporter

Professeur : Adil SAYOUTI 8 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

différemment suivant les circonstances. Les instructions permettant d’effectuer un choix est
appelée instructions alternatives.

2. Sélection simple
La syntaxe de la sélection simple est :
SI condition ALORS
bloc 1 d'instructions ;
SINON
bloc 2 d'instructions ;
FIN SI
Si la condition mentionnée après SI est VRAI, on exécute le bloc1 d'instructions (ce qui figure
après le mot ALORS); si la condition est fausse, on exécute le bloc2 d'instructions (ce qui
figure après le mot SINON).
Exemple : SI a > 0 ALORS
ECRIRE ''valeur positive''
SINON
ECRIRE ''valeur négative''
FIN SI
Dans ce programme, on vérifie si la valeur de a est supérieure à 0, on affichera le message
''valeur positive''. Dans le cas contraire, il sera affiche le message ''valeur négative''.
La structure alternative peut prendre une autre forme possible où l'une des parties du choix est
absente. Elle s'écrit dans ce cas :
SI condition ALORS
bloc d'instructions
FIN SI

En langage C, La syntaxe de la sélection s’écrit comme suit :


if ( expression_1 )
instruction_1
else if ( expression_2 )
instruction_2
….. Nombre quelconque de else if (….)
else
instruction_n

expression_1, expression_2: expressions quelconques


instruction_1, _2,… et instruction_n : instructions quelconques :
- simple (terminée par un point virgule),
- bloc (placé entre { }).
Si l'expression_1 est vraie on exécute l'instruction_1.
Sinon, si l’expression_2 est vraie on exécute l'instruction_2. ………… ………..
Sinon (else), on exécute l’instruction_n .

3. Sélection imbriquée
Il peut arriver que l'une des parties d'une instruction alternative (sélection) contienne à son tour
une instruction alternative. Dans ce cas, on dit qu'on a des sélection imbriquées les unes dans les
autres. L'instruction d'un if peut contenir un autre if :

if (expression)
instruction contenant d’autres instructions if

Remarque: un else se rapporte toujours au dernier if rencontré auquel un else n’a pas encore
été attribué.

Professeur : Adil SAYOUTI 9 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

4. Choix multiples
Elle permet d'associer à différentes valeurs discrètes des instructions à exécuter.
Cette instruction peut être réalisée par une cascade de SI SINON , mais elle offre une
présentation et une lecture plus agréable et compréhensive. Sa syntaxe est :
SELON expression
Valeur 1 : action 1
valeur 2 : action 2

Valeur N : action N
SINON : action
FIN SELON

Si expression est égale à valeur i, on exécute action i et on passe à la suite de l’algorithme. Sinon
on exécute action et on passe à la suite de l’algorithme.
En langage C, l’instruction switch permet de faire un choix. Sa syntaxe est :
switch (expression )
{
case constante_1:
liste d’instructions_1
break;
case constante_2:
Liste d’instructions_2
break;
...
default:
liste d’instructions_n
break;
}
Si la valeur de expression est égale à l’une des constantes, la liste d’instructions correspondante
est exécutée. Sinon la liste d’instructions_n correspondante à default est exécutée. break
provoque une sortie du bloc.
Exemple :

IV. Itération
1. Introduction
Reprenons le programme (série 2, exercice 1) qui calcule la moyenne générale. L’exécution de ce
programme fournit la moyenne générale des notes uniquement pour un seul étudiant. S’il l’on
veut les moyennes de 120 étudiants, il faut ré-exécuter ce programme 120 fois. Afin d’éviter

Professeur : Adil SAYOUTI 10 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

cette tâche fastidieuse d’avoir ré-exécuter le programme 120 fois, on peut faire recours à ce
qu’on appelle les structures itératives, appelées aussi les structures répétitives ou boucles.
Les boucles permettent de répéter une série d’instructions tant qu’une condition est vérifiée. Il
existe trois formes de structures répétitives : POUR (for en C), TANT QUE (While en C),
REPETER (do while en C).

2. Boucle déterministe – boucle POUR


Les boucles déterministes sont des boucles dont le nombre d’itérations est connu.
La structure POUR permet de répéter des instructions un nombre connu de fois. Sa syntaxe est :
POUR compteur = val_initial A val_final PAS DE incrément
Instructions à répéter
FIN POUR
compteur c’est ce qu’on appelle compteur. C’est une variable de type entier.
val_initial et val_final sont respectivement la valeur initiale et la valeur finale prises par le
compteur. Ce sont des valeurs entières.
incrément est la valeur d’augmentation progressive du compteur. La valeur par défaut du pas =
1 (sans le préciser).

En langage C, la syntaxe de for est : for ( expression 1 ; expression 2 ; expression 3)


{
instructions;
}
expression 1: une instruction d'initialisation de la variable de contrôle « i par exemple »
expression 2: une condition logique qui teste la valeur de la variable de contrôle
expression 3: une instruction qui fait évoluer la valeur de la variable de contrôle
(incrémentation)

Exemple : Réécrivons le programme de la moyenne générale de façon qu’il puisse calculer les
moyennes de 3 étudiants.

3. Boucle indéterministe
Lorsque le nombre d'itérations qu'effectuera la boucle n'est pas connu d'avance ou que le pas est
variable, il existe deux autres types de boucles, à savoir TANT QUE (while en C) et REPETER (do-
--while en C).

Professeur : Adil SAYOUTI 11 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

3.1. Boucle TANT QUE


Cette structure permet de répéter les instructions tant qu’une condition est satisfaite. Sa
syntaxe est:
TANT QUE condition
Instructions à répéter
FIN TANT QUE

En langage C, la syntaxe de While est: while (expression)


{
instructions;
}
Exemple : Reprenant toujours le programme de la moyenne générale.
On ne peut pas utiliser la structure POUR si on ne sait pas combien de moyennes à calculer. Dans
ce cas on est obligé d’utiliser la structure TANT QUE. Le programme sera alors :

3.2. Boucle REPETER JUSQU’A


Cette structure sert à répéter des instructions jusqu’à ce qu’une condition soit verifiée. Sa
syntaxe est :
REPETER
Instructions à répéter
JUSQU'A condition

En langage C, la syntaxe de do while est:


do
{
Instructions
}
while ( expression );

Ici, instructions seront exécutées tant que expression est verifiée. Cela signifie donc que
instructions sont toujours exécutées au moins une fois.

Exemple : Un programme qui demande un nombre à l’utilisateur (en affichant la valeur lue) tant
qu’il ne fournit pas une valeur négative.

Professeur : Adil SAYOUTI 12 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Remarque : L’instruction break permet d’interrompre le déroulement de la boucle, et passe à la


première instruction qui suit la boucle.

4. Boucle imbriquée
Une boucle imbriquée est une boucle dans une boucle, une boucle à l'intérieur du corps d'une
autre boucle. Ce qui se passe est que le premier tour de la boucle externe déclenche la boucle
interne, qui s'exécute jusqu'au bout. Puis le deuxième tour de la boucle externe déclenche la
boucle interne une nouvelle fois. Ceci se répète jusqu'à ce que la boucle externe termine. Bien
sûr, un break à l'intérieur de la boucle interne ou externe peut interrompre ce processus.
Exemple : Dans l’exemple ci-dessous, le programme demande à l’utilisateur de saisir le nombre
d’étudiants et le nombre de matières. La boucle externe correspond aux étudiants. La boucle
interne correspond aux matières par étudiant.

V. Organigramme
1. Définition
Un organigramme est une représentation graphique normalisée d’un algorithme. Un
organigramme a les caractéristiques suivantes :
 Il comporte des symboles et des liaisons ;
 Il est fermé ;
 Il comporte un début et une fin.

2. Symboles

Début, Fin
Début ou fin d’un organigramme.

Professeur : Adil SAYOUTI 13 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Traitement
Opération ou groupe d'opérations (Instructions) sur des données.

Entrée / Sortie
Lecture d'une information à traiter ou l’affichage d'une information traitée.

Embranchement (Choix)
Test de condition (vraie ou faux) impliquant un choix.

Le sens général de la lecture est de haut vers le bas, et de gauche à droite. Lorsque le sens ainsi
défini n'est pas respecté, L’utilisation des flèches permet d’indiquer le sens utilisé.

3. Sélection
La structure alternative (conditionnelle) SI…. ALORS…..SINON en organigramme est
représentée dans la figure ci-dessous.

………

SI CONDITION

ALORS action 1

SINON action 2

FINSI

4. Itération
1.3. Boucle POUR
Le nombre d’exécutions de la boucle est connu.

………

POUR variable

De valeur initiale

A valeur finale

PARPASDE pas

FAIRE action

FINPOUR

………

Professeur : Adil SAYOUTI 14 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

1.4. Boucle TANT QUE


L’action n’est pas forcément exécutée. Le nombre
d’exécutions de la boucle est inconnu.

……….

TANTQUE condition

FAIRE action

FINTANTQUE

…………

1.5. Boucle REPETER…JUSQU'A


L’action est exécutée au moins une fois. Le nombre
d’exécutions de la boucle est inconnu.

………….

FAIRE action

JUSQU'A condition

FINFAIRE

…………..
Exercice :
Ecrire un programme en C qui permet de résoudre dans R une équation du second degré
AX2+BX+C = 0. L’organigramme est présenté ci-dessous.

Professeur : Adil SAYOUTI 15 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

VI. Tableaux et chaînes de caractères


1. Tableau à une dimension
Imaginez que l’on veuille calculer la moyenne des notes d’une classe d’élèves. Pour l’instant on
pourrait écrire l’algorithme suivant :

Variables somme, nbEleves, Note : Réels


Variable i : Entier
DEBUT
somme ← 0
Ecrire " Nombre d’élèves :"
Lire nbEleves
POUR i = 1 A nbEleves
Ecrire " Note de l’élève numéro :", i
Lire Note
somme ← somme + Note
FIN POUR
Ecrire " La moyenne est :", somme / nbEleves
FIN

Si l’on veut toujours calculer la moyenne des notes d’une classe mais en gardant en mémoire
toutes les notes des élèves pour d’éventuels calculs (par exemple calculer le nombre d’élèves qui
ont des notes supérieurs à la moyenne). Dans ce cas il faudrait alors déclarer autant de variables
qu’il y a d’étudiants. Donc, si l’on a 20 élèves il faut déclarer 20 variables et si l’on a N il faut
déclarer N variables qui n’est pas pratique. Ce qu’il faudrait c’est pouvoir par l’intermédiaire
d’une seule variable stocker plusieurs valeurs de même type. Ce type de variable est appelé
tableau.
Un tableau est un ensemble de valeurs de même type portant le même nom de variable. Chaque
valeur du tableau est repérée par un indice précisant sa position au sein de l'ensemble (exemple
d’un vecteur).
Les types char, int et float sont des types "simples", car, à un instant donné, une variable d'un tel
type contient une seule valeur. Ils s'opposent aux tableaux qui correspondent à des variables
qui, à un instant donné, contiennent plusieurs valeurs de même type.
La déclaration d’un tableau sera via la syntaxe suivante dans la partie des déclarations :
Tableau nom_tableau (nombre) : Type
nom_tableau : désigne le nom du tableau
nombre : désigne le nombre d’éléments du tableau, c.à.d. sa taille
Type : c’est le type du tableau : le type de tous ces éléments

Exemple : Tableau Note (10) : Réel

Note (10) est un tableau qui contient dix valeurs réelles.

Note(0) Note (1) ………… …………… Note(9)

L’accès (en écriture et en lecture) à la i ème valeur d’un tableau se fait à l’aide de la syntaxe
suivante : nom_tableau (indice)
Par exemple si X est un tableau de 10 entiers :
o X (2) ← - 5 place la valeur -5 dans la 3 ème case du tableau.
o Lire X (1) met l’entier saisi par l’utilisateur dans la deuxième case du tableau
o Ecrire X (2) affiche la valeur de la troisième case du tableau
En langage C, la syntaxe de déclaration d’un tableau à une dimension est la suivante:
type_elements nom_tableau [nb_cases] ;

Professeur : Adil SAYOUTI 16 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Exemple : tableau T de 10 entiers int T[10] ;


o Affecter des valeurs dans des cases
syntaxe : nom_tableau[numero_case] = valeur ;
La numérotation des cases s'effectue de 0 à nb_cases-1.
o Accéder à la valeur d'une case d'un tableau
syntaxe : nom_tableau[numero_case]
o Initialisation des tableaux
Il est possible lors de la déclaration d'initialiser les cases du tableau en donnant des valeurs
entre accolades. syntaxe : type nom_tableau[N] = { val1, val2, ..., valN } ;

Exemple d’un algorithme qui calcule la somme des éléments d’un tableau :
Variables i, somme : ENTIERS
Tableau T (N) : ENTIER
DEBUT
somme ← 0
POUR i = 1 A N
somme ← somme + T (i)
FIN POUR
Ecrire ‘’La somme de tous les éléments du tableau est : ‘’, somme
FIN

2. Tableau à deux dimensions


Nous avons vu qu’un tableau à une dimension correspond à une liste ordonnée de valeurs,
repérée chacune par un indice. Dans tous les langages de programmation, il est possible de
définir des tableaux à deux dimensions (permettant par exemple de représenter des matrices).
Ainsi, on pourra placer des valeurs dans un tableau à deux dimensions et cela consiste comme
dans le cas des tableaux à une dimension à donner un nom à l’ensemble de ces valeurs. Chaque
valeur est alors repérée par deux indices qui précisent la position.
On déclare un tableau à deux dimensions de la façon suivante :
Tableau nom_tableau (i , j ) : Type

nom_tableau : désigne le nom du tableau


i : désigne le nombre de lignes du tableau
j : désigne le nombre de colonnes du tableau
Type : représente le type des éléments du tableau

Exemple : Soit T (3 ,5) un tableau d’entiers. On peut le représenter graphiquement par :


T (1,2)

T (3,5)
T (1,2) et T (3,5) sont deux éléments du tableau. Les valeurs des indices sont incluses entre
parenthèses. Le premier sert à repérer le numéro de la ligne, le second repère la colonne.
On accède en lecture ou en écriture à la valeur d’un élément d’un tableau à deux dimensions en
utilisant la syntaxe suivante : Nom_tableau (i , j)

Exemple: soit T un tableau à deux dimensions (3 lignes et 2 colonnes) Tableau T(3,2): Réel
T (2,1) ← -1.2 place la valeur -1.2 dans la case 2,1 du tableau
Soit a une variable de type Réel, a ← T (2,1) place -1.2 dans a.

Professeur : Adil SAYOUTI 17 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

En langage C, la syntaxe de déclaration d’un tableau à deux dimensions est la suivante:


syntaxe : type_elements nom_tableau[taille_dim1][taille_dim2] ;

On peut imaginer le tableau sous la forme d'un rectangle avec taille_dim1 qui représente le
nombre de lignes et taille_dim2 qui représente le nombre de colonnes.
o Affecter des valeurs dans des cases
syntaxe : nom_tableau[numero_case_dim1][numero_case_dim2] = valeur ;
Si on représente ce tableau sous la forme de lignes et de colonnes, la numérotation des cases
s'effectue de 0 à numero_ligne-1 pour les lignes et de 0 à numero_colonne-1 pour les colonnes.
o Accéder à la valeur d'une case d'un tableau
syntaxe : nom_tableau[numero_case_dim1][numero_case_dim2]
o Initialisation des tableaux
Syntaxe : type nom_tableau[N][P] = {val1_1, val1_2, ..., valN+P} ;
ou
type nom_tableau[N][P] = { {val1_1, val1_2, ..., val1_P}, {val2_1, val2_2, ..., val2_P}
..., {valN_1, valN_2, ..., valN_P}};

Exemple : algorithme qui calcule la somme des éléments d’une matrice de 20 lignes et 50
colonnes.
Tableau T (20 , 50) : Réel
Variables i , j : Entiers
Variable som : Réel
DEBUT
som ← 0
POUR i = 1 A 20
POUR j = 1 A 50
som ← som + T (i , j)
FIN POUR
FIN POUR
Ecrire ‘’La somme de tous les éléments du tableau est :’’, som
FIN

3. Chaînes de caractères
Une chaîne de caractères est traitée comme un tableau à une dimension de caractères (vecteur
de caractères). La représentation interne d'une chaîne de caractères est terminée par le symbole
'\0' (NUL). Ainsi, pour un texte de n caractères, nous devons prévoir n+1 octets.
"bonjour" est équivalente au tableau :

En langage C, la syntaxe de déclaration d’une chaîne de caractères est la suivante:


char nom_chaine[taille_chaine] ;
Exemples:
char NOM [20];
char PRENOM [20];
o Affecter une chaîne lue sur l'entrée standard à une chaîne de caractères
Attention, tant que nous parlions de valeurs scalaires (uniques), nous utilisions
scanf(format, &variable);
Dans le cas de chaînes de caractères, chaque caractère correspond à une case du tableau et le
nom d'une chaîne de caractères est le représentant de l'adresse du premier caractère de la
chaîne, il ne doit pas être précédé de l'opérateur adresse '&' : scanf("%s", chaine);
o Initialisation de chaînes de caractères
En général, les tableaux sont initialisés par l'indication de la liste des éléments du tableau entre
accolades: char CHAINE[ ] = {'H','e','l','l','o','\0'};

Professeur : Adil SAYOUTI 18 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Pour les chaînes de caractères, nous pouvons utiliser une initialisation plus confortable en
indiquant simplement une chaîne de caractère constante: char CHAINE[ ] = "Hello";
Lors de l'initialisation par [ ], l'ordinateur réserve automatiquement le nombre d'octets
nécessaires pour la chaîne, c-à-d le nombre de caractères + 1 (ici: 6 octets).

Exemples :

Remarques :
- "x" : chaîne de caractères qui contient deux caractères : la lettre 'x' et le caractère NUL: '\0‘
- 'x' est codé dans un octet et "x" est codé dans deux octet .

o Accès aux éléments d'une chaîne de caractères


L'accès à un élément d'une chaîne de caractères peut se faire de la même façon que l'accès à un
élément d'un tableau. En déclarant une chaîne par: char A[6];
nous avons défini un tableau A avec six éléments, auxquels on peut accéder par A[0],A[1],.. A[5]

Exemple :

o Lecture et affichage de chaînes de caractères


la bibliothèque <stdio.h> nous offre des fonctions qui effectuent l'entrée et la sortie des données.
A côté des fonctions printf et scanf, nous y trouvons les deux fonctions puts et gets,
spécialement conçues pour l'écriture et la lecture de chaînes de caractères.
 La fonction puts
Syntaxe: puts( <Chaîne> )
puts(TXT) est équivalente à printf("%s\n",TXT);
 La fonction gets
Syntaxe: gets( <Chaîne> ) est équivalente à scanf("%s", Chaîne)
gets lit une ligne de caractères et la copie à l'adresse indiquée par <Chaîne>. Le retour à la ligne
est remplacé par le symbole de fin de chaîne '\0‘.

VII. Programmation modulaire


1. Procédure et fonction
La structuration de programmes en sous-programmes se fait en C à l'aide de fonctions. Les
fonctions en C correspondent aux fonctions et procédures en langage algorithmique. Nous avons
déjà utilisé des fonctions prédéfinies dans des bibliothèques standard (printf de <stdio.h>, sqrt
de <math.h>, etc.). Dans ce paragraphe, nous allons découvrir comment nous pouvons définir et
utiliser nos propres fonctions et procédures.
 La modularité et ses avantages
Pour des problèmes plus complexes, nous obtenons ainsi de longues listes d'instructions, peu
structurées et par conséquent peu compréhensibles. En plus, il faut souvent répéter les
mêmes suites de commandes dans le texte du programme. Le langage C nous permet de
subdiviser nos programmes en sous-programmes « fonctions » plus simples et plus compacts. A
l'aide de ces structures nous pouvons modulariser nos programmes pour obtenir des solutions
plus élégantes et plus efficientes. Un module désigne une entité de données et d'instructions qui
fournissent une solution à une (petite) partie bien définie d'un problème plus complexe. Un

Professeur : Adil SAYOUTI 19 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

module peut faire appel à d'autres modules, leur transmettre des données et recevoir des
données en retour. L'ensemble des modules ainsi reliés doit alors être capable de résoudre le
problème global. Voici quelques avantages d'un programme modulaire:
- Meilleure lisibilité
- Diminution du risque d'erreurs
- Possibilité de tests sélectifs
- Réutilisation de modules déjà existants
- Simplicité de l'entretien
- Favorisation du travail en équipe

 Procédure et fonction
La fonction est la seule sorte de module existant en C.
Dans beaucoup de langages, on trouve deux sortes de "modules", à savoir :
o Les "fonctions", assez proches de la notion mathématique correspondante. Notamment,
une fonction dispose d'arguments (en C, une fonction peut ne comporter aucun
argument) qui correspondent à des informations qui lui sont transmises et elle fournit
un unique résultat scalaire (simple). L’appel d’une fonction peut apparaître dans une
expression.
o Les "procédures" (terme langage Pascal) ou "sous-programmes" (terme langage
Fortran ou Basic) qui élargissent la notion de fonction. La procédure ne possède plus de
valeur de retour et son appel ne peut plus apparaître au sein d'une expression. Par
contre, elle dispose toujours d'arguments.

1.1. Déclaration et définition de fonctions


En général, le nom d'une fonction apparaît à trois endroits dans un programme:
1) lors de la déclaration
2) lors de la définition
3) lors de l'appel
Exemple : Dans le programme suivant, la fonction main utilise les deux fonctions:
- ENTREE qui lit un nombre entier au clavier et le fournit comme résultat. La fonction
ENTREE n'a pas d’arguments (paramètres).
- MAX qui renvoie comme résultat le maximum de deux entiers fournis comme paramètres.

Déclaration

Appel

Définition

Professeur : Adil SAYOUTI 20 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

 Définition d'une fonction


Dans la définition d'une fonction, nous indiquons:
- le nom de la fonction
- le type, le nombre et les noms des paramètres de la fonction
- le type du résultat fourni par la fonction
- les données locales à la fonction
- les instructions à exécuter

Remarque :
- il n'y a pas de point-virgule après définition des paramètres de la fonction.
- Si nous choisissons un nom de fonction qui existe déjà dans une bibliothèque, notre
fonction cache la fonction prédéfinie.
- Si une fonction F fournit un résultat de type T, on dit que la fonction F est de type T.
Exemple :
La fonction MAX est de type int et elle a besoin de deux paramètres de type int.

 Déclaration d'une fonction


En C, il faut déclarer chaque fonction avant de pouvoir l'utiliser. La déclaration informe le
compilateur du type des paramètres et du résultat de la fonction. A l'aide de ces données, le
compilateur peut contrôler si le nombre et le type des paramètres d'une fonction sont corrects.
Si dans le texte du programme la fonction est définie avant son premier appel, elle n'a pas besoin
d'être déclarée.
o Prototype d'une fonction

o Règles pour la déclaration des fonctions :


 Déclaration locale: Une fonction peut être déclarée localement dans la fonction qui
l'appelle (avant la déclaration des variables). Elle est alors disponible à cette fonction.
 Déclaration globale: Une fonction peut être déclarée globalement au début du
programme (après les instructions #include). Elle est alors disponible à toutes les fonctions
du programme.
 Déclaration implicite par la définition: La fonction est automatiquement disponible
à toutes les fonctions qui suivent sa définition.
 main: La fonction principale main n'a pas besoin d'être déclarée.
 Arguments muets et arguments effectifs
o Les noms des arguments figurant dans l'en-tête de la fonction se nomment des
"arguments muets" (arguments formels ou paramètres formels)
o les arguments fournis lors de l’appel de la Fonction se nomment des « arguments
Effectifs » (ou paramètres effectifs).
 L’instruction return
o L'instruction return renvoie un résultat sous forme de valeur de la fonction dans
laquelle elle s'exécute, mais, en même temps elle interrompt l'exécution de la fonction en
revenant dans la fonction qui l'a appelée.

Professeur : Adil SAYOUTI 21 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

1.2. Fonctions sans valeur de retour ou sans arguments


o Quand une fonction ne renvoie pas de résultat, on le précise, à la fois dans l'en-tête et
dans sa déclaration, à l'aide du mot clé void.
Par exemple, voici l'en-tête d'une fonction recevant un argument de type int et ne fournissant
aucune valeur : void sansval (int n)
Déclaration : void sansval (int) ;
o Quand une fonction ne reçoit aucun argument, on place le mot clé void à la place de la
liste d'arguments. Voici l'en-tête d'une fonction ne recevant aucun argument et
renvoyant une valeur de type float. float tirage (void)
Déclaration float tirage (void) ;
o Il est possible de réaliser une fonction ne possédant ni arguments ni valeur de retour.
Dans ce cas, son en-tête sera de la forme : void message (void)
et sa déclaration sera : void message (void) ;

2. Variable locale et variable globale


2.1. Variable locale
Les variables déclarées dans un bloc d'instructions sont uniquement visibles à l'intérieur de ce
bloc. On dit que ce sont des variables locales à ce bloc.
Exemple :
La variable NOM est définie localement à l’intérieur de la fonction HELLO. Ainsi, aucune autre
fonction n'a accès à la variable NOM:

2.2. Variable globale


Les variables déclarées au début du fichier, à l'extérieur de
toutes les fonctions sont disponibles à toutes les fonctions
du programme. Ce sont alors des variables globales.
En général, les variables globales sont déclarées
immédiatement après les instructions #include au début du
programme.
Exemple :
La variable STATUS est déclarée globalement pour pouvoir
être utilisée dans les procédures A et B.

Remarque : Les variables déclarées au début de la fonction principale main ne sont pas des
variables globales, mais elles sont locales à main.

3. Passage par valeur et passage par adresse


3.1. Passage par valeur
En C, le passage des paramètres se fait toujours par la valeur, c.-à-d. les fonctions n'obtiennent
que les valeurs de leurs paramètres et n'ont pas d'accès aux variables elles-mêmes. Les
paramètres d'une fonction sont à considérer comme des variables locales qui sont initialisées
automatiquement par les valeurs indiquées lors d'un appel. A l'intérieur de la fonction, nous
pouvons donc changer les valeurs des paramètres sans influencer les valeurs originales dans les
fonctions appelantes.

Professeur : Adil SAYOUTI 22 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Exemple :

Résultat :
n et p restent
inchangés

Lors de l'appel de echange, il y a eu transmission de la valeur des expressions n et p. On peut dire


que ces valeurs ont été recopiées "localement" dans la fonction echange dans des emplacements
nommés a et b. C'est effectivement sur ces copies qu'a travaillé la fonction echange, de sorte que
les valeurs des variables n et p n'ont, quant à elles, pas été modifiées. C'est ce qui explique le
résultat constaté.

3.2. Passage par adresse


Pour pouvoir modifier le contenu de n et de p, la fonction echange a besoin des adresses de n et
p. dans ce cas, nous utilisons des pointeurs. Nous avons déjà vu que les données étaient rangées
dans des cases mémoires, chaque case ayant une adresse. Un pointeur n'est rien d'autre qu'une
variable, dont le contenu est l'adresse d'une autre case mémoire. Sur le schéma suivant sont
représentées deux variables. Une variable b de type int et un pointeur (qui est aussi une
variable, de type int * dans notre exemple) : p.

Le pointeur p contient la valeur 32000, qui est l'adresse de la variable b. L'adresse du pointeur p
est 1200. Dans une telle situation, on dit que p pointe sur b.
Dans cet exemple:
- b désigne le contenu de b (124)
- p désigne le contenu de p (32000)
- &b désigne l'adresse de b (ici 32000)
- *p désigne le contenu de la case sur laquelle pointe p (124)
En utilisant des pointeurs, nous écrivons une deuxième fonction « echange »:

Professeur : Adil SAYOUTI 23 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Nous appelons la fonction par: echange (&n,&p) ;


Résultat: Le contenu des variables n et p est échangé !
Explication: Lors de l'appel, les adresses de n et de p sont copiées dans les pointeurs a et b. la
fonction echange échange ensuite le contenu des adresses indiquées par les pointeurs a et b.

VIII. Récursivité
1. Récursivité simple
En mathématiques une fonction récursive est une fonction qui s'appelle elle-même. Il est
possible en langage C de définir une fonction qui s'appelle elle-même, on parlera alors de
fonction récursive.
 Prenons l’exemple de la fonction factorielle :
o en mathématiques :
n! = n.(n-1)! ,
pour n ≥ 1
avec 0!=1
o en informatique :
int factorielle ( int n )
{ return n*factorielle(n-1)
}
Exemple :

 Prenons le cas de la suite Fibonacci, définie par :


o en mathématiques :
u0 = u1 = 1
un = un-1 + un-2 pour n > 1
o en informatique :
int fib (int n)
{
return fib (n-1) + fib (n-2);
}
Appels récursifs pour fib (4)
fib (4) = fib (3) + fib (2)
= fib (2) + fib (1) + fib (1) + fib (0)
= fib (1) + fib (0) + fib (1) + fib (1)+ fib (0)

Professeur : Adil SAYOUTI 24 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Remarque : On parle de récursivité croisée lorsque deux fonctions s'appellent l'une l'autre
récursivement.

2. Terminaison d’une fonction récursive


Lors de la conception d'une fonction récursive, il est indispensable de prévoir le test d'arrêt de la
fonction qui permet d'arrêter la récursivité (en d'autres termes il est nécessaire d'éviter un
nombre infini d'appels à la même fonction).

 Prenons l’exemple de la fonction factorielle


int factorielle ( int n )
{ return n*factorielle (n-1) ;
}
On peut constater que ce code s'appelle lui-même. Le problème est qu'il ne s'arrêtera jamais car
on appelle factorielle systématiquement. On doit cesser d'appeler factorielle dès que le
paramètre qu'on lui passe n'a plus de sens. Donc dès que n-1 vaut 0, c.-à-d. dès que n vaut 1.
Rajoutons donc un test d'arrêt de la fonction factorielle :
int factorielle ( int n )
{
if (n==1) {
return (1) /* factorielle(1)=1, on s’arrête pour éviter de passer dans les entiers
négatifs*/
}
else
{
return n*factorielle (n-1) ;
}
 la suite Fibonacci
En ajoutant la condition de terminaison à la suite de
Fibonacci. La fonction peut s’écrire sous la forme suivante :

3. Récursivité et itération
Prenons par exemple le calcul de la factorielle d'un nombre. On peut écrire la fonction factorielle
sous la forme d'une simple boucle, de la manière suivante :
int factorielle(int valeur)
{
int total = 1;
int curValeur;
for (curValeur = 1; curValeur <= valeur; curValeur++)
total *= curValeur;
return total;
}
On a déjà donné une définition récursive de la fonction factorielle. Cette définition est
parfaitement équivalente à la précédente, et peut se traduire en code par une fonction récursive:
Int factorielle(int valeur)
{
if (valeur == 0)
return 1;
else return valeur * factorielle(valeur - 1);
}
On peut remarquer que le code de cette deuxième version est plus simple que la version avec
une boucle, et qu'il peut se lire quasiment comme une définition.
La première version, qui utilise une boucle, est ce que l'on appelle une implémentation itérative
de la fonction factorielle. La deuxième version s'appelle l'implémentation récursive.

Professeur : Adil SAYOUTI 25 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

4. Occupation de la mémoire
Une grande partie des problèmes peut se résoudre avec une implémentation récursive, comme
avec une implémentation itérative. L'une ou l'autre peut paraître plus ou moins naturelle suivant
le problème. L'implémentation récursive permet souvent d'avoir un programme plus simple,
plus facile à comprendre, donc à débugger. L'implémentation récursive a cependant deux
principaux inconvénients :
o Le premier inconvénient fait que des programmes implémentés avec une fonction récursive
seront souvent légèrement plus lents que leurs équivalents itératifs. Si le moindre gain de
vitesse pour cette partie de votre programme est important, il peut donc être préférable
d'utiliser une implémentation itérative. Dans le cas contraire, la perte de performances peut être
largement compensée par le gain en clarté du code, donc en réduction de risques de laisser des
bugs.
o Le deuxième inconvénient peut être très gênant si le nombre d'appels imbriqués est très
important. Chaque appel de fonction imbriqué utilise une certaine quantité de mémoire, plus ou
moins importante selon le nombre de paramètres et de variables de votre fonction. Cette
mémoire est libérée dès que l'exécution de la fonction se termine, mais dans le cas d'une
fonction récursive, cette quantité de mémoire est multipliée par le nombre d'appels imbriqués à
un moment donné. Si ce nombre d'appels imbriqués peut atteindre des centaines de milliers,
voire des millions, on peut facilement atteindre des méga-octets de mémoire, pour un calcul qui
ne prendrait aucune mémoire avec une fonction itérative.

IX. Les Structures


1. Introduction
Nous avons déjà vu comment le tableau permettait de désigner sous un seul nom un ensemble
de valeurs de même type, chacune d'entre elles étant repérée par un indice.
Imaginons que l’on veut afficher les notes d’une classe d’élèves par ordre croissant avec les nom
et prénom de chaque élève. Il est donc nécessaire d’utiliser trois tableaux (pour stocker les
noms, les prénoms et les notes). Lorsque l’on va trier le tableau des notes, il faut aussi modifier
l’ordre des tableaux qui contiennent les noms et prénoms. Mais cela multiplie le risque d’erreur.
Il serait donc intéressant d’utiliser les structures.
La structure (ou enregistrement), contrairement au tableau, nous permet de désigner sous un
seul nom un ensemble de valeurs pouvant être de types différents. L'accès à chaque élément
de la structure (nommé champ) se fera, cette fois, non plus par une indication de position, mais
par son nom au sein de la structure. Les variables de type structuré sont appelées
enregistrements.

2. Déclaration d’un enregistrement


Contrairement aux tableaux, il n’existe pas par défaut de type structure. Il est donc nécessaire de
définir un nouveau type basé sur une structure et après on peut déclarer des variables sur ce
nouveau type.

En algorithmique, la syntaxe de définition (construction) d’un type basé sur une structure est :
TYPE NomduType = STRUCTURE
attribut1 : Type
attribut2 : Type
...
attributn : Type
FIN STRUCTURE

Le type d’un attribut peut être :


o Un type simple : entier, float…
o Un type complexe : tableau, type basé sur une structure…

Professeur : Adil SAYOUTI 26 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Voici un exemple de définition d'une variable de type structuré en langage C :


struct eng
{ int numero ;
int qte ;
float prix ;
};
Celle-ci définit un modèle d’enregistrement mais ne réserve pas de variables correspondant à
cette structure. Ce modèle s'appelle ici eng et il précise le nom et le type de chacun des champs
constituant la structure (numero, qte et prix). Une fois un tel modèle défini, nous pouvons
déclarer des variables du type correspondant.
Par exemple : Struct eng art1 ;
Réserve un emplacement nommé art1 de type eng destiné à contenir deux entiers et un flottant.
De manière semblable :
struct eng art1, art2 ;
Réserve deux emplacements art1 et art2 de type eng.
Remarque :
Il est possible de regrouper la définition du modèle de structure et la déclaration dans une seule
instruction comme dans l’exemple suivant :
struct eng
{ int numero ;
int qte ;
float prix ;
} art1, art2 ;

3. Manipulation d’un enregistrement


En C, on peut manipuler un enregistrement (structure) de deux manières :
o en travaillant individuellement sur chacun de ses champs;
o en travaillant de manière globale sur l'ensemble de l’enregistrement.

3.1. Manipulation des champs d’un enregistrement


Chaque champ d'un enregistrement peut être manipulé comme n'importe quelle variable du
type correspondant. La désignation d'un champ se note en faisant suivre le nom de la variable
structure (enregistrement) de l'opérateur point (.) suivi du nom de champ tel qu'il a été défini
dans le modèle.
Voici quelques exemples utilisant le modèle eng et les variables art1 et art2 déclarées de ce type.
art1.numero = 20 ;
Affecte la valeur 20 au champ numero de l’enregistrement art1.
printf ("%f", art1.prix) ;
Affiche, suivant le code format %f, la valeur du champ prix de la structure art1.
scanf ("%f", &art2.prix) ;
Lit, suivant le code format %f, une valeur qui sera affectée au champ prix de la structure art2.
Art2.numero++
Incrémente de 1 la valeur du champ numero de l’enregistrement art2.

3.2. Manipulation globale d’un enregistrement


Il est possible d'affecter à un enregistrement le contenu d'un autre enregistrement défini à partir
du même modèle. Par exemple, si les enregistrements art1 et art2 ont été déclarés suivant le
modèle eng, nous pourrons écrire :
art1 = art2 ;
Une telle affectation globale remplace : art1.numero = art2.numero ;
art1.qte = art2.qte ;
art1.prix = art2.prix ;

Professeur : Adil SAYOUTI 27 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Remarque:
o Une affectation globale n'est possible que si les structures ont été définies avec le même
nom de modèle ; en particulier, elle sera impossible avec des variables ayant une
structure analogue mais définies sous deux noms différents.
o L'affectation globale n'est pas possible entre tableaux. Elle l'est, par contre, entre
structures. Aussi est-il possible, en créant artificiellement une structure contenant un
seul champ qui est un tableau, de réaliser une affectation globale entre tableaux.

3.3. Initialisation de structures


On retrouve pour les structures les règles d'initialisation qui sont en vigueur pour tous les types
de variables, à savoir :
o En l'absence d'initialisation explicite, les structures de classe "statique" sont, par défaut,
initialisées à zéro ; celles possédant la classe "automatique" ne sont pas initialisées par
défaut (elles contiendront donc des valeurs aléatoires).
o Il est possible d'initialiser explicitement une structure lors de sa déclaration.
Exemple: struct eng art1 = { 14, 30, 580 } ;

3.4. Synonymes de types - typedef


La déclaration typedef permet de définir des types synonymes. A priori, elle s'applique à tous
les types et pas seulement aux structures. Les synonymes permettent de simplifier les
déclarations de types.

3.4.1. Exemples d’utilisation de typedef


La déclaration : typedef int entier ;
Signifie que entier est synonyme de int, de sorte que les déclarations suivantes sont
équivalentes:
int n, p ; entier n, p ;
De même : typedef int * ptr ;
Signifie que ptr est synonyme de int *. Les déclarations suivantes sont équivalentes :
int * p1, * p2 ; ptr p1, p2 ;

3.4.2. Application aux structures


En faisant usage de typedef, les déclarations des enregistrements art1 et art2 peuvent être
réalisées comme suit :
struct eng
{ int numero ;
int qte ;
float prix ;
};
typedef struct eng s_eng ;
s_eng art1, art2 ;

ou encore, plus simplement :


typedef struct
{ int numero ;
int qte ;
float prix ;
} s_eng ;

s_eng art1, art2 ;

Professeur : Adil SAYOUTI 28 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

3.5. Imbrication de structures


Dans nos exemples d'introduction des structures, nous nous sommes limités à une structure
simple ne comportant que trois champs d'un type de base. Mais chacun des champs d'une
structure peut être d'un type quelconque : pointeur, tableau, structure,...

3.5.1. Structure comportant des tableaux


Soit la déclaration suivante :
struct personne
{ char nom[30] ;
char prenom [20] ;
float heures [31] ;
} employe, responsable;

Celle-ci réserve les emplacements pour deux enregistrements nommés employe et responsable.
Ces derniers comportent trois champs :
o nom qui est un tableau de 30 caractères,
o prenom qui est un tableau de 20 caractères,
o heures qui est un tableau de 31 flottants.
Ces structures permettent de conserver pour un employé d’une entreprise les informations
suivantes : nom, prénom et nombre d’heures de travail pendant chacun des jours du mois
courant.

 La notation : employe.heures[4] désigne le cinquième élément du tableau heures de


l’enregistrement employe.
 La notation : employe.nom[0] représente le premier caractère du champ nom de
l’enregistrement employe.
 &responsable.heures[4] représente l’adresse du cinquième élément du tableau heures de la
structure responsable .
 responsable.nom représente le champ nom de l’enregistrement responsable, c'est-à-dire
plus précisément l'adresse de ce tableau.

Voici un exemple d’initialisation d’une structure de type personne lors de sa déclaration :


struct personne emp = { "Roger", "David", { 5, 8, 7, 7, 6, 0, 9, 8} }

3.5.2. Tableau de structures


Soient les déclarations suivantes :
struct point { char nom ;
int x ;
int y ;
};
struct point courbe [40] ;

La structure point sert à représenter un point d'un plan. Un point est identifié par son nom
(caractère) et ses deux coordonnées x et y. La structure courbe représente un tableau de 40
éléments du type point.
 Si i est un entier, la notation : courbe[i].nom
Représente le nom du point de rang i du tableau courbe. Il s'agit donc d'une valeur de type char.
 la notation : courbe[i].x
Désigne la valeur du champ x de l'élément de rang i du tableau courbe.
 courbe[4] représente l’enregistrement de type point correspondant au cinquième élément
du tableau courbe.

Professeur : Adil SAYOUTI 29 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

 courbe est un identificateur de tableau : désigne son adresse de début.

Voici un exemple d'initialisation de la variable courbe, lors de sa déclaration :


struct point courbe[50]= { {'A', 12, 24}, {'M', 14, 25}, {'P', 28,2} };

3.5.3. Structures comportant d'autres structures


Supposons que, à l’intérieur de la structure employe, nous ayons besoin d’introduire deux dates:
la date d’embauche et la date d’entrée dans le dernier poste occupé. Si ces dates sont elles
mêmes des structures comportant trois champs correspondant au jour, au mois et à l'année,
nous pouvons alors procéder aux déclarations suivantes :

struct date struct personne


{ int jour ; { char nom[30] ;
int mois ; char prenom[20] ;
int annee ; float heures [31] ;
}; struct date date_embauche ;
struct date date_poste ;
} employe, responsable ;
 La notation : employe.date_embauche.annee (valeur de type int) représente l'année
d'embauche correspondant à l’enregistrement employe.
 La notation : responsable.date_embauche représente la date d'embauche
correspondant à l’enregistrement responsable. Il s'agit cette fois d'une structure de type date.
Elle pourra éventuellement faire l'objet d'affectations globales comme dans :
courant.date_embauche = employe.date_poste;

X. Allocation dynamique de la mémoire


1. Introduction
Un programme en langage C comporte trois types de données :
o Statiques,
o Automatiques,
o Dynamiques.

Les données statiques occupent un emplacement parfaitement défini lors de la compilation.


Les données automatiques, en revanche, n'ont pas une taille définie a priori. En effet, elles ne
sont créées et détruites qu'au fur et à mesure de l'exécution du programme. Elles sont souvent
gérées sous forme de ce que l'on nomme une pile, laquelle croît ou décroît suivant les besoins du
programme. Plus précisément, elle croît à chaque entrée dans une fonction pour faire place à
toutes les variables locales nécessaires pendant la durée de vie de la fonction ; elle décroît
d'autant à chaque sortie. La gestion des données automatiques reste transparente au
programmeur.
Les données dynamiques n'ont pas non plus de taille définie a priori. Leur création ou leur
libération dépend, cette fois, des demandes explicites faites lors de l'exécution du programme.
Les données dynamiques sont créées sur l’initiative du programmeur.
D'une manière générale, l'emploi de données statiques présente certains défauts intrinsèques.
Citons deux exemples :

o Les données statiques ne permettent pas de définir des tableaux de dimensions


variables, c'est-à-dire dont les dimensions peuvent être fixées lors de l'exécution et non
dès la compilation. Il est alors nécessaire d'en fixer arbitrairement une taille limite, ce
qui conduit généralement à une mauvaise utilisation de l'ensemble de la mémoire.

Professeur : Adil SAYOUTI 30 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

o La gestion statique ne se prête pas aisément à la mise en œuvre de listes chaînées (font
l’objet du paragraphe III), d'arbres binaires,... objets dont ni la structure ni la taille ne
sont généralement connues lors de la compilation du programme.
Les données dynamiques vont permettre de pallier ces défauts en donnant au programmeur
l'opportunité de s'allouer et de libérer de la mémoire au fur et à mesure de ses besoins.
Dans la première partie de ce paragraphe, nous présentons brièvement les pointeurs. Dans la
seconde partie, nous décrivons les outils de base de la gestion dynamique de la mémoire.

2. Pointeurs
2.1. Introduction
Toute variable manipulée dans un programme est stockée quelque part en mémoire centrale.
Cette mémoire est constituée d’octets qui sont identifiés par un numéro qu’on appelle adresse
mémoire. Pour retrouver une variable, il suffit donc de connaître l’adresse de l’octet où elle est
stockée (ou, s’il s’agit d’une variable qui recouvre plusieurs octets contigus, l’adresse du premier
de ces octets). Pour des raisons de lisibilité, on désigne souvent les variables par des
identificateurs, et non par leur adresse. C’est le compilateur qui fait alors le lien entre
l’identificateur d’une variable et son adresse en mémoire. Toutefois, il est parfois très pratique
de manipuler directement une variable par son adresse.

2.2. Adresse mémoire et valeur d’un objet


On appelle Lvalue (left value) tout objet pouvant être placé à gauche d’un opérateur
d’affectation. Une Lvalue est caractérisée par :
o son adresse: l’adresse mémoire à partir de laquelle l’objet est stocké ;
o sa valeur: ce qui est stocké à cette adresse.
Dans l’exemple,
int i, j;
i = 3;
j = i;
Si le compilateur place la variable i à l’adresse 4831836000 en mémoire, et la variable j à
l’adresse 4831836004, on aura :

 Deux variables différentes ont des adresses différentes. L’affectation i = j; n’opère que sur les
valeurs des variables.
 Les variables i et j étant de type int, elles sont stockées sur 4 octets. Ainsi la valeur de i est
stockée sur les octets d’adresse 4831836000 à 4831836003.
 L’adresse d’un objet étant un numéro d’octet en mémoire, il s’agit d’un entier (entier long)
quelque soit le type de l’objet considéré. Le format interne de cet entier (16 bits, 32 bits ou 64
bits) dépend des architectures.
 L’opérateur & permet d’accéder à l’adresse d’une variable. Toutefois &i n’est pas une Lvalue
mais une constante : on ne peut pas faire figurer &i à gauche d’un opérateur d’affectation.
 Pour pouvoir manipuler des adresses, on doit donc recourir à un nouveau type d’objets, les
pointeurs.

2.3. Notion de pointeur


Un pointeur est un objet (Lvalue) dont la valeur est égale à l’adresse d’un autre objet. On déclare
un pointeur par l’instruction : type * nom-du-pointeur; Où type est le type d’objet pointé.
Cette instruction déclare un identificateur, nom-du-pointeur, associé à un objet dont la valeur est
l’adresse d’un autre objet de type type. L’identificateur nom-du-pointeur est un identificateur
d’adresse. Comme pour n’importe quelle Lvalue, sa valeur est modifiable.

Professeur : Adil SAYOUTI 31 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Exemple 1:
Dans l’exemple suivant, on définit un pointeur p qui pointe vers un entier i :
int i = 3;
int * p;
p = &i;
On se trouve dans la configuration

L’opérateur unaire d’indirection * permet d’accéder directement à la valeur de l’objet pointé.


Ainsi, si p est un pointeur vers un entier i, *p désigne la valeur de i.
Exemple 2:
main ()
{
int i = 3;
int *p;
p = &i;
printf("*p = %d \n",*p);
}
Ce programme imprime à l’écran *p=3
Dans ce programme, les objets i et *p sont identiques : ils ont la même adresse et la même
valeur. Cela signifie que toute modification de *p
modifie i.
Nous sommes dans la configuration :

Dans un programme, on peut manipuler à la fois p et *p. Ces deux manipulations sont très
différentes. Comparons par exemple les deux programmes suivants:
main() et main()
{ {
int i = 3, j = 6; int i = 3, j = 6;
int *p1, *p2; int *p1, *p2;
p1 = &i; p1 = &i;
p2 = &j; p2 = &j;
*p1 = *p2; p1 = p2;
} }

Avant la dernière affectation de chacun de ces


programmes, on est dans une configuration de type :

Après l’affectation *p1 = *p2; du premier programme, on :

Par contre, l’affectation p1 = p2 du second programme conduit à la situation :

Professeur : Adil SAYOUTI 32 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

2.4. Arithmétique des pointeurs


La valeur d’un pointeur étant un entier, on peut lui appliquer un certain nombre d’opérateurs
arithmétiques classiques. Les seules opérations arithmétiques valides sur les pointeurs sont :
o l’addition d’un entier à un pointeur. Le résultat est un pointeur de même type que le
pointeur de départ ;
o la soustraction d’un entier à un pointeur. Le résultat est un pointeur de même type que
le pointeur de départ ;
o la différence de deux pointeurs pointant tous les deux vers des objets de même type. Le
résultat est un entier.

Remarque :
 La somme de deux pointeurs n’est pas autorisée.
 Si i est un entier et p est un pointeur sur un objet de type type, l’expression p + i désigne un
pointeur sur un objet de type type dont la valeur est égale à la valeur de p incrémentée de i *
sizeof (type). Il en va de même pour la soustraction, et pour les opérateurs ++ et --.
 Si p et q sont deux pointeurs sur des objets de type type, l’expression p - q désigne un entier
dont la valeur est égale à (p - q)/sizeof(type).

Exemple:
le programme suivant main()
{
int i = 3;
int *p1, *p2;
p1 = &i;
p2 = p1 + 1;
printf("p1 = %ld \t p2 = %ld\n",p1,p2);
Affiche p1= 4831835984 p2 = 4831835988

Par contre, le même programme avec des pointeurs sur des objets de type double :
main()
{
double i = 3;
double *p1, *p2;
p1 = &i;
p2 = p1 + 1;
printf("p1 = %ld \t p2 = %ld\n",p1,p2);
} Affiche p1= 4831835984 p2 = 4831835992

 Les opérateurs de comparaison sont également applicables aux pointeurs, à condition de


comparer des pointeurs qui pointent vers des objets de même type.
 L’utilisation des opérations arithmétiques sur les pointeurs est particulièrement utile pour
parcourir des tableaux.
Le programme suivant imprime les éléments du tableau tab dans l’ordre croissant puis
décroissant des indices.
#define N 5
int tab[5] = {1, 2, 6, 0, 7};
main()
{ int *p;
printf("\n ordre croissant:\n");
for (p = &tab[0]; p <= &tab[N-1]; p++)

Professeur : Adil SAYOUTI 33 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

printf(" %d \n",*p);
printf("\n ordre decroissant:\n");
For (p = &tab[N-1]; p >= &tab[0]; p--)
printf(" %d \n",*p);
}

3. Allocation dynamique et libération de la mémoire


3.1. Introduction
Avant de manipuler un pointeur, et notamment de lui appliquer l’opérateur d’indirection *, il
faut l’initialiser. Sinon, par défaut, la valeur du pointeur sera égale à une constante symbolique
notée NULL définie dans stdio.h. En général, cette constante vaut 0. Le test p == NULL permet de
savoir si le pointeur p pointe vers un objet.
 On peut initialiser un pointeur p par une affectation sur p. Par exemple, on peut affecter à p
l’adresse d’une autre variable.
 Il est également possible d’affecter directement une valeur à *p. Mais pour cela, il faut d’abord
réserver à *p un espace-mémoire de taille adéquate. L’adresse de cet espace mémoire
sera la valeur de p.
 allocation dynamique: opération consistant à réserver un espace mémoire pour stocker
l’objet pointé.

3.2. La fonction malloc


L’allocation dynamique se fait par la fonction malloc de la librairie standard stdlib.h.
Syntaxe : malloc (nombre-octets) ;

malloc retourne un pointeur pointant vers un objet de taille nombre-octets octets.


L’argument nombre-octets est souvent donné à l’aide de la fonction sizeof() qui renvoie le
nombre d’octets utilisés pour stocker un objet.

o Initialisation d’un pointeur vers un entier :


#include <stdlib.h>
int *p;
p = (int*)malloc(sizeof(int)); /* cette écriture a l’avantage d’être
portable */
ou bien p = (int*)malloc(4); /* int est stocké sur 4 octets */
Le programme suivant définit un pointeur p sur un objet *p de type int, et affecte à *p la valeur
de la variable i. #include <stdio.h>
#include <stdlib.h>
main()
{
int i = 3;
int *p;
printf("valeur de p avant initialisation = %ld\n",p);
p = (int*)malloc(sizeof(int));
printf("valeur de p après initialisation = %ld\n",p);
*p = i;
printf("valeur de *p = %d\n",*p);
}
Il imprime à l’écran :
valeur de p avant initialisation = 0
valeur de p après initialisation = 5368711424
valeur de *p = 3
o Avant l’allocation dynamique, on se trouve dans la configuration

Professeur : Adil SAYOUTI 34 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

A ce stade, *p n’a aucun sens. En particulier, toute manipulation de la variable *p génère une
violation mémoire, détectable à l’exécution par le message d’erreur Segmentation fault.

o L’allocation dynamique a pour résultat d’attribuer une valeur à p et de réserver à cette


adresse un espace mémoire composé de 4 octets pour stocker la valeur de *p.

On a alors

*p est maintenant définie mais sa valeur n’est pas initialisée.

o L’affectation *p = i; a enfin pour résultat d’affecter à *p la valeur de i. A la fin du


programme, on a donc

o Comparaison du programme
suivant avec le programme précédent :
main( )
{ int i = 3;
int *p;
p = &i;
}
Ce programme correspond à la situation suivante :

Dans ce dernier cas, les variables i et *p sont identiques (elles ont la même adresse) ce qui
implique que toute modification de l’une modifie l’autre. Ceci n’était pas vrai dans l’exemple
précédent où *p et i avaient la même valeur mais des adresses différentes.
On remarque que le dernier programme ne nécessite pas d’allocation dynamique puisque
l’espace mémoire à l’adresse &i est déjà réservé pour un entier.

o La fonction malloc permet également d’allouer un espace pour plusieurs objets


contigus en mémoire. On peut écrire par exemple
#include <stdio.h>
#include <stdlib.h>
main( )
{
int i = 3;
int j = 6;
int *p;
p = (int*)malloc(2 * sizeof(int));
*p = i;
*(p + 1) = j;
printf("p = %ld \t *p = %d \t p+1 = %ld \t *(p+1) = %d \n",p,*p,p+1,*(p+1));
}

Professeur : Adil SAYOUTI 35 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

o on a réservé, à l’adresse donné par la valeur de p, 8 octets en mémoire, qui permettent


de stocker 2 objets de type int.
Instruction : p = (int*)malloc(2 * sizeof(int));
Le programme affiche :
p = 5368711424 *p = 3 p+1 = 5368711428 *(p+1) = 6

3.3. La fonction calloc


La fonction calloc de la librairie stdlib.h a le même rôle que la fonction malloc mais elle initialise
en plus l’objet pointé *p à zéro. Sa syntaxe est
calloc(nb-objets,taille-objets)
Ainsi, si p est de type int*, l’instruction
p = (int*)calloc(N,sizeof(int));
est strictement équivalente à
p = (int*)malloc(N * sizeof(int));
for (i = 0; i < N; i++)
*(p + i) = 0;
L’emploi de calloc est simplement plus rapide.

3.4. La fonction free


Lorsque l’on n’a plus besoin de l’espace mémoire alloué dynamiquement (c.à.d. quand on
n’utilise plus le pointeur p), il faut libérer cette place en mémoire. Ceci se fait à l’aide de
l’instruction free qui a pour syntaxe :
free (nom-du-pointeur);
A toute instruction de type malloc ou calloc doit être associée une instruction de type free.

XI. Les listes chainées


1. Introduction
Comme nous l'avons déjà évoqué dans le chapitre « tableaux et chaînes de caractères », il n'est
pas possible de déclarer un tableau dont le nombre d'éléments n'est pas connu lors de la
compilation. Par contre, les possibilités de gestion dynamique du langage C nous permettent
d'allouer des emplacements aux différents éléments du tableau au fur et à mesure des besoins.
Si l'on n'a pas besoin d'accéder directement à chacun des éléments, on peut se contenter de
constituer ce que l'on nomme une liste chaînée, dans laquelle :
o Un pointeur désigne le premier élément,
o Chaque élément comporte un pointeur sur l'élément suivant.
Dans ce cas, les emplacements des différents éléments peuvent être alloués de façon dynamique,
au fur et à mesure des besoins. Il n'est plus nécessaire de connaître d'avance leur nombre ou une
valeur maximale (qui est obligatoire pour un tableau).
Le schéma ci-dessous montre les différences entre un tableau et une liste chainée.

Nous avons sur ce schéma la représentation que l'on pourrait faire d'un tableau et d'une liste
chaînée.
o Dans un tableau, la taille est connue, l'adresse du premier élément aussi. Lorsque vous
déclarez un tableau, la variable contiendra l'adresse du premier élément de votre

Professeur : Adil SAYOUTI 36 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

tableau.
Comme le stockage est contigu, et la taille de chacun des éléments connue, il est
possible d'atteindre directement la case i d'un tableau.
o Pour supprimer ou ajouter un élément à un tableau, il faut créer un nouveau tableau et
supprimer l'ancien. Ce n'est en général pas visible par l'utilisateur, mais c'est ce que
realloc souvent fait. L'adresse du premier élément d'un tableau peut changer après un
realloc, ce qui est tout à fait logique puisque realloc n'aura pas forcement la possibilité de
trouver en mémoire la place nécessaire et contiguë pour allouer votre nouveau tableau.
realloc cherche une place suffisante, recopier votre tableau, et supprimer l'ancien.
o Dans une liste chaînée, la taille est inconnue au départ, la liste peut avoir autant
d'éléments que votre mémoire le permet.
o Il est en revanche impossible d'accéder directement à l'élément i de la liste chainée.
Pour ce faire, il vous faudra traverser les i-1 éléments précédents de la liste.
o Pour déclarer une liste chaînée, il suffit de créer le pointeur qui pointe sur le premier
élément de votre liste chaînée, aucune taille n'est donc à spécifier.
o Il est possible d'ajouter, de supprimer, d'intervertir des éléments d'une liste chaînée
sans avoir à recréer la liste en entier, mais en manipulant simplement leurs
pointeurs.

Une liste chaînée est un type structuré. Chaque élément de la liste est composé de deux parties :
o la valeur (les valeurs) que vous voulez stocker,
o l'adresse de l'élément suivant.
Le dernier élément pointe vers une adresse (notée NULL) pour signifier la fin de la liste.
Le schéma suivant explique comment se passent l'ajout et la suppression d'un élément d'une
liste chaînée.

2. Création d’une liste chaînée


Nous pouvons créer des listes chaînées de n'importe quel type d'éléments : entiers, caractères,
structures, tableaux, voir même d'autres listes chaînées... Il est même possible de combiner
plusieurs types dans une même liste. Dans cette partie du cours, nous choisissons le type réel
afin de gérer les notes d’une classe d’élèves.

o Création d'une liste chaînée de réels :


struct etudiant
{
float moyenne;
struct etudiant * nxt;
};

Professeur : Adil SAYOUTI 37 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

o Création de la liste chaînée et des types etudiant et llist


# include <stdio.h>
# include <stdlib.h>
Typedef struct etudiant etudiant ;
struct etudiant
{
float moyenne;
struct etudiant * nxt;
};
typedef struct etudiant * llist;

Le bloc d’instructions ci-dessus nous a permis de créer le type étudiant qui est une (liste chaînée
de réels) structure contenant un réel (moyenne) et un pointeur sur l’élément étudiant (nxt), qui
contiendra l'adresse de l'étudiant suivant.
La déclaration : Typedef struct etudiant etudiant; signifie que etudiant est synonyme de la
structure etudiant.
L’instruction : typedef struct etudiant * llist; permet de créer le type llist (pour linked list = liste
chaînée) qui est un pointeur sur le type étudiant. Le type llist permet de simplifier la déclaration.

3. Déclaration d’une liste chaînée


Voici la déclaration de 3 listes chaînées (ma_liste, ma_liste1 et ma_liste2) de façons différentes
mais équivalentes.
#include <stdlib.h>
……
main ( )
{
llist ma_liste = NULL;
etudiant * ma_liste1 = NULL;
struct etudiant * ma_liste2 = NULL;
……..
}

Lors de déclaration de la liste chaînée, nous devons déclarer un pointeur sur étudiant,
l'initialiser à NULL, pour pouvoir ensuite allouer de la mémoire pour le premier étudiant.
N'oubliez pas d'inclure stdlib.h afin de pouvoir utiliser la macro NULL.

4. Manipulation d’une liste chaînée


Maintenant que nous savons comment déclarer une liste chaînée, il serait intéressant
d'apprendre à ajouter des éléments dans cette liste, ainsi que de lire ce qu'elle contient. Pour
cela, nous utilisons notre nouvelle liste (ma_liste), c'est-à-dire un pointeur sur étudiant
contenant l'adresse du premier élément de la liste.

4.1. Ajouter un élément à la liste chaînée


Les deux ajouts génériques des listes chaînées sont les ajouts en tête, et les ajouts en fin de liste.
Dans ce paragraphe, nous étudions ces deux moyens permettant d'ajouter un élément à une
liste.

4.1.1. Ajout en tête de liste


Lors d'un ajout en tête, nous créons un étudiant (nouvelEtudiant), lui assigner la note que l'on
veut ajouter, puis pour terminer, raccorder cet élément à la liste passée en paramètre. Lors d'un
ajout en tête, on devra assigner à nxt l'adresse du premier élément de la liste passé en
paramètre. Le principe d’ajout en tête est expliqué sur le schéma suivant :

Professeur : Adil SAYOUTI 38 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Voici la fonction qui permet l’ajout d’un étudiant en tête de liste en langage C :
llist ajouterEnTete(llist liste, float note)
{
/* Déclaration et allocation de la mémoire pour l’élément (étudiant) à ajouter */
etudiant * nouvelEtudiant ;
nouvelEtudiant = (etudiant *) malloc(sizeof(etudiant));
/* On attribue la note au nouvel étudiant */
nouvelEtudiant ->moyenne = note;
/* On affecte l'adresse de l'étudiant suivant au nouvel étudiant */
nouvelEtudiant ->nxt = liste;
/* La fonction renvoie la nouvelle liste, c.à.d. le pointeur sur le premier étudiant de la liste*/
return nouvelEtudiant;
}

4.1.2. Ajout en fin de liste


Dans l’ajout en fin de liste, il nous faut tout d'abord créer un nouvel étudiant, lui attribuer sa
note, et mettre l'adresse de l'étudiant suivant à NULL. En effet, comme cet étudiant va terminer la
liste, nous devons signaler qu'il n'y a plus d'étudiant suivant. Ensuite, il faut faire pointer le
dernier étudiant de liste originale sur le nouvel étudiant que nous venons de créer. Pour ce faire,
il faut créer un pointeur temporaire sur étudiant qui va se déplacer d'étudiant en étudiant, et
regarder si cet étudiant est le dernier de la liste. Un étudiant sera forcément le dernier de la liste
si NULL est assigné à son champ nxt.

Voici la fonction qui permet l’ajout d’un étudiant en fin de liste en langage C :

llist ajouterEnFin(llist liste, float note)


{
/* On crée un nouvel étudiant */
etudiant * nouvelEtudiant ;
nouvelEtudiant = (etudiant *) malloc(sizeof(etudiant));
/* On attribue la note au nouvel étudiant */
nouvelEtudiant->moyenne = note;

Professeur : Adil SAYOUTI 39 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

/* On ajoute l’étudiant en fin de liste, c.à.d. cet étudiant pointe sur NULL */
nouvelEtudiant->nxt = NULL;
if(liste == NULL)
{
/* Si la liste est vide, il suffit de renvoyer l'étudiant créé */
return nouvelEtudiant;
}
else
{
/* Sinon, on parcourt la liste à l'aide d'un pointeur temporaire et on
indique que le dernier étudiant de la liste est relié au nouvel étudiant */
etudiant * tmp=liste;
while(tmp->nxt != NULL)
{
tmp = tmp->nxt;
}
tmp->nxt = nouvelEtudiant;
return liste;
}
}

Dans le dernier bloc d’instructions, nous nous déplaçons le long de la liste chaînée grâce au
pointeur tmp. Si l'étudiant pointé par tmp n'est pas le dernier (tmp->nxt != NULL), on avance d'un
rang (tmp = tmp->nxt) en assignant à tmp l'adresse de l'étudiant suivant. Une fois que l'on est au
dernier étudiant, il ne reste plus qu'à le relier au nouvel étudiant.

4.2. Afficher les valeurs de la liste chaînée


Pour l’affichage, nous devrons parcourir la liste jusqu'au bout et afficher les moyennes générales
des étudiants. Voici le code en langage C de la fonction afficherListe :

void afficherListe(llist liste)


{
etudiant * tmp = liste;
/* Tant que l'on n'est pas au bout de la liste */
while(tmp != NULL)
{
/* On affiche */
printf("%f ", tmp->moyenne);
/* On avance d'un rang */
tmp = tmp->nxt;
}
}

4.3. Supprimer un élément de la liste chaînée


Dans ce paragraphe, nous étudions deux moyens permettant la suppression d’un élément de la
liste chaînée : la suppression en tête et la suppression en fin de liste.

4.3.1. Supprimer un élément en tête de liste


Il s'agit ici de supprimer le premier élément de la liste. Pour ce faire, il nous faudra utiliser la
fonction free qui libère la mémoire allouée. Si la liste n'est pas vide, on stocke l'adresse du
premier étudiant de la liste après suppression (c.à.d. l'adresse du 2ème étudiant de la liste
originale), on supprime le premier étudiant, et on renvoie la nouvelle liste. Attention quand

Professeur : Adil SAYOUTI 40 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

même à ne pas libérer le premier étudiant avant d'avoir stocké l'adresse du second, sans quoi il
sera impossible de la récupérer.
Voici le code de la fonction « suppression en tête de liste » en langage C :

llist supprimertete(llist liste)


{
if (liste == NULL)
{
return NULL;
}
else
/* Si la liste est non vide, on se prépare à renvoyer l'adresse de l'élément en 2ème position */
{
llist listearenvoyer;
listearenvoyer=liste->nxt;
/* On libère le premier élément */
free(liste);
/* On retourne le nouveau début de la liste */
return listearenvoyer;
}
}

4.3.2. Supprimer un élément en fin de liste


Cette fois-ci, il va falloir parcourir la liste jusqu'à son dernier étudiant, indiquer que l'avant-
dernier étudiant va devenir le dernier de la liste et libérer le dernier étudiant pour enfin
retourner le pointeur sur le premier étudiant de la liste d'origine. Voici le code en C de la
fonction suppression en fin de liste :
llist supprimerfin(llist liste)
{
/* Si la liste est vide, on retourne NULL */
if (liste == NULL)
return NULL;
/* Si la liste contient un seul étudiant */
else if (liste->nxt==NULL)
{
/* On le libère et on retourne NULL (la liste est maintenant vide) */
free(liste);
return NULL;
}
else
{
/* Si la liste contient au moins deux éléments */
llist tmp;
llist ptmp;
tmp=liste;
/* Tant qu'on n'est pas au dernier élément */
while (tmp->nxt !=NULL)
{
/* ptmp stock l'adresse de tmp */
ptmp=tmp;
/* On déplace tmp mais ptmp garde l'ancienne valeur de tmp */
tmp=tmp->nxt;
}

Professeur : Adil SAYOUTI 41 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

/* A la sortie de la boucle, tmp pointe sur le dernier élément, et ptmp sur l'avant-dernier. On
indique que l'avant-dernier devient la fin de la liste et on supprime le dernier élément */
ptmp->nxt=NULL;
free(tmp);
/* free(ptmp): à ne pas liberer ptmp, car le pointeur ptmp pointe sur le nouveau dernier element
de la liste --- donc il faut garder ptmp */
return liste;
}
}
XII. Les piles
1. Définition
La pile est une structure de données, qui permet de stocker les données dans l'ordre LIFO
(Last In First Out - en français Dernier Entré Premier Sorti), ce qui signifie que les derniers
éléments à être ajoutés à la pile seront les premiers à être récupérés. Il est possible de comparer
cela à une pile d'assiettes. Lorsqu'on ajoute une assiette en haut de la pile, on retire toujours en
premier celle qui se trouve en haut de la pile, c'est-à-dire celle qui a été ajoutée en dernier, sinon
tout le reste s'écroule.
Les piles ne sont que des cas particuliers de listes chaînées dont les éléments ne peuvent être
ajoutés et supprimés qu'en fin de liste. Les piles peuvent être utilisées dans des algorithmes
d'évaluation d'expressions mathématiques.

2. Création d’une pile


En langage C, l'implémentation des piles se base sur les listes chaînées. Tout d'abord, nous
commençons par la construction du prototype d’un élément de la pile (pile d’entiers dans notre
cas). Chaque élément contient un champ donnée et un pointeur précédent du même type
qu’élément. Le pointeur précédent permet l'accès vers l’élément précédent.

typedef struct ElementListe


{
int donnee;
struct ElementListe * precedent;
} Element;

Le schéma ci-dessous montre que les différents éléments de la pile (cases) sont placées les unes
sur les autres.

Professeur : Adil SAYOUTI 42 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Afin de réaliser des opérations sur la pile, nous aurons besoin du :


o Premier élément de la pile,
o Nombre d'éléments.

Le 1er élément, qui se trouve au sommet de la pile, nous permet de réaliser l'opération de
récupération (ou suppression) des données situées en haut de la pile. Pour faire cela, une autre
structure sera utilisée. Voici sa composition :
typedef struct RepererListe {
Element *sommet;
int taille;
} Pile;
Le pointeur sommet contient l'adresse du premier élément de la pile. La variable taille contient
le nombre d'éléments. Quelque soit la position dans la liste, le pointeur sommet pointe toujours
vers le 1er élément, qui est en haut de la pile. Le champ taille contient le nombre d'éléments de la
pile.

3. Manipulation d’une pile


3.1. Ajouter un élément à la pile
Pour ajouter un nouvel élément à la pile, nous réalisons la fonction empiler dont le
prototype est : void empiler (Pile * mapile, int donnees);

Voici l'algorithme d'ajout d’un élément :


o déclaration de l’élément à insérer,
o allocation de la mémoire pour le nouvel élément,
o remplir le contenu du champ de données,
o mettre à jour le pointeur sommet vers le 1er élément (le haut de la pile),
o mettre à jour la taille de la pile.
Voici le code en langage C de la fonction :
void empiler (Pile * mapile, int donnees)
{
Element * nouvel_element;
nouvel_element= (Element *)malloc(sizeof(Element));
if (nouvel_element != NULL)
{
nouvel_element->donnee = donnees;
nouvel_element->precedent = mapile->sommet;
mapile ->sommet = nouvel_element;
mapile ->taille++;
}
}

3.2. Enlever un élément de la pile


Pour supprimer (dépiler) un élément de la pile, il faut tout simplement supprimer l'élément vers
lequel pointe le pointeur sommet.
Voici le prototype de la fonction : void depiler (Pile * mapile);

Voici le code en langage C de la fonction :


void depiler (Pile * mapile)
{
Element * supp_element;
supp_element = mapile->sommet; /* Pointeur contenant l’adresse du 1er élément */
mapile ->sommet = mapile ->sommet->precedent;
/* le pointeur sommet pointe vers le 2ème élément (après la suppression du 1er élément, le 2ème
sera en haut de la pile) */

Professeur : Adil SAYOUTI 43 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

free (supp_element);
mapile ->taille= mapile -> taile - 1; /* la taille de la pile sera décrémentée d'un élément
*/
}
Remarque :
mapile -> sommet -> donnee nous permet de récupérer la donnée stockée en haut de la pile.

3.3. Afficher les éléments de la pile


Pour afficher la pile entière, il faut se positionner au début de la pile à l’aide du pointeur sommet.
Ensuite, en utilisant le pointeur précédent de chaque élément, la pile sera parcourue du 1er vers
le dernier élément. La condition d'arrêt est donnée par la taille de la pile.

Voici le code en langage C de la fonction d’affichage :


void afficher (Pile * mapile)
{
Element *courant;
int i;
courant = mapile->sommet;
for(i=0; i < mapile->taille; i++)
{
printf("\t\t%d\n", courant->donnee);
courant = courant->precedent;
}
}

Exemple :
Le programme en C ci-dessous nous permet de :
o Créer une pile d’entiers,
o Demander à l’utilisateur de saisir 3 entiers : entier 1 (le premier saisi), entier2 et entier3
(le dernier saisi).
o Afficher la pile sous la forma suivante :
**********Haut de la PILE************
Entier1
Entier2
Entier3
**********Bas de la PILE************
Le programme :
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
typedef struct ElementListe{
int donnee;
struct ElementListe * precedent;
} Element;
typedef struct Repererliste{
Element * sommet;
int taille;
} Pile;
/* empiler (ajouter) un élément dans la pile */
void empiler (Pile * mapile, int donnees);
/* affichage de la pile */
void afficher (Pile * mapile);
/* la fonction principale */

Professeur : Adil SAYOUTI 44 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

main ()
{
Pile * mapile;
int note;
/* allocation de la mémoire pour mapile */
mapile = (Pile *) malloc (sizeof (Pile));
/* initialisation */
mapile->sommet = NULL;
mapile->taille = 0;
printf ("Entrez un entier : ");
scanf ("%d", &note);
empiler (mapile, note);
printf ("La pile (%d éléments): \n",mapile->taille);
printf("\n********** Haut de la PILE **********\n");
afficher(mapile);
printf("********** Bas de la PILE **********\n\n");
printf ("Entrez un entier : ");
scanf ("%d", &note);
empiler (mapile, note);
printf ("La pile (%d éléments): \n",mapile->taille);
printf("\n********** Haut de la PILE **********\n");
afficher(mapile);
printf("********** Bas de la PILE ********** \n\n");
printf ("Entrez un entier : ");
scanf ("%d", &note);
empiler (mapile, note);
printf ("La pile (%d éléments): \n",mapile->taille);
printf("\n********** Haut de la PILE **********\n");
afficher(mapile);
printf("********** Bas de la PILE **********\n\n");
getch();
}

void empiler (Pile * mapile, int donnees){


Element * nouvel_element;
nouvel_element= (Element *)malloc(sizeof(Element));
nouvel_element->donnee = donnees;
nouvel_element->precedent = mapile->sommet;
mapile->sommet = nouvel_element;
mapile->taille++;
}

void afficher (Pile * mapile){


Element * courant;
int i;
courant = mapile->sommet;

for(i=0;i<mapile->taille;i++){
printf("\t\t %d \n", courant->donnee);
courant = courant->precedent;
}
}

Professeur : Adil SAYOUTI 45 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

XIII. Les fichiers


1. Introduction
Le langage C offre la possibilité de lire et d’écrire des données dans un fichier. Pour des raisons
d’efficacité, les accès à un fichier se font par l’intermédiaire d’une mémoire tampon (buffer), ce
qui permet de réduire le nombre d’accès aux périphériques (disque...).
Pour pouvoir manipuler un fichier, un programme a besoin d’un certain nombre d’informations :
l’adresse de l’endroit de la mémoire-tampon où se trouve le fichier, la position de la tête de
lecture, le mode d’accès au fichier (lecture ou écriture)… Ces informations sont rassemblées
dans une structure dont le type, FILE *, est défini dans la bibliothèque stdio.h. Un objet de type
FILE * est appelé flot de données (en anglais, stream). Il est donc nécessaire d’inclure les
bibliothèques stdio.h et stdlib.h au niveau des directives.

2. Manipulation des fichiers


Afin de bien manipuler un fichier, il est recommandé de suivre la procédure suivante :
o On appelle la fonction d'ouverture de fichier fopen qui nous renvoie un pointeur vers
le fichier. Ce pointeur est utilisé pour le test d’ouverture de notre fichier.
o Si le pointeur vaut NULL, c'est à dire que l'ouverture a échouée, dans ce cas on ne peut
pas continuer et il faut qu’on affiche un message d'erreur.
o Si le pointeur est différent de NULL (Ouverture avec succès), alors on peut lire et écrire
dans le fichier à travers des fonctions que nous verrons dans les paragraphes suivants.
o Une fois qu'on termine de travailler sur le fichier, il faut le fermer à l’aide de la
fonction fclose.

2.1. Ouvrir un ficher - fopen


Avant de lire ou d’écrire dans un fichier, on notifie son accès par la fonction fopen. Cette fonction
prend comme arguments le nom du fichier et le mode d’accès. fopen renvoie un pointeur vers
une structure de type FILE.
Voici le prototype de la fonction fopen :
FILE * fopen (const char * nom_fichier, const char * mode_ouverture);
nom_fichier : le nom du fichier à ouvrir.
mode_ouverture : le mode d’accès au fichier (soit en lecture, soit en écriture, soit en lecture &
écriture).
Voici quelques modes d'ouvertures de fichiers :
o "r" : lecture seule. Il est possible de lire le contenu du fichier, mais l’écriture est
impossible. Le fichier doit avoir été créé au préalable.
o "w" : écriture seule. Il est possible d’écrire dans le fichier, mais la lecture est impossible.
Si le fichier n'existe pas, il sera créé.
o "r+" : lecture et écriture. Vous pourrez lire et écrire dans le fichier. Le fichier doit avoir
été créé au préalable.

Exemple :
Le code suivant ouvre le fichier test.txt en mode "r+" (lecture / écriture) :
# include <stdio.h>
# include <stdlib.h>
main()
{
FILE * fichier = NULL; /* déclaration et initialisation d’un pointeur ‘fichier’ de type
FILE */
fichier = fopen ("test.txt", "r+"); /* le pointeur fichier pointe vers ‘test.txt’ */
if (fichier != NULL) /* test de l’ouverture du fichier */
{
// On peut lire et écrire dans le fichier
}

Professeur : Adil SAYOUTI 46 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

else
{
// On peut afficher un message d'erreur
printf("Impossible d'ouvrir le fichier test.txt");
}
……….. ……….. …………..
}

Dans l’exemple ci-dessus, le fichier test.txt doit être situé dans le même dossier que notre
programme. Le fichier test peut être de n’importe quel type.
Remarque :
Il est possible de placer le fichier à manipuler dans n’importe quel dossier. Pour y accéder, il
suffit de spécifier son chemin. Le code de l’exemple ci-dessous permet d’ouvrir le fichier tset1.txt
situé dans C:\Program Files\devc++. Dans ce cas, le chemin utilisé est appelé chemin absolu (ou
complet).
Le code : fichier = fopen("C:\\Program Files\\devc++\\test1.txt", "r+");
Il est nécessaire de préfixer un antislash dans une chaîne par un antislash afin d’indiquer au
programme qu’il s’agit d’un seul antislash.

2.2. Fermer un ficher - fclose


Une fois que vous aurez fini de travailler avec le fichier, il faudra le fermer. On utilise pour cela la
fonction fclose qui a pour rôle de libérer la mémoire, c'est-à-dire supprimer le fichier chargé
dans la mémoire vive. Le prototype de fopen est : int fclose (FILE * pointeurSurFichier);
fclose reçoit comme argument le nom du pointeur vers notre fichier. La fonction fclose retourne
un entier qui vaut zéro si l’opération s’est déroulée normalement (et une valeur non nulle en
cas d’erreur).
Exemple : fclose(fichier); permet de fermer le fichier qui a été ouvert.

2.3. Ecrire dans un fichier


Il existe plusieurs fonctions capables d'écrire dans un fichier. Dans ce paragraphe, nous étudions
les trois fonctions suivantes :
o fputc : écrit un caractère dans le fichier (un seul caractère à la fois).
o fputs : écrit une chaîne dans le fichier.
o fprintf : écrit une chaîne "formatée" dans le fichier. Elle fonctionne de la même manière
que printf.

2.3.1. fputc
La fonction fputc écrit un caractère à la fois dans le fichier. Son prototype est :
int fputc (int caractere, FILE * pointeurSurFichier);
fopen reçoit deux arguments:
o Le caractère à écrire (de type int qui revient plus ou moins à utiliser un char). Vous
pouvez donc écrire directement 'B' par exemple.
o Le pointeur sur le fichier. Dans notre exemple, notre pointeur s'appelle "fichier".
L'avantage de demander le pointeur de fichier à chaque fois, c'est que vous pouvez
ouvrir plusieurs fichiers en même temps et donc lire et écrire dans chacun de ces
fichiers. Vous n'êtes pas limités à un seul fichier ouvert à la fois.

Exemple : Le code suivant écrit la lettre 'B' dans test1.txt (si le fichier existe, il est remplacé ; si il
n'existe pas, il est créé).
main()
{
FILE * fichier = NULL;
fichier = fopen("test1.txt", "w");
if (fichier != NULL)

Professeur : Adil SAYOUTI 47 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

{
fputc ('B', fichier); // Ecriture du caractère B
fclose(fichier);
}
}

2.3.2. fputs
fputs permet d’écrire une chaîne de caractères, ce qui est en général plus pratique que d'écrire
caractère par caractère.
Prototype de la fonction :
int fputs (const char * chaine, FILE * pointeurSurFichier);

La fonction reçoit deux arguments :


o chaine : la chaîne à écrire. Notez que le type ici est const char * : en rajoutant le mot
const dans le prototype, la fonction indique que pour elle la chaîne sera considérée
comme une constante (modification impossible).
o pointeurSurFichier : comme pour fputc, il s'agit d’un pointeur de type FILE * sur le
fichier que nous avons ouvert.
La fonction renvoie EOF s'il y a eu une erreur.

Exemple :
main()
{
FILE * fichier = NULL;
fichier = fopen("test2.txt", "w");
if (fichier != NULL)
{
fputs("Bonjour tout le monde.", fichier);
fclose(fichier);
}
}

2.3.3. fprintf
fprintf permet d’écriture dans un fichier. Elle s'utilise de la même manière que printf, sauf que
fprintf demande un pointeur de type FILE en premier paramètre.

Exemple :
Voici un code qui demande l’âge de l’utilisateur et l’écrit dans un fichier :
main ( )
{
FILE * fichier = NULL;
int age;
fichier = fopen("test3.txt", "w");
if (fichier != NULL)
{
// On demande l'âge
printf("Quel age avez-vous ? ");
scanf("%d", &age);
// On l'écrit dans le fichier
fprintf(fichier, "Le Monsieur qui utilise le programme, il a %d ans", age);
fclose(fichier);
}
}

Professeur : Adil SAYOUTI 48 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

2.4. Lire le contenu d’un fichier


Il existe plusieurs fonctions capables de lire le contenu d’un fichier. Dans ce paragraphe, nous
étudions les trois fonctions suivantes :

o fgetc : lit un caractère.


o fgets : lit une chaîne.
o fscanf : lit une chaîne formatée.

2.4.1. fgetc
Voici le prototype de fgetc :
int fgetc (FILE * pointeurDeFichier);

Cette fonction retourne un int : c'est le caractère qui a été lu. Si la fonction n'a pas pu lire de
caractère, elle retourne EOF. fgetc fait avancer le curseur d'un caractère à chaque fois que nous
en lisons un. Si vous appelez fgetc une seconde fois, la fonction lira donc le second caractère, puis
le troisième et ainsi de suite.
Nous pouvons utiliser une boucle pour lire les caractères un par un dans le fichier. La boucle
s'arrête quand fgetc renvoie EOF (qui signifie End Of File, c'est-à-dire "fin du fichier").

Voici le code :
main( )
{
FILE * fichier = NULL;
int caractereActuel ;
fichier = fopen("test4.txt", "r");
if (fichier != NULL)
{
caractereActuel = fgetc(fichier); // On initialise caractereActuel
// Boucle de lecture des caractères un par un
while (caractereActuel != EOF) // On continue tant que fgetc n'a pas retourné
EOF
{
printf("%c", caractereActuel); // On affiche le caractère stocké dans caractereActuel
caractereActuel = fgetc(fichier); // On lit le caractère suivant
}
fclose(fichier);
}
}

2.4.2. fgets
fgets lit une chaîne dans un fichier. Ca nous évite d'avoir à lire tous les caractères un par un. La
fonction lit au maximum une ligne (elle s'arrête au premier \n rencontré). L’utilisation d’une
boucle est nécessaire pour la lecture de plusieurs lignes.

Voici le prototype de fgets :


char * fgets (char * chaine, int nombreDeCaracteresALire, FILE * pointeurSurFichier);

La fonction reçoit trois paramètres :


o chaine : chaîne de caractères permettant de stocker le contenu de la ligne lue par fgets.
o nombreDeCaracteresALire : le nombre de caractères à lire. Cela demande à la fonction
fgets de s'arrêter de lire la ligne si elle contient plus de ‘nombreDeCaracteresALire’
caractères.
o pointeurSurFichier : pointeur sur notre fichier.

Professeur : Adil SAYOUTI 49 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

Exemple :
Le code suivant permet de lire et afficher une ligne de taille maximale = 500 caractères.
#define TAILLE_MAX 500 // constante TAILLE_MAX = 500
main ()
{
FILE * fichier = NULL;
char chaine[TAILLE_MAX] = ""; // Chaîne de caractères vide de taille TAILLE_MAX
fichier = fopen("test5.txt", "r");
if (fichier != NULL)
{
fgets(chaine, TAILLE_MAX, fichier);
// On lit maximum TAILLE_MAX caractères du fichier, On stocke la ligne dans "chaine"
printf("%s", chaine); // On affiche la chaîne
fclose(fichier);
}
getch()
}

Pour Lire tout le fichier avec fgets, nous utilisons une boucle. La fonction fgets renvoie NULL
si elle ne parvient pas à lire ce que nous avons demandé. La boucle doit donc s'arrêter dès que
fgets renvoie NULL. Le programme en C ci-dessous lit et affiche tout le contenu du fichier 5, ligne
par ligne.

#define TAILLE_MAX 500


main( )
{
FILE* fichier = NULL;
char chaine[TAILLE_MAX] = "";
fichier = fopen("test5.txt", "r");
if (fichier != NULL)
{
while (fgets(chaine, TAILLE_MAX, fichier) != NULL)
// On lit le fichier tant qu'on ne reçoit pas d'erreur (NULL)
{
printf("%s", chaine); // On affiche la chaîne qu'on vient de lire
}
fclose(fichier);
}
getch( ) ;
}

2.4.3. fscanf
fscanf s'utilise de la même manière que printf. Cette fonction lit dans un fichier qui doit avoir été
écrit d'une manière précise. Supposons que notre fichier ‘test6.txt’ contienne 4 nombres séparés
par un espace : 10 20 30 40
La fonction fscanf nous permet de récupérer chacun de ces nombres dans une variable de type
int. Voici le code :
main( )
{
FILE * fichier = NULL;
int nbre [4] ; // Tableau de 4 entiers
fichier = fopen("test6.txt", "r");
if (fichier != NULL)
{

Professeur : Adil SAYOUTI 50 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

fscanf(fichier, "%d %d %d%d", & nbre [0], & nbre [1], & nbre [2], & nbre [3]);
printf("Les nombres sont : %d, %d, %d et %d",nbre [0],nbre [1],nbre [2],nbre [3]);
fclose(fichier);
}
getch ();
}

2.5. Se déplacer dans un fichier


Il existe différentes fonctions permettant de localiser et positionner le curseur dans un fichier.
Dans ce paragraphe, nous présentons les trois fonctions suivantes :

o ftell : indique à quelle position nous sommes actuellement dans le fichier,


o fseek : positionne le curseur à un endroit précis,
o rewind : remet le curseur au début du fichier (c'est équivalent à demander à la fonction
fseek de positionner le curseur au début).

2.5.1. ftell
Cette fonction renvoie la position actuelle du curseur sous la forme d'un long. Le nombre
renvoyé indique la position du curseur dans le fichier. Voici le prototype de ftell :
long ftell (FILE * pointeurSurFichier);

2.5.2. fseek
Le prototype de fseek est le suivant :
int fseek (FILE* pointeurSurFichier, long deplacement, int origine);
La fonction fseek permet de déplacer le "curseur" d'un certain nombre de caractères (indiqué
par deplacement) à partir de la position indiquée par origine. Le nombre deplacement peut
être un nombre positif (pour se déplacer en avant), nul (= 0) ou négatif (pour se déplacer en
arrière). Quant au nombre origine, vous pouvez mettre comme valeur l'une des 3 constantes
listées ci-dessous :
o SEEK_SET : indique le début du fichier.
o SEEK_CUR : indique la position actuelle du curseur.
o SEEK_END : indique la fin du fichier.

Exemples :
o Le code suivant place le curseur 2 caractères après le début :
fseek (fichier, 2, SEEK_SET);
o Le code suivant place le curseur 4 caractères avant la position courante :
fseek (fichier, -4, SEEK_CUR);
o Le code suivant place le curseur à la fin du fichier :
fseek (fichier, 0, SEEK_END);

2.5.3. rewind
Cette fonction est équivalente à utiliser fseek pour nous renvoyer à la position 0 dans le fichier.
Voici son prototype :
void rewind(FILE * pointeurSurFichier);

2.6. Renommer et supprimer un fichier


Dans ce paragraphe, nous présentons les deux fonctions suivantes : rename et remove.

2.6.1. rename
La fonction rename permet de renommer un fichier. La fonction renvoie 0 si elle réussit à
renommer le fichier. Voici son prototype :
int rename(const char * ancienNom, const char * nouveauNom);

Professeur : Adil SAYOUTI 51 / 52 Année Universitaire : 2017 - 2018


Ecole Royale Navale Algorithmique, Structures
1°A Cycle Ingénieur de données et Langage C

ancienNom : l’ancien nom du fichier.


nouveauNom : le nouveau nom du fichier.

Exemple : main( )
{
rename ("test_10.txt", "test_20.txt");
}

2.6.2. remove
La fonction remove permet de supprimer un fichier sans demander de confirmation. Il est
supprimé du disque dur. Voici son prototype : int remove(const char * fichierASupprimer);

Exemple : main ( )
{
remove ("test_30.txt"); // ce code supprime le fichier test_30.txt
}

Professeur : Adil SAYOUTI 52 / 52 Année Universitaire : 2017 - 2018