Vous êtes sur la page 1sur 101

Contenu du cours

Chapitre 1. STRUCTURE DE PROGRAMME EN LANGAGE C


1.1. Entête du programme
1.2. Programme principal ou fonction principale
1.3. Définitions des fonctions
Chapitre 2. PROGRAMME SIMPLE EN C
2.1. Analyse du programme
2.2. Mise en œuvre
Chapitre 3. DEBOGUEUR
3.1. Configuration du débogueur
3.2. Utilisation du débogueur
3.3. Connaissances des valeurs des variables
Chapitre 4. TYPE DE VARIABLES
4.1. Variable de type caractère unique
4.2. Types de variables entières
4.3. Types de variables réelles
Chapitre 5. OPERATEURS
5.1. Opérateur d’affectation
5.2. Opérateurs arithmétiques
5.3. Opérateurs logiques
5.4. Opérateurs de manipulation de bits
5.5. Opérateurs de décalage binaire vers la droite ou vers la gauche 1
Chapitre 6. INSTRUCTIONS D’ENTREES/SORTIES
6.1. Lecture et écriture de caractère isolé
6.2. Affichage avec printf()
6.3. Lecture avec scanf
6.4. Quelques compléments utiles
Chapitre 7. OUTILS DE CONTROLE
7.1. Le test d’exécution
7.2. Sélection avec Switch
7.3. Itérations
Chapitre 8. FONCTIONS
8.1 Règles d’utilisation
8.2 Classification des variables
8.3. Statut d’une variable ou d’une fonction
Chapitre 9. POINTEURS
9.1. Déclaration
9.2. Arithmétique des pointeurs
9.3. Allocation dynamique
9.4. Affectation d’une valeur à un pointeur
Chapitre 10. DIRECTIVES AU PREPROCESSEUR
10.1. Directives #include
10.2. Directives #define
2
Chapitre 11. TABLEAUX ET CHAINES DE CARACTERES
11.1. Tableaux
11.2. Chaines de caractères
Chapitre 12. FICHIERS SEQUENTIELS
12.1. Définitions et propriétés
12.2. Mémoire tampon
12.3. Ouverture et fermeture des fichiers séquentiels
12.4. Lecture et écriture dans des fichiers séquentiels
12.5. Résumé sur les fichiers
12.6. Mise à jour d'un fichier séquentiel

3
1. Structure d’un programme en langage C

Le langage C est un langage de programmation évolué. C'est une programmation structurée


(fonctions).

1.1. Entête du programme

1.1.1. Directives au compilateur


Le préprocesseur est un programme exécuté lors de la première phase de compilation.
Les directives sont introduites par le caractère # :
#include…: incorpore dans le fichier source le texte du fichier d’en-tête de la librairie
standard.

#define…..: définit des constantes symboliques,


des macros avec des paramètres,
des fichiers lors d’une compilation conditionnelle.

1.1.2. Prototype des fonctions


void fct1(void);
int fct2(int, double);

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

void fct1 (void)


{ printf(" la fonction ne retourne rien\n");
}

double fct2 (int x, double y)


{ double res;
res=y+x;
return res;
} //la fonction reçoit un entier et un réel puis retourne leur somme.
… 5
2. Programmes simples en C

2.1. Analyse d’un programme C

#include<stdio.h>
#include<stdlib.h>
void bonjour3(int);

int main()
{
int annee;

printf(“A quelle annee sommes-nous ? ”); scanf(“%d”, &annee);


bonjour3(annee);
return 0;
}
6
void bonjour3(int annee)
{
char nom[25];
int naissance, age;
printf("Comment vous appelez-vous ?"); scanf("%s", nom);
printf("Votre année de naissance ?");scanf("%d", &naissance);
printf("Bonjour %s ! ", nom);
age := annee-naissance;
printf("Vous etes âgé de %d ans" , age);
}

Un programme C est composé de :


- en-tête : #include<stdio.h>, #include<stdlib.h>
- prototype de fonctions : void bonjour3(int)
- programme principal : int main()
- définition de la fonction : bonjour3()
7
2.2. Mise en œuvre

Nous avons besoin de l’environnement de développement intégré (EDI


ou IDE en anglais pour integrated development environment)
CODE::BLOCKS.

Il permet de créer un programme source en C/C++, le compiler puis


l’exécuter et aussi de le déboguer.

Déboguer un programme c’est détecter les bogues dans un programme.

Télécharger le code blocks 17.12 avec le lien


codeblocks-17.12mingw-setup.exe
puis l’installer.

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.

On obtient une interface de saisie et de manipulation du programme


avec des menus, fenêtres de gestion, de saisie et des résultats du projet.
Dans la fenêtre gauche (de gestion) cliquer sur [+] Sources et double-
cliquer sur main.c, puis apparait dans la fenêtre milieu (de saisie) un
exemple de programme C.
9
#include<stdio.h>
#include<stdlib.h>

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.

Vérifions d’abord, dans la barre de menus du code::blocks, les menus :


-View : cocher les sous-menus (Manager, log, status bar)
-Debug->active debuggers puis cliquer sur Target’s default

B) Compilation du programme source : build/ build.


C) Exécution du programme : cliquer build/ Run

Dans la fenêtre résultat apparait le résultat du programme :


Hello Bienvenue ! 10
3. Débogueur en C

Tous les débogueurs fonctionnent sur le même principe. Quelque soit


l’environnement utilisé nous retrouverons les mêmes fonctionnalités.

Nous allons utiliser le débogueur /MinGW/bin/gdb32.exe, qui est


intégré dans Code::Blocks 17.12.

C’est un programme permettant de détecter des bogues dans un code C


ou C++.

Pour déboguer un projet, nous devons définir la cible, le programme


résultant, pour inclure les informations de débogage.

Exemple de code à déboguer.

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.

Dans la fenêtre du résultat, nous obtenons :


0.00-1.00
Process returned -103741676 (0xC0000094) execution 2.581s

Pour déboguer un programme, on doit le preparer.

3.1. Configuration du débogueur

Dans la barre des menus du code::blocks, configurons les menus suivants :

3.1.1. Build->selected target puis cliquer sur Debug


ou dans la barre des taches Release cliquer sur Debug.

Cette configuration permet de changer la cible de compilation en mode Debug.


13
3.1.2. Project->Build options…
Nous obtenons une fenêtre dans laquelle nous devons avoir comme compilateur
sélectionné GNU GCC Compiler.
Puis cocher sur
Produce debugging symbols [-g]
Enable all common compiler warnings (overrides many other settings)[-w
Puis cliquer sur OK.
Cette configuration vérifie que le compilateur ajoute les symboles nécessaires au
débogage.
La ligne “Produce debugging symbols [-g]” doit être cochée, mais la ligne
“Strip all symobls from binary (minimizes) [-s]” ne soit pas cochée.

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

Pour utiliser des fonctions du débogueur, accédons à la barre d’outils du débogueur.


Ce qui permet de voir ce qui est encrassé dans votre code.
Si elle n’est pas affichée, faites : View->Toolbars->Debugger.

3.2.1. Debug/Continue (F8) - Exécute le programme jusqu'au point d'arrêt suivant ou


jusqu'à la prochaine erreur.
3.2.2. Run to cursor (F4) - Exécute le programme jusqu'à la ligne du code où se
trouve le curseur.
3.2.3. Next line (F7) - Passe à la ligne suivante du code.
3.2.4. Next instruction (Alt-F7) - Passe à l'instruction suivante.
3.2.5. Step into (Ctrl-F7) - Entre dans la portée.
3.2.6. Step out (Ctrl-Alt-F7) - Sort de la portée.
3.2.7. Stop debugger (-) - Arrête le programme.
15
Pour trouver l’erreur nous exécutons le programme, en lançant : Debug/Continue
Le programme va planter et on obtient.

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

3.3.1. Placement d'un point d'arrêt


Définir un point jusqu‘où le programme devra s'exécuter. Par exemple, exécutons le
programme jusqu'au début de la fonction division().
Pour placer un point d'arrêt breakpoint. Il suffit de cliquer sur la bande grise à côté
du code à l'endroit où l'on souhaite le placer. Un rond rouge devrait apparaître.

Le menu Debug -> Debugging windows -> Breakpoints permet de connaître la


liste de tous les points d'arrêts de notre code source.
On peut alors exécuter à nouveau le programme en cliquant sur Debug/Continue
(F8). Le programme s'exécute alors jusqu'à la ligne où se situe le point rouge puis il
attend.
On aurait également pu placer le curseur à cette ligne et cliquer sur Run to cursor
(F4). 18
3.3.2. Avancement d'un pas
On peut ensuite avancer d'un pas en appuyant sur Next line (F7) ou Next instruction si nous
avons plusieurs instructions sur la même ligne.
3.3.3. Affichage de la valeur des variables
le menu Debug->Debugging windows->Watches permet de connaître la valeur des variables
présentes. La fenêtre watches affiche :

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. Variable de type caractère unique


Est une variable qui  ne contient qu’un seul caractère, par exemple ‘a’.

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 (\).

Fonction                    Séquence     ASCII


Sonnerie                         \a            7   
Return arrière                 \b             8
Tabulation horizontale   \t              9
Retour à la ligne            \n            10
Tabulation verticale       \v            11
Nouvelle page                 \f           12
Retour du chariot            \r           13
Guillemets anglais          \"           34
Apostrophe                     \’           39
Point d’interrogation      \?            63
Barre à gauche                \\            92
Caractère                         \0           0

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.2.1. Entiers courts représentés avec 16 bits (2 octets)


- le type : short,
- le domaine :{-32768 , 32767 },
- l’affichage avec printf utilise après le symbole %, le suffixe d.
Exemple : printf ("le résultat est %d\n", res) ;

4.2.2. Entiers avec 32 bits (4 octets)


Entiers [signés]
- le type : int
- le domaine :{-2147483648, 2147483647}
- l’affichage avec printf utilise après le symbole % le suffixe d.

        Entiers non signés


- le type : unsigned int
- le domaine :{0, 4294967295}
- L’affichage avec printf utilise après le symbole %, le suffixe u.
22
Type entier long
- le type : long int
- le domaine :{-2147483648, 2147483647}
- l’affichage avec printf utilise après le symbole %, le suffixe ld.

        Entiers longs non signés


- le type : unsigned long int
- le domaine :{0, 4294967295}
- l’affichage avec printf utilise après le symbole %, le suffixe lu.

4.2.3. Notations hexadécimales et Octales


Format octal : ajoutons un zéro devant la valeur, par exemple : 010 (8 en décimal).
L’affichage avec printf utilise après le symbole %, le suffixe o.

Format hexadécimal : il suffit d’écrire 0x ou 0X devant la valeur pour obtenir une


valeur en hexadécimal, par exemple : 0xF (15 en décimal).
L’affichage avec printf utilise après le symbole %, le suffixe x ou p. Le format en
hexadécimal est essentiellement utilisé sur les adresses.

NB :   Attention à ne pas faire précéder la valeur décimale par un 0.


23
4.3. Types de variables réelles
Les nombres réels sont des nombres positifs ou négatifs, entiers ou non, rationnels ou non.
Exemple : 241/657=0,366818 (rationnel).

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

4.3.2. Trois types de variables réelles


Réelle simple précision déclarée float.
- le type : float (occupe 4 octets de mémoire soit 32 bits)
- le domaine :{-2147483648 ,2147483647}
- l’affichage avec printf utilise après le symbole %, le suffixe f.

Réelle double précision déclarée double.


- le type : double (occupe 8 octets de mémoire soit 64 bits).
- l’affichage avec printf utilise après le symbole %, le suffixe lf.

Valeur à précision maximale déclaré long double


- le type : long double (occupe 10 octets de mémoire soit 80 bits).
-l’affichage avec printf utilise après le symbole %, le suffixe Lf. 24
4.3.3. Conversion des valeurs
Opérateur de cast. On peut obtenir la conversion d’une valeur d’un type dans un autre type en
demandant explicitement cette conversion au moyen de l’opérateur de cast notée ().
Exemple : float x ; int n=1 ;
x = (double)n/3 ;

Conversion forcée par une affection. Une affectation introduit une conversion d’office dans le
type de la lvalue réceptrice.

Conversions de type automatique.


A) Conversions d’ajustement de type : se font suivant une « hiérarchie » qui permet de ne pas
dénaturer la valeur initiale : int long floatdouble long double.
On peut bien sûr convertir directement d’un int vers float ou de int vers double. Par contre, on
ne peut pas convertir de float en int ou de double en int.
Exemple : int n ; long p ; float x ;
L’expression : n*p+x float
B) Conversions de promotions numériques : pour le type short ou char, le langage C prévoit que
toute valeur de l’un de ces deux types apparaissant dans une expression est d’abord converti en
int et cela sans considérer les types des éventuels autres opérandes, dans ce cas, on parle de
promotions numériques ou conversions systématiques.
Exemple : short p1, p2, p3 ; double x ;
L’ expression : p1*p2 + p3*x double
25
5. OPERATEURS
Le langage C est très riche en opérateurs. Il utilise ceux des langages évolués mais aussi ceux de
l’assembleur pour manipuler des bits.
5.1. Opérateur d’affectation
test = 5; signifie que la valeur 5 est affectée à la variable test. Le membre à droite doit être une
valeur ou une expression calculée. Le membre à gauche doit être impérativement un élément
modifiable comme une variable, on dit une lvalue.
x = y = z = 0 ; est correct.
L’associativité se fait de droite à gauche, mais double x =y = z = 3 ; est rejeté.
5.2. Opérateurs arithmétiques
5.2.1 Opérateurs unaires : ils n’agissent que sur un seul élément.
+ et - déterminent le signe d’une variable : -test.
Opérateurs de décrémentation et d’incrémentation. Le langage C offre des opérateurs qui
permettent d’accélérer ces opérations : i++ ou ++i : incrémente la variable i de 1.
j-- ou --j : décrémente j de1.
A l’intérieur d’un calcul, ces 2 termes ne sont pas équivalents.
int z, test ;
z = 10 ; // signifie : test = 10 ; z = 11 ;
test = z++ ; /* z++ : la valeur de cette expression est celle de z avant incrémentation. Ici on
affecte 10 à test, puis on incrémente z*/
z = 10 ; // signifie : test = 11 ; z = 11 ;
test = ++z ; /* ++z : la valeur de cette expression est celle de z après incrémentation. On
incrémente z et on affiche 11 à test.*/ 26
Il est possible d’écrire test = 5+ ++z ; // Le calcul est rapide, mais le code est difficile à lire.
Il est plus lisible d’écrire : ++z ou z++ ;
test = 5+z ;
Ces opérateurs unaires ont la plus forte priorité.
5.2.2. Opérateurs binaires. Ils portent sur 2 termes.
• Addition (+), soustraction (-), multiplication (*), division (/).
• Opérateurs parenthèses () fixent l’ordre des calculs.
• Opérateur modulo noté (%) donne le reste de la division entière d’un entier par un autre.
25 %5 = 0, 27 %5 = 2, 25%19 = 6.
• opérateurs raccourcis sont utilisés à cause de leur rapidité.
x op = y ;  x = x op y ;
Exemple : test = test + z ; s’écrit en raccourci  test += z ;
On a le même principe : x -= y ; x *= y ;
x /= y ; x = x / y ; x %= y ;x = x % y ;
Comme dans l’opérateur d’affectation, le membre à gauche doit être une lvalue, un élément
modifiable et le membre à droite doit être une expression calculée ou une valeur.
5.2.3. Opérateurs logiques
ils font des comparaisons ou des opérations logiques sur des variables
Si la réponse à la question posée est fausse l’opérateur renvoie 0.
Si la réponse à la question posée est vraie l’opérateur renvoie une valeur  0.
5.2.4. Opérateurs de comparaison : <, >, <=, >=, = =, !=.
x = 3 ; y = 0 ;
x < y renvoie 0, x > y renvoie 1, x >= y renvoie 1,
27
x<= y renvoie 0, x != y renvoie 1, x == y renvoie 0.
Opérateurs logiques : && intersection (et logique), || union (ou inclusif), ! négation (non logique
x y x && y x || y!x
0 0 0 0 1
1 0 0 1 0
0 1 0 1 1
1 1 1 1 0
Opérateurs de manipulation de bits : agissent directement sur le code binaire des mots machine.
On les qualifie de bas niveau. Ces opérations ne s’appliquent qu’à des variables de type entier
ou assimilé.
* Opérateur unaire d’inversion bit à bit : ~ (alt126), il inverse un à un tous les bits d’un mot. On
l’appelle aussi opérateur de complément à un.
Exemple : 32766 = 0111 1111 1111 1110
~32766 = 1000 0000 0000 0001, en décimal = -32767.
* Opérateur logique binaire bit à bit : il s’applique à des variables entières. & représente le ET
logique sur le bit, | représente le OU inclusif, ^ représente le OU exclusif sur les bits.
x y x&y x | y ^x
0 0 0 0 0
1 0 0 1 1
0 1 0 1 1
1 1 1 1 0
Exemple : l’opérateur logique bit à bit est utilisé pour le test de parité d’un entier, en examinant
seulement le dernier bit de sa représentation binaire.
28
#include<stdio.h>
int main() 
{ int n ;
printf (Saisir un entier : ) ; scanf (%d, &n) ;
if (n&1 == 1) printf (%d est impair \n, n) ;
else printf ( %d est pair \n , n) ; return 0;
}
5.2.5. Opérateur de décalage binaire vers la droite ou vers la gauche : il permet de faire des
opérations sur des entiers. Les bits sortant sont perdus et les bits entrant sont des 0.
Opérateur de décalage à gauche : x<<y. Exemple : 4345 << 2 ; la valeur binaire de 4345 est
décalée deux fois à gauche. C’est la multiplication par 2 autant de fois qu’il y a des décalages.
Opérateur de décalage à droite : x>>y. Exemple : 4345 >> 2. On divise par 2 à chaque
décalage. Le résultat est 1086.
Pour les opérateurs binaires, on dispose des opérateurs de raccourcis :
x |= y x = x | y
x <<= y x = x << y
5.2.6. Opérateurs spéciaux
Opérateur ternaire conditionnel : (?:)
C ? A : B
Il test l’expression C.
Si C est « vrai » : on évalue A et renvoie sa valeur.
Si C est « faux » : on évalue B et renvoie sa valeur.
Opérateur séquentiel : (,) permet d’enchaîner plusieurs instructions, la dernière instruction
donne sa valeur à l’expression. 29
#include <stdio.h>
int main() // Ne serait-il mieux d’écrire :
{ int x, y = 5 ; double test, z ; x = y-1 ;
test=(x=y-1, z=x*y/1. 3, z-x) ; z = x * y / 1.3 ;
printf(test =%lf \n , test ) ; test = z-x ;
return 0; }
Opérateur sizeof permet de calculer la taille en octet d’une variable et donc de son type.
int a = 25 ;
printf (« Taille de a est %d \n », sizeof (a)) ;
L’écriture de sizeof(int) aurait donné 4, tandis que sizeof(double) aurait donné 8.
Opérateur d’adresse (&) et d’indirection (*) :
L’opérateur unaire d’adresse & permet d’obtenir l’adresse d’une variable.
Si a est une variable, &a est une adresse, la référence de a.
Inversement, on peut obtenir la valeur stockée à l’adresse indiquée avec l’opérateur unaire d’
indirection (*).
Si &a est une adresse, *(&a) est la valeur de la variable située à cette adresse, donc a.
#include <stdio.h>
int main ()
{ int a=16 ;
printf (l’adresse de a : &a = %x \n, &a) ;
printf (la valeur de a : a = %d ou *(&a)= %d \n, a, *(&a)) ; return 0;
}
l’adresse de a : &a = 54ffff04
30
la valeur de a : a = 16 où *(&a) =16
Si on ajoute la ligne : *(&a) = 32 ;
printf (la valeur de a : a = %d ou *&(a)=%d\n, a, *(&a)) ;
Dans ce cas on obtient : la valeur de a : a = 32 où *(&a) = 32
La valeur de a a été modifiée indirectement à partir de son adresse.
Les développeurs de langage, pour généraliser ce comportement ont créé une nouvelle catégorie
d’objets qui sont par leur nature des adresses.
Exemple : Si b = &a alors *b la valeur déréférencée de la variable b est égale à a.
*b = *(&a) = a.
On appelle objet *b, valeur déréférencée de la variable adresse de b un POINTEUR car il semble
qu’il montre, qu’il pointe la variable a.

#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.

6.4. Quelques compléments utiles


Vider le tampon après une fonction scanf() : on demande à getchar( ) de lire un à un les
caractères qui sont encore dans le tampon jusqu’à obtenir le caractère ‘\n’: while(getchar() !
= ’\n’).

Saisie des chaînes où figurent des espaces


Il suffit de remplacer le symbole %s par une séquence de caractères entre crochets %[…],
redéfinissant à notre usage les caractères autorisés par scanf().

{ char chaîne0[16], chaîne1[16] ;
printf(saisir chaine : ) ; scanf (%[ ABCD…XYZ],chaîne0) ;
printf (\t cette chaîne est : %s, chaîne0) ; scanf (%s, chaîne1) ;
printf (\n cette chaîne est : %s, chaîne1) ;
}

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).

On peut remplacer : while(getchar() !=’\n’) par fflush(stdin).

scanf ( ) ;
fflush ( stdin) ;

Limitation du nombre de caractères saisis par scanf()


On utilise un "spécificateur de largeur" : scanf("%20s", chaîne0) ;
scanf ne lit que 20 caractères avant de les affecter à la chaîne.
38
7. OUTILS DE CONTROLE
L’ordre normal de l’exécution des instructions d’un programme est défini par un ordre
séquentiel. Le langage C possède plusieurs instructions de contrôle qui rompt cet ordre.

7.1. Le test d’exécution

7.1.1. Branchement simple avec IF


if (expression testée vraie) {bloc d’actions est exécuté}
Exemple : if(a>b){ printf(a=%d est plus grand \n, a); b=0;
}
On peut ne pas mettre des accolades si le bloc comporte une seule instruction.
Exemple : if(a > b)printf(a = %d est plus grand\n, a);

7.1.2. Forme générale de l’instruction conditionnelle


if (vraie) {le bloc d’actions1 est exécuté}
else {le bloc d’actions2 est exécuté}
Exemple : if (a>b) {printf (a=%d est max \n, a); b = 0;
}
else { printf (b=%d est max \n, b); a = 0 ;
}
On peut ne pas mettre des accolades si le bloc contient une seule action.
Exemple : if (a>b)printf (a=%d est max \n, a);
else printf (b=%d est max \n,b); 39
7.1.3. Emboîtement de IF
if … if … else if …else …else…
Règles : on associe toujours le else au if sans else le plus proche.

7.1.4. Suite d’opérations logiques


Quand plusieurs expressions sont associées comme par exemple :
expression1 && expression2 && expression3
Le programme évalue les expressions de gauche à droite et s’arrête dès que la
réponse est déterminée sans évaluer les autres
#include <stdio.h>
#include<stdlib.h>
int main ( )
{ int a=16, x, i=1 ;
printf(”entrer une valeur de x : ”) ; scanf (”%d”, &x) ;
while(getchar() != ‘\n’ ) ;
if ((x<a) && (x>i++)) printf (“l’expression est vraie et i=%d \n”, i) ;
else printf (“l’expression est fausse et i=%d \n”, i) ;
return 0;
}
40
7.2. Sélection avec Switch. Elle permet de réaliser des branchements multiples et on l’utilise
pour remplacer plusieurs if. On ne teste qu’une expression dont les valeurs doivent être du type
int ou assimilé (char, short, enum). Exemple : enum naturel {zero, un, deux, trois};
switch (expression testée)
{ case constante1 : bloc d’instruction1; break;
case constante2 : bloc d’instruction2; break;

case constanten : bloc d’instructionn; break; 
default : bloc d’instruction;
}
break est l’instruction de sortie de boucle qui permet de sortir de switch.
default est facultative et s’exécute si aucun case ne correspond à la valeur de l’expression testée,
sinon le programme sort simplement de switch et passe à la suite.
Exemple :
#include <stdio.h>
int main ( )
{ int n;
printf(”donner un entier : ”); scanf(”%d”, &n); while(getchar () != ‘\n’);
switch(n)
{ case 0  : printf(”nul \n”); break;
case 1  : printf(”un \n ”); break;
case 2  : printf(”deux \n”); break;
default : printf(”grand \n”);
} return 1; 41

Si plusieurs cas correspondent à une sortie. On écrit :
switch (n)
{ case 1 :
case 2 :
case 3 : printf("petit\n"); break;
case 4 :
case 5 :
case 6 : printf(“moyen \n”); break;
default : printf (“grand \n”);
}
7.3. Itérations
7.3.1. FOR
for(instruction d’initialisation; expression testée; instruction de fin de boucle)
{bloc d’instructions à répéter}
Tant que l’expression testée est vraie alors le bloc est exécuté. Dès qu’elle est fausse, le bloc est
sauté, mais après une dernière exécution de l’instruction de fin de boucle.
Exemple. Calculer la somme des entiers de 3 à 6.
#include <stdio.h>
int main(void)
{ int i, somme=0 ;
for(i=3; i<7; i++){ somme+ = i;
printf (“i :%d \t somme=%d \t somme %d \n”, i, somme) ; }
printf(“fin de programme :\t i : %d\t somme = %d\n”, i, somme) ;
return 0; 42
}
On peut omettre une ou plusieurs instructions dans for.
Exemple1 : i = 1 ;
for( ; i <= 5 ; i++) printf(”Bonjour\n”) ; .....
Exemple2 : i = 1 ; for( ;  ; i++){.......}
Exemple3 : i = 1 ; for( ;  ; ) ; .... Ceci est une boucle infinie.
Il possible de faire plusieurs calculs dans les instructions d’initialisation et de fin de boucle.
Avec le même exemple, mais la valeur initiale de la somme est égale à n’importe quelle valeur.
#include<stdio.h>
#include<stdlib.h>
int main ()
{ int i, somme;
printf(“Donner une valeur initiale à la somme :”);
for (scanf(“%d”, &somme), i=3; i<7; i++)
{ somme+= i; printf(“i : %d\t somme=%d\tsomme %d\n”, i, somme);
}
printf(“fin de programme:\ti : %d\tsomme=%d\n”, i, somme);
return 0;
}

7.3.2. WHILE et DO … WHILE


WHILE
Syntaxe : while (expression testée) {bloc d’instruction à répéter}
Le bloc d’instructions est exécuté tant que l’expression testée est vraie.
On peut utiliser un while sans bloc d’instruction, while(…) ; 43
Exemple : conversion d’un texte en caractères majuscules avec la boucle WHILE.
#include <stdio.h>
#include <ctype.h>// permet d’utiliser toupper() qui convertit un caractère en majuscule
#define dim 21 // définit une constante.
int main()
{ int i=0, longueur; char texte [dim];
printf (″composez un texte de %d caractères au plus : ″, dim-1);
while ((texte [i]= getchar()) != ‘\n’)
{ printf(“\n i : %d \t texte[%d]=%c”, i, texte[i]);
i++; //ou ++i;
}
longueur = i ;
printf ("\n\nComparez avec le texte converti : ");
i=0;
while(i<longueur) putchar(toupper (texte[i++]));
return 1;
}
DO…WHILE
do
{ bloc d’instructions à répéter
}
while (expression testée);
Le bloc d’instructions est toujours exécuté au moins une fois. Les instructions du bloc sont
répétées tant que l’ expression testée est vraie. 44
Exemple : calcul de la factorielle de n avec n est inférieur à 13.
#include<stdio.h>
#include<stdlib.h>
int litInt(void) ; //prototype de fonction

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 1. Ecrire un programme en langage C qui simule le fonctionnement d’une calculette. Il


lit (saisit) deux entiers et un caractère. Si le caractère lu est ‘+’ il affiche la somme, si c’est un
caractère ‘-’ il affiche la différence, si c’est un ‘*’ il affiche le produit, si c’est un ‘/’ il affiche le
quotient et si c’st un ‘%’ il affiche le reste de la division entière.

Exercice 2. Ecrire un programme en langage C qui lit un caractère A, B, C, D ou E, utilisé pour


apprécier les notes des étudiants. Puis il affiche le message « excellent », « bon », « moyen »,
« médiocre » ou « mauvais », respectivement suivant le caractère lu.

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
}

8.1. Règles d’utilisation


- Passage par valeur : saisie, modification et affichage d’un caractère.
#include <stdio.h>
#include<stdlib.h>
char litChar (void);
char minMaj (char);
void ecritChar (char);
int main()
{ char car1=’A’, car2;
car2 = ‘z’ ;
car1 = litChar() ; car1 = minMaj(car1) ; ecritChar (car1) ;
car2 = litChar() ; car2 = minMaj(car2) ; ecritChar (car2);
return 0;
} 49
char litChar (void)
{ char c0 ;
while((c0 = getchar()) == ‘\n’ );
while(getchar() != ‘\n’);
return c0;
}
char minMaj (char c1)
{ if ((c1>=’a’) && (c1<=’z’))return (‘A’+c1-‘a’);
else return c1;
}
void ecritChar (char c2)
{ printf (En majuscule le caractère saisi est :  %c , c2) ; printf (\n\n\t) ;
}

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>

void litchar (char*) ;


void minMaj (char*) ;
void ecritchar (char*) ;

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*);

void signifie que la fonction ne renvoie rien.


char* signifie qu’elle utilise un pointeur sur une variable caractère.
Le passage de l’information du programme principal à la fonction se fera par
un pointeur. On appelle passage par pointeur.

void litchar(char *c0)


{…
}

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.

La valeur déréférencée *c0 sera celle de la variable car1 (*(&car1)). Toute


modification *c0 modifiera car1. On dit que le pointeur *c0 pointe sur car1.

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.2.1. Variables locales


Ce sont celles qui sont déclarées à l’intérieur d’un bloc. Leur portée est limitée à ce bloc.
Le corps d’une fonction principale est un bloc. Les variables locales sont stockées dans la
mémoire de pile appelée stack affectée à un programme.
A l’appel de la fonction, une zone de mémoire lui est allouée où l’on trouvera l’adresse de départ
du programme. Au retour, tous sera détruit et la zone mémoire libérée. Ces informations stockées
dans la pile sont volatiles.

8.2.3. Variables globales


Ce sont celles qui sont déclarées en dehors de tout bloc c’est-à-dire dans l’entête du programme.
Elles peuvent être utilisées par plusieurs fonctions.
Une variable globale n’est pas stockée dans la mémoire de pile du programme mais dans sa
mémoire statique. Elle est automatiquement initialisée à zéro au moment de sa déclaration
quand il n’y a pas d’initialisation explicite. Elle conserve les valeurs qu’on leur affecte
successivement au cours du programme c’est un avantage et en même temps un danger. 53
exemple : calcul de cube d’un entier variant de 1 à 4, puis calcul de la somme de ces cubes.
#include<sdtio.h>
#include<sdtlib.h>
void cube (int); void affiche (void);
int somme; //variable globale
int main () 
{ int i, j = 5; //variable locale
affiche();
for (i = 1; i<j; i++)
{ int k = 2; //variable locale
cube (i*k);
}
affiche();
return 0;
}
void cube(int x)
{ int c;
c = x*x*x;
somme + = c;
printf (\n le cube de %d est %d, x, c);
}
void affiche (void)
{ printf (\n la valeur de la somme est : %d, somme);
} 54
Avantage : la qualification globale de la variable somme nous a évité de faire le passage par
valeur ou par pointeur à la fonction.
Inconvénient : si le programme est complexe la valeur globale a été manipulée dans le
programme principal et dans une série de fonctions. On ne sait plus très bien au bout d’un
moment ce qu’elle contient, il produit alors des effets de bord.

8.3. Statut d’une variable ou d’une fonction


8.3.1. statut const : si on désire empêcher toute modification d’une variable, on peut lui
donner le statut const. En déclarant const un argument passé à la fonction, on interdit la
modification de la valeur passée à la fonction.
#include<stdio.h>
#include<stdlib.h>
int calcul(const int, const int);
int main(void)
{ int a, b, c;
c = calcule (a, b);
printf ("c = %d \n", c);
return 0;
}
int calcule(const int x, const int y)
{ return x*y;
}
Les valeurs a et b ne peuvent pas être modifiées par la fonction calcul. Ce sont les valeurs de
55
a et b qui ont été passées aux variables x et y.
8.3.2. Extern
On définit comme extern des variables ou des fonctions qui sont également définies dans un
autre fichier que celui qui contient le programme. Cela évite de faire des doubles déclarations.
Elles ne sont stockées qu’une seule fois en mémoire bien que connues et manipulées par
plusieurs fichiers. Elles sont toujours globales.

Exemple : dans un entête on peut trouver.


extern void combinaison(int, int, double*);
extern int somme;
Cette fonction et cette variable sont déclarées dans un autre fichier.

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);

9.2. Arithmétique des pointeurs


On peut déplacer un pointeur dans un plan mémoire à l’aide des opérateurs d’addition, de
soustraction, d’incrémentation et de décrémentation. On ne peut le déplacer que d’un
nombre de cases mémoire multiple du nombre de cases réservées en mémoire pour la
variable sur laquelle il pointe. 60
La figure 1 montre le plan de mémoire.
Exemple : int *pi; char *pc;
*pi = 0; *pc= 0x0a;

pi 00
00
00
00

pi+1

pc 0a

pc+1

Figure 1. Plan de mémoire

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.

9.3. Allocation dynamique


Lorsqu’on déclare une variable char, int, double…un nombre de cases mémoire bien défini
est réservé pour cette variable. Il n’en est pas de même avec les pointeurs.
Exemple
char *pc;
*pc = ‘a’; //le code ASCII de a est rangé dans la case mémoire pointée par pc.
*(pc+1) = ‘b’; //le code ASCII de b est rangé dans la case mémoire pointée par pc.
*(pc+2) = ‘c’; //le code ASCII de c est rangé dans la case mémoire pointée par pc.
*(pc +3)= ‘d’; //le code ASCII de d est rangé dans la case mémoire pointée par pc.
Dans cet exemple, le compilateur a attribué une valeur au pointeur pc. Les addresses suivantes
sont donc bien définies, mais le compilateur n’a pas reserve ces places. Il peut les attribuer à
d’autre variables. Les contenus de pc, pc+i (i=1 à 3) seront donc perdues.
62
Il existe des fonctions permettant d’allouer de la place en mémoire à un pointeur, par exemple
malloc(). Il faut les utiliser dès qu’on travaille avec les pointeurs.

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.

La function free() libère la mémoire

free(pi); //on libère la place précédemment rédervée pour pi.


free(pr); //on libère la place précédemment rédervée pour pr.

9.4. Affectation d’une valeur à un pointeur


L’utilisateur ne choisit pas la valeur des adresses mémoire attribuées par le compilateur à
chaque variable. L’utilisateur se contente de lire la valeur de ces adresses en l’affichant sur
l’écran.

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

Le préprocesseur est un programme exécuté lors de la première phase de la compilation. Il


effectue des modifications textuelles sur le fichier source à partir de directives. Elles sont
introduites par le caractère # (#include, #define et #if, #ifdef,…).

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.

10.3.1  Condition liée à la valeur d'une expression


Syntaxe est :
#if condition1
partieDuProgramme1
#elif condition2
partieDuProgramme2
...
#elif conditionn
partieDuProgrammen
#else
partieDuProgramme∞

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”, &notes[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”, &notes[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>

Exercice 20. Ecrire un programme qui demande l'introduction du nom et du prénom de


l'utilisateur et qui affiche alors la longueur totale du nom sans compter les espaces. Employer
la fonction strlen.
77
Exemple:
Introduisez votre nom et votre prénom: Mickey Mouse
Bonjour Mickey Mouse !
Votre nom est composé de 11 lettres.
Exercice 21. Ecrire un programme qui lit deux chaînes de caractères CH1 et CH2 et qui copie
la première moitié de CH1 et la première moitié de CH2 dans une troisième chaîne CH3.
Afficher le résultat.
Exercice 22. Ecrire un programme qui lit deux chaînes de caractères CH1 et CH2 et qui copie
la première moitié de CH1 et la première moitié de CH2 dans une troisième chaîne CH3.
Afficher le résultat.
a) Utiliser les fonctions spéciales de <string>.
b) Utiliser uniquement les fonctions gets et puts.
Exercice 23. Ecrire un programme qui lit un verbe régulier en "er" au clavier et qui en affiche
la conjugaison au présent de l'indicatif de ce verbe. Contrôlez s'il s'agit bien d'un verbe en "er"
avant de conjuguer. Utiliser les fonctions gets, puts, strcat et strlen.
Exemple:Verbe : fêter
je fête
tu fêtes
il fête
nous fêtons
vous fêtez
ils fêtent
78
C) Bibliothèque <stdlib>
Elle contient des déclarations de fonctions pour la conversion de nombres en chaînes de
caractères et vice-versa : Chaîne -->Nombre
Les trois fonctions définies ci-dessous correspondent au standard ANSI-C et sont portables.
Conversion de chaînes de caractères en nombres
atoi(s) retourne la valeur numérique représentée par <s> comme int
atol(s) retourne la valeur numérique représentée par <s> comme long
atof(s) retourne la valeur numérique représentée par <s> comme double.
D) Fonctions de classification et de conversion
Les fonctions de classification suivantes fournissent un résultat du type int différent de zéro,
si la condition respective est remplie, sinon zéro.
La fonction retourne une valeur différente de zéro,
isupper(c) si <c> est une majuscule ('A'...'Z')
islower(c) si <c> est une minuscule ('a'...'z')
isdigit(c) si <c> est un chiffre décimal ('0'...'9')
isalpha(c) si islower(c) ou isupper(c)
isalnum(c) si isalpha(c) ou isdigit(c)
isxdigit(c) si <c> est un chiffre hexadécimal ('0'...'9' ou 'A'...'F' ou 'a'...'f')
isspace(c) si <c> est un signe d'espacement (' ', '\t', '\n', '\r', '\f')
Les fonctions de conversion suivantes fournissent une valeur du type int qui peut être
représentée comme caractère; la valeur originale de <c> reste inchangée:
tolower(c) retourne <c> converti en minuscule si <c> est une majuscule
toupper(c) retourne <c> converti en majuscule si <c> est une minuscule 79
Chapitre 12. FICHIERS SEQUENTIELS
En langage C, les communications d'un programme avec son environnement se font par
l'intermédiaire de fichiers. Pour le programmeur, tous les périphériques, même le clavier et
l'écran, sont des fichiers. Nous allons voir, comment nous pouvons créer, lire et modifier nous-
mêmes des fichiers sur les périphériques disponibles.
12.1. Définitions et propriétés
Un fichier est un ensemble structuré de données stocké en général sur un support externe
(flash, disque dur, disque optique, bande magnétique, ...). Un fichier structuré contient une
suite d'enregistrements homogènes, qui regroupent le plus souvent plusieurs composantes
appartenant ensemble (champs).
Dans des fichiers séquentiels, les enregistrements sont mémorisés consécutivement dans
l'ordre de leur entrée et peuvent seulement être lus dans cet ordre. Si on a besoin d'un
enregistrement précis dans un fichier séquentiel, il faut lire tous les enregistrements qui le
précèdent, en commençant par le premier.
En simplifiant, nous pouvons nous imaginer qu'un fichier séquentiel est enregistré sur une
bande magnétique:

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).

12.2. Mémoire tampon


Pour des raisons d'efficacité, les accès à un fichier se font par l'intermédiaire d'une mémoire
tampon (buffer). La mémoire tampon est une zone de la mémoire centrale de la machine
réservée à un ou plusieurs enregistrements du fichier. L'utilisation de la mémoire tampon a
l'effet de réduire le nombre d'accès à la périphérie d'une part et le nombre des mouvements de
la tête de lecture/écriture d'autre part.
81
Un fichier donné par son nom doit être créé, lu ou modifié. On doit relier le nom d'un fichier
sur un support externe avec les instructions qui donnent accès au contenu du fichier.
Méthode employée :
Avant de lire ou d'écrire un fichier, l'accès est notifié par la fonction fopen. fopen accepte le
nom du fichier ("A:\ADRESSES.DAT"), négocie avec le système d'exploitation et fournit un
pointeur spécial qui sera utilisé ensuite lors de l'écriture ou la lecture du fichier.
Après les traitements, il faut annuler la liaison entre le nom du fichier et le pointeur à l'aide
de l’instruction fclose.
On peut dire aussi qu'entre les événements fopen() et fclose() le fichier est ouvert.

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;
}

12.3. Ouverture et fermeture des fichiers séquentiels


L'ouverture et la fermeture de fichiers se font à l'aide des fonctions fopen et fclose définies
dans la bibliothèque standard <stdio>.
12.3.1. Ouvrir un fichier en C - fopen
Lors de l'ouverture d'un fichier avec fopen, le système s'occupe de la réservation de la
mémoire tampon dans la mémoire centrale et génère les informations pour un nouvel élément
du type FILE. L'adresse de ce bloc est retournée comme résultat si l'ouverture s'est déroulée
avec succès. La commande fopen peut ouvrir des fichiers en écriture ou en lecture en
dépendance de son deuxième paramètre ("r" ou "w") :
85
FP = fopen (Nom , "w" ); "w" pour 'ouverture en écriture‘ - write -
ou bien
FP = fopen (Nom, "r" ); "r" pour 'ouverture en lecture‘ - read -
<Nom> est une chaîne de caractères constante ou une variable de type chaîne qui
représente le nom du fichier sur le médium de stockage.
<FP> est un pointeur du type FILE* qui sera relié au fichier sur le médium de
stockage. Dans la suite du programme, il faut utiliser <FP> au lieu de <Nom> pour
référencer le fichier.
<FP> doit être déclaré : FILE *FP;

12.3.2. Fermer un fichier séquentiel


Syntaxe :
fclose(FP);
<FP> est un pointeur du type FILE* relié au nom du fichier que l'on désire fermer.
La fonction fclose provoque le contraire de fopen.

Exemples: Ouvrir et fermer des fichiers


En pratique, il faut contrôler si l'ouverture d'un fichier a été accomplie avec succès
avant de continuer les traitements. Pour le cas d'une erreur, nous allons envisager
deux réactions différentes:
86
A) Répéter l'essai jusqu'à l'ouverture correcte du fichier
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *P_FICHIER; /* pointeur sur FILE */
char NOM_FICHIER[30]; /* nom du fichier */
...
do
{
printf("Entrez le nom du fichier : ");
scanf("%s", NOM_FICHIER);
P_FICHIER = fopen(NOM_FICHIER, "w");
/* ou bien */
/* P_FICHIER = fopen(NOM_FICHIER, "r"); */
if (!P_FICHIER)
printf("\aERREUR: Impossible d'ouvrir le fichier: %s.\n", NOM_FICHIER);
}
while (!P_FICHIER);
...
fclose(P_FICHIER);
return 0;
} 87
B) Abandonner le programme en retournant un code d'erreur non nul - exit
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *P_FICHIER; /* pointeur sur FILE */
char NOM_FICHIER[30]; /* nom du fichier */
...
printf("Entrez le nom du fichier : ");
scanf("%s", NOM_FICHIER);
P_FICHIER = fopen(NOM_FICHIER, "w");
/* ou bien */
/* P_FICHIER = fopen(NOM_FICHIER, "r"); */
if (!P_FICHIER)
{
printf("\aERREUR: Impossible d'ouvrir le fichier: %s.\n", NOM_FICHIER);
exit(-1); /* Abandonner le programme en */
} /* retournant le code d'erreur -1 */
...
fclose(P_FICHIER);
return 0;
}
88
12.4. Lecture et écriture dans des fichiers séquentiels
12.4.1. Fichiers texte
Les fichiers utilisés dans ce cours sont des fichiers texte c.-à-d. les informations dans les
fichiers sont mémorisées sous forme de chaînes de caractères et sont organisées en lignes.
Pour l'écriture et la lecture des fichiers, nous allons utiliser les fonctions standard fprintf,
fscanf, fputc et fgetc.
Ecrire dans un fichier séquentiel : fprintf
fprintf(FP, "Form1\n", Expr1);
fprintf(FP, "Form2\n", Expr2);
...
fprintf(FP, "FormN\n", ExprN);
ou bien
fprintf(FP,"Form1\nForm2\n...\nFormN\n", Expr1, Expr2, ... , ExprN);
- <FP> est un pointeur du type FILE* qui est relié au nom du fichier cible.
- <Expr1>, <Expr2>, ... , <ExprN> représentent les rubriques qui forment un enregistrement
et dont les valeurs respectives sont écrites dans le fichier.
- <Form1>, <Form2>, ... , <FormN> représentent les spécificateurs de format pour l'écriture
des différentes rubriques.
Remarque
L'instruction : fprintf(stdout, "Bonjour\n");
est identique à printf("Bonjour\n");
Dans les fichiers texte, il faut ajouter le symbole de fin de ligne '\n' pour séparer les données.
89
Lire dans un fichier séquentiel - fscanf
fscanf(FP, "Form1\n", Adr1);
fscanf(FP, "Form2\n", Adr2);
...
fscanf(FP, "FormN\n", AdrN);
ou bien
fscanf(FP,"Form1\nForm2\n...\nFormN\n", Adr1, Adr2, ... , AdrN);
- <FP> est un pointeur du type FILE* qui est relié au nom du fichier à lire.
- <Adr1>, <Adr2>, ... , <AdrN> représentent les adresses des variables qui vont recevoir les
valeurs des différentes rubriques d'un enregistrement lu dans le fichier.
- <Form1>, <Form2>, ... , <FormN> représentent les spécificateurs de format pour la lecture
des différentes rubriques.
Remarque
L'instruction fscanf(stdin, "%d\n", &N);
est identique à scanf("%d", &N);
Pour les fonctions scanf et fscanf tous les signes d'espacement sont équivalents comme
séparateurs.

12.4.2. Traitement par caractères


La manipulation de fichiers avec les instructions fprintf et fscanf n'est pas assez flexible pour
manipuler de façon confortable des textes écrits. Il est alors avantageux de traiter le fichier
séquentiellement caractère par caractère.
90
A) Ecrire un caractère dans un fichier séquentiel - fputc
fputc( C , FP);
fputc transfère le caractère indiqué par <C> dans le fichier référencé par <FP> et avance la
position de la tête de lecture/écriture au caractère suivant.
- <C> représente un caractère (valeur numérique de 0 à 255) ou le symbole de fin de fichier
EOF.
- <FP> est un pointeur du type FILE* qui est relié au nom du fichier cible.
Remarque
L'instruction : fputc('a', stdout);
est identique à putchar('a');

B) Lire un caractère dans un fichier séquentiel – fgetc


C = fgetc(FP);
fgetc fournit comme résultat le prochain caractère du fichier référencé par <FP> et avance la
position de la tête de lecture/écriture au caractère suivant. A la fin du fichier, fgets retourne
EOF.
- <C> représente une variable du type int qui peut accepter une valeur numérique de 0 à 255
ou le symbole de fin de fichier EOF.
- <FP> est un pointeur du type FILE* qui est relié au nom du fichier à lire.
Remarque
L'instruction : C = fgetc(stdin);
est identique à C = getchar();
91
12.4.3. Détection de la fin d'un fichier séquentiel
Lors de la fermeture d'un fichier ouvert en écriture, la fin du fichier est marquée
automatiquement par le symbole de fin de fichier EOF (End Of File). Lors de la lecture d'un
fichier, la fonction feof(FP) nous permettent de détecter la fin du fichier:
Syntaxe : feof(FP);
feof retourne une valeur différente de zéro, si la tête de lecture du fichier référencé par <FP>
est arrivée à la fin du fichier; sinon la valeur du résultat est zéro.
<FP> est un pointeur du type FILE* qui est relié au nom du fichier à lire.

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

Vous aimerez peut-être aussi