partie i : initiation au langage c - djamel...
TRANSCRIPT
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 1
Partie I : Initiation au langage C
HISTORIQUE
Le langage C a été mis au point au début des années 1970 et normalisé (ANSI) en
1988.
Depuis cette date le langage C a conquis le monde des entreprises et des
universités malgré ses quelques défauts (syntaxe parfois illisible, pas assez de
contrôle sémantique …).
C’est un langage de haut niveau qui génère un code très rapide grâce à un
compilateur très performant.
C’est un langage tolérant, permissif !
PORTABILITE
Un programme écrit en langage C peut être exécuté sur n’importe quel système
d’exploitation sans aucune modification. C’est pour cela qu’on dit qu’il est portable.
APPRENTISSAGE
Le langage C n’est pas difficile à apprendre malgré son apparence car il dispose de
peu d’instructions et ses structures de données sont limitées.
Comme ça a été dit le langage C n’est pas difficile, il est différent !
Une fois l’apprentissage du langage C est achevé, cela nous facilite la familiarisation
avec d’autres langages tels que C++ et Java qui sont dérivés du C.
QUELQUES CONCEPTS DE BASE
Narration est un langage très simple qu’on apprendra avant l’algorithme, il nous
permet de voir les différentes étapes pour solutionner un problème donné. Voir
cours sur Narration.
Algorithme est un outil nécessaire pour n’importe quel programmeur (informaticien
ou autre) pour avoir la suite logique des instructions pour solutionner un problème
donné. Avant on utilisait des organigrammes.
On peut dire qu’un algorithme est le résultat de la décomposition d’un problème
complexe en opérations élémentaires qui seront exécutées en plusieurs étapes
successives. C’est le raffinement d’un problème.
Pour obtenir un programme il suffit de traduire l’algorithme obtenu dans n’importe
quel langage de programmation. C’est pour cela qu’un algorithme doit être écrit
indépendamment du langage de programmation.
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 1
TYPOLOGIE DES LANGAGES
Langages machine utilisant que des 0 et des 1 (langage binaire). Il est compris
directement par la machine d’où son nom.
Langages d’assemblage utilisant des mnémoniques (un semblant de mot) donc il
n’est pas compris directement par la machine par conséquent il lui faut un traducteur
(un outil logiciel) qu’on appelle « Assembleur ».
Langages évolués ou de haut niveau, pour qu’ils soient compris par la machine ils
doivent passés par l’étape de la compilation. C’est pour cela qu’on parle par exemple
du compilateur C.
COMPILATION
Tout langage évolué possède un compilateur, celui-ci sert à traduire le « code
source » d’un programme écrit en un langage de haut niveau comme le C en « code
objet » exécutable par un ordinateur.
Avant la traduction le compilateur passe par l’étape d’analyse du programme
source (Analyse syntaxique et analyse sémantique).
PRE-PROCESSEUR
Il est aussi appelé pré-compilateur c’est un utilitaire qui traite le fichier source avant
la compilation.
Tous les langages évolués ne possèdent pas ce pré-compilateur, le langage C en
possède un.
ASSEMBLEUR
Le code généré par la compilation est traité pour générer un fichier en format
relogeable (0&1). Ce fichier généré n’est pas encore exécutable, il reste encore
l’étape de l’édition de lien surtout si votre programme est composé de plusieurs
modules.
EDITEUR DE LIEN
L’éditeur de lien prend le ou les fichier(s) en format relogeable et fait le lien entre
eux pour créer un programme chargeable c’est-à-dire exécutable.
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 1
DIFFERENTES ETAPES DE CONSTRUCTION D’UN PROGRAMME
On construit un programme en passant par plusieurs étapes.
Etape 1 : On pose un problème donné dans une langue donnée (Français
par exemple)
Analyse (analyse préalable)
Etape 2 : On écrit les différentes étapes de résolutions
Formalisme
Etape 3 : On écrit une narration, un organigramme ou bien un algorithme
Traduction
Etape 4 : On traduit notre algorithme en un langage de programmation (C
par exemple)
Une fois le programme est écrit en un langage évolué, on passe aux étapes
suivantes :
Etape 1 : compilation du programme source
Etape 2 : exécution du programme compilé
NB : D’une manière générale, un programme (logiciel) est composé de plusieurs modules
(sous programmes). Dans ce cas, il est nécessaire de lier ces différents fichiers entre eux
après la compilation : c’est l’édition de lien.
STRUCTURE D’UN PROGRAMME C
Tout comme la narration et l’algorithme, le programme C a une structure (un
squelette).
Le langage C est un langage structuré et un programme C de base est constitué de :
Partie entête (Directive)
Programme principal (Fonction principale)
Partie traitement (Corps du programme = déclarations + instructions)
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 1
EXEMPLE DE BILAN
On essaie d’écrire une narration puis un algorithme correspondant et sa traduction
en langage C. Le problème est la somme de deux nombres entiers.
Ecriture de la narration
Narration somme
Objet A, B, S
1- Lire A
2- Lire B
3- Calculer S = A+B
4- Ecrire S
5- Arrêter
Ecriture de l’algorithme correspondant
Algorithme somme ;
Déclaration
A, B, S : entier ;
Début
Lire(a) ;
Lire(b) ;
S A+B ;
Ecrire(S) ;
Fin
Ecriture du programme en C
#include <stdio.h> /* Partie entête*/
Main() /*Programma principale*/
{
Int A, B, S ; /*D éclaration*/
Scanf(“%i” , &A); /*Lecture de A*/
Scanf(“%i” , &B); /*Lecture de B*/
S = A+B ; /*Calcul de S*/
Printf(“%i” , S); /*Ecriture de S*/
}
Remarque : La parenthèse ouvrante { joue le rôle de Début et la fermante } celui de Fin.
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 1
Essayons de comprendre ce programme sans trop rentrer dans les détails.
Ce programme principal main() utilise dans la partie traitement (corps du
programme) deux différentes opérations scanf() et printf(), ce sont des
opérations (fonctions) d’entrée/sortie (input/output),il utilise aussi une
déclaration int A,B,S ; et une affectation S = A=B ;
Les deux dernières opérations (déclaration et affectation) sont reconnues par
le compilateur par contre les opérations d’input/output ne le sont pas, donc il
faut les inclure à l’aide de la directive #include qui précise au compilateur
dans quel fichier se trouve la définition de ces fonctions.
Que signifie stdio.h ?
Std : Standard
io : input/output
.h : header (entête)
L’inclusion qui est une opération appartenant aux instructions du pré
processeur se fait comme suit : #include <stdio.h>
Que signifie %i et & dans les opérations d’input/output scanf() et printf() ?
%i : veut dire que c’est le format entier (i comme integer), %i
est équivalent à %d
& : cet opérateur doit précéder toute variable de type scalaire
(entier, réel, caractère …).
Remarques
Attention un mot écrit en minuscule est différent de celui qui est écrit en
majuscule. Si vous écrivez main() et Main() ce n’est pas la même chose.
L’écriture du programme C doit être en minuscule !
Le langage C permet la déclaration des variables à n’importe quel endroit
dans le programme, il n’a pas une zone précise appelée zone déclarative. Il est
permissif.
Par contre la déclaration des constantes est dans l’entête (partie du pré
compilateur), à l’aide de #define.
En C, toute déclaration et/ou toute instruction se termine par un point-
virgule ; sauf les instructions destinées au pré processeur comme par
exemple #include …
A chaque fois qu’on utilise une fonction standard, on doit inclure son fichier
entête !
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 1
BIBLIOGRAPHIE
1- Langage C : Cours et références
Pierre NERZIC Mars 2003
2- Langage C : Support de cours
Patrick CORDE Mars 2006
3- Programmation en langage C
M.C. BELAID
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
Partie II : Plus sur le langage C
TYPES DE DONNEES
Pour l’instant, on va étudier seulement trois (03) types de données (Variables) : les
entiers, les réels et les caractères.
int pour les entiers (integer)
float pour les réels (flottant ou virgule flottante)
char pour les caractères
la virgule flottante est composée d’une mantisse et d’un exposant. Par exemple la
valeur 3.14 peut s’écrire sous un autre format 314 * 10-² : ici la mantisse est 314 et
l’exposant est -2.
COMMENT DECLARER UNE VARIABLE ?
Algorithme Langage C
Déclaration Pas d’équivalent
Variable Pas d’équivalent
A : entier ; Int A ;
C, D : réel ; float C, D ;
K : caractère ; char K ;
NB : Dans la partie III de ce fascicule vous aurez un tableau récapitulatif de tous les
types en langage C.
Aussi la déclaration d’une variable en C, se fait dans le corps du programme et
non dans une zone déclarative comme dans un algorithme !
COMMENT DECLARER UNE CONSTANTE ?
Elles sont déclarées différemment des variables, on doit les définir dans la partie
entête du programme C à l’aide de la directive #define et de la façon suivante :
#define nom_de_la_constante valeur_de_la_constante
Algorithme Langage C
Déclaration Pas d’équivalent
Constante Pas d’équivalent
PI = 3.14 ; #define PI 3.14
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
FICHIERS ENTETES
On a déjà vu ce genre de déclaration quand on a écrit notre premier programme en
langage C qui fait la somme de deux entiers A et B dans S, on a utilisé :
#include <stdio.h>
D’une manière générale cela s’écrie comme suit :
#include <nom_du_fichier_entête>
Attention : On n’inclut pas seulement les fichiers input /output mais il y en a d’autres
qu’on verra par la suite !
LES OPERATIONS ELEMENTAIRES DU LANGAGE C
L’affectation : c’est l’attribution d’une valeur ou d’un résultat d’une opération
à une variable de même type.
Algorithme Langage C
Début {
… …
A 5 ; A = 5 ;
B 18 ; B = 18 ;
S A + B ; S = A + B ;
… …
Fin }
La lecture (input) : C’est l’attribution d’une valeur choisie par l’utilisateur à
une variable d’entrée.
La valeur choisie doit être de même type que la variable d’entrée. Cette
opération se fait à l’aide de la fonction scanf() ;
En générale cette fonction s’écrit sous la forme suivante :
Scanf( ‘’ indicateur_de_type ‘’, &nom_de_la_variable) ;
L’opérateur & précède toute variable de type scalaire (entier, réel ou
caractère).
Algorithme Langage C
Début {
… …
Lire(A) ; scanf(‘’ %d ‘’, &A) ;
… …
Fin }
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
L’écriture (output) : C’est l’affichage (l’impression) du contenu d’une variable,
d’une constante ou d’un simple message.
Cette opération se fait à l’aide de la fonction printf() ;
En générale cette fonction s’écrit sous la forme suivante :
Pintf(‘’ indicateur_de_type ‘’, nom_de_la_variable) ; ou bien
Printf(‘’ message ‘’) ;
Algorithme Langage C
Début {
… …
Ecrire(‘Bonjour’) ; printf(‘’ Bonjour ‘’) ;
Ecrire(S) ; printf(‘’ %i ‘’, S) ;
… …
Fin }
%i est un indicateur de format (i comme integer c’est-à-dire entier). Vous
aurez la liste de tous les indicateur de format par la suite.
Vous avez remarqué que le message doit être mis entre deux (02) double
côtes ‘’message’’.
L’incrémentation/la décrémentation : C’est une affectation un peu
particulière, car on trouve le nom de la variable de part et d’autre du symbole
d’affectation ( en algorithme et = en langage C).
L’incrémentation permet d’augmenter d’une valeur une variable par contre la
décrémentation permet de diminuer d’une valeur une variable. Cette valeur
est appelée le pas et le pas peut être positif ou négatif.
Algorithme Langage C
Début {
… …
X X + 1 ; X = X + 1 ;
Y Y – 1 ; Y = Y – 1 ;
A A + 2 ; A = A + 2 ;
B B – 5 ; B = B – 5 ;
… …
Fin }
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
Attention : Il y a une écriture en langage C que je vous conseille d’éviter
quoiqu’elle est permise. C’est ce genre de fantaisies qui rend le code du
langage C illisible ! Malheureusement il y a ceux qui en raffolent.
X = X + 1 ; peut s’écrire X + + ;
Y = Y – 1 ; peut s’écrire Y - - ;
LES OPERATIONS ARITHMETIQUES
L’addition +
La soustraction -
La multiplication *
La division /
Le modulo % c’est le reste de la division Euclidienne.
Algorithme Langage C
Début {
… …
∆ b² - 4ac ; Delta = b*b – 4*a*c ;
Q X/2;
R X – 2Q; Q = X % 2;
… …
Fin }
LES OPERATEURS LOGIQUES
Une condition peut être elle-même composée de plusieurs conditions reliées entre
elles par des opérateurs logiques tels que et, ou, non. En langage C :
Le et est représenté par &&
Le ou est représenté par ││
Le non est représenté par !
Algorithme Langage C
A et B A && B
X OU Y X ││ Y
Non Z ! Z
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
SIGNES DE COMPARAISON
On les appelle aussi signes de test.
== teste si les deux informations sont égales. Ne pas confondre avec l’affectation.
< teste si l’information à gauche est inférieur à celle de droite.
< teste si l’information à gauche est supérieur à celle de droite.
<= teste si l’information à gauche est inférieur ou égale à celle de droite.
>= teste si l’information à gauche est supérieur ou égale à celle de droite.
!= teste si les deux informations sont différentes.
CARACTERE D’ECHAPPEMENT
On va écrire deux instructions presque identiques pour ordonner la machine
d’afficher le contenu d’une variable S de type entier.
printf(‘’%i’’, S) ;
printf(‘’%i\n’’, S) ;
La première instruction ordonne la machine à écrire la variable S et de rester sur la
même ligne.
La seconde instruction ordonne la machine à écrire la variable S et d’aller à la ligne
suivante, c’est ce qu’on appelle un retour chariot et cela grâce à \n.
COMMENTAIRES
Pour rendre notre programme plus lisible, on doit le commenter c’est-à-dire écrire
des commentaires pour expliquer ce qu’on est en train de faire. Ces commentaires
seront ignorés par le compilateur C.
Tout commentaire est mis entre /* et */.
Exemple : /* ceci est un commentaire* /
LES STRUCTURES DE CONTROLE
On les appelle aussi les primitives, elles servent à contrôler le déroulement d’un
traitement.
Un traitement peut s’exécuter de différentes manières :
Séquentielle.
Conditionnelle.
Alternative.
Itérative (ou répétitive).
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
Le traitement séquentiel : C’est une suite d’instructions qui s’exécutent l’une à la
suite de l’autre sans aucune contrainte.
Algorithme Langage C
Début {
Instruction1 ; instruction1 ;
Instruction2 ; instruction2 ;
… …
Instruction n ; instruction n ;
Fin }
Le traitement conditionnel : Dans ce cas l’instruction est exécutée que si la
condition est vérifiée. On dit que l’instruction est sous condition.
Algorithme Langage C
(* Première écriture*) /* Première écriture*/
Début {
… …
Si condition(s) alors if condition(s)
Action ; action ;
Finsi
… …
fin }
NB : on remarque que le mot alors en algorithme n’a pas d’équivalent en langage C
de même pour le vocable finsi.
Algorithme Langage C
(* Deuxième écriture*) /* Deuxième écriture*/
Début {
… …
Si condition(s) alors if condition(s)
Début {
Action1 ; action1 ;
Action2 ; action2 ;
Fin }
… …
Fin }
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
Le traitement alternatif : Comme son nom l’indique, si ce n’est pas l’une ça sera
l’autre ; c’est-à-dire si la condition est vérifiée l’action 1 est exécutée si ce n’est pas
le cas l’action 2 sera exécutée et en aucun cas les actions 1 et 2 ne seront
exécutées en même temps !
Algorithme Langage C
(*Premier format*) /*Premier format*/
Début {
… …
Si condition(s) alors if condition(s)
Action1 ; action1 ;
Sinon else
Action2 ; action2 ;
Finsi
… …
fin }
NB : si on a plusieurs actions que ce soit dans le alors ou/et dans le sinon, on utilisera des
blocs. Début et fin en algorithme et les parenthèses ouvrantes et fermantes pour le langage
C. C’est l’autre format d’écriture.
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
Algorithme Langage C
(*Deuxième format*) /*Deuxième format*/
Début {
… …
Si condition(s) alors if condition(s)
Début {
Action11 ; action11 ;
Action12 ; action12 ;
Action13 ; action13 ;
Fin }
Sinon else
Début {
Action21 ; action21 ;
Action22 ; action22 ;
Fin }
… …
Fin }
La primitive selon : Elle indique le choix multiple, et au lieu d’utiliser une cascade
de si alors sinon, on préfère cette primitive pour nous faciliter l’écriture de
l’algorithme ou le programme et le rendre plus lisible.(Voir cours sur algorithme).
Algorithme Langage C
Début {
… …
Selon variable_de_choix switch variable_de_choix
{
Choix1 : action1 ; case choix1 :action1 ;breack ;
Choix2 :action2 ; case choix2 :action2 ;breack ;
… …
Choix n :action n ; case choix n :action n ;breack ;
Finselon }
… …
Fin }
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
Il existe un autre format d’écriture qui utilise le mot « autre » en algorithme qui
sera traduit par « default » en langage C.
Algorithme Langage C
Début {
… …
Selon variable_de_choix switch variable_de_choix
{
Choix1 : action1 ; case choix1 :action1 ;breack ;
Choix2 :action2 ; case choix2 :action2 ;breack ;
… …
Choix n :action n ; case choix n :action n ;breack ;
Autre : action n+1 ; default : action n+1 ;
Finselon }
… …
Fin }
NB : En langage C la primitive switch utilise un bloc { } qui englobe tous les cas possibles et à
la fin de chaque action il y a l’instruction breack pour casser la séquence. La finselon de
l’algorithme n’a pas d’équivalent en C.
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
EXEMPLE DE BILAN SUR LE SWITCH
#include <stdio.h>
main()
{
int M ;
printf(‘’Entrer une valeur entière’’) ;
scanf(‘’%d’’, &M) ;
switch (M)
{
case 1 : printf(‘’ c’est Janvier ’’) ;breack ;
case 2 : printf(‘’c’est Février’’) ;breack ;
case 3 : printf(‘’c’est Mars’’) ;breack ;
case 4 : printf(‘’c’est Avril’’) ;breack ;
case 5 : printf(‘’c’est Mai’’) ;breack ;
case 6 : printf(‘’c’est Juin’’) ;breack ;
case 7 : printf(‘’c’est Juillet’’) ;breack ;
case 8 : printf(‘’c’est Aout’’) ;breack ;
case 9 : printf(‘’c’est Septembre’’) ;breack ;
case 10 : printf(‘’c’est Octobre’’) ;breack ;
case 11 : printf(‘’c’est Novembre’’) ;breack ;
case 12 : printf(‘’c’est Décembre’’) ;breack ;
default : printf(‘’Votre nombre ne correspond à aucun mois’’);
}
}
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
Le traitement itératif : On dispose de trois primitives pour contrôler un traitement
répétitif.
La boucle tant que faire
La boucle pour faire
La boucle faire tant que (c’est l’équivalente de la boucle répéter jusqu’à vue
au cours d’algorithmique).
La boucle Tant que faire : Son principe est très simple tant que la condition ou les
conditions est/sont vérifiée(s), on exécute l’instruction ou les instructions qui
est/sont à l’intérieur de la boucle. Une fois la condition ou les conditions n’est/sont
plus vérifiée(s) on sort de la boucle. Si on a plus d’une action à l’intérieur de la
boucle, on utilisera un bloc.
Algorithme Langage C
Tantque condition(s) Faire while condition(s)
Action ; action ;
Fintantque
Algorithme Langage C
Tantque condition(s) Faire while condition(s)
Début {
Action1 ; action1 ;
Action2 ; action2 ;
Fin }
Fintantque
On remarque que le mot clé Faire n’a pas d’équivalent en C, du même pour la
fintantque.
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
Exemple : Afficher les dix (10) premiers nombres entiers positifs et chaque nombre
est écrit sur une ligne.
#include <stdio.h>
main()
{
Int co ; /*déclaration du compteur*/
co = 1 ; /*initialisation du compteur*/
while (co <= 10) /*condition d’arrêt*/
{
printf(‘’%d\n’’, co) ; /*affichage du compteur*/
co = co+1 ; /*incrémentation du compteur*/
}
}
La boucle Pour : Contrairement à la boucle tant que où devant le while on ne
trouve que la/les condition(s) ; devant le pour (for en C) on trouve l’initialisation
d’une variable, la condition d’arrêt et le pas (incrémentation ou décrémentation).
Algorithme Langage C
Pour V allant de V_I à V_F pas P faire for (initialisation ; condition ; pas ;)
Action ; action ;
Finpour
Le même principe que la boucle tant que, si on a plus d’une action, un bloc est nécessaire.
Algorithme Langage C
Pour V allant de V_I à V_F pas P faire for (initialisation ; condition ; pas ;)
Début {
Action1 ; action1 ;
Action2 ; action2
Action3 ; action3
Fin }
Finpour
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
Où V est le nom d’une variable déclarée.
V_I est la valeur initiale (valeur de départ).
V_F est la valeur finale (valeur d’arrive).
P est le pas (incrémentation on dit que le pas est positif ou décrémentation on dit
que le pas est négatif).
Reprenons l’exemple traité avec la boucle while et écrivons le en utilisant la boucle for.
#include <stdio.h>
main()
{
Int co ;
For (co = 1 ; co <= 10 ; co = co+1 ;)
printf(‘’%d’\n’’, co);
}
Faisons un parallele entre les deux écritures (while et for).
Boucle while Boucle for
#include <stdio.h> #include <stdio.h>
main() main()
{ {
Int co; int co;
Co = 1;
while (co <= 10) for (co=1; co<=10; co=co+1;)
{
printf(‘’%d\n’’, co); printf(‘’%d\n’’, co);
co=co+1;
}
} }
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
On Remarque qu’avec la boucle for on a gagné deux (02) lignes de code:
l’initialisation (co=1;) et l’incrémentation (co=co+1;) et même un bloc { }.
Par conséquent si on a la possibilité d’utiliser la boucle for n’hésiter pas un seul
instant ! à condition qu’on ait une et une seule variable V, une valeur initiale V_I,
une valeur de fin V_F et le pas P (positif : incrémentation ou négatif :
décrémentation).
La boucle faire tant que : Cette boucle est un peu spéciale, on traite l’action puis
on vérifie la condition. C’est pour cela que cette boucle n’est utilisée que si on est
assuré qu’elle va s’exécuter au moins une fois. Bizarrement, elle ressemble à la
boucle (répéter jusqu’à) qu’on a étudié en algorithmique. Sa syntaxe est la
suivante :
Algorithme Langage C
Faire do
Action ; action ;
Tantque condition(s) ; while (condition(s)) ;
Ou bien l’autre format d’écriture:
Algorithme langage C
Faire do
Début {
Action1 ; action1 ;
Action2 ; action2 ;
Action3 ; action3 ;
Fin }
Tantque condition(s) ; while (condition(s)) ;
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
Traitons le même exemple du comptage avec la boucle (do while).
#include <stdio.h>
main()
{
Int co ;
Co = 1 ;
do
{
printf(‘’ %d\n’’, co) ;
co = co+1;
}
while (co <= 10);
}
Remarque:
Si j’ai à choisir entre ces trois (03) boucles, la priorité est donnée à la boucle (for) puis
la boucle (while) et en dernier la boucle (do while) car son problème est qu’il faut
être assuré qu’elle va s’exécuter au moins une fois !
N. BENTOUNSI CONSTANTINE 2 Langage C Partie II
BIBLIOGRAPHIE
1- Langage C : Cours et références
Pierre NERZIC Mars 2003
2- Langage C : Support de cours
Patrick CORDE Mars 2006
3- Programmation en langage C
M.C. BELAID
4- Algorithmique, structures de données et langage C
J.M. ENJALBERT Janvier 2005
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 3
Partie III : Procédures et fonctions
LES SOUS PROGRAMMES
Un programme écrit en langage C ou autre langage de programmation devient
de plus en plus important, c’est-à-dire le nombre de lignes de code est très
grand. A cet effet, il est conseiller de partager ce programme en plusieurs
sous programmes (modules) et chaque module aura une tache bien
déterminée à traiter. On parle d’une analyse descendante.
On distingue deux (02) genres de sous programmes : les procédures et les
fonctions (voir cours algorithmique) mais le langage C permet de définir
seulement les fonctions.
En effet une procédure en langage C n’est autre qu’une fonction qui ne
retourne aucun résultat dans son nom !
POURQUOI LES SOUS PROGRAMMES ?
L’utilisation des sous programmes est plus que nécessaire car elle nous
permet de raffiner notre programme et arriver à avoir des modules
élémentaires faisant une et une seule tache.
Un programme utilisant des sous programmes est lisible donc facile à
comprendre.
En résumé les sous programmes sont utilisés pour les raisons suivantes :
Raffinage des programmes donc lisibilité des programmes.
Réutilisation des modules.
Compilation des modules à part donc la maintenance est beaucoup
plus facile.
Gain de temps (CPU).
Gain d’espace mémoire (MC).
…
DEFINITION D’UNE FONCTION
Une fonction est un sous programme qui a une tache unique, peut être
utilisée autant de fois que l’on veut (réutilisabilité) et peut retourner une
valeur à l’appelant (le programme ayant fait appel à cette fonction).
En langage C, on ne parle seulement de fonction à moins qu’on utilise une
fonction de type void. C’est une fonction qui ne retourne aucune valeur : elle
correspond au concept de procédure. Le mot clé void permet de déclarer un
objet sans type. Un exemple sera donné par la suite pour illustrer ce genre de
fonction.
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 3
En général, l’écriture des fonctions se fait après l’entête et avant main() qui
représente à son tour une fonction principale (Par abus de langage on dit que
c’est le programme principal).
A vrai dire on ne parle pas d’écriture d’une fonction mais plutôt d’une
déclaration de fonction vu l’endroit qu’elle occupe dans le programme.
COMMENT DECLARER UNE FONCTION ?
Une fonction est déclarée par son type, son nom et ses arguments qu’on
appelle aussi paramètres.
La syntaxe de déclaration est la suivante :
Type nom_de_la_fonction (typ1 arg1, typ2 arg2, … , typn argn)
Une fois la fonction est déclarée, on définit son corps qui sera composé
éventuellement de déclarations de variables (variables locales propres à cette
fonction) et des instructions.
Voici la syntaxe :
Type nom_de_la_fonction (typ1 arg1, typ2 arg2, … , typn argn)
{
Déclaration_de_variables ;
Instructions ;
}
Parmi les instructions, il y a l’instruction return qui fait retourner le résultat à
l’appelant.
Exemple : Le maximum entre deux nombres entiers.
int max(Int a, Int b)
{
If (a > b) return a ;
return b;
}
L’instruction return casse la séquence d’instructions, c’est à dire elle nous
permet de sortir de la fonction sans se soucier de ce qu’il vient par la suite.
Voici une autre écriture de l’exemple précédant proche de l’algorithmique :
int max(Int a, Int b)
{ If (a > b)
Return a ;
else
return b; }
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 3
Du moment qu’on ait déclaré et défini une fonction, on l’utilise dans le
programme principal main() comme s’il s’agit d’une fonction intégrée au
langage C. On l’appelle par son nom ou on l’affecte à une variable de même
type.
Exemple de bilan :
/* Première écriture appel par le nom*/
#include <stdio.h>
int max(Int a, Int b)
{
if (a > b)
return a ;
else
return b;
}
main()
{
int x;
int y;
scanf(‘’%d’’, &x);
scanf(‘’%d’’, &y);
printf(‘’%d’’, max(x, y)); /*On voit bien l’appel par le nom*/
}
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 3
/* Deuxième écriture appel par l’affectation*/
#include <stdio.h>
int max(Int a, Int b)
{
if (a > b)
return a ;
else
return b;
}
main()
{
int x;
int y;
int m ;
scanf(‘’%d’’, &x);
scanf(‘’%d’’, &y);
m = max(x, y) ; /*l’appel est par l’affectation*/
printf(‘’%d’’, m);
}
« PROCEDURE » EN LANGAGE C
Voici maintenant comment on définit une « procédure » en langage C : on
met le mot clé void pour dire qu’il n’y ait pas de résultat.
Exemple : on écrit une procédure qui affiche seulement un nombre entier.
void afficher(int nb)
{
Printf’’nb= %i\n’’, nb) ;
}
Utilisant cette procédure dans le programme écrit ci-dessus :
C’est-à-dire on aura une fonction principale main(), une fonction max() et la
procédure afficher.
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 3
/* Nouveau programme*/
#include <stdio.h>
int max(int a, int b) /* la fonction max()*/
{
if (a > b)
return a ;
else
return b;
}
void afficher(int nb) /*la procedure afficher*/
{
printf’’nb= %i\n’’, nb) ;
}
main() /*la fonction principale*/
{
int x;
int y;
scanf(‘’%d’’, &x);
scanf(‘’%d’’, &y);
afficher(max(x, y));
}
NB: On remarque que dans la procédure (void afficher(int nb)) il n’y a pas de return (return
sans valeur).
Un autre exemple de procédure qui nous permet de sauter x lignes.
void sauter_ligne(int x)
{
int co ;
for (co = 1 ; co <= x ; co = co+1;)
printf(‘’\n’’);
}
C’est comme si on écrit l’instruction printf(‘’\n’’), x fois!
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 3
VARIABLES GLOBALES ET VARIABLES LOCALES
Une variable globale est déclarée en général dans le programme principal
mais en langage C, elle est déclarée en dehors de toutes les fonctions même
la fonction principale main() qui est considérée comme étant le programme
principal. Une fois la variable globale est déclarée, elle sera utilisable par
toutes les fonctions définies dans le programme.
Une variable locale est déclarée à l’intérieur d’une fonction et elle ne peut
être utilisée que par celle-ci !
Exemple de bilan sur les variables globales et les variables locales.
#include <stdio.h>
Int x, y ; /*x et y sont des variables globales*/
void echanger(int A, int B)
{
int C ; /* C est une variable locale*/
A = x ;
B = Y ;
C = A ;
A = B ;
B = C ;
x = A ;
y = B ;
}
main()
{
printf(‘’Entrer x et y \n’’) ;
scanf(‘’%i’’, &x) ; /*on a utilisé x et y et pourtant ils ne sont pas*/
scanf(‘’%i’’, &y) ; /* déclarés dans la fonction principale main()*/
echange(x, y) ; /* car ils sont des variables globales*/
printf(‘’x = %i et y = %i’’ , x, y) ;
}
NB : Il est conseillé de ne pas utiliser les variables globales car on ne sera jamais exempt de
pas mal de surprises ! (Effet de bord).
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 3
PARAMETRES
Regardons l’exemple ci-dessus, on a dit que x et y sont des variables globales,
C est une variable locale et qu’appelle-t-on A et B qu’on voit dans la fonction
void echanger(Int A, Int B) ?
A et B sont appelés paramètres ou arguments.
On distingue deux (02) genres de paramètres : les paramètres formels et les
paramètres réels.
Quant aux paramètres formels, ce sont ceux qu’on voit dans la
déclaration d’une fonction. Exemple void echanger(Int A, Int B). Donc
on peut dire que A et B sont des paramètres formels.
Les paramètres formels n’ont pas d’existance en mémoire centrale
(MC).
Quant aux paramètres réels, appelés aussi paramètres d’appel, on les
trouve au moment de l’appel de la fonction, comme par exemple
echanger(x, y) ; donc x et y sont des paramètres réels.
NB : Attention les paramètres formels d’une fonction doivent être au même nombre,
dans le même ordre et de même type que les paramètres d’appel.
PASSAGES DE PARAMETRE
Avant de parler des modes de passage des paramètres, il est impératif de
donner quelques notions sur l’adressage ou pointeur.
En langage C, un pointeur est une variable contenant l’adresse d’une autre
variable d’un type donné. Sa déclaration se fait comme suit :
type *nom_du_pointeur ;
Exemple : Int *pt ;
Dans ce cas, on a déclaré un pointeur appelé pt qui va contenir l’adresse
d’une variable de type entier.
Si on déclare une variable X de type entier par Int X ; On peut se demander
comment aurait-on l’adresse de cette variable X ?
C’est très simple il suffit de précéder cette dernière par le caractère &, donc
&X est l’adresse de la variable X.
Si on écrit l’instruction pt= &X ; on dit qu’on a affecté l’adresse de la variable
X au pointeur pt.
Donc pt est un pointeur qui contient l’adresse de la variable X de type entier.
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 3
Exemple de bilan.
#include <stdio.h>
main()
{
int X ; /*Déclaration de la variable X de type entier*/
int *pt ; /*Déclaration du pointeur pt*/
printf(‘’Entrer une valeur entière’’) ;
scanf(‘’%d’’, X) ; /*Lecture d’une valeur dans X*/
pt = &X ; /* pt contient l’adresse de la variable X*/
}
On va étudier deux modes de passages de paramètres : l’un par valeur et
l’autre par variable.
Le passage par valeur : Dans ce cas on dit qu’il y ait lecture mais pas
d’écriture ; c’est-à-dire au moment de l’appel les paramètres réels
sont affectés l’un après l’autre dans les paramètres formels (on dit
qu’il y a lecture) mais au moment du retour à l’appelant rien n’est fait
(on dit qu’il n’y a pas d’écriture) et par conséquent il n’y a pas de
modifications des paramètres réels.
Le passage par variable, il est appelé aussi passage par adresse : Dans
ce cas on dit qu’il ait lecture et écriture ; cela veut dire qu’au moment
de l’appel les paramètres réels sont affectés l’un après l’autre dans les
paramètres formels (on dit qu’il y a lecture), après le traitement au
sein de la fonction et au moment du retour à l’appelant les
informations qui sont dans les paramètres formels sont affectés dans
les paramètres réels (on dit qu’il y a écriture) . Par conséquent les
valeurs des paramètres réels seront modifiées.
NB : Ces explications sont plutôt schématiques, à vrai dire si le passage est par valeur
donc la fonction appelée traite l’information et ne modifie en aucun cas la variable
passée par l’appelant ; par contre si le passage est par variable, la fonction appelée
reçoit l’adresse de la valeur et non pas la valeur elle-même. A l’aide de cette adresse
la machine va pointer l’emplacement physique et traite l’information qui y est. Donc
automatiquement cette information va éventuellement changer !
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 3
Exemple de bilan sur le passage par valeur.
/* l’appel est par valeur*/
#include <stdio.h>
void echanger(Int V1, Int V2)
{
int inter ;
inter = V1 ; /*on est en train de manipuler*/
V1 = V2 ; /*les valeurs*/
V2 = inter ;
}
main()
{
int a, b ;
a = 14 ;
b = 23 ;
printf(‘’avant : a = %d b = %d\n’’, a,b) ;
echanger(a, b) ; /*Envoi des valeurs de a et de b*/
printf(‘’aprés : a = %d b = %d\n’’, a,b) ;
}
Après le traitement le résultat est :
avant : a = 14 b = 23
après : a = 14 b = 23
On remarque que malgré l’échange qui a été fait à l’intérieur de la fonction
echanger(), a et b sont restées telles qu’elles sont.
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 3
Exemple de bilan sur le passage par variable.
/* l’appel est par variable*/
#include <stdio.h>
void echanger(int *V1, int *V2)
{
int inter ;
inter = (*V1) ; /*On est en train de manipuler*/
(*V1) = (*V2) ; /*les adresses*/
(*V2) = inter ;
}
main()
{
int a, b ;
a = 14 ;
b = 23 ;
printf(‘’avant : a = %d b = %d\n’’, a,b) ;
echanger(&a, &b) ; /*Envoi des adresses de a et de b*/
printf(‘’aprés : a = %d b = %d\n’’, a,b) ;
}
Après le traitement le résultat est :
Avant : a = 14 b = 23
Après : a = 23 b = 14
On remarque que les valeurs des variables a et b ont changées, donc il y a bel
et bien un échange entre a et b !
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 3
UTILISATION DES FONCTIONS MATHEMATIQUES
Avant l’utilisation des fonctions mathématiques qui existent en langages C, il
faut les inclure pour que le compilateur C les reconnaisse. Cette inclusion est
provoquée par la directive #include.
#include <math.h>
Une fois elles sont incluses, on peut utiliser les fonctions mathématiques
telles que racines, logarithmes, trigonométrie, …
Voici quelques fonctions :
Sqrt(a) racine carrée de a
Pow(a, b) ab
Log(a) logarithme népérien de a
Log10(a) logarithme de base 10 de A
Exp(a) ea
Sin(a) acos(a) tanh(a) … fonctions trigonométriques et hyperboliques
…
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 3
ANNEXES
Tableau récapitulatif des types de données en langage C
Types de données Signification Taille en octets Plage de valeurs acceptées
char Caractère 01 -128 à 127
Unsigned char Caractère non signé
01 0 à 255
Short Int Entier court 02 -32768 à 32767
Unsigned short Int Entier court non signé
02 0 à 65535
Int Entier 02 sur processeur 16 bits
04 sur processeur 32 bits
-32768 à 32767
-2147 483 647 à 2147 483 647
Unsigned Int Entier non signé 02 sur processeur 16 bits
04 sur processeur 32 bits
0 à 65535
0 à 4294 967 295
Long Int Entier long 04 -2147 483 647 à 2147 483 647
Unsigned long Int Entier long non signé
02 0 à 4294 967 295
float Réel 04 3.4*10-38 à 3.4*1038
Double Réel double 08 1.7*10-308 à 1.7*10308
Long double Réel long double
10 3.4*10-4932 à 3.4*104932
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 3
Tableau récapitulatif des indicateurs de format en langage C
Indicateurs Descriptions
%c Format caractère
%d Format entier
%i Format entier (équivalent à %d)
%f Format réel
%e ou %E Format de notation scientifique
%g ou %G Utilise %f ou %e selon la longueur de la valeur décimale
%o Format octal non signé
%s Format chaîne de caractères
%u Format entier non signé
%x Format hexadécimal non signé (Les lettres en minuscules)
%X Format hexadécimal non signé (Les lettres en majuscules)
%p Affichage du pointeur d’argument
%n Enregistre le nombre de caractères affichés
%% Permet d’afficher le caractère %
Tableau des caractères d’échappement
Caractères Descriptions
\n Saut de ligne (retour chariot)
\t Caractère tabulation
\b Déplace le curseur d’un caractère vers la gauche
\r Place le curseur au début de la ligne en cours
\f Saut de page (place le curseur au début de la page suivante)
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE 3
BIBLIOGRAPHIE
1- Langage C : Cours et références
Pierre NERZIC Mars 2003
2- Langage C : Support de cours
Patrick CORDE Mars 2006
3- Programmation en langage C
M.C. BELAID
4- Algorithmique, structures de données et langage C
J.M. ENJALBERT Janvier 2005
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE IV
Partie IV : Structures de données de base
Dans cette partie, on va étudier quelques structures de données de base tels que :
Les tableaux (vecteur et matrice).
Les chaînes de caractères.
type énuméré.
Les enregistrements.
…
LA STRUCTURE DE TABLEAU
Introduction : supposons qu’on ait une dizaine de variables de même type
(entier par exemple) qu’on appellera A1, A2, A3, A4, … , A10.
Ces dix variables simples occupent chacune un emplacement physique en
mémoire, après leur déclaration.
Il y a une autre possibilité : Les rassembler toutes dans une seule structure
appelée « tableau » dans dix emplacements contigus mais avec un seul nom.
Cela ressemble à ce tableau.
A1 A2 A3 A4 A5 A6 A7 A8 A9 A10
Qui représente les dix cases contigus A1, A2, A3, …, A10
Sous le même nom : Le tableau A par exemple.
Comme vous pouvez le remarquer, pour définir un tableau on doit donner son
nom, son type et sa taille (nombre de cases).
En langage C, la syntaxe est la suivante :
Type-case nom-tableau[ nombre-case ] ;
Attention : le nombre de cases est entre crochets et non entre parenthèses.
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE IV
Reprenons l’exemple ci-dessus, si on veut déclarer les dix variables on peut le
faire de deux manières : L’une les déclarer une par une et l’autre les déclarer
dans la même structure.
/* Déclaration une par une*/ /*Déclaration dans une seule structure*/
main() main()
{ {
Int A1 ; Int A[10] ;
Int A2 ;
.
.
.
Int A10 ;
} }
Faites un parallèle entre les deux déclarations et dites quelle est la meilleur
des deux !
Maintenant qu’on ait une petite idée sur la structure tableau essayons
d’approfondir nos connaissances.
DEFINITION D’UNE VARIABLE TABLEAU
Dans le cours d’algorithmique, on a étudié deux genres de tableaux : les
vecteurs (tableaux à une dimension) et les matrices (tableaux à deux
dimensions), on va faire de même avec le langage C et mettre en évidences
les quelques différences qui existent dans la gestion des tableaux entre le
langage C et l’algorithmique.
Le langage C propose un type tableau qui est plus contraignant qu’en
algorithmique et même quelques langages de haut niveau. En effet, les
indices de parcours des cases ne peuvent commencer qu’à zéro et non pas
par une valeur quelconque. D’autre part, les indices sont obligatoirement
numériques ou de type énuméré qu’on étudiera par la suite.
Enfin, la gestion de tableaux de plusieurs dimensions est délicate car très
proche des mécanismes sous-adjacents (modes d’adressage et allocation
mémoire qui sera étudier ultérieurement).
La définition d’un tableau à une dimension (vecteur) suit la syntaxe suivante :
Type-case nom-tableau[nombre-case] ;
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE IV
Exemple de bilan
En langage C En algorithmique
variable
Char nom[30] ; nom : vecteur de 30 caractère ;
Int A[10] ; A : vecteur de 10 entier ;
Float note[5] ; note : vecteur de 5 réel ;
Il faut faire attention, les cases du vecteur sont numérotées de zéro à
nombre-case – 1. D’autre part le langage n’assure aucun contrôle sur les
indices de parcours employés, ils peuvent être négatif ou supérieurs à la taille
indiquée (nombre-case), le seul résultat à craindre est un « bug » voire un
plantage du programme !
Un tableau à deux dimensions (matrice) est défini selon la syntaxe suivante :
Type-case nom-tableau[nbrligne][nbrcolonne] ;
Il faut remarquer que les deux nombres de cases (ligne et colonne) sont
donnés séparément.
Exemple de bilan
En langage C En algorithmique
variable
float note[10][3] ; note : matrice de 10*3 réel ;
INITIALISATION DES CASES D’UN TABLEAU
Pour initialiser un tableau (vecteur ou matrice) comme d’ailleurs une variable
simple, on a le choix entre l’initialisation au même temps que la définition ou
après à l’aide d’affectations ou même d’opérations d’input : c’est-à-dire que
définition et initialisation sont faites séparément.
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE IV
Regardons la syntaxe de l’initialisation :
Pour le vecteur
Type nom*nombre+ = ,V0, V1, … Vn- ;
Pour la matrice
Type nom[Nligne][Ncolonne]= {
,V00, V01, V02, …V0m-,
,V1O, V11, V12, …V1m-,
.
.
.
,Vn0, Vn1, Vn2, …Vnm-
} ;
Pour cet exemple Nligne = n et Ncolonne = m.
Exemple de bilan
main()
{
Int tab[3] = {21, 9, 55} ;
float pts[4][3] = { {1.0 , 0.0, 0.0},
{0.0, 2.0, 0.0},
{0.0, 0.0, 1.0},
{1.1, 1.1, 1.1}
} ;
}
On peut définir (créer) un type tableau sans pour autant définir une variable
par la syntaxe suivante :
Typedef type-case nom-type[nombre-case] ;
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE IV
Exemple
En langage C En algorithmique
Déclaration
type
Typedef char typnom[30] ; typnom = vecteur de 30 caractères ;
variable
Typnom nom ; nom : typnom ;
Avant d’étudier le mécanisme de manipulation des tableaux (vecteur et matrice), on
va se familiariser avec les chaînes de caractères.
CHAINES DE CARACTERES
Le langage C n’offre pas un traitement confortable des chaînes de caractères.
En effet, elles sont gérées sous forme d’un vecteur de taille fixe, dans lequel
on ajoute une sentinelle au bout du contenu utilisable. Cette sentinelle est le
caractère de code ASCII nul (‘\o’).
Déclaration d’une chaine de caractères : Il suffit de définir un vecteur de
caractères. Seulement, il faut tenir compte de la sentinelle et par conséquent
la réservation d’une case en plus de la longueur utile est primordiale !
La syntaxe est :
Char nom-chaine[longueurutile + 1] ;
Exemple :
En langage C En algorithmique
Déclaration
Variable
Char nom[31] ; nom : chaine de 30 caractère ;
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE IV
On peut aussi déclarer et initialiser en même temps une chaîne de caractères,
selon la syntaxe suivant :
Char nom-chaine[longueurutile + 1+ = ‘’contenu’’ ;
Exemple :
Char ligne*81+ = ‘’salem’’ ;
Char texte[81] = ,‘s’, ‘a’, ‘l’, ‘e’, ‘m’, ‘\o’- ;
NB : Les deux initialisations sont équivalentes car en langage C, les chaînes de
caractères sont considérées comme des tableaux de type caractère. Mais je préfère
la première écriture qui est simple et nous donne l’impression qu’on est en train
d’utiliser une chaîne de caractères et non un vecteur.
Lorsqu’on manipule des chaînes de caractères, il est nécessaire de prendre en
considération leurs représentations en mémoire. En particulier, les
comparaisons de chaînes peuvent sembler surprenantes, c’est-à-dire il faut
comparer les contenus et non les contenants qui sont considérés comme des
adresses.
Exemple :
Char chaine1*81+ = ‘’salem’’ ;
Char chaine2*81+ = ‘’salem’’ ;
Si vous comparez les deux chaînes (chaine1 et chaine2), vous allez dire
qu’elles sont égales, et bien non ! En langage C elles sont différentes car ce
langage établit une distinction entre le contenu (les caractères : ‘s’, ‘a’, ‘l’, ‘e’,
‘m’, ‘\o’) et les contenants (chaine1 et chaine2) qui sont des adresses de leurs
emplacements en mémoire et donc chaine1 ≠ chaine2, quoiqu’elles
contiennent la même information !
Un autre problème, peut surgir lorsqu’on affecte des chaînes. Pour illustrer
cela, regardons l’exemple suivant :
char chaine1*81+ = ‘’bonjour’’ ;
char chaîne2[81] ;
chaine2 = chaine2 ; /*Affectation d’une chaîne dans une autre*/
L’affectation vous parer correcte et pourtant elle provoque une erreur de
compilation !
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE IV
Opérations sur les chaînes de caractères : Si un programme va utiliser des
chaînes de caractères, il est utile d’inclure le fichier d’entête <string.h> pour
pouvoir profiter de certaines fonctions prédéfinies qui manipulent les chaînes
telles que :
Strlen() donne le nombre d’octets occupés par la chaîne.
Strcpy() copie une chaîne dans une autre.
Strupr() convertit la chaine en majuscule.
Strcat() concatène une chaîne à une autre.
Etc …
Il y a plusieurs fonctions de ce genre qui seront très utiles.
TRAITEMENT DES TABLEAUX
Pour accéder à une case d’un tableau, il faut un indice de parcours pour les
vecteurs et deux autres pour les matrices, le premier pour les lignes et le
deuxième pour les colonnes.
Exemple :
En langage C En algorithmique
X = vect[i] ; x vect(i) ;
Y = mat[i][j]; y mat(I, j);
La lecture d’un vecteur est tributaire d’une boucle sur l’indice de parcours (la
boucle for est souhaitable).
Exemple : lecture du vecteur vect de N entier.
En langage C En algorithmique
For (i=1; i<N ; i=i+1) Pour i allant de 1 à N faire
Scanf(‘’%d\n’’, &vect*i+) ; lire(vect(i));
Finpour
Attention: Dans ce cas la lecture de chaque case se fait sur une ligne, si on
veut les lire toutes sur la même ligne, il suffit d’écrire scanf(‘’%d’’, &vect*i+) ;
c’est-à-dire se débarrasser du retour chariot \n.
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE IV
L’écriture d’un vecteur, idem que pour la lecture, une boucle est plus que
nécessaire !
En langage C En algorithmique
For (i=0; i<N ; i=i+1) Pour i allant de 1 à N faire
printf(‘’%d\n’’, &vect*i+) ; ecrire(vect(i));
Finpour
/*ou bien printf(‘’%d’’, &vect*i+);*/
Pour lire une matrice, deux boucles imbriquées sont nécessaires l’une sur les
lignes et l’autre sur les colonnes.
Exemple : Lecture d’une matrice mat de type réel, de N lignes et M colonnes.
En Langage C En algorithmique
For (i=0 ; i<N ; i=i+1) pour i allant de 1 à N faire
{ pour j allant de 1 à M faire
For (j=0 ; i<N ; i=i+1)
scanf(‘’%f’’, &mat*i+*j+) ; lire(mat(i, j)) ;
printf(\n) ; finpour
} finpour
Dans cet exemple, la lecture se fait ligne par ligne. Voir le retour chariot.
Pour écrire une matrice on utilisera aussi deux boucles imbriquées.
Exemple : Ecriture de la même matrice citée ci-dessus.
En Langage C En algorithmique
For (i=1 ; i<N ; i=i+1) pour i allant de 1 à N faire
{ pour j allant de 1 à M faire
For (j=1 ; i<N ; i=i+1)
printf(‘’%f’’, &mat*i+*j+) ; Ecriture(mat(i, j)) ;
printf(\n) ; finpour
} finpour
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE IV
TABLEAUX ET ADRESSES
Le langage C permet de manipuler les tableaux comme en langages de bas
niveau (langage d’assemblage), à l’aide de pointeurs. Le nom d’un tableau est
considéré comme un pointeur (adresse) sur son premier élément.
Exemple :
float tab[10] ;
float *adr ;
adr = tab ;
printf(‘’Le contenu de adr : %f\n’’, *adr) ;
Ici, adr est une adresse de flottants, elle désigne le premier élément du
tableau tab.
L’élément suivant se trouve à l’agresse adr+1. Le type de l’élément (donc la
place qui occupe en mémoire) est pris en compte dans cette incrémentation.
Cette règle permet ces deux écritures :
&(tab[i]) ≡ tab+1 tab[i] ≡ *(tab+i)
&(tab[0]) ≡ tab tab[0] ≡ *tab
Ces équations font le lien entre les tableaux et les pointeurs. Pour revenir en
arrière, on peut soustraire un entier d’un pointeur.
Exemple de manipulation d’adresse :
Float tab[10] ;
Float *adr ;
Adr = tab ;
While (*adr != 0)
{
Printf(‘’%f’’, *adr);
Adr = adr + 1;
}
Ce laps de programme affiche les premiers éléments du tableau tab, jusqu’à
rencontrer la valeur zéro. Il faut noter qu’aucun contrôle de sortie du tableau
n’est fait.
Attention : La manipulation des adresses est très dangereuse !
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE IV
TYPE ENUMERE
Les variables de ce type prennent une valeur parmi un ensemble. Par exemple
pour un feu de circulation, la couleur est : vert, orange ou rouge.
Le langage C propose deux syntaxes pour définir ce type.
Première syntaxe :
Enum nouveautype {liste de symboles} ;
Cette syntaxe définit un nouveau type qui s’appelle enum nouveautype (et
non seulement nouveautype, voir la deuxième syntaxe). Le domaine des
variables de ce type est la liste des symboles fournis. Ces symboles sont
équivalents à des constantes entières commençant par 0.
Exemple de bilan :
En langage C En algorithmique
Déclaration
Type
Enum jour{dimanche, lundi, mardi, jour=(dimanche,lundi
mercredi,jeudi,vendredi ,mardi,mercredi
samedi} ; ,jeudi,vendredi
,samedi) ;
Enum jour j ; variable
J : jour ;
J= jeudi ; début
J jeudi ;
Fin
Cet exemple permet de définir le type enum jour et les variables de ce type
peuvent prendre pour valeur dimanche, lundi, mardi, mercredi, jeudi, vendredi
ou, samedi.
Deuxième syntaxe :
Typedef enum {liste de symboles} nouveautype ;
Cette syntaxe permet de définir un nouveau type qui s’appelle nouveautype
(sans évoquer le mot clé enum).
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE IV
Exemple de bilan :
Typedef enum{dimanche, lundi, mardi, mercredi, jeudi, vendredi,
samedi} jour;
Jour j ;
J = jeudi ;
TYPE ENREGISTREMENT
Définition d’un type enregistrement : Un enregistrement est composé de
champs qui peuvent être de types différents et la liste des champs est
composée de définitions identiques à celle de variables. Par exemple
l’enregistrement « date » est composé de trois champs « jour », « mois » et
« année ».
En langage C, on utilise le mot clé « struct » pour définir les enregistrements.
Deux syntaxes sont disponibles.
Syntaxe 1
Struct nouveautype {liste de champs} ;
Donc le nouveau type est appelé désormais stuct nouveautype (et non
nouveautype).
Exemple de bilan
En langage C En algorithmique
Déclaration
Type
Typedef enum{nul, moyen, intensite=(nul, moyen, fort) ;
Fort} intensite ;
Struct point { point= enregistrement
Float x, y ; x, y : réel ;
Intensite brillance ; brillance : intensite ;
} fin
Variable
Struct point p ; p : point ;
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE IV
Syntaxe 2
Typedef struct{liste de champs} nouveautype ;
Cette syntaxe définit un nouveau type appelé simplement
nouveautype (sans évoquer le mot clé struct).
Reprenant l’exemple ci-dessus.
Typedef struct {
Float x, y ;
Intensite brillance ;
} point ;
Point p ; /*sans évoquer le mot clé struct*/
Traitement des enregistrements : Pour atteindre un champ donné d’un
enregistrement donné, il suffit d’écrire le nom de l’enregistrement et le nom
du champ séparés par un point.
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE IV
Exemple de bilan
enum sexe {M, F} ;
struct identite {
int numero ;
int age ;
char nom[30] ;
char prenom[35] ;
enum sexe S ;
}
Struct etudiant identite ;
Etudiant.numero = 12345 ;
Etudiant.age = 18 ;
Etudiant.nom = ‘’Mohamed’’ ;
Etudiant.prenom = ‘’Ali’’ ;
Etudiant.S = M ;
On peut écrire aussi :
Etudiant = ,12345, 18, ‘’Mohamed’’, ‘’Ali’’, M- ;
C’est-à-dire affecter à l’enregistrement, tous les champs en même temps!
On peut définir et initialiser une structure enregistrement, en même
temps et cela est possible grâce aux deux syntaxes suivantes :
Syntaxe 1
Struc nom-type nom-variable = {valeurs-champs} ;
Syntaxe 2
Nom-typedef nom-variable = {valeurs-champs} ;
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE IV
Exemple de bilan :
Struct point p = {1.5, -2.4, nul} ;
/* Ou bien*/
p.x = 1.5 ;
p.y = -2.4 ;
p.intensite = nul ;
N. BENTOUNSI CONSTANTINE 2 LANGAGE C PARTIE IV
BIBLIOGRAPHIE
1- Langage C : Cours et références
Pierre NERZIC Mars 2003
2- Langage C : Support de cours
Patrick CORDE Mars 2006
3- Programmation en langage C
M.C. BELAID Mars 2008
4- Algorithmique, structures de données et langage C
J.M. ENJALBERT Janvier 2005
5- Langage C
Claude Delannoy 2iéme édition 2009
N. BENTOUNSI CONSTANTINE 2 Listes chaînées en C PARTIE V
1
PARTIE V : Les listes chaînées
Définitions :
C’est un ensemble de nœuds reliés entre eux par des liens de chaînage (des adresses).
Chaque nœud est constitué de deux champs l’un pour l’information et l’autre pour l’adresse du nœud suivant.
Un nœud est appelé aussi élément ou maillon ou chaînant ou encore node c’est ce dernier qu’on va utiliser dans ce fascicule.
Définition d’une liste chaînée unidirectionnelle.
#include <stdlib.h>
Typedef struct node node;
Struct node {
Item info;
Struct node *suiv;
};
Typedef node* liste;
NB : item c’est le type de l’information qui sera mise dans la liste. Si item est un type simple
genre Int, float ou bien char … Au lieu d’utiliser le vocable « item », on utilisera le type simple. Par contre si item est de type complexe, il est utile d’utiliser le vocable item et le définir à l’aide de « typedef ».
Si vous voulez utiliser le type même complexe, à vous de choisir car ce n’est pas une règle !
Exemple1 :
Typedef Int item;
Item info ; /* C’est comme si on déclare Int info;*/
Dans ce cas, il est préférable d’utiliser directement le type Int :
Int info ;
N. BENTOUNSI CONSTANTINE 2 Listes chaînées en C PARTIE V
2
Exemple2 :
Typedef struct item { int num;
int qte ;
float prix ;
} item;
Item info ;
/* On a déclaré info de type item qui est synonyme du type enregistrement*/
NB : Vous remarquez qu’avant de déclarer la liste L, on a inclus <stdlib.h> pour pouvoir utiliser la macro NULL qui veut dire ‘pas d’adresse’ ou ‘Nulle part’ tout simplement pour dire qu’il n’y a pas de suivant.
Après avoir définit le type ‘liste’, on peut déclarer une liste L.
Il existe trois (03) déclarations possibles, elles sont différentes mais équivalentes :
Liste L = NULL ; Node *L = NULL ; Struct node *L = NULL ;
On remarque qu’on est en train de déclarer la liste L et en même temps l’initialiser avec NULL car pour le moment elle est vide.
Pour la suite, on va utiliser deux fonctions prédéfinies pour allouer de l’espace mémoire à l’aide de « malloc » et pour libérer de l’espace mémoire à l’aide de « free » et cela est possible grâce à l’inclusion de la < stdlib.h >.
Quand une liste est vide cela veut dire qu’elle est égale à NULL.
Quand une liste est constituée d’un seul nœud, on dit que la liste est un singleton.
N. BENTOUNSI CONSTANTINE 2 Listes chaînées en C PARTIE V
3
OPERATIONS DE BASE SUR LES LISTES CHAINEES
Les plus importantes des opérations de bases sont les ajouts et les suppressions.
Ajout d’un nouveau nœud à la liste L. Trois cas de figure se présentent à savoir : En
tête de la liste, en fin de la liste ou encore au milieu de la liste à une adresse donnée.
1. Ajouter en tête de liste. /*Fonction qui réalise le travail*/ Liste AjouterEnTete(liste L , item valeur) ; { /*créer un nouveau nœud*/ Node* nouveaunode = malloc(sizeof(node)) ; Nouveaunode->info = valeur ; Nouveaunode->suiv = L ; Return nouveaunode ; }
/* La fonction prédéfinie « malloc » permet de faire une allocation dynamique de la mémoire */
/*Si la liste L était vide, on retourne une liste singleton constituée du nouveau nœud qu’on vient d’ajouter*/
N. BENTOUNSI CONSTANTINE 2 Listes chaînées en C PARTIE V
4
2. Ajout en fin de liste.
Le nouveau nœud qu’on va ajouter à la fin de la liste L sera le dernier de la liste donc son suivant (suiv) sera égal à NULL.
/*Fonction qui réalise le travail*/ Liste AjouterEnFin(liste L , item valeur) ; { /*créer un nouveau nœud*/ Node* nouveaunode = malloc(sizeof(node)) ; Nouveaunode->info = valeur ; Nouveaunode->suiv = NULL ; If (L == NULL) Return nouveaunode ; Else { /*On doit parcourir la liste L jusqu’au dernier nœud*/ /*pour cela on aura besoin d’un pointeur */ Node* pt = L; While (pt->suiv != NULL) { Pt = pt->suiv ; } Pt->suiv = nouveaunode ; Return L ; } }
3. Ajouter au milieu de la liste L et à l’adresse ADR. Trois cas de figure se présentent :
ADR = L donc l’ajout se fait en tête de la liste L. ADR n’existe pas, on retourne NULL. ADR est trouvée donc l’ajout se fait entre deux nœuds.
Pour ce dernier cas, on aura besoin de deux pointeurs, l’un pour pointer le nœud qui se trouve à l’adresse ADR et le deuxième pour pointer le nœud précédant.
Les deux pointeurs utilisés seront pt et ptprec.
N. BENTOUNSI CONSTANTINE 2 Listes chaînées en C PARTIE V
5
/*Fonction qui réalise le travail*/ Liste AjouterAuMilieu(liste L , liste ADR , item valeur) ; { /*créer un nouveau nœud*/ Node* nouveaunode = malloc(sizeof(node)) ; Nouveaunode->info = valeur ; Node* pt = L ; Node* ptprec = L; While ((pt != ADR) && (pt != NULL)) { Ptprec = pt; Pt = pt->suiv; } If (pt == NULL) /* ADR n’existes pas*/ { Return NULL ; } Else If (pt == L) /*L = ADR*/ { Return AjouterEnTete(L, valeur) ; } Else /* l’ajout se fait entre deux nœuds ptprec et pt*/ { Nouveaunode->suiv = pt ; Ptprec->suiv = nouveaunode ; } }
REFLEXION : Et si on utilisera des procédures au lieu des fonctions !
N. BENTOUNSI CONSTANTINE 2 Listes chaînées en C PARTIE V
6
Suppression d’un nœud de la liste L. Trois cas de figure se présentent à savoir :
En tête de la liste, en fin de la liste ou encore au milieu de la liste à une adresse donnée.
Attention : Dans tous les cas et avant de faire la suppression, il est nécessaire de vérifier si la liste n’est pas vide. Et dans ce cas soit faire retourner NULL, soit écrire un message d’erreur.
I. Suppression du nœud du début de la liste L. /*Une fonction pour supprimer ce nœud*/ Liste suppressionEnTete(liste L) { If (L == NULL) Return NULL ; Else { Node* nodesuivant = L->suiv ; Free(L) ; /* Libérer la mémoire*/ Return nodesuivant ; } }
NB : D’une manière générale, il est prudent de faire suivre tout appel à la fonction « free » d’une mise à NULL du pointeur correspondant.
Dans notre cas free(L) ; sera free(L), L = NULL ;
Mais cela n’est pas obligatoire !
II. Suppression d’un nœud à la fin d’une liste. On peut distinguer trois cas possibles :
La liste est vide donc il n’y a rien à supprimer. On retourne NULL ou bien un message d’erreur est écrit.
La liste est singleton donc elle contient un seul nœud qui va être supprimé et la liste devient vide! (Retourner NULL).
La liste contient plus d’un nœud par conséquent on va parcourir la liste à l’aide de deux pointeurs pour qu’à la fin un pointeur est sur l’avant dernier nœud et le second sur le dernier.
N. BENTOUNSI CONSTANTINE 2 Listes chaînées en C PARTIE V
7
/*La fonction qui fait ce travail*/
Liste suppressionEnFin(liste L)
{
If (L == NULL) /*liste vide*/
Return NULL ;
else /* La liste n’est pas vide*/
if (L->suiv == NULL) /* liste singleton*/
{
Free(L), L=NULL; /*libérer la mémoire*/
Return NULL;
}
Node* Pt = L; /* cas général*/
Node* Ptprec = L;
While (Pt->suiv != NULL)
{
Ptprec = Pt;
Pt = Pt->suiv;
}
Ptprec->suiv = NULL;
Free(Pt), Pt = NULL; /* Libérer la mémoire*/
Return L;
}
N. BENTOUNSI CONSTANTINE 2 Listes chaînées en C PARTIE V
8
III. Supprimer un nœud au milieu d’une liste, ce nœud se trouve à l’adresse ADR par exemple.
Plusieurs cas peuvent se présenter :
Liste vide donc rien à supprimer. ADR est l’adresse du premier nœud (L=ADR) donc la suppression se
fait en tête de liste ! ADR n’a pas été trouvée, retourner NULL ou retourner L (à choisir). ADR a été trouvée mais il n’a pas de suivant donc la suppression se
fait en fin de liste ! ADR a été trouvée et ce nœud a un suivant et un précédant donc il
est bel et bien au milieu de la liste (cas général) ; on le supprime le plus normalement du monde !
N. BENTOUNSI CONSTANTINE 2 Listes chaînées en C PARTIE V
9
/*La fonction qui fait ce travail*/
Liste suppressionAuMilieu(liste L, liste ADR)
{
If (L == NULL) /*liste vide*/
Return NULL ;
else
if (L == ADR) /* Suppression en tête de liste*/
return SuppressionEnTete(L);
else /* Chercher ADR qui n’est pas en tête de liste*/
{
Node* Pt = L;
Node* Ptprec = L;
While ((Pt != NULL) && (Pt != ADR))
{
Ptprec = Pt;
Pt = Pt->suiv;
}
If Pt = NULL /* ADR n’a pas été trouvée*/
Return NULL; /* ou bien return L;*/
Else /* ADR a été trouvée et Pt = ADR*/
If Pt->suiv = NULL /* dernier noeud*/
Return SuppressionEnFin(L);
/* Le noeud est au milieu de la liste : cas général*/
Ptprec->suiv = Pt->suiv;
Free(Pt), Pt = NULL;
Return L;
}
N. BENTOUNSI CONSTANTINE 2 Listes chaînées en C PARTIE V
10
/*La fonction qui fait ce travail autre version*/
Liste suppressionAuMilieu(liste L, liste ADR)
{
If (L == NULL) /*liste vide*/
Return NULL ;
else
if (L == ADR) /* Suppression en tête de liste*/
return SuppressionEnTete(L);
else /* ADR n’est pas en tête de liste il faut la chercher */
{
Node* Pt = L;
Node* Ptprec = L;
While ((Pt != NULL) && (Pt != ADR))
{
Ptprec = Pt;
Pt = Pt->suiv;
}
If Pt == NULL /* ADR n’existe pas dans la liste*/
Return L;
/* Le noeud est au milieu de la liste ou en fin de liste*/
/* ça marche dans les deux cas*/
Ptprec->suiv = Pt->suiv;
Free(Pt), Pt = NULL;
Return L;
}
N. BENTOUNSI CONSTANTINE 2 Listes chaînées en C PARTIE V
11
BIBLIOGRAPHIE
1- Langage C : Cours et références
Pierre NERZIC Mars 2003
2- Langage C : Support de cours
Patrick CORDE Mars 2006
3- Data structures and programming design
Robert L. Kruse
4- Algorithmique, structures de données et langage C
J.M. ENJALBERT Janvier 2005
5- An introduction to methodical programming
W. Findbay and D. Wattes
6- Apprendre à programmer
Algorithmes et conception objet.
C. Dabancourt