Conservatoire National des Arts et Métiers
Polycopié de cours ELE002
Version du 19/03/2007
Outils Logiciels de base
C.ALEXANDRE – C.PAUTOT
1. INTRODUCTION ... 1
1.1 L’informatique... 1
1.2 Le système de traitement de l’information ... 1
1.3 Le codage de l’information... 2
1.4 L’ordinateur ... 5
1.5 Le système d’exploitation... 6
1.6 Les applications logicielles... 9
1.7 Le système de fichiers ... 10
1.8 Les répertoires (syntaxe Unix) ... 10
1.9 Partitionnement et montage ... 11
1.10 Informations associées aux fichiers... 13
1.11 Protection des fichiers (Unix)... 14
1.12 Tableau comparatif des systèmes de fichiers ... 16
1.13 Les fichiers textes ... 16
2. LANGAGES DE PROGRAMMATION... 19
2.1 Définitions ... 19
2.2 Méthodologie pour l’écriture d’un programme ... 20
2.3 Le langage C... 21
3. BASES DU LANGAGE C ... 23
3.1 Les variables ... 23
3.2 L’instruction d’affectation... 24
3.3 Les types entier... 27
3.4 Les types flottants (réels)... 29
3.5 Les conversions de type... 30
3.6 Les types char ... 32
3.7 Communiquer avec le programme : les entrées-sorties standard ... 33
3.8 L’instruction printf ... 37
3.9 L’instruction scanf... 40
3.10 Structure de choix : l’instruction if... 42
3.11 Structure de choix : les conditions en C ... 45
3.12 Structure de choix : l’instruction switch... 49
3.13 Structure de répétition conditionnelle : l’instruction do... while ... 51
3.14 Structure de répétition conditionnelle : l’instruction while... ... 52
3.15 Structure de répétition inconditionnelle : l’instruction for... 53
3.16 Algorithmes élémentaires ... 56
4. LES FONCTIONS ...59
4.1 Introduction... 59
4.2 Premier exemple ... 60
4.3 Fonction sans résultat ou sans paramètres ... 64
4.4 L’instruction return... 66
4.5 Variables globales et locales ... 67
4.6 Variable statique ... 70
4.7 La récursivité... 71
4.8 Passage des paramètres par valeur. ... 72
4.9 Les pointeurs ... 74
4.10 Passage de pointeurs comme paramètres d’une fonction... 83
5. LES TABLEAUX ...85
5.1 Tableaux à une dimension... 85
5.2 Remarques importantes sur les tableaux... 90
5.3 Les tableaux à deux dimensions... 93
5.4 Passage d’un tableau comme paramètre d’une fonction... 97
5.5 Relations entre tableaux et pointeurs ... 100
5.6 Allocation dynamique de la mémoire ... 104
6. LES CHAINES DE CARACTERES ... 107
6.1 Déclaration ... 107
6.2 Lire ou écrire des chaînes... 110
6.3 Connaître la longueur d’une chaîne ... 117
6.4 Copier une chaîne dans une autre chaîne ... 118
6.5 Comparer deux chaînes... 119
6.6 Concaténer deux chaînes... 120
6.7 Rechercher un caractère dans une chaîne ... 121
6.8 Rechercher une chaîne dans une autre chaîne... 121
6.9 Fonctions diverses... 123
6.10 Le passage d’une chaîne comme paramètre d’une fonction. ... 124
6.11 Les tableaux de chaînes de caractères. ... 126
7. LES PARAMETRES DE LA FONCTION MAIN... 129
8. LES STRUCTURES ... 133
8.1 Définition... 133
8.2 Transmission d’une structure en paramètre d’une fonction ... 137
8.3 La définition de types nouveaux... 140
9. LES FICHIERS... 143
9.1 Introduction ... 143
9.2 Ouverture et fermeture d’un flux... 145
9.3 Buffers associés aux flux... 149
9.4 Lecture et écriture dans un flux ... 150
9.4.1 Les lectures et écritures par caractère... 150
9.4.2 Les lectures et écritures par ligne ... 151
9.4.3 Les lectures et écritures formatées... 153
9.4.4 Les lectures et écritures binaires... 155
9.5 Positionnement dans un flux ... 158
9.6 Utilisation d’un fichier de configuration ... 160
10. DIVERS ... 163
10.1 Exécution de commandes ... 163
10.2 Les opérateurs binaires ... 164
10.3 Les énumérations... 165
10.4 Les opérateurs d’incrémentation et de décrémentation ... 166
10.5 L’opérateur virgule ... 168
10.6 L’opérateur conditionnel ? ... 168
10.7 Les macros avec paramètres ... 169
10.8 Ce que vous ne verrez pas ... 172
10.9 Définition de macro à l’invocation du compilateur... 172
10.10 Compilation conditionnelle ... 173
11. EDITION DE LIEN ... 179
11.1 Les pointeurs de fonction ... 179
11.2 Notion de processus... 182
11.3 Les zones mémoires d’un processus... 183
11.4 Projets multi-fichiers : édition de liens ... 186
12. LES LIBRAIRIES...195
12.1 Les bibliothèques statiques (archive)... 195
12.2 Les bibliothèques dynamiques (partagées) ... 196
12.3 Avantages et inconvénients des bibliothèques dynamiques ... 200
12.4 La bibliothèque standard du C ... 202
12.4.1 Les entrées-sorties <stdio.h> ... 202
12.4.2 Les fonctions mathématiques <math.h> ... 203
12.4.3 Les manipulations de caractères <ctype.h> ... 204
12.4.4 Les manipulations de chaînes <string.h>... 204
12.4.5 Manipulations de l’heure <time.h>... 205
12.4.6 Diverses fonctions utilitaires <stdlib.h> ... 205
13. INTRODUCTION A LA PROGRAMMATION EN C++ ...207
13.1 Généralités ... 207
13.2 Intérêt de la conception objet ... 207
13.3 Un exemple de programmation classique ... 208
13.4 Les classes... 213
13.4.1 Définition ... 213
13.4.2 Syntaxe... 213
13.4.3 Constructeur ... 214
13.4.4 Destructeur ... 214
13.4.5 Restriction d’accès ... 214
13.4.6 Les fonctions (méthodes) de la classe... 215
13.4.7 Organisation en fichiers source et header ... 216
13.5 Création d’un objet... 217
13.5.1 Au moyen d’une déclaration ... 217
13.5.2 Avec l’opérateur new ... 217
13.6 Manipulation des objets ... 219
13.6.1 Accès à une variable ... 219
13.6.2 Accès à une fonction ... 219
13.7 Surcharge des fonctions et des opérateurs ... 221
13.8 Passage par référence ... 224
13.9 Héritage et composition ... 225
13.9.1 Introduction ... 225
13.9.2 La composition ... 226
13.9.3 L’héritage ... 226
13.9.3.1 Principe de l’héritage... 226
13.9.3.2 Restriction d’accès... 228
13.9.3.3 Substitution des membres hérités ... 229
13.9.3.4 Gestion des constructeurs ... 230
13.9.3.5 Fonctions virtuelles ... 232
13.10 Les flux d’entrée sortie ... 235
13.10.1 Généralités ... 235
13.10.2 Opérateur d'insertion et d’extraction de flux ... 235
13.10.2.1 Extraction de flux >>... 235
13.10.2.2 Insertion de flux >>... 235
13.10.3 Modification des formatages ... 236
13.10.3.1 Liste des drapeaux ... 236
13.10.3.2 Fonctions permettant de modifier les drapeaux... 237
13.10.3.3 Formatage de la sortie... 237
13.10.4 Manipulateurs non paramétriques ... 238
13.10.5 Entrée et sortie non formatées ... 239
13.10.5.1 La fonction get()... 239
13.10.5.2 La fonction getline()... 240
13.10.5.3 La fonction read()... 240
13.10.6 Fonctions de sortie non formatées ... 241
13.10.6.1 La fonction put... 241
13.10.6.2 La fonction write... 241
13.10.7 Les fonctions de manipulations évoluées ... 241
13.11 Bibliographie ... 242
1. Introduction
1.1 L’informatique
C’est la manipulation de l’information à l’aide d’un ordinateur.
Ordinateur : machine électronique programmable destinée au traitement de l’information numérique.
Information : texte, son, image, données binaires (produites par un système électronique, utilisées par un système électronique), …
1.2 Le système de traitement de l’information
ordinateur
Système d’exploitation
Dispositif électronique matériel (hardware) Logiciels (software)
Applications Logicielles
Interface de programmation
Interface matériel/logiciel
Interface de programmation, l’Application Programming Interface (API) : exemple, l’API Win32 pour Windows.
Interface matériel/logiciel : exemple, le Basic Input Output System (BIOS). Jusqu’à MS- DOS, le BIOS sert d’interface entre l’ordinateur et le système d’exploitation. Cette méthode n’est plus utilisée aujourd’hui, l’interface est intégrée au système d’exploitation (voir HAL sous NT).
1.3 Le codage de l’information
Un bit (contraction de Binary digIT) est un chiffre pouvant prendre la valeur 0 ou 1 (base 2).
Dans un ordinateur, un bit est représenté par deux niveaux de tension électrique. Un nombre binaire est une suite de bits (comme un nombre décimal est une suite de chiffres compris entre 0 et 9).
En décimal (base 10) :
Le nombre 259 = 2*102 + 5*101 + 9*100 = 2*100 + 5*10 + 9*1
En binaire (base 2) :
Le nombre 10101 = 1*24 + 0*23 + 1*22 + 0*21 + 1*20 = 1*16 + 1*4 + 1*1
Un octet (ou byte) est une suite de 8 bits :
L’octet 10000001 = 1*27 + 1*20 = 129
La valeur d’un octet est comprise entre 0 et 255. Pour raccourcir l’écriture, on utilise la notation hexadécimale (base 16).
Un chiffre en base 16 peut prendre 16 valeurs allant de 0 à 15. Il peut être codé avec 4 bits.
Comme les chiffres s’arrêtent à 9 en décimal, on utilise les lettre a, b, c, d ,e et f pour représenter les derniers états.
décimal binaire hexadécimal
0 0000 0
1 0001 1
2 0010 2
3 0011 3
4 0100 4
5 0101 5
6 0110 6
7 0111 7
8 1000 8
9 1001 9
10 1010 a
11 1011 b
12 1100 c
13 1101 d
14 1110 e
15 1111 f
L’octet 10000001 = 81 en hexa
Notation pour les bases :
(10000001)b = (81)h = (129)d
101011002 = ac16 = 17210
On utilise couramment les multiples suivants :
multiple valeur
Kilo 210 = 1024
Méga 220 = 1048576 = 1024 kilo
Giga 230 = 1073741824 = 1024 méga
Tera 240 = 1099511627776 = 1024 giga
Exemples :
1 kilobit = 1024 bits
128 mégaoctet = 128*1024*1024 octets
Attention : en informatique
1 kilo ≠ mille 1 méga ≠ 1 million 1 giga ≠ 1 milliard
Changement de base :
• Décimal vers binaire
47 2
1 23 2
1 11 2
1 5 2
1 2 2
0 1 2
1 0
4710 = 1011112
• Binaire vers décimal
1 x 25 + 0 x 24 + 1 x 23 + 1 x 22 + 1 x 21 + 1 x 20 = 4710
• Hexadécimal vers binaire
ab8516 = 1010 1011 1000 01012
• Binaire vers hexadécimal
1001 0000 1010 11112 = 90af16
Pour passer d’hexadécimal en décimal (et vice versa), vous pouvez passer par l’intermédiaire du binaire ou faire le calcul directement.
Exercice 1.1 :
1) Quel est le nombre décimal le plus grand que l’on peut coder avec 4 bits, 8 bits, 16 bits, 32 bits.
2) Convertissez 11011012 en décimal.
3) Convertissez 1910 et 4510 et 6310 en binaire.
4) Convertissez 11001010010101112 en hexadécimal.
5) Convertissez 10A416 et CF8E16 et 974216 en binaire et en décimal.
1.4 L’ordinateur
Un ordinateur :• Traite l’information grâce à un programme qu’il mémorise,
• Communique et archive des informations.
Il est constitué de trois éléments :
1. La mémoire centrale (vive) qui permet de stocker les programmes pendant le temps nécessaire à leur exécution ainsi que les informations temporaires manipulées par ces programmes. La mise hors tension de l’ordinateur efface le contenu de la mémoire vive.
2. L’unité centrale qui exécute les instructions contenues dans le programme qui se trouve en mémoire vive.
3. Les périphériques qui échangent des informations avec l’unité centrale. On en trouve de deux sortes :
• Les périphériques de communication : clavier, écran, souris, imprimante, modem, carte son, carte réseau, …
• Les périphériques d’archivage : disque dur, disquette, CD-ROM, bande magnétique, ...
ils assurent le stockage permanent des données et des programmes.
Exemple : un PC.
Processeur Intel pentium IV, fréquence 3 GHz.
Mémoire vive 512 Mo.
Disque dur 200 Go.
Lecteur de DVD -ROM + graveur.
Lecture de disquette 3,5 pouces.
Ecran 17 pouces LCD.
Carte son.
Carte réseau.
Clavier, souris.
1.5 Le système d’exploitation
C’est un ensemble de programmes qui servent :
• à gérer les ressources de l’ordinateur et notamment à assurer leur partage harmonieux entre les différents programmes.
• à présenter à l’utilisateur et aux programmes une interface plus facile à utiliser que la machine physique. Cette interface est celle d’une machine virtuelle qui cache la complexité du matériel. Elle sert :
Ö d’interface entre l’ordinateur et les applications logicielles (API).
Ö d’interface entre l’ordinateur et l’utilisateur (IHM).
L’utilisateur d’un ordinateur effectue généralement les tâches suivantes : 1. il développe un programme,
2. ou bien il utilise un programme pour générer ou manipuler de l’information.
Pour faciliter ces opérations, le système d’exploitation peut utiliser deux sortes d’interface avec l’utilisateur :
1. L’interface graphique (mode graphique). Elle utilise la souris, les icônes et les menus déroulants. Très conviviale elle ne permet pas l’automatisation des traitements.
Exemples : MAC OS, Windows, …
2. L’interpréteur de commandes textuel (mode ligne de commande ou console ou terminal).
L’utilisateur manipule ici les données à l’aide de commandes tapées au clavier. On l’appelle un shell sous unix ou command.com sous MS-DOS. L’interpréteur de commande est peu intuitif car l’utilisateur doit connaître les commandes pour pouvoir l’utiliser mais il est en revanche plus puissant car programmable.
Le système d’exploitation comprend 4 parties essentielles : 1. La gestion des programmes.
2. Les entrées/sorties.
3. La gestion de la mémoire.
4. Le système de fichiers.
Caractéristiques :
⇒ La mémoire virtuelle. La taille des programmes et des données manipulées en mémoire vive dépasse généralement la quantité de mémoire physiquement disponible dans l’ordinateur. Pour augmenter la quantité de mémoire disponible, le système d’exploitation va garder en mémoire vive les parties actives du programme et des données et stocker le reste sur le disque dur. Un mécanisme de pagination permet de charger en mémoire quand il le faut les parties de programme à exécuter. Le nombre de bits codant l’adresse maximale de la mémoire utilisable par un programme caractérise le système d’exploitation (16, 32 ou 64 bits). La MMU est chargé de traduire les adresses virtuelles en adresses physiques.
⇒ La gestion des programmes. Le système monotâche ne peut exécuter qu’un seul programme à la fois même si plusieurs programmes peuvent être présents à la fois en mémoire (permutation). Un système multitâches préemptif fait tourner plusieurs programmes en même temps grâce à un planificateur (scheduler) qui attribue à chacun des programmes un petit laps de temps (slice time) pour s’exécuter. Tous les programmes se trouvent dans une file d’attente et attendent leur tour. Les programmes les plus prioritaires reviennent plus souvent dans la queue et donc s’exécutent plus rapidement.
Un système multitâches coopératif est un système monotâche (pas de scheduler) car ce sont les programmes qui sont conçus pour s’arrêter de temps en temps pour passer à un autre programme.
⇒ La gestion des utilisateurs. Un système multi-utilisateurs permet à plusieurs utilisateurs d’accéder simultanément à l’ordinateur pour exécuter des taches différentes. Le système doit distinguer les différents utilisateurs en les dotant d’un nom et d’un mot de passe ainsi que d’un espace disque (et d’un espace mémoire) réservé. C’est le compte utilisateur. Le système est forcément multitâches préemptif. Un système monotâche est forcément mono-utilisateur.
Caractéristiques des principaux systèmes :
MS-DOS Mono-tache Mono-utilisateur 16 (20) bits
MAC OS 9 Mono-tache Mono-utilisateur 32 bits
Unix multi-tâches Multi-Utilisateurs 32 bits ou 64 bits
VMS multi-tâches Multi-Utilisateurs 32 bits
Windows 95 et 98 multi-tâches Mono-utilisateur 32 bits + 16 bits
Windows NT multi-tâches Mono-utilisateur
Multi-Utilisateurs
32 bits
Windows 2000 Windows XP
Vista
multi-tâches Multi-Utilisateurs 32 bits ou 64 bits
1.6 Les applications logicielles
Ce sont des programmes. Exemples :• Bureautique : traitement de texte, tableur, gestion de base de données, …
• Gestion et comptabilité : facturation, paye, stocks, …
• Jeux vidéo,
• Navigation Internet,
• Prévisions météorologiques,
• Conception assistée par ordinateur (CAO),
• Gestion d’une chaîne de fabrication,
• Simulateur de vol,
• …
Un ordinateur est capable de mettre en mémoire un programme (résidant généralement sur le disque dur), puis de l’exécuter.
Un programme est constitué d’instructions qui spécifient :
• Les opérations élémentaires que va exécuter l’ordinateur,
• La manière dont elles s’enchaînent.
programme
données résultats
Les données d’entrée du programme peuvent être fournies manuellement ou lues sur un disque dur (base de données) ou bien sur des capteurs.
Les résultats fournis par le programme peuvent être lus directement par l’utilisateur (sous forme de textes ou de graphiques) ou bien stockés sur disque.
Si le temps de réaction du programme entre un changement sur une entrée (capteur) et le résultat en sortie (actuateur) doit être garanti, alors on dit que le programme s’exécute en temps réel.
1.7 Le système de fichiers
On ne peut pas maintenir toutes les informations utiles d’un ordinateur en mémoire vive. Il faut les sauvegarder sur un support qui stocke l’information même lorsqu’il est hors tension (un disque dur par exemple). Le système de fichiers est l’ensemble des mécanismes destiné à manipuler de l’information sur ce support.
Un fichier est un objet qui peut contenir des programmes, des données ou tout autre type d’information. Le système d’exploitation fournit des opérations spéciales, les appels système, pour les créer, les détruire, les écrire ou les modifier.
Le système de fichiers est, avec le bureau, la partie la plus visible du système d’exploitation.
La plupart des programmes lisent ou écrivent au moins un fichier et les utilisateurs manipulent beaucoup de fichiers. L’utilisateur attache une grande importance à l’interface du système de fichiers, c’est-à-dire aux fichiers, à la manière de les nommer et de les protéger, aux opérations permises sur les fichiers, …
1.8 Les répertoires (syntaxe Unix)
Le système de fichiers range les fichiers dans des répertoires (directories) ou dossiers. Un répertoire contient un certain nombre d’entrées, une par fichier ou par répertoire. Etant donné la très grande quantité de fichiers à gérer sur un ordinateur (plusieurs centaines de milliers), il faut un système de classement performant. Une organisation hiérarchique constituée d’une arborescence de répertoires permet d’avoir autant de répertoires qu’il est nécessaire afin de regrouper les fichiers logiquement. Un répertoire peut contenir soit des fichiers, soit d’autres répertoires.
Le premier répertoire dans la hiérarchie (ici
/
) s’appelle le répertoire racine (root).L’utilisateur peut définir le répertoire dans lequel il veut travailler, le répertoire de travail (working directory) ou répertoire courant (il y a une valeur par défaut spécifiée par le système d’exploitation). Il existe deux méthodes pour spécifier l’emplacement (chemin d’accès ou path) d’un fichier :
• Le chemin d’accès absolu. C’est le chemin spécifié à partir du répertoire racine.
Ex : /usr/local/bin/nom_de_fichier.
Le
/
(slash) représente soit le répertoire racine s’il est au début du chemin d’accès, soit un séparateur qui indique un changement de niveau.• le chemin d’accès relatif. C’est le chemin spécifié à partir du répertoire courant.
Exemple : le répertoire courant est /usr/local/bin. On veut accéder à un fichier se trouvant dans /usr/bin
../../bin/ nom_de_fichier
.. désigne le répertoire père (répertoire juste au-dessus dans l’arborescence).
. désigne le répertoire dans lequel vous êtes.
Exercice 1.2 : le répertoire courant est /usr/local. Donnez le chemin d’accès absolu et relatif des répertoires man, Yves et etc.
1.9 Partitionnement et montage
Il est possible de diviser un disque en plusieurs morceaux (des partitions) en effectuant un partitionnement. Cela permet, par exemple, d’installer plusieurs systèmes d’exploitation (OS
= operating system) sur un disque.
Sur les OS Microsoft, on affecte aux partitions une lettre appelée lettre de lecteur. Chaque partition sera appelée C, D, E, … Les lettres A et B sont généralement affectées aux lecteurs amovibles.
Un chemin d’accès absolu sera nommé : C:\users\dut_info. Le répertoire racine est précédé de la lettre du lecteur suivie de :. Le slash (/) d’Unix est remplacé par un backslash (\). Le chemin d’accès relatif sous Windows est le même que sous Unix, mais avec un \ à la place d’un /.
Sous Unix, il n’y a pas de lettre de lecteur pour identifier un lecteur physique (disque dur ou disquette) ou bien une partition. Tout est monté sous le répertoire racine /.
Partition 1 Partition 0 /
usr etc tmp home
lib bin include
dut tpb
montage
L’opération de montage sert à indiquer au système à quel niveau de l’arborescence se placent les différents disques et partitions. Dans l’exemple précédent, la partition utilisateurs (partition 1) est rattachée (montée) au répertoire home de la partition racine (partition 0). Un lecteur de DVD-ROM sera par exemple monté sous le répertoire mount (/mnt/cdrom).
Sous Unix, il faut ajouter à la notion de répertoire de travail (working directory) celle de répertoire privé (home directory). Le répertoire privé est le répertoire dans lequel l’utilisateur se retrouvera lors de sa connexion au système. C’est l’emplacement où il enregistrera ses fichiers par défaut. Cette notion n’a pas de sens avec Windows 98 ni même avec Windows NT (NT ne créé pas automatiquement un répertoire par utilisateur), mais existe sous XP et Vista.
1.10 Informations associées aux fichiers
L’entrée du répertoire contient des informations associées au fichier telles que :
• Le nom du fichier. La FAT-16 (MS-DOS) ne permet que des noms de 8 caractères suivis d’une extension de trois caractères (format 8+3).
Exemple : essai.txt
L’extension indique le type du fichier. Seuls les .bat, .exe et .com sont exécutables. La FAT-32 et NTFS (XP, Vista) autorisent les noms longs.
Unix permet des noms longs de 255 caractères avec différentiation des majuscules et des minuscules. Il n’y a pas d’extension, le . est un caractère comme un autre. Un attribut décide de la possibilité pour un programme de s’exécuter ou non.
• La date et l’heure de la dernière modification.
• La taille du fichier en octets.
• Les attributs de protection (sous Unix).
• Les numéros de bloc contenant les données du fichier. Ces informations ne sont pas visibles par l’utilisateur.
Exemple d’informations retournées par une commande dir sous Windows NT :
Le volume dans le lecteur D s'appelle MainNT Le numéro de série du volume est EB2A-9B11 Répertoire de D:\users\dut_info
09/10/00 16:36 <DIR> . 09/10/00 16:36 <DIR> ..
09/10/00 16:35 <DIR> essai
09/10/00 08:53 21 fichier_dos.txt 09/10/00 08:53 20 fichier_unix.txt 09/10/00 16:36 531 result.txt
6 fichier(s) 41 octets 8 242 147 328 octets libres
Nous sommes sur le disque D dans le répertoire \users\dut_info. <DIR> indique la présence d’un répertoire. Le . représente le répertoire courant. Le .. représente le répertoire père.
1.11 Protection des fichiers (Unix)
La protection des fichiers est inexistante sous Windows 98 (il n’y a en général qu’un utilisateur sur le PC), correcte sous Windows XP et Vista (avec NTFS mais pas avec la FAT- 32 utilisée par défaut) et bonne sous Unix.
Sous Unix. Chaque utilisateur fait partie d’un groupe de travail. Les protections d’un fichier concernent :
Le propriétaire du fichier Users (u) Le groupe d’utilisateurs Group (g)
Les autres utilisateurs Others (o) Les opérations concernées par les permissions rwx sont :
La lecture read (r)
L’écriture write (w)
L’exécution execute (x)
Ces permissions rwx ont une signification particulière lorsqu’il s’agit d’un répertoire :
• r : ce droit permet uniquement de lire les noms des fichiers du répertoire.
• w : cette permission autorise la création et la destruction de fichiers dans ce répertoire.
• x : cette permission indique que l’on pourra passer par ce répertoire (avec une commande cd).
Exemple du contenu d’un répertoire sous Unix :
drwxr-xr-x 4 dut dut 4096 jui 12 15:30 Desktop/
-rw-r--r-- 1 dut dut 18 oct 9 09:26 essai.txt drwx--- 2 dut dut 4096 oct 9 09:48 nsmail/
-rw-r--r-- 1 dut dut 0 oct 9 14:03 result.txt drwx--- 2 dut dut 4096 oct 9 09:48 tmp/
-rwxr--r-- 1 dut dut 0 oct 9 13:53 toto*
drwxr-xr-x 4 dut dut 4096 jui 12 15:30 Desktop/
-rw-r--r-- 1 dut dut 18 oct 9 09:26 essai.txt drwx--- 2 dut dut 4096 oct 9 09:48 nsmail/
-rw-r--r-- 1 dut dut 0 oct 9 14:03 result.txt drwx--- 2 dut dut 4096 oct 9 09:48 tmp/
-rwxr--r-- 1 dut dut 0 oct 9 13:53 toto*
drwxr-xr-x 4 dut dut 4096 jui 12 15:30 Desktop/
-rw-r--r-- 1 dut dut 18 oct 9 09:26 essai.txt drwx--- 2 dut dut 4096 oct 9 09:48 nsmail/
-rw-r--r-- 1 dut dut 0 oct 9 14:03 result.txt drwx--- 2 dut dut 4096 oct 9 09:48 tmp/
-rwxr--r-- 1 dut dut 0 oct 9 13:53 toto*
Permissions (rwx) user : group : others
d : directory
l : lien symbolique user group taille
Date et heure de la dernière modification
(pas d’année si année en cours) Nom
Exercice 1.3 : donnez tout les renseignements que vous pourrez sur essai.txt, toto et tmp.
Tout fichier dont le nom commence par un . est dit caché. Cela signifie simplement deux choses :
1. Le fichier ne sera pas vu lors d’une commande de listage de fichier classique de type ls.
2. Ce fichier ne sera pas détruit par une commande de destruction de type rm si vous utilisez un joker comme *.
1.12 Tableau comparatif des systèmes de fichiers
Système de fichiers
FAT 16 FAT 32 NTFS EXT2FS
OS MS-DOS, Windows 95
Windows 98, XP, Vista
Windows NT, 2000, XP, Vista
linux
Format des noms 8.3 (255 avec VFAT)
8.3 (255 avec VFAT)
255 255
protection non non oui oui
Sensible à la casse non non Non [1] oui
Mécanismes de correction
non non oui oui
Taille max cluster 32 ko 4 ko 4 ko 4 ko
Taille max partition
(216 – 10)*32 ko = 2 Go
(228 – 10)*8 ko = 2 To
(264 * 4ko) 2 To pour un disque « de base »
4 To
[1] NTFS affiche la différence entre majuscule et minuscule, mais vous ne pouvez avoir dans le même répertoire deux fichiers toto.txt et TOTO.txt.
1.13 Les fichiers textes
Le codage d’un caractère dans un fichier (on parle alors de fichier texte) doit utiliser un code pour faire correspondre un octet (en général) à un caractère. Le code le plus connu et le plus utilisé est le code ASCII (American Standard Code for Information Interchange). Mais il existe aussi le code EBCDIC (Extended Binary Coded Decimal Interchange Code), code propriétaire IBM utilisé pour ses gros ordinateurs (mainframes).
Le code ASCII est un jeu normalisé de 128 caractères codés sur 7 bits, devenu un standard quasi universel. Il comporte tous les caractères alphanumériques non accentués et est lisible par pratiquement n'importe quelle machine. Ce sont les 8 premières lignes du tableau suivant.
Les 32 premiers codes sont utilisés comme caractères de contrôle pour représenter, par exemple, une fin de ligne ou une tabulation.
Le code ASCII ne contient pas de caractères accentués et il a été complété par le code ISO- 8859-1 (ou Latin 1). Ce n’est hélas pas le seul. Les 128 premiers caractères correspondent au code ASCII, les 128 suivants aux caractères accentués et caractères spéciaux (voir les 8 dernières lignes du tableau).
Unicode est un jeu de caractères codé sur 16 bits (contre 7 ou 8 bits pour les standards anciens) qui permet le codage des caractères utilisés par toutes les langues du monde au sein d'une table unique. 16 bits permettent de coder 65 536 (2 puissance 16) caractères différents ce qui couvre largement les besoins en la matière. Unicode est supporté par tous les sytèmes d’exploitations « modernes ». Les 256 premiers caractères d'Unicode correspondent au jeu ISO Latin 1.
Un fichier texte comporte un caractère spécial EOL (End Of Line) pour signaler la fin d’une ligne. Ce caractère est différent suivant que le fichier est créé sous Unix ou bien sous Windows.
Fin de ligne Unix 0x0A (line feed)
Fin de ligne Windows 0x0D 0x0A (carriage return, line feed)
C’est une des principales sources d’incompatibilité aux transferts de fichiers entre les deux mondes.
Exercice 1.4 :
1) Sous Unix, donnez le fichier texte correspondant aux octets suivants : 43 0A 4F 0A 55 0A 43 0A 4F 0A 55 0A.
2) Sous Windows, donnez les octets correspondant au fichier précédent.
2. Langages de programmation 2.1 Définitions
Langage machine Langage assembleur
Langage évolué
ordinateur homme
compilateur
assembleur
• Langage machine. L’ordinateur ne sait exécuter qu’un nombre limité d’opérations élémentaires codées en binaire. C’est le langage machine. Les instructions sont généralement codées avec un ou plusieurs octets.
• Langage assembleur (ou d’assemblage). Quand le programmeur veut écrire en langage machine, au lieu d’écrire directement les instructions en binaire, il utilise un langage un peu plus parlant quoique strictement équivalent, le langage assembleur. Celui-ci traduit les codes binaires par des mnémoniques. Chaque microprocesseur a son propre langage assembleur. Les langages machine et assembleur possèdent pratiquement les mêmes instructions.
ADD A, B ≡ additionner (code 0101) les valeurs A (adresse mémoire 010010) et B (adresse mémoire 010011) ≡ 0101010010010011.
• Langage évolué. C’est un langage général utilisable sur n’importe quel ordinateur. Il y en a plusieurs : fortran, pascal, basic, C/C++, ADA, java, … Exemple :
Y = A*X + 2*B + C
à partir des variables A, B, C et X, cette instruction calcule l’expression mathématique et range le résultat dans Y. Pour calculer le même genre d’instruction en assembleur, il faudrait beaucoup d’instructions élémentaires (d’autant qu’il n’y généralement pas de multiplication native dans un microprocesseur).
Dans tous les langages évolués, on trouvera les notions suivantes :
Ö La variable : c’est un nom donné à un emplacement de la mémoire vive destiné à contenir une information. La nature de cette information (entier, caractère, adresse, …) est appelée son type.
Ö L’affectation : elle permet de calculer la valeur d’une expression et de la ranger dans une variable.
Ö Le test conditionnel : il permet de faire un choix du genre « si le cours m’intéresse alors j’écouterai le professeur, sinon je penserai à autre chose ».
Ö La répétition : elle permet d’exécuter une action jusqu’à satisfaire une condition du genre « tant que je ne serai pas suffisamment bon en C, je travaillerai le cours de programmation ».
2.2 Méthodologie pour l’écriture d’un programme
problème
analyse
programmation
Compilation/assemblage
Edition de liens
tests
programme exécutable algorithme
programme en langage évolué
programme en langage machine
L’analyse du problème commence en général par une réécriture du problème sous une forme textuelle précise ou bien mathématique. On passe ensuite à l’écriture de l’algorithme.
Définition : un algorithme est une suite finie de règles à appliquer dans un ordre déterminé à un nombre fini de données pour arriver, en un nombre fini d'étapes, à un certain résultat, et cela indépendamment des données. Un algorithme est donc une suite finie d'instructions qui sert à réaliser un travail, un peu à la manière d’une recette de cuisine.
En règle générale, un algorithme doit tenir sur une page. Si tel n’est pas le cas, il faut le diviser en plusieurs parties indépendantes à l’aide de fonctions de façon à ce que chaque partie tienne sur une page. On peut l’écrire de deux manières :
• A l’aide d’un langage algorithmique (ou un langage de programmation comme le C),
• A l’aide d’un ordinogramme.
Dans ce cours, nous commencerons l’écriture des algorithmes avec un langage algorithmique simplifié (voir sa définition au §3.16), puis nous passerons progressivement au langage C dès que nous aurons vu sa syntaxe. Nous n’utiliserons les ordinogrammes qu’à titre d’exemple dans les deux exercices suivants.
Exercice 2.1 : calcul de la somme des N premiers nombres entiers. On dispose d’une fonction LireEntier() qui permet de lire la valeur de N au clavier.
Exercice 2.2 : calcul de la moyenne de N notes. On dispose de la fonction LireEntier() qui permet de lire la valeur de N ainsi que la valeur d’une note au clavier.
2.3 Le langage C
Le langage C a été inventé vers 1972 pour réécrire Unix dans un langage évolué et le porter sur d’autres machines que le PDP 7 pour laquelle il avait été écrit à l’origine en assembleur.
Le langage C a été normalisé ANSI en 1988 (le C pré-ANSI est appelé le C Kernighan et Ritchie ou K&R ou encore compatible).
C’est un langage qui, par ses origines, est assez proche du matériel et donc bien adapté à l’électronique. C’est un langage :
Evolué : il dispose des structures de contrôle d’un langage évolué ainsi que de la récursivité.
Impératif : il faut déclarer les variables (et leur type) et les fonctions avant de pouvoir les utiliser.
Modulaire : le programme peut être découpé en modules indépendants qui seront compilés séparément. C’est l’édition de liens qui regroupe les morceaux et crée l’exécutable.
Compilé : par opposition avec un langage interprété qui n’est pas traduit en langage machine.
Lors de l’exécution d’un programme interprété, un programme spécialisé, l’interpréteur, se charge d’exécuter les instructions du langage sur une machine donné. Le langage compilé est beaucoup plus rapide que le langage interprété.
Efficace : comme il est assez proche de l’ordinateur, le compilateur peut assez facilement optimiser le code machine pour qu’il soit le plus rapide possible. Il est peu probable qu’un programmeur humain écrive en assembleur un programme plus rapide qu’en C (sauf pour un microprocesseur spécialisé de type DSP).
Très permissif : contrairement à Pascal ou à Fortran, le C permet d’écrire des absurdités que le compilateur ne verra pas, notamment dans les opérations arithmétiques. Tout est permis, le programmeur est supposé savoir ce qu’il fait ! ! !
Nous allons maintenant voir comment on définit en langage C : Ö les variables,
Ö les affectations,
Ö les entrées-sorties (clavier, écran), Ö les structures de choix,
Ö les structures de répétition conditionnelle.
Avec ces éléments, vous serez à même d’écrire des programmes élémentaires.
3. Bases du langage C
3.1 Les variables
Une variable est un nom qui sert à repérer un emplacement en mémoire, dont on peut faire évoluer la valeur au fil du déroulement du programme. Les noms de variables sont sensibles à la casse. Les caractères qui les composent doivent être choisis parmi les 26 lettres majuscules et minuscules de l’alphabet, les chiffres de 0 à 9 et l’underscore _. Le premier caractère du nom ne doit pas être un chiffre. Il ne doit pas y avoir d’espace ni de caractères accentués dans le nom. Le compilateur traite les noms de variable jusqu’à 32 caractères.
Noms corrects : A, A1, n_1, racine_carree
Noms incorrects : 1a, nombre 1, racine_carrée, nombre-1
Une variable peut contenir plusieurs types de données : nombre entier, nombre réel, caractère,
…
⇒ Il faut donc spécifier le type de la variable lors de sa déclaration.
A chaque type de variable correspond un nombre d’octets destinés à stocker un nombre limité de valeurs différentes. Exemple :
Un entier non signé (>0) est stocké à l’aide de 4 octets et donc sa valeur est comprise entre 0 et 232-1 (soit 4 294 967 295).
Exemple de types : 1) int = nombre entier, 2) float = nombre réel, 3) char = caractère.
Exemples de déclaration : int n, p;
float valeur, X1, X2;
char reponse;
Vous devez écrire vos instructions de déclaration avant les instructions d’exécution.
Le compilateur réserve un emplacement mémoire pour la variable, mais cet emplacement n’est pas initialisé. Vous ne pouvez prévoir quelle valeur sera stockée dans la variable si vous ne l’initialisez pas vous-même. Deux méthodes :
Ö Au moment de la déclaration.
int n = 0, p = 100;
Ö Dans le programme avant sa première utilisation.
int n;
…
n = 0;
3.2 L’instruction d’affectation
L’instruction d’affectation a pour rôle :1) de calculer la valeur de l’expression figurant à droite du signe =, 2) de ranger le résultat dans la variable se trouvant à gauche du signe =.
Exemple : int n, p;
n = 10;
p = 2*n – 3;
instructions n p commentaire
déclaration - - les variables ne sont pas initialisées n = 10 ; 10 - affectation d’une constante p = 2*n – 3 ; 10 17 calcul de l’expression puis affectation
C’est après l’exécution de l’instruction que la variable à gauche du signe = change de valeur.
Attention à ne pas confondre l’affectation avec l’égalité mathématique.
Exemple 1 : a = b;
Mathématiquement, cette expression signifie que a est égal à b pendant toute la durée du problème.
En informatique, cette expression signifie que a prend la valeur de b au moment de l’exécution de l’instruction.
⇒ en informatique, a = b; n’est pas équivalent à b = a; (alors que c’est la même chose en mathématique)
Exemple 2 :
instructions a b commentaire
déclaration - - les variables ne sont pas initialisées
a = 5 ; 5 - affectation d’une constante
b = a + 1; 5 6 l’action de cette instruction est purement instantanée (au moment de son exécution)
a = 2; 2 6 le changement de la valeur de a n’affecte plus b (qui ne passe pas à 3)
Exemple 3 : a = a + 1;
Mathématiquement, cette expression n’a pas de sens.
En informatique, cette expression signifie que la nouvelle valeur de a (après exécution de l’instruction) est égale à l’ancienne valeur de a (avant exécution de l’instruction) + 1. C’est une incrémentation.
Exemple 4 : a + 5 = 3;
Mathématiquement, cette expression a un sens. C’est une banale équation.
En informatique, cette expression est fausse. On ne peut attribuer une valeur qu’à une variable et pas à une expression.
Exercice 3.1 : remplir les tableaux suivants.
instructions a b commentaire
déclaration a = 5 ;
b = a + 4;
a = a + 1;
b = a - 4;
instructions n1 n2 commentaire
déclaration n1 = 5 ;
n2 = 7 ; n1 = n2;
n2 = n1;
instructions n1 n2 commentaire
déclaration n1 = 5 ;
n2 = 7 ; n2 = n1;
n1 = n2;
Exercice 3.2 : soit trois variables entières. Compléter le programme suivant pour permuter leurs valeurs, de telle sorte que a → b, b → c et c → a.
main() {
int a = 1, b = 2, c = 3, tmp;
/* à compléter */
printf("a = %d, b = %d, c = %d\n ", a, b, c);
}
3.3 Les types entier
Les types entiers permettent de représenter une partie des nombres entiers naturels et relatifs :
type nombre de bits intervalle
unsigned short int 16 0 à (216-1 = 65535)
short int 16 (-215=-32768) à (215-1 = 32767)
unsigned long int 32 0 à (232-1= 4 294 967 295)
long int 32 (-231=-2 147 483 648) à (231-1 = 2 147 483 647) int 32* (-231=-2 147 483 648) à (231-1 = 2 147 483 647)
* : le type int dépend du compilateur utilisé qui est généralement lié au système d’exploitation (ou au compilateur). Sur un système 32 bits, il est généralement codé sur 32 bits. Il est préférable de spécifier short ou long sinon le comportement de vos programmes changera avec la machine cible (absence de portabilité).
Les nombres signés utilisent le codage en complément à 2. Les constantes de type int s’écrive comme en mathématique :
short int n, o, p;
n = 10;
o = +15;
p = -2542;
Les opérateurs mathématiques sont de deux types : les opérateurs unaires qui ne portent que sur un terme et les opérateurs binaires qui portent sur deux termes.
symbole opération
+, - addition, soustraction
* multiplication
/ division entière
% reste de la division entière (modulo) - opérateur unaire de négation (ex : -b;)
Lorsque plusieurs opérateurs apparaissent dans une même expression, le compilateur respecte des règles de priorités qui sont celles de l’algèbre traditionnelle.
priorité symbole commentaires
max - (négation)
moy *, /, % en cas d’égalité, le calcul s’effectue de gauche à droite min +, - en cas d’égalité, le calcul s’effectue de gauche à droite
En utilisant des parenthèses, vous pouvez outrepasser ces règles de priorité en forçant le calcul préalable de l’expression qu’elles contiennent.
Exercice 3.3 : quelles sont les valeurs des expressions suivantes ?
main() {
int n = 8, p = 13, q = 29, result;
result = n + p / q;
printf("resultat = %d\n ", result);
result = n + q / p;
printf("resultat = %d\n ", result);
result = (n + q) / p;
printf("resultat = %d\n ", result);
result = n + p % q;
printf("resultat = %d\n ", result);
result = n + q % p;
printf("resultat = %d\n ", result);
result = (n + q) % p;
printf("resultat = %d\n ", result);
result = n + p / n + p;
printf("resultat = %d\n ", result);
result = (n + p) / (n + p);
printf("resultat = %d\n ", result);
}
3.4 Les types flottants (réels)
Les types flottants permettent de représenter, de manière approchée, une partie des nombres réels. La valeur d’un réel ne peut être ni trop grande, ni trop petite, ni trop précise. Le codage en binaire est de la forme :
signe exposant (signé) mantisse (non signée)
type nombre de bits format valeur max (valeur min*)
précision max
float 32 1 + 8 + 23 2128 ≈ 10+38 2-23 ≈ 10-7 double 64 1 + 11 + 52 21024 ≈ 10+308 2-52 ≈ 10-15 long double 80 1 + 15 + 64 216384 ≈ 10+4932 2-64 ≈ 10-19
* : la valeur min se détermine à partir de la valeur max (ex : max = 10+38 ⇒ min = 10-38)
Les flottants s’écrivent en C sous la forme : mantisse + exposant (avec un point décimal et pas une virgule) tels que :
+4.25E+4 ou encore -58.0e-25
C’est la notation scientifique traditionnelle comme sur une calculatrice. Le E signifie 10 puissance. Les constantes de type flottant s’écrivent sous la forme :
float n, o, p;
n = 12.43;
o = -0.38e-33;
p = -4.0;
On retrouve les mêmes opérateurs binaires (portant sur 2 termes) que pour les entiers (sauf le
%) ainsi que l’opérateur unaire de négation. Les règles de priorité restent les mêmes.
Attention : l’expression 5/2 sera calculée en entier (résultat = 2) même si le résultat est rangé dans un flottant. Il faut utiliser 5./2. pour obtenir 2,5.
main() {
float p;
p = 5/2;
printf("resultat = %f\n",p);
p = 5.0/2.0;
printf("resultat = %f\n",p);
}
3.5 Les conversions de type
En C, vous pouvez mélanger dans une expression des variables de types différents sans effectuer aucune conversion contrairement au langage Pascal où la conversion est obligatoire (le langage est dit fortement typé). La conversion en C est implicite (elle est effectuée automatiquement par le compilateur) alors qu’elle est explicite en Pascal, en Fortran ou en Ada.
Le compilateur C réalise automatiquement la conversion de type en respectant : 1) la règle de priorité des opérateurs,
2) la règle « du type le plus petit (en nombre d’octets) vers le type le plus grand » (c’est la règle la moins dégradante pour les données). En général, tous les types plus petits qu’un int sont systématiquement convertis en int avant toute autre conversion.
Exemple : int n, p;
float x, y;
Dans l’expression y = n + x, n est d’abord converti en flottant puis l’addition est effectuée en flottant et le résultat est rangé dans le flottant y.
Dans l’expression y = n*p + x, n*p est calculé en entier, le résultat est converti en flottant puis additionné à x en flottant et le résultat est rangé dans y.
Exercice 3.4 : soient les instructions suivantes.
int n, p;
float x;
n = 10;
p = 7;
x = 2.5;
Donnez le type et la valeur des expressions suivantes : x + n % p;
x + n / p;
(x + n) / p;
5. * n;
(n + 1) / n;
(n + 1.0) / n;
Il est à noter que la conversion d’int vers float est non dégradante (on ne perd rien dans la conversion) alors que la conversion float → int est dégradante (on prend la partie entière du réel que l’on met dans l’entier, la partie fractionnaire est perdue).
Il est possible de convertir explicitement une expression grâce à un cast en mettant l’expression entre parenthèse et en la précédant de (type). Par exemple :
x + (float)n/p;
Exercice 3.5 : quels résultats donne le programme suivant ?
main() {
int n=15, p=4;
float x;
x = n/p;
printf("resultat = %f\n",x);
x = (float)n/p;
printf("resultat = %f\n",x);
x = (float)(n/p);
printf("resultat = %f\n",x);
}
Une règle élémentaire de prudence consiste à ne jamais mélanger des types signés avec des types non signés car les conversions n’ont généralement pas de sens. Si vous introduisez une variable non signée dans une expression signée, utilisez un cast (exemple typique, un indice de boucle).
main() {
long int n=-15, p; /* signé */
unsigned long int x; /* non signé */
...
p = n + (long int)x;
...
}
3.6 Les types char
Les types char permettent de coder des caractères en utilisant le code ASCII. Mais ils peuvent aussi être utilisés comme des petits entiers signés ou non signés ou bien directement en binaire si le programme travaille sur des octets.
type nombre de bits intervalle
unsigned char 8 0 à (28-1 = 255) char 8 (-27=-128) à (27-1 = 127)
Les constantes de type char s’écrivent sous la forme :
char n, o, p;
n = ‘s’; /* un caractère normal */
o = 100; /* un petit entier */
p = ‘\n’; /* le caractère spécial line feed */
p = 0x0A ; /* toujours un line feed */
Les caractères imprimables sont simplement écrits entre quotes et les caractères spéciaux utilisent l’antislash (backslash).
Tout ce qui s’applique aux types entiers s’applique aux types char (opérateurs, priorités, conversions). C’est une des grandes qualités du C (mais c’est aussi un défaut) de pouvoir effectuer des opérations arithmétiques avec des variables de type char. Les possibilités de confondre caractères et petits entiers sont nombreuses.
Exemple : main() {
char n;
n = 'A';
printf("Caractere = %c\n",n);
printf("Code ASCII en hexadecimal = %x\n",n);
printf("Code ASCII en decimal = %d\n",n);
}
Exercice 3.6 : soient trois variables c1, c2 et c3 de type char. Ecrivez un programme permettant de permuter le contenu de ces variables.
3.7 Communiquer avec le programme : les entrées-sorties standard
La manière la plus simple de lire un caractère à la fois sur l’entrée standard (qui est normalement le clavier) est d’utiliser la fonction getchar :
int getchar(void)
type de la variable d’entrée type de la variable de sortie
Cette fonction n’accepte en entrée aucune variable (entrée de type void qui veut dire vide) et retourne un entier (type int) dont la valeur est égale au code ASCII du caractère tapé au clavier suivi d’un « retour chariot ».
Pour la sortie d’un caractère, on utilise la fonction putchar :
int putchar(int)
Cette fonction accepte un entier en entrée (celui retourné par getchar() par exemple) et retourne un entier (type int). putchar(c) envoie le caractère c sur la sortie standard (l’écran par défaut) et retourne le caractère écrit ou -1 en cas d’erreur.
Exemple 1 : ce programme accepte un caractère en entrée puis le restitue en sortie.
#include <stdio.h> /* déclaration de fonctions getchar et putchar */
main() {
int c;
c=getchar();
putchar(c);
}
Exemple 2 : ce programme accepte un caractère en entrée puis le restitue en sortie et recommence tant que vous ne tapez pas Ctrl-d pour sortir de la boucle (Ctrl-z sous Windows).
#include <stdio.h>
main() {
int c;
c=getchar();
while (c != EOF) {
putchar(c);
c=getchar();
} }
Sous Unix, on peut rediriger la sortie standard (StdOut) d’un programme (l’écran par défaut) vers un fichier grâce à la commande :
prog > sortie.txt
Mais il est aussi possible de remplacer le clavier comme entrée standard (StdIn) par un fichier en utilisant la commande :
prog < entree.txt
De plus, il existe encore une autre sortie, la sortie des erreurs (StdErr, par exemple les messages d’erreurs du compilateur), qui peut être redirigée vers un fichier grâce à :
prog 2> error.txt
ou bien vers le même endroit que la sortie standard en tapant :
prog 2>&1
C’est grâce aux entrées-sorties standards que le pipe fonctionne. Quand on tape :
prog1 | prog2
on redirige la sortie standard de prog1 vers l’entrée standard de prog2 (le pipe « branche » StdOut1 sur StdIn2).
Reprenons l’exemple 2. Si vous créez avec un éditeur un petit fichier texte (entree.txt) comportant des lettres majuscules et minuscules ainsi que des chiffres puis que vous tapez la commande suivante :
exemple2 < entree.txt > sortie.txt
Vous obtenez un fichier sortie.txt identique à entree.txt. Ainsi, la fonction getchar() peut accepter un flot de texte issu d’un fichier plutôt qu’un seul caractère au clavier et la fonction putchar peut renvoyer un caractère dans un fichier.
Exemple 3 : le programme suivant accepte un caractère en entrée puis le restitue en sortie mais converti en minuscule et recommence tant que vous ne tapez pas Ctrl-d pour sortir de la boucle (en manuel au clavier) ou tant que le programme n’a pas atteint la fin du fichier EOF (pour une entrée via un fichier). La fonction tolower est définie dans ctype.h. Elle convertit les lettres majuscules en minuscules et retourne les autres caractères tels quels.
#include <stdio.h>
#include <ctype.h>
main() {
int c, cm;
c=getchar();
while (c != EOF) {
cm = tolower(c) ; putchar(cm);
c=getchar();
} }
Cet exemple fonctionne aussi bien au clavier qu’avec le fichier entree.txt.
Il existe en C des instructions plus sophistiquées pour afficher du texte à l’écran ou bien pour lire du texte à partir du clavier : printf et scanf.
3.8 L’instruction printf
Nous avons déjà vu à plusieurs reprises des exemples simples de cette instruction : int n=10, p=25;
printf("nombre : %d, valeur %d ", n, p);
On a entre double quotes ("") soit du texte, soit un ou plusieurs codes de format (%d) qui seront remplacés par la valeur d’une ou de plusieurs variables lors de l’affichage. On trouve ensuite les variables à afficher séparées par une virgule. Il doit y avoir autant de codes de format que de variables à afficher. Il existe de nombreux codes de formats (%d correspond à un entier) pour tous les types de variables. Ils commencent tous par un %. Tout ce qui n’est pas un code de format mais qui se trouve entre doubles quotes est affiché tel quel.
On peut omettre les codes de format pour afficher seulement du texte : printf("bonjour");
Il est possible, quoique non recommandé, de remplacer une variable par une expression : printf("la somme de %d et de %d est %d", n, p, n+p);
Le code de format %c permet d’afficher un caractère : int n=10;
char c=’c’;
printf("nombre : %d, caractère %c ", n, c);
Le code de format %e permet d’afficher un nombre flottant en notation exponentielle et le code %f permet d’afficher un nombre flottant en notation décimale.
float x=1.23456e4;
printf("notation exp : %e, notation déc %f ", x, x);
Le formatage des données permet de contrôler le nombre de chiffres affichés à l’écran. Dans ce texte, le caractère ^ sert à matérialiser un espace lors de l’affichage. En insérant un nombre dans le code de format après le %, on précise le gabarit d’affichage, c’est-à-dire le nombre minimal de caractères à utiliser pour afficher la valeur du nombre. Si le nombre peut s’afficher avec moins de caractères, printf le fera précéder d’un nombre suffisant d’espaces. Par contre, si le nombre nécessite plus de caractères pour s’afficher que vous en avez spécifiés, printf utilisera le nombre de caractères nécessaire. Exemples :
printf("%3d", n);
valeur affichage
n = 20 ^20
n = 3 ^^3
n = -5 ^-5
n = 2358 2358
n = -5200 -5200
printf("%10f", x);
valeur affichage
x = 1.2345 ^^1.234500
x = 12.345 ^12.345000
x = 1.2345E5 123450.00000
Par défaut, les flottants sont affichés avec 6 chiffres à droite du point décimal. Dans l’exemple précédent, %10f signifie « en utilisant 10 caractères (y compris le .) avec au moins 6 chiffres après le point ». Il est possible de modifier ce nombre est écrivant %A.Bf qui signifie « au minimum A caractères dont au moins B après le point décimal ». Exemples :
printf("%10.3f ", x);
valeur affichage
x = 1.2345 ^^^^^1.234
x = 1.2345E3 ^^1234.500
x = 1.2345E7 12345000.000
printf("%12.4e", x);
valeur affichage
x = 1.2345 ^^1.2345e+00
x = 123.456789E8 ^^1.2345e+10
Chaque instruction printf affiche ses informations à la suite de celles qui ont déjà été affichées par un printf précédent, ce qui revient à dire qu’il n’y a pas de passage à la ligne entre deux printf. Exemple :
int n=10;
char c=’c’;
printf("nombre : %d, ", n);
printf("caractère %c", c);
Les deux printf précédent affichent exactement la même chose que le printf suivant : printf("nombre : %d, caractère %c", n, c);
Le changement de ligne est réalisé avec le caractère non imprimable \n placé au milieu ou à la fin d’un printf :
printf("nombre : %d\ncaractère %c", n, c);
Il est aussi possible d’afficher une tabulation avec \t comme dans : printf("\n\tTerminé\n\n");
Exercice 3.7 : soient les déclarations : int qte=50;
char cat=’B’;
char art=’S’;
Ecrivez le programme permettant d’afficher les variables de la manière suivante : 50 articles S, de la catégorie B
Quel sera le résultat si art est déclarée de la manière suivante : char art=’\n’;
Exercice 3.8 : écrivez le programme qui calcule le prix TTC d’un nombre donné d’articles d’un prix unitaire égal à 10.51 euros, compte tenu d’un taux de TVA de 19,6 %. Les résultats seront affichés de la manière suivante :
nombre d'articles : 15 prix unitaire HT : 10.51 prix total TTC : 188.55
3.9 L’instruction scanf
La lecture au clavier d’un entier que l’on range dans la variable n s’écrit en langage C : scanf("%d", &n);
Lorsque le programme exécute cette instruction, il attend que vous tapiez une valeur au clavier suivie d’un retour chariot (↵). Le format (entre " ") est similaire à celui de l’instruction printf, mais on voit que la variable est spécifiée par &n (et pas seulement n). Le & qui précède n signifie « adresse de n ». Par contre, le gabarit, comme %3d, est interdit. Exemples d’entrées entières :
12 ↵ 0 ↵ -1 ↵ +1234 ↵
La lecture d’un nombre flottant s’effectue avec l’instruction suivante : scanf("%f", &x);
scanf("%e", &x);
Les codes %e et %f jouent strictement le même rôle. Exemples d’entrées flottantes : -12 ↵
0.5 ↵ -1. ↵
+1.2e-4 ↵ 32e45 ↵
Attention : il ne faut pas placer de texte avant ou après le code de format ni même d’espace.
L’instruction suivante n’est pas légale :
scanf("Entrez une valeur : %f", &x);
Il faut écrire :
printf("Entrez une valeur : ");
scanf("%f", &x);
Si vous souhaitez lire deux valeurs numériques sur la même ligne, vous pouvez utiliser l’instruction (sans aucun espace entre les ") :
scanf("%d%d", &n, &p);
Les deux valeurs doivent être saisies au clavier séparées par au moins un espace.
12^-15 ↵
L’entrée suivante est identique à la précédente car les codes de format %d ou %f sautent tous les blancs qui précèdent une valeur.
^^^12^^^-15 ↵
Exercice 3.9 : modifiez le programme de l’exercice 3.8 afin de pouvoir saisir le nombre d’articles et le prix unitaire.
Attention : lorsque l’utilisateur fournit trop peu de données, scanf continue d’attendre les données suivantes. Exemple :
scanf("%d%d", &n, &p);
L’entrée 12^-15 ↵ donnera le même résultat que 12 ↵ suivi de -15 ↵. Supposons qu’à cette même instruction, vous fournissiez :
12^-15^1254 ↵
Il y a maintenant trop de données. scanf va mettre bien 12 dans n, -15 dans p mais il va garder 1254 en mémoire dans le buffer clavier pour la prochaine lecture avec scanf. En cas de besoin, vous pouvez purger le buffer clavier à l’aide de l’instruction :
fflush(stdin);
Le code de format %c permet de lire un caractère. Ainsi, l’instruction suivante :
char c1 ; ...
scanf("%c", &c1);
lit un caractère au clavier et le range dans c1. Si vous souhaitez lire plusieurs caractères à la suite, il suffit de les taper successivement et de les faire suivre d’un retour chariot. Exemple : scanf("%c%c%c", &c1, &c2, &c3) ;
Avec l’entrée : abc↵
a est rangé dans c1, b dans c2 et c dans c3. L’espace n’est en aucun cas un séparateur comme pour les nombres. C’est un caractère comme un autre. Si vous tapez :
a^bc↵
a va dans c1, un espace va dans c2, b va dans c3 et c est disponible pour la lecture suivante.
En règle générale, évitez de lire plusieurs valeurs (numériques ou caractères) avec une même instruction scanf.
Exercice 3.10 : modifiez le programme de l’exercice 3.7 afin de pouvoir saisir le nom de l’article, sa catégorie ainsi que la quantité.
3.10 Structure de choix : l’instruction if
L’instruction if permet de réaliser des choix dans un programme comme le montre l’exemple suivant :
main() {
int n, p;
printf("Donnez deux nombres entiers : ");
scanf("%d%d", &n, &p);
if (n < p)
printf("croissant\n");
else
printf("non croissant\n");
printf("au revoir\n");
}
Sa compréhension est assez intuitive. Si n < p, alors on exécute l’instruction : printf("croissant\n");
sinon on exécute l’instruction :
printf("non croissant\n");
Dans tous les cas, on exécute la dernière instruction du programme : printf("au revoir\n");
Le compilateur se moque de la présentation des instructions. Ainsi, la forme : if (n < p)
printf("croissant\n");
else
printf("non croissant\n");
est strictement équivalente à :
if (n < p) printf("croissant\n"); else printf("non croissant\n");
Mais il est fortement conseillé d’utiliser la première forme pour une meilleure lisibilité du programme et donc une maintenance plus facile.
Dans l’exemple précédent, chacune des deux parties du choix se limite à une instruction (printf). Il est possible d’en placer plusieurs à condition de les placer dans un bloc entre {}. Voyons une nouvelle version de notre exemple :
main() {
int n, p, maxi;
printf("Donnez deux nombres entiers : ");
scanf("%d%d", &n, &p);
if (n < p) {
maxi = p;
printf("croissant\n");
}
else {
maxi = n;
printf("non croissant\n");
}
printf("Le plus grand des deux nombres est : %d\n", maxi);
}
L’instruction if correspond maintenant à : if (n < p) bloc_instructions_1 else bloc_instructions_2
ce qui équivaut dans notre exemple à : si n < p alors maxi prend la valeur de p et printf sinon maxi prend la valeur de n et printf.
Les blocs d’instructions peuvent contenir n’importe quelles instructions du C y compris d’autres instructions if. Un bloc peut ne contenir qu’une seule instruction :
if (n < p) {
printf("croissant\n");
}
ou bien une instruction vide (parfaitement légale en C) : if (n < p) {
; }
ou bien aucune instruction : if (n < p) {
}
Il est possible d’avoir un bloc dans le if et une instruction dans le else : if (n < p) {
maxi = p;
printf("croissant\n");
} else
printf("non croissant\n");
ou le contraire.
if (n < p)
printf("croissant\n");
else {
maxi = n;
printf("non croissant\n");
}
La branche else de l’instruction if n’est pas obligatoire. Ainsi, l’enchaînement : if (n < p)
printf("croissant\n");
printf("au revoir\n");
est tout à fait acceptable. Si n < p, le programme exécute le printf. Dans tous les cas, le dernier printf est exécuté. Cette remarque est aussi valable avec un bloc d’instructions : if (n < p) {
maxi = p;
printf("croissant\n");
}
printf("au revoir\n");
D’une manière générale, l’instruction if se présente de la manière suivante (La branche else est optionnelle) :
if (condition)
instruction_1 [else
instruction_2]
où instruction_1 et instruction_2 sont :
• une instruction simple (terminée par un ;),
• une instruction structurée (comme un autre if),
• un bloc d’instructions entre {}.
Voyons maintenant la condition plus en détail.
3.11 Structure de choix : les conditions en C
Les opérateurs de comparaisons ont deux significations suivant qu’ils s’appliquent à deux types numériques (float ou int) ou à deux types caractères (char) :