Académique Documents
Professionnel Documents
Culture Documents
3
1. Structure d’un programme en langage C
4
1.2. Programme principal ou fonction principale
…
int main()
{ int nombre; double reel;
printf(“saisir un entier : ”); scanf(“%d”, &nombre);
printf(“saisir un reel : ”); scanf(“%ld”, &reel);
fct1();
printf(“Resultat : %lf”, fct2(nombre, reel));
return 0;
}
…
1.3. Définitions des fonctions
#include<stdio.h>
#include<stdlib.h>
void bonjour3(int);
int main()
{
int annee;
8
Une fois code::blocks installé, nous allons suivre les étapes :
• Cliquez sur l’icone de Code::Bolcks
• Cliquer sur Create a new project
• Cliquer sur Console application puis GO
• Cliquer sur next/ choisir C/ puis cliquer sur next
• Remplir le nom du projet et le dossier pour ce projet/ puis next
• Dans Compiler, choisir GNU GCC Compiler
Cocher la case Create Debug Configuration. Assurons-nous que l’
élément Create Release Configuration est également sélectionné.
• Cliquer sur Finish.
int main(void)
{ printf("Hello Bienvenue !");
return 0;
}
Nous pouvons le modifier pour insérer le notre. Puis nous sauvegardons notre
programme:
A) Création du fichier source ou du programme source.
11
#include <stdio.h>
#include<stdlib.h>
double division(int, int);
int main()
{ int x, y ;
for(x=1; x<10; ++x)
{
for(y=0; y<10; ++y)
{
printf("%.2lf ", division(x, y)); //Affiche le résultat de la fonction
}
}
return 0;
}
double division(int a, int b)
{ int c, double d;
c = b-2;
d = (double)a / c;
return d;
} 12
Compiler et exécuter.
3.1.3. Settings->Debugger…
une fenêtre s’ouvre et cliquons sur Default puis remplissons le chemin complet du
débogueur MinGW\bin\gdb dans le cadre de saisie de Executable path : C:\
Program Files (x86)\codeBlocks\MinGW\bin\gdb32
3.1.4. Build->Rebuild
permet de recompiler tous les fichiers avec les nouvelles options.
14
3.2. Utilisation du débogueur
Première information : l’erreur qui a planté le code est une Arithmetic exception. C’est une
erreur de calcul qui s’est produite.
Deuxième information : le débogueur a gardé une trace de l’erreur. Il nous propose de voir
cette trace, en cliquant sur Oui.
Une nouvelle fenêtre s’ouvre.
16
Elle présente un tableau de toutes les fonctions en cours d’exécution (pile de fonctions).
La 3ième colonne indique le nom de la fonction avec la valeur des arguments qu’elle a reçue lors
du plantage, par exemple a=0, b=2.
La 4ième colonne donne le fichier dans lequel s’est produit l’erreur.
La 5ième colonne montre la ligne où se trouvait l’ordinateur dans chacune des fonctions au
moment du plantage :
- La ligne 12 correspond à la ligne d'appel de la fonction division(). Le programme s'est donc
arrêté à la ligne 12 du main() pendant l'appel à la fonction division(). Et plus précisément, à la
ligne 21 de cette dernière.
Une petite flèche jaune dans la bordure indique l'endroit précis de l'erreur :
La ligne 21 correspond à la ligne où se situe l’erreur. On peut obtenir des informations sur la
valeur des variables pour détecter ce qui a causé l’erreur.
17
3.3. Connaissances des valeurs des variables
Les valeurs de c et d sont "bizarres" parce qu'à ce moment-là du code, elles n'ont pas encore
été initialisées.
Si nous avançons dans le programme, toujours avec F7, nous verrons les valeurs des deux
variables changées.
19
4. Type de variables
Le type de base de variable sont caractères uniques, nombres entiers, nombres
réels.
4.1.1. Exemple
#include<stdio.h>
#include<stdlib.h>
int main ()
{ char car1=’A’, car2 ;
car2= ‘a’ ;
printf (″le premier affichage : car1 : %c\t car2 :%c\ n″, car1, car2) ;
car1=90 ; /* modification de variable car avec la code ASCII de Z et z */
car2=122 ;
printf(″le second affichage : car1: %c\t car2 %c\n″, car1, car2) ;
return 0;
}
20
4.1.2. Séquences dites d’échappement
Des caractères non affichables sont représentés par des séquences particulières
précédées de la barre à gauche (\).
Le caractère nul est utilisé pour marquer la fin d’une chaîne de caractères. Ce n’est
pas l’équivalent du caractère zéro qui se note ‘0’ ou 48 en code ASCII. 21
4.2. Types de variables entières
Les entiers courts sont codés sur 2 octets, les entiers et les entiers longs signés ou
non sont codés sur 4 octets.
4.3.1 Virgule flottante. On les dit en virgule flottante puisqu’un nombre réel peut toujours
s’écrire en représentation décimale de façon unique sous la forme approchée.
Partie entière.Partie décimale *e +- exposant
0.xxxxxx*e^y
Exemple :1024 = 0.1024*10^4 = 0.1024E4
0.0001024 = 0.1024E-3
Conversion forcée par une affection. Une affectation introduit une conversion d’office dans le
type de la lvalue réceptrice.
#include <stdio.h>
int main (void)
{ int a,*b ;
b = &a ;
a = 10 ; printf(“\n *b :%d”, *b);
*b=12 ; printf(“\n a:%d”, a); return 1;
}
Attention
Pour que l’adresse de a existe, il faut déclarer la variable a.
Pour que b existe, il faut déclarer le pointeur *b. 31
5.2.7. Ordre de priorité des opérateurs en C
Opérateurs Evaluation
() gauche à droite
[] gauche à droite
-> . gauche à droite
sizeof( ) gauche à droite
++ -- ~ ! droite à gauche
- + & * droite à gauche
(type) droite à gauche
* / % gauche vers droite
+ - -/-
< <= > >=-/-
= = ! = -/-
& -/-
^ -/-
| -/-
&& || -/-
? : -/-
= += … droite à gauche
, gauche vers droite
32
6. INSTRUCTIONS d’ENTREES\SORTIES
Les fonctions qui permettent de dialoguer avec une machine sont regroupées dans <stdio.h>.
6.1. Lecture et écriture de caractère isolé
Elles sont effectuées par getchar() et putchar(). Ce sont des macro-instructions très rapides d’
exécution. Ses définitions sont copiées directement par le compilateur dans votre code.
#include <stdio.h>
int main (void)
{ char car ;
car = getchar() ;
putchar (car) ;
putchar (‘z’) ; return 0;
}
Ils sont utiles dans les cas suivants :
- Il est possible de lire et écrire une chaîne de caractère en les traitant un à un au moyen d’une
boucle à répéter plusieurs fois.
- On peut les utiliser pour détecter un caractère ’\n’ qui clôt la saisie d’une chaîne. Si la valeur
renvoyée par la macro est le caractère ‘\n’ alors nous sommes au bout de la chaîne.
6.2. Affichage avec printf()
La fonction printf() affiche à l’écran une chaine de caractères.
Syntaxe : printf(une chaîne de caractère, éventuellement des arguments) ;
Parmi ces caractères, on peut écrire une séquence qui permet l’affichage de valeurs. Elle débute
par le symbole %suivi d’un code. Une suite de code permet de définir avec une précision le
format d’affichage de la variable : %x.y (code de type). 33
• x : précise la longueur de l’espace alloué pour la variable.
Si x>0 la variable est cadrée à droite et si x<0 la variable est cadrée à gauche.
Exemple : int a = 16 ;
printf (a = %10d \n, a) ;
printf (a= %-10d \n, a) ;
• .y indique le nombre décimal à écrire après la virgule.
Exemple : double b = 9.87654321;
printf (b = %.12 lf , b) ;
Il est possible de définir dynamiquement c-à-d pendant le déroulement du programme les
paramètres x et y, dans la suite de code, on les remplace par des symboles *.
Exemple : int p = 20, q = 14 ;
printf (b = %*.* lf , p, q ,b);
p et q remplace dans l’ordre les 2 étoiles. La fonction printf après l’affichage renvoie une valeur.
Cette valeur correspond au nombre de caractères affichés.
Ex emple : n = printf (chaîne \n) ; printf (n = %d \n, n) ;
6.3. Lecture avec scanf
La fonction scanf permet la saisie au clavier d’une valeur qui sera affectée à une variable.
6.3.1. Saisie de valeur numérique
#include <stdio.h>
int main ()
{ int entier0 ; double réel0 ;
printf (Entrer au clavier un entier :) ; scanf (%d, &entier0) ;
printf (Saisir le réel double) ; scanf (%lf , &réel0) ;
printf (Entier0 = %d \t Réel = %lf \n), entier0, réel0 ; return 0; } 34
Remarque
- On donne à la fonction scanf l’adresse de la variable.
- Il est possible d’enregistrer plusieurs variables dans scanf.
règle 1 : les codes formats correspondant à un nombre entraîne l’avancement éventuel du
pointeur jusqu’au premier caractère différent d’un séparateur puis scanf prend en compte les
caractères suivants jusqu’à la rencontre d’un séparateur.
Exemple : int n, p ;
scanf (%d %d, &n, &p) ;
scanf renvoie la valeur qui est le nombre de variables enregistrées avec succès.
6.3.2. saisie de caractères et de chaîne de caractères
#include <stdio.h>
int main ()
{ int n ; char car, chaîne[16] ;
printf (Saisir un caractère:) ; scanf (%c, &car) ;
printf (Ce caractère est : %c\n″, car) ;
printf (″\n\n Entrer une chaîne de caracteres moins de 16 caractères :″) ;
n= scanf (%s, chaîne) ;
printf (″\n n = %d \t\t\t cette chaîne est %s ″, n, chaîne) ;
return 0;
}
La saisie d’un seul caractère est l’équivalent de getchar( ).
Règle 2 : le code format c entraîne la prise en compte du caractère désigné par le pointeur (même
s’il s’agit d’un séparateur comme espace ou fin d’une ligne) et le pointeur avance sur le caractère
suivant. 35
Exemple : char c, int n ;
scanf( %c%d,&c,&n) ;
• Une chaîne de caractères est une séquence de caractères placée dans une suite de zone contiguë
de mémoire qu’on appelle tableau. Elle doit être terminée par \0 qui est un caractère nul
indiquant la fin du tableau.
Exemple : char chaîne[7] = Petrus ;
chaîne[0] = ‘P’
chaîne[1] = ‘e’
chaîne[2] = ‘t’
……………..
chaîne[6] = \0
Le nom du tableau est une adresse.
Exemple d’exécution du programme précédent.
Saisir un caractere : Q <ENTREE>
Ce caractere est Q
Entrer une chaîne de caractère moins de 16 caractères : Petrus Albulus <ENTREE>
n = 1 cette chaîne est Petrus
La chaîne Albulus n’a pas été prise en compte par scanf. En effet, scanf prend en compte les
caractères de la chaîne jusqu’à la rencontre d’un espace ou d’une tabulation ou d’un retour à la
ligne. Albulus est resté dans le tampon.
• Tampon : le mécanisme de scanf() est l’existence d’un tampon, une zone de mémoire
intermédiaire entre le clavier et l’unité centrale. A l’appel de la fonction scanf(), le tampon
intermédiaire est interrogé. Si le tampon est vide, le clavier est sollicité pour fournir des
caractères, sinon, c’est son contenu qui est proposé. 36
6.3.3. Quelques règles de sécurité
*Ne saisir qu’une variable dans scanf(), mais il est parfois nécessaire de transgresser la règle.
*Vider le tampon après un appel de scanf().
*Contrôler la présence du & pour les variables dont le nom n’est pas déjà par lui-même
équivalent à une adresse.
*Vérifier la compatibilité entre le code format et les variables.
37
Exécution du programme
saisir chaine : PETRUS ALBulus
cette chaine est : PETRUS ALB
cette chaine est : ulus
L’expression ^\n placé entre crochets signifie que tous les caractères ASCII sont autorisés sauf le
retour à la ligne.
scanf (" %[^\n] ", chaîne1) ;
while (getchar() != ‘\n’);
Dans la bibliothèque <stdio.h>, il existe une fonction d’utilisation générale fflush qui permet de
vider le tampon associé aux flux de données stdin (flux d’Entrée in et standard std).
scanf ( ) ;
fflush ( stdin) ;
int main()
{ int i, n ; int factorielle ;
do
{ printf(″Composez un nombre dont la factorielle est à calculer : n =″) ; n=litInt() ;
}
while ((n<0) || (n>12)) ;
factorielle = 1 ;
for (i=2 ; i<=n ; i++)factorielle*= i ;
printf(“\n\t\t !%d =%d \n » , n, factorielle ;
return 0;
}
int litInt(void)
{ int entier;
while (scanf ("%d", &entier) != 1)while (getchar() != ‘\n’);
while(getchar() !=’\n’);
return entier;
} 45
7.4. Branchements inconditionnels
7.4.1. BREAK
Elle permet de sortir aussitôt du bloc de la boucle où elle se trouve. Dans des boucles
imbriquées, elle ne s’applique qu’à la boucle qui la contient.
Exemple : …
for (i=0 ; i<j ; i++)
{…
while ((car = getchar()) != ‘\n’)
{…if (car = = ‘+’) break; …
}
…
}
7.4.2. CONTINUE
Elle permet de rester dans la boucle mais après avoir sauté les lignes de code placées
entre continue et la fin du bloc.
Le traitement reprend au début de l’itération suivante dans la même boucle.
46
Exemple : calcul de valeurs entre 16 et 26 parmi 100 nombres générés aléatoirement.
#include <stdio.h>
#include <stdlib.h> //rand()
int main (void)
{ int i, j= 100, x=16, y= 26, nbre_aleatoire, somme= 0 ;
printf (Calcul du nombre de valeurs générées aléatoirement :) ;
for (i=0 ; i<j ; i++)
{ nbre_aléatoire = rand () %100 ;
if ((nbre_aléatoire<x || (nbre_aléatoire>y)) continue ;
printf (\n %d , nbre_aléatoire) ; somme++ ;
}
printf (« \n Résultat = %d », somme) ;
return 0;
}
7.4.3. GOTO
L’instruction break ne permet pas de sortir en une seule fois de plusieurs boucles imbriquées. On
utilise alors pour cela l’instruction goto.
goto erreur ;
où erreur est une étiquette qui référence une instruction. La définition d’une étiquette se fait de
la manière suivante.
erreur : instruction ;
47
L’instruction continue permet parfois d’éviter l’ emploi d’un goto.
Exercices 1 et 2 utilisent l’instruction switch.
Exercice 3. Ecrire un programme en langage C qui saisit une suite de caractères, compte et
affiche le nombre de lettres ‘e’ et d’espaces. Utiliser les propriétés des tampons (getchar() dans
une boucle while).
Exercice 4. Ecrire un programme en langage C qui permet de saisir en Ariary, la suite des achats
d’un client (terminée par la saisie de 0). Le programme calcule la somme que le client doit et
affiche la somme due. Puis le programme saisit la somme que le client paye, et simule la remise
de la monnaie en affichant des textes « 10000 Ariary », «5000 Ariary» et « 1000 Ariary » autant
de fois qu’il y a de coupures de chaque sorte à rendre (5 points).
48
8. FONCTION
Une fonction permet d’éviter la répétition d’une séquence de codes et de rendre plus claire la
structure d’un programme.
C’est la possibilité de fractionner les grosses choses complexes à des petits trucs simples.
Syntaxe : typeRésultat nomfct(argument)
{ déclaration des variables locales
corps de la fonction
}
car2 = minMaj(car2) ;
On dit que le C passe la valeur car2 à la fonction minMAJ().
- Passage par pointeur : le passage par valeur ne permet de modifier (manipuler) qu’une seule
variable extérieure à la fonction en utilisant l’instruction return. Si on veut manipuler par une
même fonction plusieurs variables extérieures à la fonction, on utilise le passage par pointeur.
50
#include <stdio.h>
#include<stdlib.h>
int main(void)
{ char car1, car2;
litchar (&car1) ; minMaj(&car1) ; ecritchar (&car1);
litchar (&car2) ; minMaj(&car2) ; ecritchar (&car2);
return 0;
}
void litchar (char *c0)
{ while ((*c0 = getchar()) = = ‘\n’);
while (getchar () != ‘\n’);
}
void minMaj(char *c1)
{ if (( *c1> =’a’) && (*c1<=’z’))
*c1 = ‘A’+ *c1-‘a’
}
void ecritchar (char *c2)
{ printf ("En majuscule la caractère saisi est : %c", *c2);
printf ("\n\n\t"); 51
}
void litchar(char*);
Tout se passe comme si la fonction créait une valeur c0 pour recevoir l’adresse de la
variable car1 (&car1) du programme principal celle qu’il faudra manipuler.
si b = &a alors *b = a;
si c0 = &car1 alors *c0 = car1;
52
8.2. Classification des variables
La portée de variable c’est l’étendue du programme dans laquelle une variable est reconnue et
peut être utilisée.
En fonction de leur portée les variables sont classées en :
variable de classe mémoire automatique qu’on appelle variable automatique (variable locale).
variable de classe mémoire externe qu’on appelle variable externe (variable globale).
8.3.3. Static
Quand on veut qu’une variable locale puisse être reconnue dans tout un fichier, on lui donne le
statut static. Dans ce cas, elle est stockée dans la mémoire statique comme les variables globales.
Exemple : calcul des termes de la série de Léonardo Fibonacci.
Le terme de rang n est égal à la somme de 2 termes précédents : s(n) = s(n-1)+ s(n-2)
#include<stdio.h>
#include<stdlib.h>
int litInt (void) ;
int calcterme(int) ;
56
int main ()
{ int i, n;
printf(nombre de termes à calculer :); n = litInt (); printf(\n );
for(i = 1; i<=n; i++)printf (“\n s(%d) = %d”, i, calcterme(i)); return 1;
}
int calcterme (int indice)
{ static int s_moins1 = 1, s_moins2 = 1; int terme;
terme = indice < 3 ? 1 : s_moins1 + s_moins2;
s_moins2 = s_moins1;
s_moins1 = terme; return terme;
}
int litInt (void)
{ int entier;
while(scanf(%d , &entier)!=1)while(getchar() != ‘\n’);
while (getchar () != ‘\n’);
return entier;
}
Les variables s_moins1 et s_moins2 sont des variables locales dans la fonction calcterme().
Normalement, elles seraient détruites à chaque clôture de la fonction et réinitialisées à 1 à
chaque appel.
Quand elles sont déclarées comme variables statiques, elles sont initialisés à 1 au premier
appel de la fonction, elles ne sont pas détruites à la clôture et gardent leurs valeurs à l’appel
suivant. Elles ne sont pas réinitialisées à 1 puisqu’elles se comportent désormais comme des
variables globales à l’intérieur du fichier. 57
8.4. Propriétés de récurrence des fonctions
Les fonctions qui possèdent la propriété de récurrence peuvent s’appeler elles même. Il faut
prévoir une condition d’arrêt pour ne pas déclencher des boucles infinies.
C’est parfois très élégant
A chaque appel d’une fonction, un jeu de variables locales se crée. Elles sont ajoutées sur la pile.
Lorsque la condition d’arrêt s’exécute, les jeux des variables sont retirés successivement de la
pile et utilisés par la fonction.
Ce processus est gourmand en termes de temps d’exécution et d’occupation de mémoire.
Exemple : calcul d’une série par récurrence.
#include <stdio.h>
#include<stdlib.h>
int litInt (void); int sommeSerie(int);
int main (void)
{ int n;
printf(***calcul de la somme de n premiers entiers : *** \n );
do
{ printf (\n saisir n : ); n = litInt();
}
while (n<=0);
printf (\n somme de terme : s(%d) = %d, n, sommeSerie (n)); return 0;
}
int sommeSerie(int p)
{ if (p<=0) return 0;
else return (p + sommeSerie (p-1)); 58
int litInt(void)
{ int entier;
while(scanf(%d , &entier) != 1)while(getchar() != ‘\n’);
while(getchar() != ‘\n’);
return entier;
}
Exercice 5. Ecrire un programme C qui calcule et affiche les racines de ax2+bx+c. Pour ce
faire, effectuez les opérations suivantes :
-Déclarations des variables,
-Appel de la fonction void saisie(int *, int *, int*) qui permet de saisir la valeur de a, b, et c.
-Appel de la fonction calcul(int, int, int) qui exécute les calculs et affiche les résultats.
Note : la fonction standard sqrt(valeur) dans <math.h> renvoie la racine carrée de valeur.
Exercice 6. Ecrire un programme C qui calcule une facture correspondant au nombre de
photocopies effectuées par un magasin de reproduction. Il effectue les opérations suivantes :
-Déclaration des variables,
-Utilisation de la fonction int liInt(void) qui retourne le nombre de photocopies effectuées.
-Appel de la fonction double facture(int) qui reçoit en paramètre le nombre de photocopies
retourné par la fonction litInt() et calcule le montant de la facture suivant l’algorithme ci-
après. Le magasin facture à 50 Ariary les 10 premières photocopies, à 40 Ariary les 20
suivantes et à 30 Ariary au-delà.
Exercice7. Ecrire un programme C qui effectue les opérations :
-Déclare et saisit une variable entière appelée nombre;
- Appelle une fonction qui reçoit en argument nombre (un nombre de départ) et affiche les dix
nombres suivants. 59
9. POINTEURS
L’étude des pointeurs montre l’adaptation du langage C à la conduite de processus. Un
pointeur est une adresse mémoire. On dit que le pointeur pointe sur cette adresse.
9.1. Déclaration
Une variable de type pointeur se déclare à l’aide de l’objet pointé précédé du symbole *
(opérateur d’indirection).
Exemple : char *pc; //pc est un pointeur pointant sur un objet de type char.
int *pi; //pi est un pointeur pointant sur un objet de type int.
double *pr; //pr est un pointeur pointant sur un objet de type double.
L’opérateur d’indirection * désigne le contenu d’une adresse.
Exemple : *pc = 34;
printf(“Contenu de la case mémoire : %c\n”, *pc);
printf(“Valeur de l’adresse en hexadecimal : %p\n”, pc);
L’opérateur adresse & retourne l’adresse d’une variable en mémoire.
Exemple : int i;
printf(“Valeur de i : %d\n”, i);
printf(“Adresse de i en hexadecimal : %\n”, &i);
pi 00
00
00
00
pi+1
pc 0a
pc+1
61
Exemples
int *pi; double *pr; char *pc;
*pi=421; //421 est le contenu de la case mémoire pi et des 3 suivantes.
*(pi+1)=53; // on range 53 dans 4 cases mémoires plus loin.
*(pi+2)=0xabcd; // on range 0xabcd dans 8 cases mémoires plus loin.
*pr=45.7; //45.7 est rangé dans la case mémoire pr et les 3 suivantes.
pr++; //incrémente la valeur du pointeur pr de 8 cases mémoire.
printf ("l’adresse r vaut : %p« , pr); //Affichage de la valeur de l’adresse r;
*pc=‘j’; //le contenu de la case mémoire pc est le code ASCII de ‘j’.
pc--; //décrémente la valeur du pointeur pc d’une case mémoire.
Exemple
char* pc; float *pr;
int* pi, *pj, *pk;
pc=(char*)malloc(10); //On reserve 10 cases mémoire, soit la place pour 10 caractères.
pi=(int*)malloc(16); //On reserve 16 cases mémoire, soit la place pour 4 entiers.
pr=(float*)malloc(24); //On reserve 24 cases mémoire, soit la place pour 6 réels.
pj=(int*)malloc(sizeof(int)); //On reserve la taille d’un entier en mémoire.
pk=(int*)malloc(3*sizeof(int)); //On reserve la place en mémoire pour 3 entiers.
63
On ne peut pas affecter directement une valeur à un pointeur. L’écriture suivante est interdite.
char *pc;
pc=0xbffff41;
On peut cependant être amené à définir par programmation la valeur d’une adresse. On
utilise pour cela l’opérateur de cast, jeu de deux parenthèses.
Par exemple : pour adresser un périphérique (adressage physique).
Ou pour contrôler la zone DATA dans un plan mémoire.
Exemple.
char *pc;
pc=(char*)0xbffff51; //pc est l’adresse0xbffff51 et pointe sur un caractère.
int *pi;
pi=(int*)0xaffffc15; //pi est l’adresse de 0xaffffc15 et pointe sur un entier.
Lorsqu’on utilise une fonction d’allocation dynamique, on ne peut affecter de valeur au
pointeur à l’aide de l’opérateur de cast.
Exercice 8. adr1 et ard2 sont des pointeurs sur des reels. Le contenu de adr1 vaut -45,78; le
contenu adr2 vaut 678,89. Ecrire un programme qui affiche les valeurs de adr1, adr2 et de leur
contenu.
Exercice 9. adr_i est un pointeur de type entier, son contenu i vaut 0x12345678. A l’aide d’une
conversion de type de pointeur, écrire un programme montrant le rangement des 4 octets en
mémoire.
Exercice 10. Saisir un texte. Ranger les caractères en mémoire. Lire le contenu de la mémoire
et y compter le nombre d’espaces et de lettres e. 64
10. DIRECTIVES AU PREPROCESSEUR
10.1 Directive #include
Elle permet d'incorporer dans le fichier source le texte figurant dans un autre fichier, qui peut
être un fichier d’en-tête de la librairie standard stdio.h, math.h,... ou n'importe quel autre
fichier.
#include<nom-de-fichier>
Recherche le fichier mentionné dans un ou plusieurs répertoires systèmes définis par
l'implémentation par exemple /usr/include/.
#include "nom-de-fichier"
Recherche le fichier dans le répertoire courant (celui où se trouve le fichier source).
La première syntaxe est généralement utilisée pour les fichiers en-tête de la librairie standard,
tandis que la seconde est plutôt destinée aux fichiers créés par l'utilisateur.
10.2 Directive #define
Elle permet de définir des constantes symboliques et des macros avec paramètres
65
10.2.1 Définition de constantes symboliques
La directive #define nom restedelaligne demande au préprocesseur de substituer toute
occurrence de nom par la chaîne de caractères restedelaligne dans la suite du fichier source.
Par exemple : donner un nom parlant à une constante.
#define NBLIGNES 10
#define NBCOLONNES 33
#define TAILLEMATRICE NBLIGNES * NBCOLONNES
10.2.2 Définition de macros
Une macro avec paramètres se définit de la manière suivante.
#define nom(listeDeParametres) corpsDeLamacro
où listeDeParamètres est une liste de variables séparées par des virgules.
Par exemple : #define MAX(a,b) (a > b ? a : b)
le processeur remplacera dans la suite du code toutes les occurrences du type MAX(x,y)
où x et y sont des symboles quelconques par (x > y ? x : y).
L’emploi d’une macro permet en général d'obtenir de meilleures performances en temps d‘
exécution, par rapport à celui d’une fonction.
le préprocesseur n'effectue que des remplacements de chaînes de caractères. Il est conseillé de
toujours mettre entre parenthèses le corps de la macro et les paramètres formels qui y sont
utilisés. Par exemple, si l'on écrit sans parenthèses : #define CARRE(a) a*a
CARRE(a + b) sera remplacé par a + b * a + b et non par (a + b) * (a + b).
!CARRE(x) sera remplacé par ! x * x et non par !(x * x). 66
CARRE(x++) aura pour expansion (x++) * (x++). L'opérateur d'incrémentation sera donc
appliqué deux fois au lieu d'une. Cela entraîne des effets de bord.
10.3 Compilation conditionnelle
Elle permet d'incorporer ou d'exclure des parties du code source dans le texte qui sera généré
par le préprocesseur. Elle permet d'adapter le programme au matériel ou à l'environnement sur
lequel il s'exécute, ou d'introduire dans le programme des instructions de débogage.
Les directives de compilation conditionnelle se répartissent en deux catégories, suivant le type
de condition invoquée : la valeur d'une expression, l'existence ou non de symboles.
67
Le nombre de #elif est quelconque et le #else est facultatif. Chaque condition-i doit être une
expression constante. Une seule partieDuProgramme sera compilée : celle qui correspond à
la première condition-i non nulle, ou bien la partieDuProgramme∞ si toutes les conditions
sont nulles.
Exemple
#define PROCESSEUR ALPHA
#if PROCESSEUR == ALPHA
taille_long = 64;
#elif PROCESSEUR == PC
taille_long = 32;
#endif
10.3.2 Condition liée à l'existence d'un symbole
Sa syntaxe est
#ifdef symbole
partieDu-Programme1
#else condition2
partieDuProgramme2
#endif
Si symbole est défini au moment où l'on rencontre la directive #ifdef, alors partieDu-
Programme1 sera compilée. Dans le cas contraire, c'est partieDuProgramme2 qui sera
compilée.
68
Pour tester la non-existence d'un symbole, il suffit de remplacer seulement :
#ifdef symbole par #ifndef symbole, le reste est inchangé.
Ce type de directive est utile pour rajouter des instructions destinées au débogage
du programme :
#define DEBUG
....
#ifdef DEBUG
for (i = 0; i < N; i++)
printf("%d\n",i);
#endif /* DEBUG */
Il suffit alors de supprimer la directive #define DEBUG pour que les instructions
liées au débogage ne soient pas compilées.
69
Chapitre 11. TABLEAUX ET CHAINES DE CARACTERES
Les tableaux correspondent aux matrices en mathématiques. Les chaînes de caractères sont
déclarées en C comme tableaux de caractères.
11.1. Tableaux
Un tableau est caractérisé par sa taille et par ses éléments.
11.1.1.Tableaux à une seule dimension (vecteur)
Un tableau (uni-dimensionnel) tab est une variable structurée formée d'un nombre entier N de
variables simples du même type, qui sont appelées les composantes du tableau. Le nombre de
composantes N est alors la dimension (taille) du tableau.
Déclaration
type nom[dim];
int compteur[10]; float reelSimple[15]; double nombre[20];
int mois[12]={1, 2, 3, 4, 5, 6 7, 8, 9, 10, 11, 12};
Utilisation
Un élément du tableau est répéré par son indice. Le tableau commence par l’indice 0. L’indice
maximal est dim-1. L’appel d’un élément se fait par nom[indice].
Exemple
mois[0] permet d’accéder à 1, mois[1] à 2, …mois[11] à 12.
compteur[5]=2500; nombre[10]=15;…
for(i=0; i<10; i++)
{ scanf(“%d”, &nombre[i]); printf(“%d ”, nombre[i]);
scanf(“%lf”, &compteur[i]); printf(“%.2lf ”, nombre[i]);
} 70
Exercice 11. Ecrire un programme qui lit la dimension N d'un tableau Tab du type int
(dimension maximale: 50 composantes), remplit le tableau par des valeurs entrées au clavier et
affiche le tableau.
Exercice 12. Ecrivez un programme C qui saisit 10 reels, les ranger dans un tableau. Calculez
et affichez la moyenne et l’écart-type.
Exercice 13. Ecrire un programme qui lit la dimension N d'un tableau Tab du type int
(dimension maximale: 50 composantes), remplit le tableau par des valeurs entrées au clavier et
affiche le tableau. Effacer ensuite toutes les occurrences de la valeur 0 dans le tableau Tab et
tasser les éléments restants. Afficher le tableau résultant.
11.1.2. Tableaux à plusieurs dimensions (matrice)
En C, un tableau à deux dimensions Tab est à interpréter comme un tableau (uni-
dimensionnel) de dimension L dont chaque composante est un tableau (uni-dimensionnel) de
dimension C.
L est le nombre de lignes du tableau et C le nombre de colonnes du tableau. L et C sont alors
les deux dimensions du tableau. Un tableau à deux dimensions contient L*C composantes.
Déclaration d’un tableau à deux dimensions
type nom[ligne][colonne];
Exemple. Considérons un tableau notes à une dimension pour mémoriser les notes de 20
élèves d'une classe dans un devoir:
double notes[20];
for(i=0;i<20; i++){
printf(“Saisir une note : ”); scanf(“%lf”, ¬es[i]);
} 71
Pour mémoriser les notes des 20 élèves dans les 10 devoirs d'un trimestre.
double notes[10][20];
for(i=0; i<10; i++)
for(j=0; j<20; j++)
{ printf(“Saisir une note : ”); scanf(“%lf”, ¬es[i][j]);
}
Dans une ligne nous retrouvons les notes de tous les élèves dans un devoir. Dans une colonne,
nous retrouvons toutes les notes d'un élève.
Exercice 14. Ecrivez un programme C qui met à 0, les éléments de la diagonale centrale d’une
matrice carrée donnée.
Exercice 15. Ecrire un programme qui réalise la multiplication d'une matrice mat par un réel X.
Exercice 16. Ecrire un programme C qui réalise l'addition de deux matrices matA et matB de
mêmes dimensions ligne et colonne.
11.1.3. Tableaux et pointeurs
En declarant un tableau, on définit automatiquement un pointeur (on définit en fait l’adresse du
premier élément du tableau).
Tableau à une dimension : les écritures suivantes sont équivalentes.
int* tableau; int tableau[10]; declaration
tableau=(int*)malloc(sizeof(int)*10);
*tableau tableau[0] Le 1er élément
*(tableau+i) tableau[i] un autre élément
tableau &tableau[0] adresse du 1er élément
(tableau+i) &(tableau[i]) adresse d’un autre élément 72
Un tableau à plusieurs dimensions est un pointeur de pointeur.
int[3][4]; est un pointeur de 3 tableaux de 4 éléments ou bien 3 lignes à 4 éléments.
Les écritures suivantes sont équivalentes.
t[0] &t[0][0] t adresse du 1er élément
t[1] &t[1][0] adresse du 1er élément de la 2ème ligne
t[i] &t[i][0] adresse du 1er élément de la ième ligne
t[i]+1 &(t[i][0])+1 adresse du 1er élément de la ième ligne + 1 ligne
11.2. Chaines de caractères
En langage C, les chaines de caractères sont des tableaux de caractères. Leur manipulation
est équivalente à un tableau à une dimension.
11.2.1. Création et initialisation de chaines de caractères
Déclaration
char nom[dim]; ou bienchar* nom;
nom=(char*)malloc(dim);
Le compilateur reserve (dim-1) places en mémoire pour la chaine de caractères. En effet, il
ajoute toujours le caractère NUL (‘\0’) à la fin de la chaine.
Exemple
char texte[10]=“Saisie un texte ”; ou bien char* texte;
texte=(char*)malloc(10);
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.
texte [0] permet d’accéder à ‘S’, texte[1] à ‘a’, texte[2] à ‘i’…
73
Conversions et tests
En tenant compte de l'ordre alphabétique des caractères, on peut contrôler le type du
caractère (chiffre, majuscule, minuscule).
Exemples
if (C>='0' && C<='9') printf("Chiffre\n", C);
if (C>='A' && C<='Z') printf("Majuscule\n", C);
if (C>='a' && C<='z') printf("Minuscule\n", C);
Il est facile, de convertir des lettres majuscules dans des minuscules:
if (C>='A' && C<='Z') C = C-'A'+'a';
ou vice-versa:
if (C>='a' && C<='z') C = C-'a'+'A';
11.2.2. Fonctions de manipulation de chaines de caractères
Les bibliothèques de fonctions de C contiennent une série de fonctions spéciales pour le
traitement de chaînes de caractères.
A) Bibliothèque <stdio>
Elle 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.
Affichage de chaînes de caractères
printf avec le spécificateur de format %s permet d'intégrer une chaîne de caractères dans
une phrase.
char NOM[] = "hello, world";
printf(":%s:", NOM); 74
puts écrit une chaîne constante ou le contenu d'une variable dans une ligne isolée.
Syntaxe: puts(chaîne)
puts écrit la chaîne de caractères désignée par chaîne sur stdout et provoque un retour à la
ligne. En pratique, puts(TXT); est équivalent à printf("%s\n", TXT);
Exemples
char TEXTE[] = "Voici une première ligne.";
puts(TEXTE);
puts("Voici une deuxième ligne.");
Lecture de chaînes de caractères
scanf avec le spécificateur %s permet de lire un mot isolé à l'intérieur d'une suite de données
du même ou d'un autre type.
scanf avec le spécificateur %s lit un mot du fichier d'entrée standard stdin et le mémorise à
l'adresse qui est associée à %s.
Exemple
char LIEU[25];
int JOUR, MOIS, ANNEE;
printf("Entrez lieu et date de naissance : \n");
scanf("%s %d %d %d", LIEU, &JOUR, &MOIS, &ANNEE);
gets est idéale pour lire une ou plusieurs lignes de texte (des phrases) terminées par un retour à
la ligne.
Syntaxe: gets(chaîne)
gets lit une ligne de caractères de stdin et la copie à l'adresse indiquée par chaîne. Le retour à
la ligne finale est remplacé par le symbole de fin de chaîne '\0'. 75
Exemple
int MAXI = 1000;
char LIGNE[MAXI];
gets(LIGNE);
Exercice 17. Ecrire un programme qui lit 5 mots, séparés par des espaces et qui les affiche
ensuite dans une ligne, mais dans l'ordre inverse. Les mots sont mémorisés dans 5 variables
M1, ... ,M5. Exemple.
voici une petite phrase !
! phrase petite une voici
Exercice 18. Ecrire un programme qui lit une ligne de texte (ne dépassant pas 200 caractères)
la mémorise dans une variable TXT et affiche ensuite:
a) la longueur L de la chaîne.
b) le nombre de 'e' contenus dans le texte.
c) toute la phrase à rebours, sans changer le contenu de la variable TXT.
d) toute la phrase à rebours, après avoir inversé l'ordre des caractères dans TXT: Exemple.
voici une petite phrase !
! esarhp etitep enu iciov
Exercice 19. Ecrire un programme qui lit un texte TXT (de moins de 200 caractères) et qui
enlève toutes les apparitions du charactère 'e' en tassant les éléments restants. Les
modifications se feront dans la même variable TXT. Exemple:
Cette ligne contient quelques lettres e.
Ctt lign contint qulqus lttrs .
76
B) Bibliothèque <string>
Elle fournit une multitude de fonctions pratiques pour le traitement de chaînes de caractères.
Nous allons présenter une brève description des fonctions les plus fréquemment utilisées.
Dans la liste suivante, n représente un nombre du type int. Les symboles s et t peuvent être
remplacés par :
* une chaîne de caractères constante
* le nom d'une variable déclarée comme tableau de char
* un pointeur sur char.
Fonctions pour le traitement de chaînes de caractères sont :
strlen(s) fournit la longueur de la chaîne sans compter le '\0' final
strcpy(s, t) copie <t> vers <s>
strcat(s, t) ajoute <t> à la fin de <s>
strcmp(s, t) compare <s> et <t> lexicographiquement et fournit un résultat:
négatif si <s> précède <t>
zéro si <s> est égal à <t>
positif si <s> suit <t>
strncpy(<s>, <t>, <n>) copie au plus <n> caractères de <t> vers <s>
strncat(<s>, <t>, <n>) ajoute au plus <n> caractères de <t> à la fin de <s>
80
Les fichiers séquentiels considérés dans ce cours auront les propriétés suivantes:
- les fichiers se trouvent en état d'écriture ou bien en état de lecture; nous ne pouvons pas
simultanément lire et écrire dans le même fichier.
- A un moment donné, on peut uniquement accéder à un seul enregistrement; celui qui se
trouve en face de la tête de lecture/écriture.
- Après chaque accès, la tête de lecture/écriture est déplacée derrière la donnée lue en dernier
lieu.
Fichiers standards
Il existe deux fichiers spéciaux qui sont définis par défaut pour tous les programmes :
- stdin le fichier d'entrée standard
- stdout le fichier de sortie standard
En général, stdin est lié au clavier et stdout est lié à l'écran, c’est à dire. les programmes
lisent leurs données au clavier et écrivent les résultats sur l'écran.
Remarque avancée
En UNIX et en MS-DOS, il est possible de dévier l'entrée et la sortie standard vers d'autres
fichiers ou périphériques à l'aide des symboles < (pour stdin ) et > (pour stdout).
82
12.2.1. type FILE*
Pour pouvoir travailler avec un fichier, un programme a besoin d'un certain nombre
d'informations au sujet du fichier :
- adresse de la mémoire tampon,
- position actuelle de la tête de lecture/écriture,
- type d'accès au fichier : écriture, lecture, ...
- état d'erreur,
-...
Ces informations sont rassemblées dans une structure du type spécial FILE. Lorsque nous
ouvrons un fichier avec la fonction fopen, le système génère automatiquement un bloc du
type FILE et nous fournit son adresse.
Tout ce que nous avons à faire dans notre programme est:
- déclarer un pointeur du type FILE* pour chaque fichier dont nous avons besoin,
- affecter l'adresse retournée par fopen à ce pointeur,
- employer le pointeur à la place du nom du fichier dans toutes les instructions de lecture ou
d'écriture,
- libérer le pointeur à la fin du traitement à l'aide de fclose.
12.2.2. Exemple
Créer et afficher un fichier séquentiel
Problème. On se propose de créer un fichier qui est formé d'enregistrements contenant comme
information le nom d'une personne. Chaque enregistrement est donc constitué d'une seule
rubrique, à savoir, le nom de la personne.
83
L'utilisateur doit entrer au clavier le nom du fichier, le nombre de personnes et les noms des
personnes. Le programme se chargera de créer le fichier correspondant sur disque dur ou sur
disquette.
Après avoir écrit et fermé le fichier, le programme va rouvrir le même fichier en lecture et
afficher son contenu, sans utiliser le nombre d'enregistrements introduit dans la première
partie.
#include<stdio.h>
#include<stdlib.h>
int main()
{ FILE *P_FICHIER; /* pointeur sur FILE */
char NOM_FICHIER[30], NOM_PERS[30]; int i,NB_ENREG;
/* Première partie : créer et remplir le fichier */
printf("Entrez le nom du fichier à créer : "); scanf("%s", NOM_FICHIER);
P_FICHIER = fopen(NOM_FICHIER, "w"); /* write */
printf("Nombre d'enregistrements à créer : "); scanf("%d", &NB_ENREG);
i = 0;
while (i<NB_ENREG)
{ printf("Entrez le nom de la personne : "); scanf("%s", NOM_PERS);
fprintf(P_FICHIER, "%s\n", NOM_PERS);
i++;
}
fclose(P_FICHIER);
84
/* Deuxième partie : Lire et afficher le contenu du fichier */
P_FICHIER = fopen(NOM_FICHIER, "r"); /*read*/
i = 0;
while (!feof(P_FICHIER))
{
fscanf(P_FICHIER, "%s\n", NOM_PERS);
printf("NOM : %s\n", NOM_PERS);
i++;
}
fclose(P_FICHIER);
return 0;
}
Pour que la fonction feof détecte correctement la fin du fichier, il faut qu'après la lecture de la
dernière donnée du fichier, la tête de lecture arrive jusqu'à la position de la marque EOF. Nous
obtenons cet effet seulement si nous terminons aussi la chaîne de format de fscanf par un retour
à la ligne '\n' (ou par un autre signe d'espacement).
Exemple
Une boucle de lecture typique pour lire les enregistrements d'un fichier séquentiel référencé par
un pointeur FP peut avoir la forme suivante:
while (!feof(FP))
{
fscanf(FP, "%s\n ... \n", NOM, ... );
...
}
92
Exemple : le programme suivant lit et affiche le fichier "C:\AUTOEXEC.BAT" en le
parcourant caractère par caractère:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *FP;
FP = fopen("C:\\AUTOEXEC.BAT", "r");
if (!FP)
{
printf("Impossible d'ouvrir le fichier\n");
exit(-1);
}
while (!feof(FP))
putchar(fgetc(FP));
fclose(FP);
return 0;
}
Dans une chaîne de caractères constante, il faut indiquer le symbole '\' (back-slash) par '\\', pour
qu'il ne soit pas confondu avec le début d'une séquence d'échappement (par ex: \n, \t, \a, ...).
93
12.5. Résumé sur les fichiers
Langage algorithmique C
Ouverture en écriture ouvrir <Nom> en écriture FP = fopen(Nom,"w");
Ouverture en lecture ouvrir <Nom> en lecture FP = fopen(Nom,"r");
Fermeture fermer <Nom> fclose(FP);
Fonction fin de fichier finfichier(<Nom>) feof(FP)
Ecriture écrire <Nom>:<Exp> fprintf(FP,"...", Expr);
fputc(C, FP);
Lecture lire <Nom>:<Var> fscanf(FP,"...", Adr);
C = fgetc(FP);
12.6. Mise à jour d'un fichier séquentiel
Les traitements de mise à jour sont : ajout d'un enregistrement à un fichier, suppression et
modification d'un enregistrement dans un fichier.
Comme il est impossible de lire et d'écrire en même temps dans un fichier séquentiel, les
modifications doivent se faire à l'aide d'un fichier supplémentaire. Nous travaillons donc
typiquement avec au moins deux fichiers: l'ancien fichier ouvert en lecture et le nouveau
fichier ouvert en écriture.
12.6.1. Ajouter un enregistrement à un fichier
Nous pouvons ajouter le nouvel enregistrement à différentes positions dans le fichier:
- Ajoute à la fin du fichier
L'ancien fichier est entièrement copié dans le nouveau fichier, suivi du nouvel enregistrement.
- Ajoute au début du fichier
L'ancien fichier est copié derrière le nouvel enregistrement qui est écrit en premier lieu. 94
- Insertion dans un fichier trié relativement à une rubrique commune des enregistrements.
Le nouveau fichier est créé en trois étapes:
- copier les enregistrements de l'ancien fichier qui précèdent le nouvel enregistrement,
- écrire le nouvel enregistrement,
- copier le reste des enregistrements de l'ancien fichier.
Le programme suivant effectue l'insertion d'un enregistrement à introduire au clavier dans un
fichier trié selon la seule rubrique de ses enregistrements: le nom d'une personne. La
comparaison lexicographique des noms des personnes se fait à l'aide de la fonction strcmp.
#include <stdio.h>
#include <string.h>
int main() { /* Déclarations : Noms des fichiers et pointeurs de référence */
char ANCIEN[30], NOUVEAU[30];
FILE *INFILE, *OUTFILE;
/* Autres variables */
char NOM_PERS[30], NOM_AJOUT[30]; int TROUVE;
/* Ouverture de l'ancien fichier en lecture */
do
{ printf("Nom de l'ancien fichier : ");
scanf("%s", ANCIEN);
INFILE = fopen(ANCIEN, "r");
if (!INFILE)printf("\aERREUR: Impossible d'ouvrir le fichier: %s.\n", ANCIEN);
}
while (!INFILE); 95
/* Ouverture du nouveau fichier en écriture */
do
{ printf("Nom du nouveau fichier : "); scanf("%s", NOUVEAU);
OUTFILE = fopen(NOUVEAU, "w");
if(!OUTFILE)printf("\aERREUR: Impossible d'ouvrir le fichier: %s.\n",
NOUVEAU);
}
while (!OUTFILE);
/* Saisie de l'enregistrement à insérer */
printf("Enregistrement à insérer : "); scanf("%s", NOM_AJOUT);
/* Traitement */
TROUVE = 0;
/* Copie des enregistrements dont le nom précède lexicographiquement celui à insérer.*/
while (!feof(INFILE) && !TROUVE)
{ fscanf(INFILE, "%s\n", NOM_PERS);
if (strcmp(NOM_PERS, NOM_AJOUT) > 0)TROUVE = 1;
else fprintf(OUTFILE, "%s\n", NOM_PERS);
}
/* Ecriture du nouvel enregistrement, */
fprintf(OUTFILE, "%s\n", NOM_AJOUT);
/* suivi du dernier enregistrement lu. */
if (TROUVE) fprintf(OUTFILE, "%s\n", NOM_PERS);
96
/* Copie du reste des enregistrements */
while (!feof(INFILE)){ fscanf(INFILE, "%s\n", NOM_PERS);
fprintf(OUTFILE, "%s\n", NOM_PERS);
}
/* Fermeture des fichiers */
fclose(OUTFILE); fclose(INFILE);
return 0;
}
12.6.2. Supprimer un enregistrement dans un fichier
Le nouveau fichier est créé en copiant tous les enregistrements de l'ancien fichier qui
précèdent l'enregistrement à supprimer et tous ceux qui le suivent.
#include <stdio.h>
#include <string.h>
int main() { /* Déclarations : Noms des fichiers et pointeurs de référence */
char ANCIEN[30], NOUVEAU[30]; FILE *INFILE, *OUTFILE;
/* Autres variables */
char NOM_PERS[30], NOM_SUPPR[30];
/* Ouverture de l'ancien fichier en lecture */
do { printf("Nom de l'ancien fichier : "); scanf("%s", ANCIEN);
INFILE = fopen(ANCIEN, "r");
if (!INFILE)printf("\aERREUR: Impossible d'ouvrir le fichier: %s.\n", ANCIEN);
}
while (!INFILE); 97
/* Ouverture du nouveau fichier en écriture */
do
{ printf("Nom du nouveau fichier : "); scanf("%s", NOUVEAU);
OUTFILE = fopen(NOUVEAU, "w");
if (!OUTFILE)
printf("\aERREUR: Impossible d'ouvrir le fichier: %s.\n", NOUVEAU);
}
while (!OUTFILE);
/* Saisie de l'enregistrement à supprimer */
printf("Enregistrement à supprimer : "); scanf("%s",NOM_SUPPR);
/* Traitement */
/* Copie de tous les enregistrements à l'exception de celui à supprimer. */
while (!feof(INFILE))
{ fscanf(INFILE, "%s\n", NOM_PERS);
if (strcmp(NOM_PERS, NOM_SUPPR) != 0)
fprintf(OUTFILE, "%s\n", NOM_PERS);
}
/* Fermeture des fichiers */
fclose(OUTFILE); fclose(INFILE);
return 0;
}
98
12.6.3. Modifier un enregistrement dans un fichier
Le nouveau fichier est créé de tous les enregistrements de l'ancien fichier qui précèdent
l'enregistrement à modifier, de l'enregistrement modifié et de tous les enregistrements qui
suivent l'enregistrement à modifier dans l'ancien fichier.
#include <stdio.h>
#include <string.h>
int main()
{ /* Déclarations : Noms des fichiers et pointeurs de référence */
char ANCIEN[30], NOUVEAU[30]; FILE *INFILE, *OUTFILE;
/* Autres variables */
char NOM_PERS[30], NOM_MODIF[30], NOM_NOUV[30];
/* Ouverture de l'ancien fichier en lecture */
do { printf("Nom de l'ancien fichier : "); scanf("%s", ANCIEN);
INFILE = fopen(ANCIEN, "r");
if (!INFILE)printf("\aERREUR: Impossible d'ouvrir le fichier: %s.\n", ANCIEN);
}
while (!INFILE);
/* Ouverture du nouveau fichier en écriture */
do{ printf("Nom du nouveau fichier : "); scanf("%s", NOUVEAU);
OUTFILE = fopen(NOUVEAU, "w");
if(!OUTFILE)printf("\aERREUR:Impossible d'ouvrir le fichier: %s.\n", NOUVEAU);
}
while (!OUTFILE); 99
/* Saisie de l'enregistrement à modifier et de sa nouvelle valeur. */
printf("Enregistrement à modifier : "); scanf("%s",NOM_MODIF);
printf("Enregistrement nouveau : "); scanf("%s",NOM_NOUV);
/* Copie de tous les enregistrements en */
/* remplaçant l'enregistrement à modifier par sa nouvelle valeur. */
while (!feof(INFILE)){ fscanf(INFILE, "%s\n", NOM_PERS);
if(strcmp(NOM_PERS, NOM_MODIF) == 0)
fprintf(OUTFILE, "%s\n", NOM_NOUV);
else fprintf(OUTFILE, "%s\n", NOM_PERS);
}
fclose(OUTFILE); fclose(INFILE);
return 0;
}
Exercice 24. Créer sur disque dur puis afficher à l'écran le fichier INFORM.TXT dont les
informations sont structurées de la manière suivante:
Numéro de matricule (entier)
Nom (chaîne de caractères)
Prénom (chaîne de caractères)
Le nombre d'enregistrements à créer est à entrer au clavier par l'utilisateur.
Exercice 25. Ecrire un programme qui crée sur disque dur un fichier INFBIS.TXT qui est la
copie exacte (enregistrement par enregistrement) du fichier INFORM.TXT.
Exercice 26. Ajouter un nouvel enregistrement (entré au clavier) à la fin de INFORM.TXT et
sauver le nouveau fichier sous le nom INFBIS.TXT. 100
Exercice 27. Insérer un nouvel enregistrement dans INFORM.TXT en supposant que le
fichier est trié relativement à la rubrique NOM et sauver le nouveau fichier sous le nom
INFBIS.TXT.
Exercice 28. Supprimer dans INFORM.TXT tous les enregistrements:
a) dont le numéro de matricule se termine par 8
b) dont le prénom est "Paul" (utiliser strcmp)
c) dont le nom est un palindrome. Définir une fonction d'aide PALI qui fournit le résultat 1
si la chaîne transmise comme paramètre est un palindrome, sinon la valeur zéro.
Sauver le nouveau fichier à chaque fois sous le nom INFBIS.TXT.
Exercice 29. Ecrire un programme qui crée sur un flash le fichier MOTS.TXT contenant une
série de 50 mots au maximum (longueur maximale d'un mot: 50 caractères). La saisie des
mots se terminera à l'introduction du symbole '*' qui ne sera pas écrit dans le fichier.
Exercice 30. Ecrire un programme qui affiche le nombre de mots, le nombre de palindromes
ainsi que la longueur moyenne des mots contenus dans le fichier MOTS.TXT. Utiliser les
deux fonctions d'aide PALI et LONG_CH.
Exercice 31. Ecrire un programme qui charge les mots du fichier MOTS.TXT dans la
mémoire centrale, les trie d'après la méthode par propagation (méthode des bulles) et les écrit
dans un deuxième fichier MOTS_TRI.TXT sur le flash. Les mots seront mémorisés à l'aide
d'un tableau de pointeurs sur char et la mémoire nécessaire sera réservée de façon
dynamique.
Exercice 32. A l'aide d'un éditeur de textes, créer un fichier NOMBRES.TXT qui contient
une liste de nombres entiers. Dans le fichier, chaque nombre doit être suivi par un retour à la
ligne. Ecrire un programme qui affiche les nombres du fichier, leur somme et leur moyenne.101