Cours de C
Cours de C
Cours de C
PARTIE I :
LE LANGAGE C
INTRODUCTION
Remarque
1)La directive define ne sert pas strictement à définir ou déclarer des paramètres.
Elle sert également à déclarer des MACROS.
2) La définition d’une constante à l’aide d’une valeur donne implicitement le type
de la constante :
EXEMPLE DE PROGRAMME
include "stdio. h"
define imax 9
define absol(a) ( ((a) > –(a)) ? (a) : – (a))
else <instruction(s)>
e1) if (i > 0) j = i ; e2) if (i > 0) {
else j = –i ; j=i;
k=2*i;
}
else k = –i ;
e3) if (i > 0) {j=i; e4) if (i > 0) k = i ;
k=2*i; else { k = –i ;
} j = –2 * i ;
else { j=2*i ; k=i ; } }
Remarque :
Lorsque la partie "if" n'est pas un bloc d'instructions mais une instruction
élémentaire, il ne faut pas oublier le ";" qui termine l’instruction et précède le
"else" .
LangC 7c L’interrogative (expression1) ?expresssion2 : expression3
Exemple 1 char c ;
int i=10 ;
do i = i–1 ; while (i>0) ;
do c = getch () ; while ( kbhit ( ) ) ;
Exemple 2 int i =0 , j=10 ;
do{
i++ ; j– – ;
} while(j>0) ;
Exemple int i ;
Exemple 1
switch( i) {
case 0 : j=0 ;
case 1 : { j=1 ; i++ ; }
case 2 : k++ ;
default : k=20 ;
}
Exemple
int i=10 ;
float x;
x = (float)i ;
/*convertit i en nombre réel avant d’installer le résultat 10.0 dans x */
p est un pointeur qui indique l’adresse de x , *p est donc la valeur de x , p+1 est
l’adresse de l’entier qui suit x , c'est à dire y (parce que x est entier et l’entier
déclaré après x est y ), *(p+1) est donc la valeur de y , *(p+i) est donc la valeur
de l’entier à l’emplacement i+1 à partir de l’emplacement de x.
Généralement lorsque nous déclarons un pointeur, nous devons veiller à ce qu’il
"pointe" à un emplacement "qui nous appartient" : c’est le grand problème des
pointeurs.
PARTIE II :
STRUCTURES de DONNEES et
ALGORITHMES en C
INTRODUCTION
Les structures de données et algorithmes mentionnés dans cette partie sont conçus
en C mais pourront assez facilement être transposés dans d'autres Langages de
programmation structurés. L'auteur garde ici assez d'originalité due à une
expérience et n'oublie cependant pas qu'il s'adresse à des débutants ou des initiés.
Le lecteur avancé pourra d'une part consulter l'auteur pour des conseils
particuliers, d'autre part s'intéresser plus efficacement à la troisième partie de ce
fascicule qui développe des minis–projets.
Remarque
Si p est un pointeur sur une structure (un enregistrement) ayant un champ libellé
c, l'accès à ce champ se fera par l'appel (*p).c ; le langage C simplifie cette
expression en permettant l'appel p–>c . A l'aide de notre exemple sur les
employés , la déclaration Employe *eptr nous donne un pointeur eptr sur une
structure Employe, on affectera donc le champ numatric par
(*eptr).numatric=1 ou eptr–>numatric=1
La deuxième méthode d'accès sera la plus utilisée dans la suite de ce fascicule.
#define nbMax 50
typedef union{FAUX , VRAI}Bool;
typedef int typinfo;
typedef struct {
int sommet ;
typinfo corps[nbMax] ;
}PILE ;
Ici nbMax représente le nombre maximum d'éléments que nous désirons gérer
dans la pile, les informations stockées dans la pile sont de type typinfo, le type
typinfo est int à présent mais devra être déclaré selon le contexte d'étude; nos
algorithmes devant être standards et adaptables nous avons donc choisi le type
simple int; sommet est un champ qui indique la prochaine position de stockage
d'une information dans la pile, et corps est un tableau contenant les éléments
rangés dans la pile. On adopte une fois pour toutes, les deux schémas suivants
pour empiler et dépiler. A chaque fois qu’on veut empiler, on empile dans le corps
de la pile à l’emplacement indiqué par la valeur de sommet et on incrémente la
valeur de sommet afin d'apprêter la prochaine position d’empilage. Pour la
récupération d’un élément, on décremente sommet et ensuite on récupère la valeur
indiquée à la position de sommet. Bien entendu, il faut qu’au départ sommet soit
égal à 0 et dans ce cas la pile est vide. La fonction d’initialisation de la pile va
affecter 0 à sommet.
Dans notre gestion, comme les indices commencent par 0 et qu’il y a nbMax
éléments au total, le dernier élément est stocké à la position nbMax–1 et alors
sommet vaut nbMax c’est–à–dire que la pile est pleine quand sommet vaut nbMax.
Si jamais notre programme tente de dépiler dans une pile vide, nous allons afficher
le message "dépilage refusé" et ensuite arrêter le programme.
Si notre programme tente d'empiler dans une pile pleine, nous allons afficher le
message "empilage refusé" et arrêter le programme.
Bool PileVide(PILE p)
{
Bool b;
return (b=((p.sommet = = 0) ?VRAI :FAUX)) ;
}
Bool PilePleine(PILE p)
{
Bool b=((p.sommet == nbMax) ?VRAI:FAUX);
return (b) ;
}
/*_______________________________________________________________
PROGRAMME DONNE PAR L'auditeur Ingénieur
DOFFONSOU Adrien au cours du Module17 Année 1997/1998
REVU et CORRIGE par:
Constant K. KOUAKOU
_______________________________________________________________
*/
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#define Nmax 100
int PilePleine(PILE p)
{
return ((p.sommet ==TailleMax) ?1 : 0) ;
}
do {
do{
secour=Depiler(p1) ;
if (secour < *t) Empiler (p2,secour) ;
}while ( !PileVide (*p1)&&(secour< *t)) ;
if (secour>= *t) Empiler (p1, secour) ;
Empiler (p1, *t);
while ( !PileVide(*p2)) Empiler (p1, Depiler(p2)) ;
i++ , t++ ;
}while (i< Nmax);
}
void Affichage (PILE *p) {
while( !PileVide(*p)) printf ("%d\n", Depiler(p)) ;
getch();
}
Exercice de recherche
X Y Z T NULL ;
lst
Décrochez un stage , un suivi de stage , et autres prestation; grâce à l'original de ce fascicule
… 37
Algorithmes avancés en C Edition Stage et Concours / SINUS-APLIQ
Par K. Constant KOUAKOU deuxième tirage
lst est un pointeur donnant l'adresse de départ de la liste qui a ici quatre éléments;
les listes chaînées à simple chaînage que nous allons voir sont généralement
définie de la manière suivante:
X Y Z • NULL
lst
Nous désirons ajouter en queue de lst un élément dont l'information capitale est
e afin d'obtenir la liste suivante:
• NULL
X Y Z
•
lst e • NULL
tp
if ((*lst)–>data==e) {
q=*lst , *lst = (*lst)–>link ;
free(q);
}
else (*lst)–>link = SupprimerRec( &((*lst)–>link) , e );
return (*lst);
}
Nous donnons également une version itérative de Detruire( ) qui doit libérer
toutes les cellules allouées dans le chaînage de la liste passée en argument :
Les piles dynamiques sont des cas particuliers de listes chaînées ; le type abstrait
de données Pile dynamique peut être déclaré simplement de la manière suivante
typedef list_ptr PileDyn ;
Les prototypes des primitives de base pour la gestion d’une pile dynamique
sont :
void CreerPileDyn( PileDyn *p );
int PileVideDyn( PileDyn p );
int EmpilerDyn( PileDyn *p , typinfo e );
int DepilerDyn( PileDyn *p , typinfo *e );
void DetruirePileDyn ( PileDyn *p );
La plupart des primitives seront celles des listes chaînées ; il n ' y a pas de test
d'une pile pleine .
void CreerPileDyn ( PileDyn *p) { Creer ( p ) ; /*appel de Creer liste chaînée*/ }
Remarques :
– L'élément dépilé en cas de dépilage est stocké dans l'argument e passé à
DepilerDyn( ) .
– Même si nous n'avons pas donné de test de pile pleine , nous pouvons quand
même admettre qu'une pile est pleine lorsqu'il n ' y a plus assez d'espace en
mémoire pour une allocation .Notons également que la fonction d'allocation
malloc( ) n'accède qu'à la mémoire conventionnelle et pour accroître les
possibilités d'allocation il faut d'autres primitives d'allocation .
X Y Z T NULL
TETE QUEUE
Au lieu d’une liste chaînée qui a une seule entrée, on veut maintenant 2 entrées
appelées tête et queue. Nous adaptons les algorithmes afin qu’ils prennent
maintenant en compte ces deux nouveaux pointeurs . Avant tout , le type abstrait
de données file dynamique peut être déclarée par
typedef struct { list_ptr tete , queue ; } FileDyn ;
Nous donnons donc une définition adaptée de AjoutQueue( ) appelée EnfilerDyn(
) :
Remarques :
1 . Les sous arbres étant des ensembles disjoints, il n ' y a pas de connexion entre
eux .
2 . Un arbre est donc une structure de données récursive dans laquelle chaque
nœud de l'arbre est racine d'un sous–arbre vide ou pas .
Les arbres sont généralement étudiés avec certaines définitions que nous donnons
ci–dessous .
Degré d ' un nœud Le degré d'un nœud est le nombre de sous arbres du nœud.
C'est aussi le nombre de nœuds qui sont directement reliés au nœud considéré.
Feuille Un nœud ayant pour degré 0 est une feuille .
Parent ou père Un nœud ayant des sous arbres est le père ou le parent des racines
de ces sous arbres
Enfant ou fils Les racines des sous arbres d'un nœud sont appelées les fils
(enfants) de ce nœud
Ancêtres Les ancêtres d'un nœud sont tous les nœuds sur le chemin de la racine
à ce nœud.
Descendants Les descendants d'un nœud sont tous les nœuds de ses sous arbres
Niveau d'un nœud Le niveau d'un nœud est défini comme suit:
• La racine de l'arbre a pour niveau 1
• Tout autre nœud a pour niveau le niveau du père plus 1
Profondeur ou hauteur d'un arbre La hauteur/profondeur d'un arbre est le
maximum des niveaux des nœuds de l'arbre .
l r
Y
Z
l r
NULL NULL
NULL NULL
5
10
1
11
0 3 7
4 9
(((A/B)C)D)
* + E =E
A/BCD + E =
ACD
* + E D
B
/ C
A B
Si on adopte la convention que nous parcourons le sous arbre gauche avant le sous
arbre droit, alors il reste trois ordres de parcours LVR , LRV et VLR appelés
respectivement ordre infixe , suffixe et préfixe; les primitives qui leur sont
associées sont : Inf_order ( ) , Pre_order ( ) et Pst_order ( ) .
} File;
File fl;
Seuls les types de données stockées dans la file changent. Le parcours par niveau
est alors défini par :
0
0 0
1 2 1 2 1
3 3 4 5 6 2
Remarque Un graphe n'aura pas d'arc cyclique d'un sommet vers lui–même.
• Un graphe non orienté est connexe si toute paire de sommets distincts est
associée à un chemin (2 sommets quelconques distincts sont connectés) .
Exemple
0
Sous– G (G n'est pas connexe)
graphe
maximal 1 2
connexe
(composant
connexe)
4 3 Composant connexe
0 0
G3
2
• Pour un graphe orienté, le ½ degré intérieur d'un sommet est le nombre d'arcs
qui arrivent vers ce sommet , ou qui ont pour tête ce sommet.
• Le ½ degré extérieur d'un sommet est le nombre d'arcs qui partent de ce
sommet.
• Si di est le degré d'un sommet i d'un graphe G ayant n sommets et e arcs, alors
n–1
di
i=0
e=
2
B Types abstraits de données graphe
G1 0 1 2 3 G2 0 1 2 3 4 5 6 G3 0 1 2
0 0 1 1 1 0 0 1 1 0 0 0 0 0 0 1 0
1 1 0 1 1 1 1 0 0 1 1 0 0 1 1 0 1
2 1 1 0 1 2 1 0 0 0 0 1 1 2 0 0 0
3 1 1 1 0 3 0 1 0 0 0 0 0
4 0 1 0 0 0 0 0
5 0 0 1 0 0 0 0
• Pour un graphe non orienté, le degré du sommet i est la somme des éléments
de la ième ligne de la matrice d'adjacence
n–1 n–1
di = Adj_mat [i] [j] = Adj_mat [j] [i] , i fixé
j=0 j=0
• Pour un graphe orienté, la somme des éléments de la ième ligne est le demi degré
extérieur.
La somme des éléments de la ième colonne est le demi degré intérieur
0
0 1 2 3 4 5 6
0 0 1 1 0 0 0 0 1 2
1 1 0 0 1 1 0 0
2 1 0 0 0 0 1 1 3 4 5 6
3 0 1 0 0 0 0 0
4 0 1 0 0 0 0 0
5 0 0 1 0 0 0 0
6 0 0 1 0 0 0 0
graph [0] : 1 2 •
0 3 4
Décrochez un stage , un suivi de stage , et autres prestation; grâce à l'original de ce fascicule
… 0 5 6 56
1
1
Algorithmes avancés en C Edition Stage et Concours / SINUS-APLIQ
Par K. Constant KOUAKOU deuxième tirage
graph [1] : : •
graph [2] : : •
graph [3] : •
graph [4] : •
graph [5] : •
graph [6] : •
Les déclarations C pour les structures listes d'adjacence seront :
void bfs(int v)
{ /* Parcours en largeur d'abord d'un graphe commençant au
sommet v. Le tableau global visited doit être initialisé à 0.*/
node_pointer w ;
Exercices de recherche : Adapter les fonctions dfs( ) et bfs( ) pour qu'elles soient
opérationnelles avec les matrices d'adjacence (et non des listes d'adjacence).
Comme application nous pouvons afficher les composantes connexes à l'aide de
dfs( ) par la fonction suivante :
void connected(void){
/* Determine les composantes connexes d'un graphe */
int i ;
for (i = 0 ; i < n ; i ++)
if (!visited [i]) {
dfs (i) ;
printf ("\n\n ") ;
}
}
Ouverture
Le prototype est :
int open (char* nomfich , int mode);
nomfich représente le nom du fichier à ouvrir.
mode indique les opérations que le programmeur désire effectuer sur le fichier.
Il y a des constantes à manipuler pour la valeur de mode.
O_APPEND si toutes les écritures doivent être effectuées à la fin du fichier.
O_CREATE s'il faut créer un fichier inexistant à l'ouverture.
S_IWRITE pour autoriser des écritures dans le fichier
S_IREAD pour autoriser la lecture du fichier.
S_I WRITE |S_IREAD pour un accès à la fois en lecture et écriture.
Les constantes qui commencent par S sont déclarées dans le fichier sys\ stat.h
O_TRUNC : nécessaire s'il faut vider le fichier à l'ouverture.
O_TEXT : pour l'ouverture d'un fichier en mode texte.
O_BINARY : pour l'ouverture d'un fichier en mode binaire
En cas d'erreur la variable errno contient le code de l'erreur.
IL suffit de déclarer "errno" par "extern int errno" . Les codes à tester sont
déclarés dans le fichier errno.h; parmis ces codes on trouve
Création :
Le prototype de la fonction de création de fichier est :
int creat (char *nomfich , int mode);
Si le fichier existe déjà et s'il autorise des écritures, il est vidé de son contenu.
Le mode d'accès (O_BINARY ou O_TEXT) est celui de la variable prédéfinie
"_fmode", qu'il faut modifier pour créer le fichier en mode binaire. Les modes
d'autorisation sont précisés par les combinaisons de S_I WRITE , S_I READ
(sys\stat.h).
creat( ) retourne le numéro du fichier créé ou "–1" en cas d'erreur, et errno
donne le code de l'erreur.
Lecture :
Le prototype de la fonction de lecture est
int read (int nf , void *buf , unsigned nbytes);
read( ) lit à partir de la position actuelle dans le fichier nbytes octets (caractère)
dans le fichier ou le périphérique dont le numéro est nf ( valeur de retour attribué
au fichier par open). Les caractères ou octets lus sont rangés dans "buf". La valeur
maximale de nbytes est 65534 (car 65535 est considéré comme négatif). Le
nombre d'octets ou de caractères effectivement lus est donné par la valeur de
retour de read( ). S'il y a une erreur cette valeur est –1; on teste alors errno.
Ecriture :
Le prototype de la fonction d'écriture est :
int write(int nf , void *buf , unsigned nbytes);
Positionnement :
Le prototype est :
long lseek (int nf , long depl , int mode);
lseek( ) modifie la position du pointeur de fichier et prépare ainsi une lecture ou
écriture. depl contient la valeur du déplacement désiré ; la nouvelle position,
exprimée en nombre de caractères et sous forme d'un entier long par rapport au
début du fichier est la valeur de retour de lseek( ). La valeur de retour est
"–1L" en cas d'erreur. Dans ce cas l'erreur est codé dans "errno". Les codes
possibles sont EBADF (numéro du fichier erroné), EINVAL (argument erroné).
Notons que lseek( ) n'a de sens que si le fichier a été ouvert dans le mode binaire.
Le mode de déplacement est indiqué par mode via les constantes :
SEEK_SET (effectué à partir du début du fichier),
SEEK_CUR (déplacement à partir de la position courante),
SEEK_END (déplacement à partir de la fin du fichier)
Ces trois constantes sont déclarées dans le fichier "io.h".
Fermeture :
Le prototype de la fonction de fermeture d'un fichier est :
int close (int nf);
Une fois qu'on n'a plus besoin d'un fichier ouvert par open( ), il faut le fermer à
l'aide de la fonction close( ), car le système d'exploitation gère un nombre
maximum de fichiers simultanément ouverts.
Nous donnons trois exemples de programme de gestion de fichier au niveau 1
Exemple 1 :
Quand un fichier est ouvert avec intention de mise à jour, une écriture ne
peut être directement suivie d'une lecture. Un "fseek( )" est nécessaire entre les
deux opérations. Après une écriture, un fseek( ) doit également être exécuté. Il
suffit d'exécuter fseek (P , 0 , SEEK_CUR).
Réouverture
Le prototype est :
FILE *freopen(char *nomfich , char *mode , FILE *fp);
Gestion du Tampon
Le tampon peut être géré par deux fonctions de prototype
void setbuf (FILE *fp , char *buf) et
int setvbuf(FILE *fp , char *buf , int type , unsigned taille);
Les deux fonctions provoquent l'utilisation d'un même tampon que celui
utilisé par défaut. Elle doivent être appelées immédiatement après l'ouverture.
setvbuf( ) est plus intéressante, car elle permet de modifier la taille du buffer en
REMARQUES
Pour une bonne méthodologie de programmation , les fonctions de niveau 1 ne
doivent pas être appelées concurremment avec celles de niveau 2 ainsi par
exemple un fichier ouvert par open( ) ne devra pas être fermé par fclose( ).
PARTIE III :
MINIS PROJETS EN C
Cette troisième partie de notre fascicule doit développer des minis projets
et s'enrichira au fil des années . Seuls les auditeurs ayant écrit des codes lisibles,
intéressants et mettant en œuvre les structures de données mentionnées dans le
fascicule pourront voir leurs projets figurer dans cette partie . Nous ne donnons à
la date actuelle que deux programmes de tri et un programme système, pour le
démarrage concurrentiel .
/* PROGRAMME 1 :
PROGRAMME DE TRI PAR RECHERCHE ET POSITIONNEMENT
SIMULTANE DU MINIMUM ET DU MAXIMUM DANS UN TABLEAU
*/
/* PROGRAMME 2 :
UN PROGRAMME DE TRI PAR RECUPERATION DU MINIMUM DANS
UN TABLEAU DE DEPART TDEP , ET AFFECTATION DANS UN
TABLEAU FINAL TFIN, TRIE FINALEMENT EN ORDRE CROISSANT
*/
/* PROGRAMME 3 :
UN PROGRAMME SYSTEME DE CONTROLE DE L'ESPACE
GASPILLE EN MEMOIRE DE MASSE, SON PERFECTIONNEMENT
PEUT FAIRE L'OBJET D'UN STAGE EN INFORMATIQUE
INDUSTRIELLE OU MAINTENANCE INFORMATIQUE AU SINUS-
APLIQ
*/
if(!_dos_getdiskfree(N_Unite, &Free))
Conclusion
Au cours de ce fascicule qui est un support des cours que je dispense depuis
quelques années aux auditeurs des sections informatiques, nous avons eu
l'occasion de découvrir le Langage C , sa puissance et ses finesses ; ce fascicule
étant encore en phase de perfectionnement, l'auteur ne pourrait faire autrement
que conseiller le lecteur d'être critique envers son contenu, car il a été rédigé en
tenant compte des besoins pressants des auditeurs. Il doit être accompagné de
certains documents sur le Langage C et les Algorithmes. Vous remarquerez par
exemple que les fonctions d'entrée et/ou sortie des bibliothèques C ne sont pas
décrites dans ce fascicule. En effet cette tâche est toujours accomplie oralement
pendant mes cours . Les Algorithmes également sont sélectionnés pour les
auditeurs , vous pourrez par exemple consulter d'autres ouvrages pour un
complément sur les Algorithmes d'équilibre d'arbres et d'autres structures de
données . Ce support est néanmoins très satisfaisant pour les auditeurs ayant suivi
les cours oraux, et il est destiné à évoluer pour ses prochaines éditions.