Programmation Structurée en Langage C


Télécharger Programmation Structurée en Langage C
3.53.5 étoiles sur 5 a partir de 1 votes.
Votez ce document:

Télécharger aussi :


cole Centrale Marseille

Programmation StructurØe en Langage C

StØphane Derrode

MathØmatique et Informatique RØvision 2.5, 2006.


Table des mati?res

1     En guise d’introduction      7

1.1    Quelques rep?res historiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .               7

1.2    PrØsentation du langage C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  8

1.3    Premier programme en C   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .   9

1.4    Langage C et programmation structurØe . . . . . . . . . . . . . . . . . . . . . . . . .          10

2     Types et variables     13

2.1    Types de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .           14

2.2    Constantes associØes aux types de base . . . . . . . . . . . . . . . . . . . . . . . . . .        15

2.3    Variables de base : dØclaration et initialisation . . . . . . . . . . . . . . . . . . . . . .     16

2.4    Types dØrivØs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .          18

2.5    Conversion de types            . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .            19

3     Lire et Øcrire               21

3.1    Exemple de lecture et Øcriture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .               21

3.2    Les fonctions printf() et scanf() en dØtail . . . . . . . . . . . . . . . . . . . . . . .              22

4     OpØrateurs et expressions   25

4.1    OpØrateurs unaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .       25

4.2    OpØrateurs binaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     27

4.3    OpØrateur ternaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .       30

4.4    PrØcØdence des opØrateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .              30

5     Instructions de contr le           33

5.1    Instructions conditionnelles ou de sØlection . . . . . . . . . . . . . . . . . . . . . . . . 33 5.2 Instructions de rØpØtition ou d’itØration . . . . . . . . . . . . . . . . . . . . . . . . . 37

       5.3     Ruptures de sØquence                             . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                               40

6     Fonctions     45

6.1    DØ nition d’une fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 6.2 Passage des param?tres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

       6.3                          Utilisation de pointeurs en param?tres . . . . . . . . . . . . . . . . . . . . . . . . . .                            47

       6.4                          Conversions de type des param?tres . . . . . . . . . . . . . . . . . . . . . . . . . . . .                            47

       6.5                                Retour de fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                   48

       6.6     RØcursivitØ                                 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                    48

       6.7       Param?tres de la fonction principale                         . . . . . . . . . . . . . . . . . . . . . . . . . . .                          48

       6.8                               tapes d’un appel de fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                               49

7     PrØprocesseur          51

7.1      Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .      51

7.2      Inclusion de         chiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .             52

7.3      Variables de prØcompilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .             52

7.4      DØ nition de macro-expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .           53

7.5      SØlection de code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    53

8     Compilations sØparØes         55

8.1      Programme et     chiers sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .           55

8.2      VisibilitØ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .          56

8.3      Prototypes des fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                59

8.4      Fonctions externes et fonctions dØ nies ultØrieurement  . . . . . . . . . . . . . . . . .         60

8.5      DØclarations et dØ nitions multiples         . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

9     Pointeurs et tableaux              65

9.1      Tableaux               une dimension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     65

9.2      ArithmØtique d’adresse et tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . .        66

9.3      Tableaux multi-dimensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .              67

9.4      Pointeurs et tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  67

9.5      Tableau de pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .   69

10    Structures, unions, ØnumØrations et types synonymes           71

10.1   Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .          71

10.2   Unions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .            74

10.3   numØrations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .         76

10.4   Types synonymes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     77

11    EntrØes-sorties de la biblioth?que standard  79

11.1   EntrØes-sorties standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

11.2   Ouverture d’un  chier      . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .               82

11.3   Fermeture d’un  chier      . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .               84

11.4   Acc?s au contenu du         chier      . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .      84

11.5   EntrØes-sorties formatØes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .            89

11.6   DØplacement dans le       chier      . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .      94

A    Table des caract?res ASCII     107

B    Mots rØservØs du C 108

C    Quelques pointeurs sur Internet        109

C.1 Quelques cours de programmation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 C.2 Librairies scienti ques et graphiques . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 C.3 Sources et sites de programmeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

Liste des          gures                                                                                                                                                                     111

Liste des tableaux                                                                                                                                                                        111

Liste des programmes                                                                                                                                                                113

Bibliographie                                                                                                                                                                                 115

6                                                                                                                                                                      TABLE DES MATI¨RES

Chapitre 1

En guise d’introduction

Sommaire

                                              1.1 Quelques rep?res historiques. . . . . . . . . . . . . . . . . . . . . . . . . .                                      7

                                               1.2 PrØsentation du langage C . . . . . . . . . . . . . . . . . . . . . . . . . . .                                      8

                                                1.3 Premier programme en C . . . . . . . . . . . . . . . . . . . . . . . . . . .                                        9

                                            1.4 Langage C et programmation structurØe . . . . . . . . . . . . . . . . . .                                 10

1.1           Quelques rep?res historiques

Le langage C est liØ la conception du syst?me UNIX par les Bell-Labs [JR78]. Les langages ayant in uencØs son dØveloppement sont :

ë Le langage BCPL de M. Richards - 1967, ë Le langage B dØveloppØ aux Bell-Labs - 1970.

Ces deux langages partagent avec le C :

ë Les structures de contr le, ë L’usage de pointeurs, ë La rØcursivitØ.

Les deux langages prØdØcesseurs du C avaient la particularitØ d’Œtre sans type. Ils ne connaissent que le mot machine, ce qui leur donne un degrØ de portabilitØ nul. Le langage C comble ces lacunes en introduisant des types de donnØes tels que l’entier ou le caract?re. Les dates marquantes de l’histoire du C sont les suivantes :

1972   La premi?re version du C est Øcrite en assembleur par B. Kerninghan et D. Ritchie,

1973   Alan Snyder Øcrit un compilateur C portable,

1989   Sortie du premier document normalisØ appelØ norme ANSIX3-159,

1990   RØalisation du document nal normalisØ aupr?s de l’ISO: ISO/IEC 9899 [98990].

Jusqu’en 1987, il n’y avait pas de norme. Le livre The C programming language [RK78] contient une dØ nition prØcise du langage C appelØe C reference manual . C’est principalement de ce livre (et de sa traduction fran aise [KR94]) que s’inpire ce support de cours.


8                                                                                                                          CHAPITRE 1. EN GUISE D’INTRODUCTION

1.2           PrØsentation du langage C

Le langage C est un langage de bas niveau dans la mesure oø il permet l’acc?s des donnØes que manipulent les ordinateurs (bits, octets, adresses) et qui ne sont pas toujours disponibles dans les langages ØvoluØs tels que le Fortran, le Pascal ou ADA.

Le langage C a ØtØ con u pour l’Øcriture de syst?mes d’exploitation. Plus de 90% du noyau du syst?me UNIX est Øcrit en C. Le compilateur C lui-mŒme est Øcrit en grande partie en langage C ou partir d’outils gØnØrant du langage C. Il en est de mŒme pour les autres outils de la cha ne de compilation : assembleurs, Øditeurs de liens, prØ-processeurs. De plus, tous les utilitaires du syst?me UNIX sont Øcrits en C ( shell , outils).

Il est cependant su samment gØnØral pour permettre de dØvelopper des applications variØes de type scienti que, ou encore pour l’acc?s aux bases de donnØes (application de gestion). Le langage C est disponible sur pratiquement toutes les plate-formes, de l’ordinateur personnel jusqu’aux gros calculateurs scienti ques, en passant par les stations de travail. De nombreux logiciels du domaine des ordinateurs personnels, tels que Microsoft Word ou Excel sous le syst?me Windows, sont eux-aussi Øcrits partir du langage C, ou de son successeur orientØ objet C++ [Str86]. Le C est un langage impØratif classique qui comporte ë des types standards de base (entiers, rØels, caract?res), ë des structures de contr le ( Si alors, sØquences, boucles), ë des constructions de types (tableaux, unions, enregistrements), ë des sous-programmes (appelØes fonctions).

Il re ?te bien le savoir faire des annØes 70, et se situe dans la famille du langage Pascal. Son avantage vis- -vis du Pascal est son plus grand pragmatisme. Il autorise clairement deux styles de programmation, le style bidouille pour produire du code e cace et le style gØnie logiciel pour produire des programmes plus lisibles, plus sßrs et plus facilement modi ables.

Bien que pouvant Œtre considØrØ de bas niveau, le langage C supporte les structures de base nØcessaires la conception des applications structurØes. Cette caractØristique le range dans la catØgorie des langages de haut niveau. Il est aussi un des premiers langages o rant des possibilitØs de programmation modulaire. Un programme en C peut Œtre constituØ de plusieurs modules. Chaque module est un chier source qui peut Œtre compilØ de mani?re autonome pour obtenir un chier objet. L’ensemble des chiers objets participant un programme doivent Œtre associØs pour constituer un chier exØcutable (cf. gure 1.1).

Lorsque nous parlons du langage C, nous faisons rØfØrence ce que sait faire le compilateur lui-mŒme. Plusieurs outils interviennent dans la transformation d’un ensemble de chiers sources, constituant un programme, en un chier binaire exØcutable, rØsultat de ce que l’on appelle communØment, la compilation d’un programme.

Le langage C se limite aux fonctionnalitØs qui peuvent Œtre traduites e cacement en instructions machine. Cette r?gle de fonctionnement doit permettre de dØtecter ce qui est fait directement par le compilateur lui-mŒme et ce qui ne peut pas Œtre fait. Illustrons cette r?gle par quelques exemples :

ë Le compilateur est capable de gØnØrer des instructions machines qui permettent de manipuler des ØlØment binaires ( bit ) ou des groupes d’ØlØments binaires ( octet , ou byte en anglais).

ë Il permet les manipulations algØbriques (addition, multiplication, ) de groupes d’octets qui reprØsentent des valeurs enti?res ou des valeurs rØelles. ë Il permet de manipuler des caract?res en considØrant que ceux-ci sont reprØsentØs par un octet

1.3. PREMIER PROGRAMME EN C                                                                                                                                               9

partir du code ASCII.

ë Il ne permet pas de manipuler directement les tableaux. En particulier, il ne permet pas de manipuler directement les groupes de caract?res utilisØs pour stocker les cha nes de caract?res. Pour cela, il faut faire appel des fonctions de biblioth?ques (cf. section 12.1).

ë De mani?re contradictoire la r?gle prØcØdente, le compilateur accepte l’a ectation d’une collection de donnØes groupØes (structure) par une collection de donnØes de type identique. Ceci s’explique par le fait qu’une structure est considØrØe dans le langage comme une donnØe simple, elle doit donc pouvoir Œtre a ectØe une autre donnØe de mŒme type.

Pour rØaliser des fonctions plus compliquØes, le programmeur doit Øcrire ses propres fonctions ou faire appel aux fonctions prØ-dØ nies de la biblioth?que du langage C (cf. chapitres 11 et 12). Ces fonctions sont elles-aussi standardisØes.

1.3           Premier programme en C

Le programme 1.1 a       che      Hello World!       sur une console      l’Øcran.

                                                                                Prog. 1.1       Hello World!

/?

Premier Programme : Affiche Hello World!

?/

#include <stdio.h> int main() {

// Affiche le message printf("\nHello World!");

// Valeur de retour de la fonction return 0;

}

Notez en premier lieu que toutes les instructions se terminent par un point-virgule ; . Omettre un point virgule dØclenche un message d’erreur lors de la compilation.

Des commentaires peuvent Œtre insØrØs n’importe oø dans le programme, d?s lors qu’ils sont placØs entre les dØlimiteurs de dØbut /* et de n */ (cf. lignes 1 3). Un commentaire peut Øgalement Œtre introduit par // (cf. lignes 8 et 11). On peut ainsi dØtailler de fa on particuli?rement utile les fonctionnalitØs du programme et sa logique sous-jacente.

La ligne 6 dØ nit l’en-tŒte de la fonction principale de notre programme. La fonction principale d’un programme s’appellera toujours main. Dans notre cas, cette fonction ne prend pas de param?tre en entrØe (parenth?ses ouvrante et fermante), mais retourne une valeur de type int (c’est dire une variable de type entier). Le retour est rØalisØ gr ce l’instruction return en ligne 12 (soit juste avant la n du programme), la valeur de retour de la fonction Øtant 0. En gØnØral, 0 signi e une terminaison sans erreur.

10                                                                                                                       CHAPITRE 1. EN GUISE D’INTRODUCTION

Voyons maintenant plus prØcisØment le contenu de la ligne 9 qui rØalise l’a chage de la phrase Hello World! sur une console. La commande utilisØe pour a cher la cha ne de caract?res est l’instruction printf( ), la cha ne a cher Øtant entre guillemets. Le caract?re \n placØ en dØbut de cha ne indique qu’un saut de ligne doit Œtre rØalisØ avant d’a cher la phrase.

sa base, le langage C n’est qu’un ensemble de biblioth?ques partir desquelles le compilateur trouve les fonctions et les applications qui lui permettent de crØer un programme exØcutable. Exactement ce que l’on fait lorsqu’on cherche dans une encyclopØdie pour faire un exposØ. Certaines biblioth?ques (les plus courantes) sont incluses dans le compilateur, ce qui permet notre programme de compiler. Ainsi, l’instruction printf est dØ nie dans la biblioth?que stdio.h (biblioth?que standard d’entrØes/sorties), que l’on inclus dans le programme gr ce la directive #include (ligne 4).

Les librairies standards du C seront prØsentØes au fur et mesure de leur utilisation dans ce cours. NØanmoins, nous pouvons dØj en dire quelques mots. l’instar de l’Øtudiant qui recherche dans des livres, on peut dire que le chier .h reprØsente l’index du livre et le chier .cpp correspondant le contenu du chapitre concernØ. Ainsi, lorsque le compilateur rencontre le mot printf, il regarde dans chacun des chiers .h dØclarØ par l’instruction #include si ce mot est dØ ni. Il trouve celuici dans la biblioth?que stdio.h. l’inverse, s’il ne le trouve pas, le compilateur Ømet un message d’erreur.

1.4              Langage C et programmation structurØe

La programmation structurØe est un nom gØnØrique qui couvre un courant de pensØe. Ce courant de pensØe s’est dØveloppØ entre les annØes 1965 et 1975. La programmation structurØe a pour but de faciliter le travail de relecture des programmes et de minimiser le travail de maintenance (corrections, ajouts de fonctionnalitØs, ).

Le langage C est apparu en 1972, c’est dire en pleine pØriode de rØ exion sur les langages structurØs. Il supporte donc un ensemble de fonctionnalitØs qui sont directement issues de ce courant de pensØe. Le langage C a ØtØ con u et rØalisØ pour Øcrire un syst?me d’exploitation et le logiciel de base de ce syst?me. Il doit Œtre capable de faire les mŒmes choses que l’assembleur. Il est assez permissif, ce qui va l’encontre de la programmation structurØe telle que Wirth [Wir74] l’a dØcrite. En e et, en C, le programmeur peut Øcrire des choses explicites qui sont liØes la structure de la machine.

Le langage C est assez peu contraignant. Il o re des structures de programme mais il n’oblige pas les utiliser. En particulier, il autorise les entrØes multiples et les sorties multiples dans les t ches. La mise en page est libre, ce qui permet d’Øcrire des programmes dont la mise en page re ?te la structure. Les programmes sans mise en page sont rapidement illisibles du fait de la richesse de la syntaxe du C.

Comme le montre la gure 1.1, un programme en C est constituØ d’un ensemble de chiers sources destinØs Œtre compilØs sØparØment et subir une Ødition de liens commune. Ces chiers sources sont Øgalement appelØs modules , et ce type de programmation est appelØe programmation modulaire . La visibilitØ des modules entre-eux est expliquØe plus loin dans ce support de cours (cf. chapitre 8).

Le fait de pouvoir compiler chaque chier source de mani?re autonome am?ne concevoir des programmes de mani?re modulaires en regroupant, dans chaque chier source, des fonctions qui manipulent les mŒmes variables ou qui participent aux mŒmes algorithmes.

1.4. LANGAGE C ET PROGRAMMATION STRUCTUR E                                                                                                       11

                                              Figure 1.1        Les Øtapes de compilation d’un programme.

Ma triser la programmation en langage C nØcessite beaucoup de savoir faire et donc de pratique.

Elle s’apprend essentiellement partir de ses erreurs, alors n’hØsitez pas mettre les mains dans le cambouis

12                                                                                                                       CHAPITRE 1. EN GUISE D’INTRODUCTION

Chapitre 2

Types et variables

Sommaire

                                                  2.1 Types de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                        14

2.1.1 Lestypeentiers:charetint . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.1.2 LestypesrØels:float,doubleetlong double. . . . . . . . . . . . . . . . 14

2.1.3 Letypevide:void. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

2.1.4 Tailledestypesdebase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

                                           2.2 Constantes associØes aux types de base . . . . . . . . . . . . . . . . . . .                                 15

2.2.1 Constantesdetypeentier . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

2.2.2 ConstantesavecpartiedØcimale . . . . . . . . . . . . . . . . . . . . . . . . 16

2.2.3 Constantescaract?re . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

                                         2.3 Variables de base : dØclaration et initialisation . . . . . . . . . . . . . . .                               16

2.3.1 ClassemØmoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

2.3.2 Quali catifsdesvariables . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

                                                 2.4 Types dØrivØs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                       18

2.4.1 Pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

2.4.2 Leschanesdecaract?res . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

                                                2.5 Conversion de types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                      19

Ce chapitre traite des dØ nitions de variables. Dans tous les langages, une dØ nition de variable a les r les suivants :

1.    dØ nir le domaine de valeur de cette variable (taille en mØmoire et reprØsentation machine).

2.    dØ nir les opØrations possibles sur cette variable.

3.    dØ nir le domaine de validitØ de cette variable.

4.    permettre l’environnement d’exØcution du programme d’associer le nom de la variable une adresse mØmoire.

5.    initialiser la variable avec une valeur compatible avec le domaine de valeur.

En langage C, une variable se caractØrise partir de son type et de sa classe mØmoire. Les points 1 et 2 sont associØs au type de la variable, les points 3 et 4 sont associØs la classe mØmoire de la variable.


2.1         Types de base

Ce sont les types prØ-dØ nis du compilateur. Ils sont au nombre de cinqet on peut les classer en trois catØgories :

ë Les types entiers, ë Les type rØels, ë Le type vide.

2.1.1            Les type entiers : char et int

On distingue principalement deux types entiers :

char : C’est le type caract?re. Il reprØsente un nombre entier codØ sur un octet (huit bits). Sa valeur peut donc Øvoluer entre ?128 et +127 (28 = 256 possibilitØs). Le nom char n’est pas anodin car il est le support des caract?res au sens commun du terme. La correspondance entre un nombre et le caract?re qu’il reprØsente est transcrite dans la table ASCII (cf. annexe A).

int : C’est le type entier. Il est gØnØralement codØ sur 4 octets (32 bits) et permet de reprØsenter 23= 4294967295 nombres entiers. La plage des valeurs acceptables pour ce type est donc [?2147483648;2147483647]!

Les types entiers peuvent Œtre quali Øs l’aide du mot unsigned qui force les variables de ce type Œtre considØrØes comme uniquement positives2. Par exemple, la valeur d’une variable du type unsigned char ne peut Øvoluer qu’entre 0 et 255.

Le type int se dØcline avec les quali cateurs short ou long pour prØciser sa taille. Cela donne la possibilitØ d’avoir des dØ nitions du type : short int ou long int. Le langage C consid?re les types char, short int, int et long int comme des types entiers et permet de les mØlanger lors des calculs.

Les plages des valeurs acceptables pour tous les types entiers sont donnØes dans le                 chier limits.h.

2.1.2              Les types rØels : float, double et long double

Ces types servent reprØsenter les nombres rØels. On distingue trois types rØels qui se distinguent par : ë la prØcision sur les parties dØcimales et, ë les plages des valeurs acceptables.

Les plages des valeurs acceptables pour tous les types rØels sont donnØes dans le             chier float.h.

oat :                        C’est le type de base des nombres rØels.

double : Ce type permet de reprØsenter des valeurs ayant une partie dØcimale avec une plus grande prØcision que le type float.

long double : Ce type est rØcent. Il permet de reprØsenter des nombres avec parties dØcimales qui nØcessitent une tr?s grande prØcision, si la machine le permet.

2.2. CONSTANTES ASSOCI ES AUX TYPES DE BASE

2.1.3          Le type vide : void

C’est le type vide. Il est surtout utilisØ pour prØciser les fonctions sans argument ou sans retour. Il joue Øgalement un r le particulier dans l’utilisation des pointeurs (voir chapitre 9).

2.1.4          Taille des types de base

L’espace qu’occupent les di Ørents types en mØmoire dØpend de la machine sur laquelle est implantØ le compilateur. Le choix est laissØ aux concepteurs des compilateurs. Les seules contraintes sont des inØgalitØs non strictes, savoir :

ë sizeof(short) ? sizeof(int) ? sizeof(long) ë sizeof(float) ? sizeof(double) ? sizeof(long double)

sizeof est un opØrateur qui donne la taille en nombre d’octets du type dont le nom est entre parenth?ses. Le tableau 2.1 donne les tailles des types dans le cas d’un PC. Les machines supportant un type long double di Ørent du type double sont assez rares actuellement.

                               Tableau 2.1          Longueur des types de base sur un processeur Intel i686.

Type

Taille (octets)

char

1

short

2

int

4

long

4

float

4

double

8

long double

8

2.2            Constantes associØes aux types de base

Les constantes sont reconnues par le compilateur gr ce l’utilisation de caract?res qui ne participent pas la construction d’un identi cateur.

2.2.1          Constantes de type entier

Les constantes de type entier sont construites partir de chi res. Elles sont naturellement exprimØes en base dix mais peuvent Œtre exprimØes en base huit (octale) ou seize (hexadØcimale).

Une constante est a priori du type int si le nombre qu’elle reprØsente est plus petit que le plus grand entier reprØsentable. Si la valeur de la constante est supØrieure au plus grand entier reprØsentable, la constante devient de type long.

Les constantes peuvent Œtre su xØes par un l ou un L pour prØciser que leur type associØ est long int. Les constantes peuvent Œtre prØcØdØes par un signe - ou + . Elles peuvent Œtre su xØes par un u ou U pour prØciser qu’elles expriment une valeur positive (quali Øes unsigned).

Voici quelques exemples de constantes de type entier :

ë constante sans prØcision de type : 0377 octal, 0X0FF hexadØcimal, 10 dØcimal, -20 dØcimal ë constante longue enti?re : 120L, 0364L, 0x1faL, 120l, 0364l, 0x1fal

ë constante enti?re non signØe : 120U, 0364U, 0x1faU, 120u, 0364u, 0x1fau

ë constante longue enti?re non signØe : 120UL, 0364UL, 0x1faUL, 120uL, 0364uL, 0x1fauL, 120Ul, 0364Ul, 0x1faUl, 120ul, 0364ul, 0x1faul

2.2.2           Constantes avec partie dØcimale

Les constantes avec partie dØcimale ont le type double par dØfaut. Elles peuvent Œtre exprimØes partir d’une notation utilisant le point dØcimal ou partir d’une notation exponentielle. Ces constantes peuvent Œtre su xØes par un f ou F pour prØciser qu’elles reprØsentent une valeur de type float. Elles peuvent de mŒme Œtre su xØes par un l ou un L pour exprimer des valeurs de type long double. Voici quelques constantes avec partie dØcimale :

       ë 121.34              constante exprimØe en utilisant le point dØcimal, son type implicite est double.

ë 12134e-2 constante exprimØe en notation exponentielle.

ë 121.34f         constante de valeur identique mais de type float car su   xØe par f. ë 121.34l          constante de valeur identique mais de type long double car su          xØe par l.

2.2.3         Constantes caract?re

Les constantes du type caract?re simple sont toujours entourØes d’apostrophes (simples quotes aiguºs). En gØnØral, lorsque le caract?re est disponible au clavier, le caract?re correspondant est donnØ directement, par exemple ’a’. Certains caract?res du clavier ne sont pas disponibles directement et il convient d’utiliser les notations suivantes:

’\\’ :       Barre de fraction inversØe;

’\ :           Apostrophe;

’\t’ :          Tabulation horizontale (HT);

’\b’ :        Backspace (BS);

Un certain nombre d’abrØviations est Øgalement disponible :

’\a’ :

Alerte ou sonnerie (BEL);

’\f’ :

Saut de page (FF);

’\n’ :

Fin de ligne (LF);

’\0’ :

Fin de cha ne de caract?res;

’\r’ :

Retour chariot (CR);

’\v’ :

Tabulation verticale (VT);

2.3

Variables de base : dØclaration et initialisation

En langage C, une variable de type entier notØe i se dØclare par int i; et s’initialise la valeur 3 gr ce l’instruction i = 3;. De mŒme, une variable de type caract?re se dØclare par char c; et s’initialise gr ce l’instruction c = ’d’; ou bien c = 100;. Nous pourrons dØclarer et initialiser une variable en une seule instruction : int i = 5; ou char c = ’\t’;.

Une dØ nition de variable a les r les suivants :

DØ nir : le domaine de valeur de cette variable et les opØrations lØgales sur cette variable (gr ce au type);

2.3. VARIABLES DE BASE : D CLARATION ET INITIALISATION

RØserver : l’espace mØmoire nØcessaire lors de l’exØcution au support de la variable (gr ce au type et la classe mØmoire);

Initialiser :           la variable        l’aide d’une constante dont le type correspond         celui de la variable;

DØterminer : la durØe de vie de la variable et permettre l’utilisation dans certaines parties du programme (gr ce la classe mØmoire).

Une dØ nition de variable est l’association d’un identi cateur un type et la spØci cation d’une classe mØmoire.

2.3.1        Classe mØmoire

La classe mØmoire sert expliciter la visibilitØ d’une variable et son implantation en machine. Nous approfondirons les di Ørentes possibilitØs associØes ces classes mØmoire dans le chapitre 8 sur la visibilitØ. Les classes mØmoire sont :

global cette classe est celle des variables dØ nies en dehors d’une fonction. Ces variables sont accessibles toutes les fonctions. La durØe de vie des variables de type global est la mŒme que celle du programme en cours d’exØcution. local ou auto cette classe comprend l’ensemble des variables dØclarØes dans un bloc d’instructions. C’est le cas de toute variable dØclarØe l’intØrieur d’une fonction. L’espace mØmoire rØservØ pour ce type de variable est allouØ dans la pile d’exØcution. C’est pourquoi elles sont appelØes aussi auto c’est- -dire automatique car l’espace mØmoire associØ est crØØ lors de l’entrØe dans la fonction et il est dØtruit lors de la sortie de la fonction. La durØe de vie des variables de type local est celle de la fonction dans laquelle elles sont dØ nies. static ce prØdicat modi e la visibilitØ de la variable, ou son implantation :

ë dans le cas d’une variable locale il modi e son implantation en attribuant une partie de l’espace de mØmoire globale pour cette variable. Une variable locale de type static a un nom local mais a une durØe de vie Øgale celle du programme en cours d’exØcution.

ë dans le cas d’une variable globale, ce prØdicat restreint la visibilitØ du nom de la variable l’unitØ de compilation. Une variable globale de type static ne peut pas Œtre utilisØe par un autre chier source participant au mŒme programme par une rØfØrence avec le mot rØservØ extern (voir point suivant).

extern ce prØdicat permet de spØci er que la ligne correspondante n’est pas une tentative de dØ nition mais une dØclaration. Il prØcise les variables globales (noms et types) qui sont dØ nies dans un autre chier source et qui sont utilisØes dans ce chier source. register ce prØdicat permet d’informer le compilateur que les variables locales dØ nies dans le reste de la ligne sont utilisØes souvent. Le prØdicat demande de les mettre si possible dans des registres disponibles du processeur de mani?re optimiser le temps d’exØcution. Le nombre de registres disponibles pour de telles demandes est variable selon les machines. Seules les variables locales peuvent Œtre dØclarØes register.

2.3.2           Quali catifs des variables

Nous avons dØj parlØ des quali catifs unsigned et signed qui s’appliquent aux variables de type entier. Il existe deux autres quali catifs qui ont ØtØ spØci Øs par la norme. Il s’agit de const et volatile.

Une dØ nition de variable quali Øe du mot const informe le compilateur que cette variable est considØrØe comme constante et ne doit pas Œtre utilisØe dans la partie gauche d’une a ectation. Ce type de dØ nition autorise le compilateur placer la variable dans une zone mØmoire accessible en lecture seulement l’exØcution. Exemple : const double PI = 3.14;. Notez qu’une variable de type const doit Œtre initialisØe au moment de sa dØclaration. Il ne sera plus possible ensuite de modi er cette valeur.

Le quali catif volatile informe le compilateur que la variable correspondante est placØe dans une zone de mØmoire qui peut Œtre modi Øe par d’autres parties du syst?me que le programme lui-mŒme. Ceci supprime les optimisations faites par le compilateur lors de l’acc?s en lecture de la variable. Ce type de variable sert dØcrire des zones de mØmoire partagØes entre plusieurs programmes ou encore des espaces mØmoires correspondant des zones d’entrØe-sorties de la machine. Exemple : volatile char c;.

Les deux quali catifs peuvent Œtre utilisØs sur la mŒme variable, spØci ant que la variable n’est pas modi Øe par la partie correspondante du programme mais par l’extØrieur.

2.4        Types dØrivØs

Un type dØrivØ est crØØ partir de types standards pour l’usage propre un programme. Les types dØrivØs sont principalement : ë les pointeurs, ë les cha nes de caract?res, ë les tableaux et ë les structures, les unions et les ØnumØrations.

Nous allons examiner en dØtails les deux premiers types dØrivØes. Les tableaux et les structures seront examinØs dans les chapitres 9 et 10.

2.4.1        Pointeurs

Le pointeur est une variable destinØe contenir une adresse mØmoire. Un pointeur est associØ un type d’objet et est reconnu par l’emploi d’un * lors de sa dØ nition. Ce type est utilisØ en particulier lors des calculs d’adresse qui permettent de manipuler des tableaux partir de pointeurs (voir chapitre 9).

Prenons les dØ nitions suivantes : int* ptint; char* ptchar;. Dans cet exemple, ptint est une variable du type pointeur sur un entier. Cette variable peut donc contenir desvaleurs qui sont des adresses de variables du type entier (int). De mŒme, ptchar est une variable du type pointeur sur un caract?re. Elle peut donc contenir des valeurs qui sont des adresses de variables de type caract?re (char).

Le compilateur C vØri e le type des adresses mises dans un pointeur. Le type du pointeur conditionne les opØrations arithmØtiques sur ce pointeur. Les opØrations les plus simples sur un pointeur sont les suivantes :

ë a ectation d’une adresse au pointeur; ë utilisation du pointeur pour accØder           l’objet dont il contient l’adresse.

ConsidØrons les variables suivantes int in; char car; int* ptint; char* ptchar;. Un pointeur peut Œtre a ectØ avec l’adresse d’une variable ayant un type qui correspond celui du pointeur : ptint = &in; ptc = &car;.

Une fois un pointeur a ectØ avec l’adresse d’une variable, ce pointeur peut Œtre utilisØ pour accØder aux cases mØmoires correspondant la variable (valeur de la variable) : *ptint = 12; *ptc = ’a’;. La premi?re instruction met la valeur enti?re 12 dans l’entier in, la deuxi?me instruction met le caract?re ’a’ dans l’entier car.

2.5. CONVERSION DE TYPES

2.4.2          Les cha nes de caract?res

Les constantes du type cha ne de caract?res doivent Œtre mises entre guillemets (double quote). Le compilateur gØn?re une suite d’octets terminØe par un caract?re nul (’\0’) partir des caract?res contenus dans la cha ne. Une cha ne de caract?res est en fait un tableau de char. La cha ne est rØfØrencØe par l’adresse du tableau (i.e. l’adresse du premier ØlØment du tableau, cf. chapitre 9). Par exemple, la cha ne de caract?res message est gØnØrØe par le compilateur selon le schØma du tableau 2.2.

                                                          Tableau 2.2        Cha ne de caract?res constante

m

e

s

s

a

g

e

\0

La dØclaration d’une variable ch dØsignant une cha ne de 10 caract?res s’Øcrit : char ch[10];. Nous pouvons Øcrire au plus 9 caract?res, sachant que le 10e emplacement est rØservØ pour le caract?re de terminaison de cha ne ’\0’.

Il est impossible d’utiliser les opØrateurs sur les cha nes de caract?res. Notamment, il est interdit de concatØner deux cha nes de caract?res en utilisant l’opØrateur +. De mŒme, il n’est pas possible de copier une cha ne dans une seconde en utilisant le signe =. Pour obtenir ce rØsultat, il faut travailler non pas au niveau de la cha ne de caract?res, mais au niveau des caract?res qui la compose (en programmant des boucles, comme nous le verrons par la suite). NØanmoins, la norme du langage C a prØvu un certain nombre d’outils (c’est dire de fonctions , notion ØtudiØe au chapitre 6) permettant de manipuler avec aisance les cha nes de caract?res. Ces outils font partie de la biblioth?que standard du C (cf. chapitre 12).

Il est nØanmoins possible d’initialiser une cha ne de caract?res constante de la mani?re suivante : char ch[50] = "Bonjour";. Dans cet exemple, nous avons rØservØ un espace mØmoire de 50 caract?res. Les 7 premiers espaces sont utilisØs pour les 7 caract?res du mot, le 8e espace est utilisØ pour le caract?re de terminaison de cha ne. Les 42 espaces restant sont non utilisØs (et donc perdus!). D’autres exemples d’initialisation sont prØsentØs dans le tableau 2.3.

                                                     Tableau 2.3         Exemples d’initialisation de cha nes.

char t1[10] = "Coucou";

Tableaux de 10 caract?res initialisØ avec ’c’, ’o’, ’u’, ’c’, ’o’, ’u’, ’\0’. Les trois derniers caract?res sont eux aussi initialisØs avec ’\0’.

char t2[] = "bonjour";

Tableau de caract?res initialisØ avec la cha ne "bonjour". La taille du tableau est calculØe selon le nombre de caract?res +1.

char t3[10] = {’a’, ’b’, ’c’};

Tableau de 10 caract?res dont les 3 premiers ØlØments sont initialisØs avec les lettres ’a’, ’b’ et ’c’.

2.5          Conversion de types

La conversion de type est un outil tr?s puissant, elle doit donc Œtre utilisØe avec prudence. Le compilateur fait de lui-mŒme des conversions lors de l’Øvaluation des expressions. Pour cela, il applique des r?gles de conversion implicite. Ces r?gles ont pour but la perte du minimum d’information dans l’Øvaluation de l’expression.

R?gle de conversion implicite Convertir les ØlØments de la partie droite d’une expression d’a ectation dans le type de la variable ou de la constante le plus riche. Faire les opØrations de calcul dans ce type. Puis convertir le rØsultat dans le type de la variable a ectØe (partie gauche de l’a ectation). La notion de richesse d’un type est prØcisØe dans la norme. Le type dans lequel le calcul d’une expression deux opØrandes doit se faire est donnØ par les r?gles suivantes :

1.    si l’un des deux opØrandes est du type long double alors le calcul doit Œtre fait dans le type long double;

2.    sinon, si l’un des deux opØrandes est du type double alors le calcul doit Œtre fait dans le type double;

3.    sinon, si l’un des deux opØrandes est du type float alors le calcul doit Œtre fait dans le type float;

4.    sinon, appliquer la r?gle de promotion en entier, puis :

(a)  si l’un des deux opØrandes est du type unsigned long int alors le calcul doit Œtre fait dans ce type;

(b)  si l’un des deux opØrandes est du type long int alors le calcul doit Œtre fait dans le type long int;

(c)   si l’un des deux opØrandes est du type unsigned int alors le calcul doit Œtre fait dans le type unsigned int;

(d)  si l’un des deux opØrandes est du type int alors le calcul doit Œtre fait dans le type int.

La r?gle de promotion en entier prØcise que lorsque des variables ou des constantes des types suivants sont utilisØes dans une expression, alors les valeurs de ces variables ou constantes sont transformØes en leur Øquivalent en entier avant de faire les calculs. Ceci permet d’utiliser des caract?res et des entiers courts de la mŒme fa on que des entiers.

Des exemples de conversions implicites sont donnØs dans le tableau 2.4.

                                                        Tableau 2.4        Exemples de conversion implicite.

float f; double d; int i; long li;

li = f + i;

i est transformØ en float puis additionnØ              f, le rØsultat est transformØ en long et rangØ dans li.

d = li + i;

i est transformØ en long puis additionnØ               li, le rØsultat est transformØ en double et rangØ dans d.

i = f + d;

f est transformØ en double puis additionnØ         d, le rØsultat est transformØ en int et rangØ dans i.

Il est possible de forcer la conversion d’une variable (ou d’une expression) dans un autre type avant de l’utiliser par une conversion implicite. Cette opØration est appelØe cast . Elle se rØalise de la mani?re suivante : (type) expression.

Prenons pour exemple l’expression : i = (int) f + (int) d;. f et d sont convertis en int, puis additionnØs. Le rØsultat entier est rangØ dans i. Il peut y avoir une di Ørence avec l’expression : i = f + d;, du fait de la perte des parties fractionnaires des nombres.


Chapitre 3

Lire et Øcrire

Sommaire

                                              3.1 Exemple de lecture et Øcriture . . . . . . . . . . . . . . . . . . . . . . . .                                    21

                                             3.2 Les fonctions printf() et scanf() en dØtail . . . . . . . . . . . . . . . . .                                  22

Le langage C est utilisØ dans un contexte interactif. Ce qui veut dire que la plupart des programmes Øcrits en langage C font des Øchanges d’information avec un utilisateur du programme.

Bien sßr, le langage C est un langage des annØes 70 et l’idØe de l’interaction avec l’utilisateur est celle des syst?mes centralisØs temps partagØ. Un utilisateur de ce type de syst?me est connectØ via une voie d’entrØe-sortie qui permet d’Øchanger des caract?res. Ces voies sont la plupart du temps reliØes un tØlØtype (Øcran, clavier, avec sortie optionnelle sur papier). Les caract?res sont Øcrits sur l’Øcran du terminal et lus partir du clavier.

Les entrØe-sorties en langage C ne sont pas prises en charge directement par le compilateur mais elles sont rØalisØes travers de fonctions de la biblioth?que "stdio.h" (librairie d’entrØe/sortie standard). Le compilateur ne peut pas faire de contr le de cohØrence dans les arguments passØs ces fonctions car ils sont de type variable. Ceci explique l’attention toute particuli?re avec laquelle ces opØrations doivent Œtre programmØes.

3.1           Exemple de lecture et Øcriture

LA fonction scanf() fait le pendant la fonction printf(). scanf() permet de lire des valeurs partir du clavier (entrØe), alors que printf() permet d’a cher une valeur (sortie). Le programme 3.1 est un exemple de lecture et d’Øcriture d’une cha ne de caract?res.

                                    Prog. 3.1             Lecture et Øcriture de cha ne par scanf() et printf()

#include <stdio.h> char tt[80]; // Tableau de 80 caract?res

int main() { printf("Ecrivez une chaine de caracteres : "); scanf("%s", tt);

printf("\nLa chaine entree est : %s\n", tt);

return 0;

}

                                                                                                                                               CHAPITRE 3. LIRE ET                CRIRE

Ce programme contient la dØ nition d’une variable globale. Cette variable est un tableau de quatrevingt caract?res destinØ recevoir les caract?res lus au clavier. Les seules instructions sont les appels aux fonctions de lecture et d’Øcriture (scanf() et printf()). Remarquons que l’a chage de la cha ne dans le second printf() se fait gr ce %s (s dØsigne le mot string , soit cha ne en fran ais).

3.2                Les fonctions printf() et scanf() en dØtail

Les fonctions printf() et scanf() transforment des objets d’une reprØsentation partir d’une cha ne de caract?res (vision humaine) en une reprØsentation manipulable par la machine (vision machine), et vice et versa. Pour rØaliser ces transformations, ces fonctions sont guidØes par des formats qui dØcrivent le type des objets manipulØs (vision interne) et la reprØsentation en cha ne de caract?res cible (vision externe). Par exemple, un format du type %x signi e d’une part que la variable est du type entier et, d’autre part, que la cha ne de caract?res qui la reprØsente est exprimØe en base 16 (notation hexadØcimale). Autre exemple : %d signi e d’une part que la variable est du type entier et, d’autre part, que la cha ne de caract?res qui la reprØsente est exprimØe en base 10 (notation dØcimale).

Pour printf() un format est une cha ne de caract?res dans laquelle sont insØrØs les caract?res reprØsentant la ou les variables Øcrire. Pour scanf(), un format est une cha ne de caract?res qui dØcrit la ou les variables lire. Pour chaque variable, un type de conversion est spØci Ø. Ce type de conversion est dØcrit par les caract?res qui suivent le caract?re %.

Dans une premi?re approche de scanf(), nous considØrerons qu’il ne faut mettre que des types de conversions dans le format de lecture. Le lecteur curieux peut se reporter la section 11.5. Le tableau 3.1 donne un rØsumØ des dØclarations de variables et des formats nØcessaires leurs manipulations pour printf() et scanf(). Pour scanf(), il faut le mettre devant le nom de la variable, sauf pour les variables du type tableau de caract?res.

L’exemple 3.2 montre qu’il est possible de rØaliser l’Øcriture ou la lecture de plusieurs variables en utilisant une seule cha ne de caract?res contenant plusieurs descriptions de formats.

                                                            Prog. 3.2          Lectures multiples avec scanf()

#include <stdio.h> int main() {

int i=10; float l=3.14159;

char p[50]="Bonjour"; printf("Apres lecture au clavier : %d %f %s\n",i ,l ,p); scanf("%d%f%s",&i,&l ,p); printf("Apres lecture au clavier : %d %f %s\n",i ,l ,p);

return 0;

}

En ce qui concerne printf(), un certain nombre de caract?res optionnels peuvent Œtre insØrØs entre le symbol % et le caract?res spØci ant la conversion (d, x, e, ). Par exemple : ë le signe - pour demander un cadrage           gauche, au lieu du cadrage droite (par dØfaut).

3.2. LES FONCTIONS PRINTF() ET SCANF() EN D TAIL                                                                                                        23

                                                      Tableau 3.1            Exemples de printf() et scanf().

DØclaration

Lecture

criture

Format externe

int i;

scanf("%d",&i);

printf("%d",i);

dØcimal

int i;

scanf("%o",&i);

printf("%o",i);

octal

int i;

scanf("%x",&i);

printf("%x",i);

hexadØcimal

unsigned int i;

scanf("%u",&i);

printf("%u",i);

dØcimal

short j;

scanf("%hd",&j);

printf("%d",j);

dØcimal

short j;

scanf("%ho",&j);

printf("%o",j);

octal

short j;

scanf("%hx",&j);

printf("%x",j);

hexadØcimal

unsigned short j;

scanf("%hu",&j);

printf("%u",j);

dØcimal

long k;

scanf("%ld",&k);

printf("%ld",k);

dØcimal

long k;

scanf("%lo",&k);

printf("%lo",k);

octal

long k;

scanf("%lx",&k);

printf("%lx",k);

hexadØcimal

unsigned long k;

scanf("%lu",&k);

printf("%lu",k);

dØcimal

float l;

scanf("%f",&l);

printf("%f",l);

point dØcimal

float l;

scanf("%e",&l);

printf("%e",l);

exponentielle

float l;

printf("%g",l);

la + courte

double m;

scanf("%lf",&m);

printf("%lf",m);

point dØcimal

double m;

scanf("%le",&m);

printf("%le",m);

exponentielle

double m;

printf("%lg",m);

la + courte

long double n;

scanf("%Lf",&n);

printf("%Lf",n);

point dØcimal

long double n;

scanf("%Le",&n);

printf("%Le",n);

exponentielle

long double n;

printf("%Lg",n);

la plus courte

char o;

scanf("%c",&o);

printf("%c",o);

caract?re

char p[10];

scanf("%s",p);

printf("%s",p);

cha ne de caract?res

ë un nombre indiquant la taille minimale en caract?res du champs imprimer. Les espaces jouant le r le de caract?re de remplissage.

ë un point dØcimal (virgule ottante), suivi d’un nombre donnant la prØcision de la partie fractionnaire, c’est dire le nombre de chi re signi catifs apr?s le point. Si la donnØe n’est pas du type ottant, ce nombre reprØsente la taille maximale du champs imprimer.

Voici quelques exemples :

%8d : Imprime un nombre en dØcimal cadrØ droite dont la longueur du champ imprimable est de huit caract?res. Des espaces de remplissage prØcØdent le nombre.

%-25s : Imprime une cha ne de caract?res cadrØe                    gauche assurant une longueur minimum de 25

caract?res.

%.6f :        Imprime un nombre          ottant avec un maximum de six chi res signi catifs.

En ce qui concerne scanf(), une option intØressante est la suivante. Le caract?re * prØcØdØ de % spØci e que la valeur lue sera sautØe, donc non a ectØe la variable suivante. Exemple : scanf("%d %*s %d %*s %d %*s", &heure, &minutes, &secondes); L’entrØe des donnØes au clavier pourrait se prØsenter de la forme : 17 H 35 min 30 secondes. Les cha nes de caract?res H , min et secondes seront alors ignorØes.


24                                                                                                                                          CHAPITRE 3. LIRE ET                CRIRE

Chapitre 4

OpØrateurs et expressions

Sommaire

                                                 4.1 OpØrateurs unaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                      25

4.1.1 OpØrateurd’adresseetd’indirection . . . . . . . . . . . . . . . . . . . . . . 26

4.1.2 OpØrateurd’incrØmentationetdedØcrØmentation. . . . . . . . . . . . . . . 26

4.1.3 OpØrateurdedimension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

4.1.4 Nonlogique,plusetmoinsunaires . . . . . . . . . . . . . . . . . . . . . . . 27

                                                4.2 OpØrateurs binaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                      27

4.2.1 OpØrateursarithmØtiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

4.2.2 OpØrateursdemanipulationdebits . . . . . . . . . . . . . . . . . . . . . . 28

4.2.3 OpØrateursboolØens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

4.2.4 OpØrateurd’a ectationetdesuccession . . . . . . . . . . . . . . . . . . . . 29

                                                4.3 OpØrateur ternaire. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                      30

                                             4.4 PrØcØdence des opØrateurs . . . . . . . . . . . . . . . . . . . . . . . . . . .                                  30

Le langage C est connu pour la richesse de ses opØrateurs et il apporte quelques notions innovantes. En particulier, le langage C consid?re l’a ectation comme un opØrateur normal alors que la plupart des langages la consid?rent comme une opØration privilØgiØe. Cette richesse permet d’Øcrire des expressions (combinaisons d’opØrateurs et d’opØrandes) parfois complexes. Les opØrateurs sont les ØlØments du langage qui permettent de faire du calcul ou de dØ nir des relations. Ils servent combiner des variables et des constantes pour rØaliser des expressions.

L’organisation de ce chapitre est guidØe par le nombre d’opØrandes mis en cause par l’opØrateur et non par l’utilisation des opØrateurs.

4.1         OpØrateurs unaires

Un opØrateur unaire agit sur un opØrande qui peut-Œtre une constante, une variable, ou une expression. Ainsi, l’opØrateur unaire - permet d’inverser le signe et on peut Øcrire :

-2 oø 2 est une constante;

-i oø i est une variable;

-(i+2) oø i+2 est une expression.

Le tableau 4.1 donne la liste des opØrateurs unaires. Nous allons prendre quelques exemples pour expliquer l’utilisation de base de ces opØrateurs sur les variables : int var=10, *pint=&var, nvar=0; et long f=20L;.

                                                             Tableau 4.1       Liste des opØrateurs unaires.

OpØrateur

Utilisation

&

opØrateur d’adresse

*

opØrateur d’indirection sur une adresse

opØrateur de dØcrØmentation

++

opØrateur d’incrØmentation

sizeof

opØrateur donnant la taille en octet

!

non logique, il sert         inverser une condition

-

moins unaire : inverse le signe

+

plus unaire : sert         con rmer

4.1.1            OpØrateur d’adresse et d’indirection

Ces deux opØrateurs, notØ * et & ont dØj       ØtØ ØtudiØs dans la section 2.4 du chapitre 2.

Le & est l’opØrateur d’adresse, il retourne l’adresse de la variable suivant le &. &var donne l’adresse de la variable var. Cette adresse peut Œtre utilisØe pour a ecter un pointeur ( la condition que le pointeur soit d’un type compatible avec l’adresse de la variable) : int* pint = &var;

Le * est l’opØrateur d’indirection. Il permet d’accØder une variable partir d’une adresse (souvent contenue dans un pointeur). *&var donne la valeur 10, de mŒme que *pint, puisque pint a ØtØ initialisØ partir de l’adresse de var.

4.1.2            OpØrateur d’incrØmentation et de dØcrØmentation

Les opØrateurs et ++ permettent de dØcrØmenter et d’incrØmenter des variables de type entier. D’une mani?re simpliste, nos pouvons considØrer que : ë var est Øquivalent var = var - 1; ë var++ est Øquivalent var = var + 1.

Il est possible d’utiliser les opØrateurs unaires d’incrØmentation et de dØcrØmentation apr?s ou avant la variable. Ce qui permet de post-incrØmenter, de prØ-incrØmenter, de post-dØcrØmenter ou de prØdØcrØmenter. Lorsque l’opØrateur est prØ xØ, l’opØration est appliquØe avant que la valeur correspondant l’opØration ne soit calculØe. Dans le cas oø l’opØration est post- xØe, la valeur de la variable avant l’opØration est utilisØe pour les autres calculs et ensuite l’opØration est appliquØe.

Prenons comme exemple, deux entiers i et j, et initialisons ces deux variables avec la valeur 0 : int i=0, j=0;. Si nous Øcrivons j = ++i, c’est une prØ-incrØmentation de la variable i. Cela signi e incrØmenter i de 1 puis mettre la valeur de i dans j. la n de cette opØration i vaut 1 et j vaut

1. En anticipant sur l’opØrateur de succession 4.2.4, nous pouvons considØrer cette opØration comme Øquivalente i = i+1; j = i;. Si au contraire, nous Øcrivons j = i++;, cela signi e mettre la valeur de i dans j puis incrØmenter i de 1. En partant des mŒmes valeurs, i valant 0 et j valant 0, la n de cette opØration i vaut 1 et j vaut 0. Cette opØration est Øquivalente j = i; i = i+1;.


4.2. OP RATEURS BINAIRES

4.1.3          OpØrateur de dimension

sizeof donne la taille en octets de la variable. Exemple : long int f; sizeof(f); donne la valeur 4. L’opØrateur sizeof peut aussi donner la taille d’un type, le type doit Œtre entre parenth?ses. Dans notre exemple, sizeof(f) est Øquivalent sizeof(long).

Les calculs associØs l’opØrateur sizeof() sont rØalisØs par le compilateur lors de la traduction du langage en assembleur, et non lors de l’exØcution du programme. L’expression sizeof(f) est donc une valeur constante et peut entrer dans la construction d’une expression constante (i.e. calculable lors de la compilation).

4.1.4            Non logique, plus et moins unaires

Le non logique sert inverser une condition de vrai faux et rØciproquement. En langage C, une expression est fausse si la valeur qu’elle retourne est Øgale 0, elle est vraie sinon. De plus, la norme spØci e que!0 vaut 1. Dans notre exemple,!var vaut 0 car var est vraie puisque var contient 10. Le moins unaire inverse le signe de l’expression qui le suit. Le plus unaire sert con rmer le signe de l’expression.

4.2          OpØrateurs binaires

Le tableau 4.2 donne la liste des opØrateurs binaires.

                                                             Tableau 4.2       Liste des opØrateurs binaire.

Type d’opØrateurs

OpØrateurs

Usage

ArithmØtique

+ -

addition, soustraction,

* /

multiplication, division,

%

reste de la division enti?re.

Masquage

& ? ?

et, ou, ou exclusif, complØment

1.

DØcalage

vers la droite ou vers la gauche.

Relation

< <=

infØrieur, infØrieur ou Øgal,

> >=

supØrieur, supØrieur ou Øgal,

==!=

Øgal, non Øgal.

Logique

&& ??

et logique, ou logique.

A ectation

cf. tableau 4.3

a ectation.

Succession

,

succession.

4.2.1         OpØrateurs arithmØtiques

Le langage C permet l’utilisation des opØrateurs de calcul que l’on trouve habituellement dans les autres langages, savoir : l’addition, la soustraction, la multiplication et la division. Il utilise pour cela les symboles respectifs : + - * /.

Comme nous avons dØj vu les opØrateurs unaires, vous remarquerez l’utilisation contextuelle dans le cas des trois symboles : + - *. Le compilateur dØtermine la signi cation de l’opØrateur               son nombre d’opØrandes.

Comme nous le verrons plus loin, le type (au sens type des donnØes) de l’opØration est dØterminØ par le type des valeurs sur lesquelles portent l’opØration. Les opØrations arithmØtiques classiques peuvent s’appliquer aux types entiers et dans ce cas elles ont un comportement d’opØration enti?re (en particulier la division). Ces opØrations s’appliquent aussi aux types avec partie dØcimale et dans ce cas, elles donnent un rØsultat avec partie dØcimale.

Le langage C introduit l’opØrateur modulo, notØ %, qui permet d’obtenir le reste de la division enti?re dØterminØe par les deux opØrandes associØs au %. Par exemple, l’expression 14 % 3 donne la valeur

2.

4.2.2            OpØrateurs de manipulation de bits

Ces opØrateurs servent manipuler des mots bit bit. Les opØrandes doivent Œtre de type discret. On distingue les opØrateurs de masquage des opØrateurs de dØcalage.

OpØrateur de masquage :

ë & : a & b, ET logique bit bit (AND), ë | : a | b, OU logique inclusif bit bit (OR), ë ? : a ? b, OU logique exclusif bit bit (XOR), ë : a b, complØment 1, les bits sont inversØs 1 >0 et 0 >1. OpØrateur de dØcalage :

ë : a b, DØcalage vers la gauche (en nombre de bits),les bits de poids fort disparaissent tandis que des 0 par la droite.

ë : a b, DØcalage vers la droite (en nombre de bits),les bits de poids faible disparaissent tandis que des 0 par la gauche (quand le nombre est nØgatif, sur certaines machines, c’est un 1 qui appara t).

4.2.3        OpØrateurs boolØens

Les opØrateurs de relation servent rØaliser des tests entre les valeurs de deux expressions. Comme nous le verrons dans le chapitre 5, ces opØrateurs sont surtout utilisØs l’intØrieur des instructions de contr le (tests).

Les opØrateurs de relation algØbrique sont au nombre de six : <, <=, >, >=, == et!=. Ces opØrateurs peuvent Œtre utilisØs avec des variables de type entier ou des variables ayant une partie dØcimale. Notez le double Øgal pour le test d’ØgalitØ qui est souvent source de confusion avec le simple Øgal qui dØcrit l’a ectation.

Les deux autres opØrateurs de tests (&& et ??) sont appelØs respectivement le et logique et le ou logique . Le et logique permet de dØcrire qu’une condition constituØe de deux parties est satisfaite si et seulement si les deux parties sont satisfaites. Le ou logique permet de dØcrire qu’une condition constituØe de deux parties est satisfaite dØs lors qu’une des deux parties au moins est satisfaite. Ces opØrateurs s’appliquent des expressions que l’on peut considØrer comme de type entier et dont la valeur est testØe comme pour le non logique savoir une expression est considØrØe comme fausse si la valeur correspondante cette expression est Øgale 0.

Ainsi en langage C, pour tester si une variable de type entier j contient une valeur comprise entre deux bornes non strictes 12 et 143 on Øcrit : (j >= 12) && (j <= 143). De mŒme, un test pour savoir si un caract?re car contient un ’a’ en minuscule ou en majuscule s’Øcrit : (car == ’a’) ?? (car == ’A’).

4.2. OP RATEURS BINAIRES

4.2.4             OpØrateur d’a ectation et de succession

En langage C, l’a ectation est un opØrateur comme les autres. Ceci permet d’Øcrire des expressions comme : i = j = k = 1 qui dØtermine une a ectation multiple.

Le langage C permet de construire des opØrateurs binaires d’a ectation partir des opØrateurs binaires arithmØtiques, des opØrateurs de masquage et des opØrateurs de dØcalage, en les faisant suivre d’un Øgal (=). Ceci donne les opØrateurs dØcrits dans le tableau 4.3.

                                              Tableau 4.3         Liste des opØrateurs binaires d’a ectation.

ArithmØtique

+= -= *= /= %=

Masquage

&= ?= ?=

DØcalage

=        =

Le tableau 4.4 donne un exemple d’utilisation de chacun de ces opØrateurs et la fa on de lire ces di Ørentes expressions. Ce tableau est construit en supposant le type entier sur 32 bits, et les deux variables i et j dØ nies de la mani?re suivante : int i = 100, j = 5;.

                                             Tableau 4.4         Exemple d’opØrateurs binaires d’a ectation.

ArithmØtique

RØsultat

quivalence

Lecture

OpØrateurs arithmØtiques

i += 10

110

i = i + 10

ajoute 10       i

i += j

115

i = i + j

ajoute j         i

i -= 5

110

i = i - 5

retranche 10       i

i -= j

105

i = i - j

retranche 10       i

i *= 10

1050

i = i * 10

multiplie i par 10

i *= j

5250

i = i * j

multiplie i par j

i /= 10

525

i = i / 10

divise i par 10

i /= j

105

i = i / j

divise i par j

i %= 10

5

i = i % 10

i re oit le reste de la division enti?re de i par 10

OpØrateurs de masquage

i &= 8

0

i = i & 8

ET de i avec 8

i ?= 8

8

i = i ? 8

OU de i avec 8

i ?= 8

0x0C

i = i ? 4

OU exclusif de i avec 4

OpØrateurs de dØcalage

i          = 4

0xC0

i = i             4

dØcale i gauche de 4 positions

i          = 4

0x0C

i = i             4

dØcale i droite de 4 positions

La partie droite de l’opØrateur peut Œtre une expression : i += ( j * 25 + 342 ). Ceci permet d’Øcrire des expressions plus complexes : i += ( j += j * 25 + 342 ) - 12. La derni?re expression arithmØtique fait plusieurs a ectations : ë celle de j avec j + j * 25 + 342;

ë celle de i avec i + j - 12



Soit, si i et j ont pour valeur 1 avant cette expression, j vaudra 368 apr?s et i vaudra 357. Ce type d’expression est un peu compliquØ pour favoriser une bonne lisibilitØ du programme. Les possibilitØs o ertes en mati?re d’expressions sont de ce fait peu utilisØes.

La virgule, quant elle, sert sØparer deux expressions qui sont ØvaluØes successivement. La valeur associØe sera la derni?re valeur calculØe. Une expression comme : i = (j=2, k=3) associe i la valeur 3.

4.3         OpØrateur ternaire

L’opØrateur? : est un opØrateur ternaire dont la syntaxe est rØsumØ par :

expression1? expression2 : expression3;

Si expression1 rend la valeur vraie (l’entier 0) alors expression2 est ØvaluØe et le rØsultat est celui d’expression2, sinon c’est expression3 qui est ØvaluØe et le rØsultat est celui d’expression3. L’exemple c = a<b? a : b; calcule le minimum des nombres a et b, et place le rØsultat dans c.

4.4         PrØcØdence des opØrateurs

Les opØrateurs sont liØs par des relations de prØcØdence qu’il faut conna tre. Cette prØcØdence dØtermine l’ordre d’Øvaluation de l’expression par le compilateur.

R?gle de prØcØdence : La prioritØ des opØrateurs du langage C est dØcroissante de haut en bas selon le tableau 4.5. Lorsque deux opØrateurs se trouvent dans la mŒme ligne du tableau 4.5, la prioritØ d’Øvaluation d’une ligne de C dans laquelle se trouvent ces opØrateurs, est donnØe par la colonne de droite (associativitØ).

                      Tableau 4.5       PrØcØdence des opØrateurs (prioritØ dØcroissante de haut en bas).

PrØcØdence des opØrateurs

Classe d’opØrateur

OpØrateurs

AssociativitØ

Parenth?sage

()

de gauche

droite

Su       xes

[] -> . ++

de gauche

droite

Unaires

& * + -! sizeof ?

de droite

gauche

Changement de type

(type)

de droite

gauche

Multiplicatifs

* / %

de gauche

droite

Additifs

+ -

de gauche

droite

DØcalage

de gauche

droite

Comparaisons

< <= > >=

de gauche

droite

galitØs

==!=

de gauche

droite

ET bit            bit

&

de gauche

droite

OU exclusif bit             bit

?

de gauche

droite

OU bit           bit

?

de gauche

droite

ET logique

&&

de gauche

droite

OU logique

??

de gauche

droite

Condition

? :

de droite

gauche

A ectations

= += -= *= /= &= ?= ?=

=

=

de droite

gauche

Succession

,

de gauche         droite

Il faut consulter ce tableau pour Œtre sßr de l’ordre d’Øvaluation d’une expression.

L’associativitØ n’est pas la prØcØdence entre opØrateurs d’une mŒme ligne de ce tableau. C’est la fa on dont le compilateur analyse la ligne source en C. C’est l’ordre dans la ligne source qui est important

4.4. PR C DENCE DES OP RATEURS

et non l’ordre sur la ligne du tableau.

Pour rØsumer, cette r?gle de prØcØdence suit l’ordre :

ë parenth?sage;

ë opØrateurs d’acc?s, appel de fonction et post incrØmentation ou dØcrØmentation; ë opØrateurs unaires (associativitØ de droite             gauche);

ë opØrateurs binaires (mØ ez vous des relations opØrateurs de test et opØrateurs bit bit); ë opØrateur ternaire (associativitØ de droite gauche); ë opØrateurs binaires d’a ectation (associativitØ de droite gauche); ë succession.


32                                                                                                                        CHAPITRE 4. OP RATEURS ET EXPRESSIONS

Chapitre 5

Instructions de contr le

Sommaire

                                          5.1 Instructions conditionnelles ou de sØlection . . . . . . . . . . . . . . . . .                               33

5.1.1 Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

5.1.2 Tabledebranchementoud’aiguillage . . . . . . . . . . . . . . . . . . . . . 35

5.2 Instructions de rØpØtition ou d’itØration. . . . . . . . . . . . . . . . . . . 37 5.2.1 Lewhile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

5.2.2 Lefor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

5.2.3 Ledo while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

5.3 Ruptures de sØquence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 5.3.1 continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

5.3.2 break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

5.3.3 goto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 5.3.4 return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

On distingue les instructions conditionnelles ou de sØlection (instructions de test et d’aiguillage), les instructions de rØpØtition ou d’itØration, et les instructions de rupture de sØquence.

5.1             Instructions conditionnelles ou de sØlection

Les instructions conditionnelles permettent de rØaliser des tests, et suivant le rØsultat de ces tests, d’exØcuter des parties de code di Ørentes.

5.1.1       Test

L’opØrateur de test se prØsente sous les deux formes prØsentØes dans le tableau 5.1.

                                                                          Tableau 5.1         Syntaxes du if.

1     if ( expression ) instruction

2     if ( expression ) instruction1 else instruction2

Comme nous l’avons dØj vu : une expression est vraie si le rØsultat est non nul, elle est fausse si le rØsultat est nul.

L’expression est ØvaluØe. Si le rØsultat est di Ørent de 0, alors l’instruction qui suit le if est exØcutØe, et on n’exØcute pas la partie du else. Si l’expression rend un rØsultat Øgal 0, c’est l’instruction qui suit le else (si elle existe) qui est exØcutØe.

Voici quelques exemples de tests en langage C :

if (a == b)

usuel comme dans tous les autres langages

int b = 1; if (a = b)

est vrai puisque b est Øgal            1 (donc non nul), attention car cela met la valeur de b dans a.

int c; if (c = getchar())

vrai si la fonction getchar ne ram?ne pas ’\0’.

if (c)

vrai si c n’est pas Øgal          0 sinon faux.

Les tests sont souvent imbriquØs :

if (n>0)

printf("n=\%d est positif", n);

else

if (n<0)

printf("n=\%d est negatif", n);

else

printf("n=\%d est nul", n);

Le programme 5.1 propose un autre exemple tr?s simple qui compare deux nombres a et b, et regarde si deux autres nombres c et d sont donnØe dans le mŒme ordre.

                                                                    Prog. 5.1       Premier exemple de test

int a, b, c, d, result; a=1; b=4; c=5; d=6;

result = (a<b) == (c<d); if (a<b)

printf("%d est infØrieur %d\n", a,b);

if (result) printf("\n%d et %d sont dans le meme ordre\n", c,d);

Le second exemple d’utilisation de if est prØsentØ dans le programme 5.2.

                                                                     Prog. 5.2       Second exemple de test

printf("Voulez vous le mode d’emploi ?\n");

char c = getchar();

if (c==’o’)

printf("\nDesole c’est une farce!");

else

if (c==’n’)

printf("\nTant mieux car il n’y en a pas !");

else

printf("Vous auriez du repondre par o ou n");


5.1. INSTRUCTIONS CONDITIONNELLES OU DE S LECTION

5.1.2            Table de branchement ou d’aiguillage

Pour Øviter les imbrications d’instructions if, le C poss?de une instruction qui crØe une table de branchement : c’est l’instruction switch. Le switch est une instruction de choix multiple qui e ectue un aiguillage direct vers des instructions en fonction d’une valeur discr?te rØsultat de l’Øvaluation de l’expression. La syntaxe du switch est rØsumØe dans le tableau 5.2.

                                                            Tableau 5.2         Syntaxe classique du switch.

switch

(expression) case value1 :

{ inst 10 inst 11 break;

case value2 :

inst 20 inst 21 break;

case valueN :

inst N0 inst N1 break;

}

default :

inst d0 inst d1 break;

L’exØcution du switch est rØalisØe selon les r?gles et les Øtapes suivantes :

1.    l’expression est ØvaluØe comme une valeur enti?re;

2.    les valeurs des case sont ØvaluØes comme des constantes enti?res;

3.    l’exØcution se fait partir du case dont la valeur correspond l’expression. Elle s’exØcute en sØquence jusqu’ la rencontre d’une instruction break;;

4.    les instructions qui suivent la condition default sont exØcutØes lorsqu’aucune constante des case n’est Øgale la valeur retournØe par l’expression;

5.    l’ordre des case et du default n’est pas prØ-dØ ni par le langage mais par les besoins du programme;

6.    l’exØcution partir d’un case continue sur les instructions des autres case tant qu’un break n’est pas rencontrØ;

7.    plusieurs valeurs de case peuvent aboutir sur les mŒmes instructions;

8.    le dernier break est facultatif. Il vaut mieux le laisser pour la cohØrence de l’Øcriture, et pour ne pas avoir de surprise lorsqu’un case est ajoutØ.

Dans l’exemple 5.3, si vous omettez les instructions break, vous imprimerez l’ensemble de tous les messages dans le cas oø un caract?re ’a’ a ØtØ frappØ.

                                                                Prog. 5.3        Premier exemple de switch.

char c;

printf("Entrez une voyelle : ");

c=getchar(); switch(c) { case ’a’ : case ’A’ :

printf("C’est un a\n");

break; case ’e’ : case ’E’ :

printf("C’est un e\n");

break; case ’i’ : case ’I’ :

printf("C’est un i\n");

break; case ’o’ : case ’O’ :

printf("C’est un o\n");

break; case ’u’ : case ’U’ :

printf("C’est un u\n");

break; case ’y’ : case ’Y’ :

printf("C’est un y\n");

break; default : printf("Ce n’est pas une voyelle\n");

break;

}

                                                                 Prog. 5.4        Second exemple de switch.

switch(i) {

case 6:

case

8: printf("le nombre est superieur a 5\n");

case

0:

case

2:

case

4: printf("le nombre est pair\n");

break;

case

9:

case

7: printf("le nombre est superieur a 5\n");

case

5:

case

1:

case

3: printf("le nombre est impair\n");

break; default: printf("ceci n’est pas un nombre\n"); break; }

Dans l’exemple 5.4 :

1.    lorsque i est Øgal 6 ou 8, le programme Øcrit le nombre est supØrieur a 5 , puis Øcrit le nombre est pair ;

2.    lorsque i est Øgal       0, 2 ou 4, le programme Øcrit     le nombre est pair            ;

3.    lorsque i est Øgal 9 ou 7, le programme Øcrit le nombre est supØrieur a 5 , puis Øcrit le nombre est impair ;

5.2. INSTRUCTIONS DE R P TITION OU D’IT RATION

                                                                 Figure 5.1         Organigramme du while.

4.    lorsque i est Øgal       1, 3 ou 5, le programme Øcrit le nombre est impair; 5. dans les autres cas, le programme Øcrit          ceci n’est pas un nombre              .

5.2             Instructions de rØpØtition ou d’itØration

Les instructions rØpØtitives sont commandØes par trois types de boucles :

ë le while ë le for ë le do while

5.2.1        Le while

La syntaxe du while est la suivante :

while ( expression )

instruction

Le while rØp?te l’instruction tant que la valeur de l’expression s’interpr?te comme vraie (di Ørente de zØro). Il correspond l’organigramme de la gure 5.1.

L’exemple 5.5 correspond la recopie de la cha ne de caract?res contenue dans tab dans tab2. Le test de n de cha ne correspond tab[i] == ’\0’ puisque le test de passage dans la boucle correspond tab[i]!= ’\0’. Ce test marche car le compilateur a mis un octet nul (’\0’) la n de la cha ne

tab (qui sans cela n’en serait pas une).

Prog. 5.5        Recopie d’une cha ne avec une boucle while().

char tab[]="Coucou c’est moi"; char tab2[50];

int i=0; while( tab[i] != ’\0’){ tab2[i] = tab[i ];

i++;

}

tab2[i] = ’\0’;

                                                                    Figure 5.2        Organigramme du for.

5.2.2        Le for

La syntaxe du for est la suivante :

for ( exp1; exp2; exp3 )

instruction

Le for s’utilise avec trois expressions, sØparØes par des points virgules, qui peuvent Œtre vides :

1.    l’expression expr1 est une instruction d’initialisation. Elle est exØcutØe avant l’entrØe dans la boucle;

2.    l’expression expr2 est la condition de passage. Elle est testØe chaque passage, y compris le premier. Si elle calcule une valeur vraie l’instruction est exØcutØe, sinon la boucle se termine;

3.    l’expression expr3 est une instruction de rebouclage. Elle fait avancer la boucle, elle est exØcutØe en n de boucle avant le nouveau test de passage.

Le for correspond           l’organigramme 5.2. Elle est Øquivalente         la construction suivante :

exp1; while (exp2) { instruction;

exp3; }

Dans le for comme dans le while, il est remarquer, que le test est placØ en tŒte et donc que l’instruction n’est pas forcØment exØcutØe. Voici quelques exemples de boucle for :

1.    for (i=0; i<n; i++); Cet exemple correspond une boucle de parcours classique d’un tableau. Dans ce cas, l’indice de dØbut est 0; la condition de passage se fait en testant si l’indice courant est strictement infØrieur la taille du tableau, et la progression d’indice se fait par pas de un (i++).

2.    for (i=0, j=n; i<j; i++,j ); Le deuxi?me exemple montre comment avoir plusieurs initialisations et plusieurs expressions dans l’instruction de rebouclage, en utilisant l’opØrateur de succession , .

3.    for (;; ) instruction. Le dernier exemple est une convention pour Øcrire une boucle in nie. Ce type de boucle in nie est utilisØ lorsque l’instruction n’est pas une instruction simple mais

5.2. INSTRUCTIONS DE R P TITION OU D’IT RATION

                                                             Figure 5.3         Organigramme du do while.

plut t un bloc d’instructions dans lequel se trouvent des conditions de sortie de la boucle (voir section 5.3 sur les ruptures de sØquence).

Le programme 5.6 est un programme qui rØcup?re une ligne contenant au maximum 80 caract?res. La fonction getchar() est une fonction qui lit un seul caract?re au clavier. Nous supposerons que la valeur EOF est connue.

Prog. 5.6 Lecture d’une ligne avec for.

char tab[80];

int rang, c;

for( rang=0; rang<80 && (c=getchar()) != EOF; rang++ ) tab[rang] = c;

5.2.3        Le do while

La syntaxe du do while est la suivante :

do instruction

while ( expression );

l’inverse du while, le do while place son test en n d’exØcution, d’oø au moins une exØcution. L’organigramme correspond celui du while mais inversØ.

L’exemple donnØ dans le programme 5.7 teste la divisibilitØ par trois d’un nombre.

                                                                        Prog. 5.7        DivisibilitØ par trois.

int nombre;

do {

printf("\nEntrez un nombre divisible par trois : "); scanf("%d", &nombre); if (nombre%3 == 0) printf("\nLe nombre est divisible par trois !");

else printf("\nLe nombre n’est pas divisible par trois ! Recommencez "); } while (nombre%3 != 0);

5.3          Ruptures de sØquence

Dans le cas oø une boucle commande l’exØcution d’un bloc d’instructions, il peut Œtre intØressant de vouloir sortir de cette boucle alors que la condition de passage est encore valide. Ce type d’opØration est appelØ une rupture de sØquence. Les ruptures de sØquence sont utilisØes lorsque des conditions multiples peuvent conditionner l’exØcution d’un ensemble d’instructions.

Les ruptures de sØquence peuvent Œtre classØes en quatre genres qui correspondent leur niveau de travail :

1.    continue,

2.    break,

3.    goto, 4. return.

Notons Øgalement que l’appel la fonctionvoid exit(int status) termine brutalement l’exØcution d’un programme (cf. chapitre 12, librairie stdlib.h).

5.3.1       continue

Le continue est utilisØ en relation avec les boucles. Il provoque le passage l’itØration suivante de la boucle en sautant la n du bloc. Ce faisant, il provoque la non exØcution des instructions qui le suivent l’intØrieur du bloc.

Prenons l’exemple 5.8, qui compte le nombre de caract?res non blancs rentrØs au clavier, et le nombre total de caract?res. Les caract?res sont considØrØs comme blancs s’ils sont Øgaux soit l’espace, la tabulation horizontale, le saut de ligne ou le retour la colonne de numØro zØro. la n de l’exØcution :

1. i contient une valeur qui correspond au nombre total de caract?res qui ont ØtØ tapØs au clavier; 2. j contient une valeur qui correspond au nombre de caract?res non blancs; 3. i-j contient une valeur qui correspond au nombre de caract?res blancs.

Prog. 5.8 Utilisation du continue dans une boucle for()

int i , j, c;

for (i=0, j=0; (c=getchar()) != EOF; i++){ if (c == ’ ’ ) continue; if (c == ’\t’) continue; if (c == ’\r’) continue; if (c == ’\n’) continue; j++; }

5.3.2      break

Nous avons dØj vu une utilisation du break dans le switch. Plus gØnØralement, il permet de sortir de la boucle d’itØration. Il ne peut sortir que d’un niveau d’accolade.

Dans l’exemple 5.9, nous reprenons l’exemple 5.8, de mani?re crØer une boucle qui compte le nombre de caract?res jusqu’ l’obtention de EOF (cf. note de bas de page 1, page 39) en utilisant des instructions break. Le break provoque la sortie de la boucle for. Il en serait de mŒme avec un while ou un do while.

5.3. RUPTURES DE S QUENCE

Prog. 5.9 Utilisation des ruptures de sØquence dans une boucle for().

int i , j, c;

for( i=j=0; (c=getchar()) != EOF; i++ ) { if( c == ’\r’ ) break; if( c == ’ ’ ) continue; j++ ;

}

Dans l’exemple 5.10, nous reprenons l’exemple 5.6, de mani?re crØer une boucle qui remplit le tableau en vØri ant qu’il n’y a pas de dØbordement de taille. Dans ce cas, nous utilisons le break pour sortir de la boucle lorsque la n de chier est rencontrØe.

Prog. 5.10 Lecture d’une ligne avec for et break

char tab[80];

int c, rang;

for( rang=0; rang<80; rang++ ) { if( (c=getchar()) != EOF ) tab[rang] = c ;

else break ;

}

Les gures 5.4(a), 5.4(b) et 5.4(c) sont des organigrammes qui montrent les e ets de break et de continue sur les boucles for, while et do while.

5.3.3      goto

Le goto permet d’aller n’importe oø l’intØrieur d’une fonction. Son utilisation systØmatique nuit la lisibilitØ des programmes. Il est cependant tr?s utilisØ apr?s des dØtections d’erreur, car il permet de sortir de plusieurs blocs imbriquØs. Il est associØ une Øtiquette appelØe label. Un label est une cha ne de caract?res suivie de : (cf. programme 5.11).

                                                              Prog. 5.11         Utilisation de l’inf me goto.

while(exp1) { while(exp2) { while(exp3) { if (probleme) goto erreur;

}

}

} erreur : printf("Erreur au niveau untel");

5.3.4       return

L’instruction return provoque la terminaison de l’exØcution de la fonction dans laquelle elle se trouve et le retour la fonction appellante. Cette instruction peut Œtre mise tout moment dans le corps d’une fonction; son exØcution provoque la n de celle-ci. Cette instruction est appelØe de mani?re implicite la n d’une fonction. Le return est associØ l’expression dont il est suivi.

(c)

                  Figure 5.4                Break et continue dans (a) un for, (b) un while et (c) un do while.

5.3. RUPTURES DE S QUENCE

Cette expression est ØvaluØe et la valeur calculØe est retournØe la fonction appellante. C’est la valeur que retourne la fonction contenant le return. Les formes possibles du return sont :

ë return; ë return expression;

Nous allons crØer une fonction (voir prog. 5.12) qui retourne :

ë 0 si elle lit une majuscule, ë 1 si elle lit une minuscule, ë 2 si elle lit un chi re,

ë -1 si elle lit EOF (cf. note de bas de page 1, page 39), ë -2 dans tous les autres cas.

                                                           Prog. 5.12         Utilisation de plusieurs return.

int lec() {

int c;

switch( c=getchar() ) {

                                                                           case EOF :                                                   return ?1;

case ’A’ : case ’Z’ : return 0;

case ’a’ : case ’z’ : return 1;

case ’0’ : case ’9’ : return 2;

                                                                            default :                                                  break;

}

return ?2;

}


44                                                                                                                           CHAPITRE 5. INSTRUCTIONS DE CONTR LE

Chapitre 6 Fonctions

Sommaire

                                               6.1 DØ nition d’une fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                     45

                                               6.2 Passage des param?tres . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                    46

                                            6.3 Utilisation de pointeurs en param?tres . . . . . . . . . . . . . . . . . . .                                  47

                                            6.4 Conversions de type des param?tres . . . . . . . . . . . . . . . . . . . . .                                  47

                                                 6.5 Retour de fonction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                      48

                                                  6.6 RØcursivitØ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                        48

                                            6.7 Param?tres de la fonction principale . . . . . . . . . . . . . . . . . . . . .                                  48

                   6.8                                 tapes d’un appel de fonction . . . . . . . . . . . . . . . . . . . . . . . . .                                49

Les fonctions sont des parties de code source qui permettent de rØaliser le mŒme type de traitement plusieurs fois ou sur des objets di Ørents. Les mots procØdure et fonction sont employØs dans le reste de ce chapitre de mani?re indi Ørente. Une fonction en langage C peut :

ë modi er des donnØes globales. Ces donnØes sont dans une zone de mØmoire qui peut Œtre modi Øe par le reste du programme. Une fonction peut dans ces conditions rØaliser plusieurs fois le mŒme traitement sur un ensemble de variables dØ ni statiquement la compilation;

ë communiquer avec le reste du programme par une interface. Cette interface est spØci Øe la compilation. L’appel de la fonction correspond un Øchange de donnØes travers cette interface, au traitement de ces donnØes (dans le corps de fonction), et un retour de rØsultat via cette interface. Ainsi, une fonction permet de rØaliser le mŒme traitement sur des ensembles di Ørents de variables.

6.1           DØ nition d’une fonction

Lors de leur dØ nition ou de leur utilisation les fonctions sont distinguØes des variables par la prØsence des parenth?ses ouvrantes et fermantes. Une dØ nition de fonction, cf. gure 6.1, contient :

ë une interface; ë un corps de fonction.

L’interface compl?te d’une fonction contient :

ë la dØclaration du type et du nom de la fonction; ë une parenth?se ouvrante;

ë la dØclaration des types et des noms des param?tres;

int add(int a, int b)

Interface

Fonction

{       int c;       c = a + b;       return c;

}

Corps

Bloc

                                                                 Figure 6.1        Structure d’une fonction.

ë une parenth?se fermante.

Le corps de fonction est un bloc, c’est     dire : ë une accolade ouvrante;

ë des dØclarations de variables locales au bloc; ë des instructions; ë une accolade fermante.

Le tableau 6.1 donne des exemples de dØ nition de fonctions en C.

                                                     Tableau 6.1        Exemples de dØ nition de fonctions.

Code C

Explications

int plus(int a, int b){

fonction plus qui retourne un rØsultat de type entier et

a += b;

return a;

}

dont les deux arguments a et b sont de type entier.

void add(int a, int b, int* c)

fonction add qui ne retourne pas de rØsultat (void)

{

et qui a trois arguments : a et b sont deux entiers

*c = a+b;

et c est un pointeur d’entier. Cette fonction

}

modi e l’entier pointØ par c.

long push(double X, int Y)

fonction push qui retourne un rØsultat de type long

{

et qui a deux arguments : X de type double

}

et Y de type int.

6.2           Passage des param?tres

En langage C, les passages de param?tres se font par valeur, c’est dire que la fonction appellante fait une copie de la valeur passØe en param?tre et passe cette copie la fonction appelØe l’intØrieur d’une variable crØØe dans l’espace mØmoire gØrØ par la pile d’exØcution. Cette variable est accessible de mani?re interne par la fonction partir de l’argument formel correspondant.

Voici deux exemples d’appels de la fonction plus() dØ nie dans le tableau 6.1 : int x = 4, y = 6, z; z = plus(1,23); z = plus(x,y);

Le premier appel               la fonction constitue un passage de constantes. Le second appel             la fonction

6.3. UTILISATION DE POINTEURS EN PARAM¨TRES                                                                                                           47

constitue un passage de variables. Ces deux exemples montrent que la fonction appelØe peut modi er les param?tres (sauf s’ils sont quali Øs par const), mais ces param?tres sont dans son univers local. Les modi cations des param?tres formels par une fonction n’ont aucune in uence sur la valeur des param?tres utilisØs lors de l’appel.

6.3             Utilisation de pointeurs en param?tres

Il est possible,    partir d’une fonction, de modi er des objets de la fonction appellante. Pour cela, il faut que la fonction appellante passe les adresses de ces objets.

Les adresses sont considØrØes comme des pointeurs dans la fonction appelØe. Comme pour les autres constantes, il y a promotion de constante variable lors du passage des param?tres par valeur. Il est en e et possible d’appeler la fonction plus() avec des constantes et d’utiliser en interne de cette mŒme fonction des variables. Dans le cas d’une adresse, la promotion en variable rend un pointeur.

Voici deux exemples d’appels de la fonction add( ) dØ nie dans le tableau 6.1. Cette fonction utilise le troisi?me argument pour communiquer le rØsultat de ses calculs. int x = 5, y = 7, z;

add(x, y, &z); add(43, 4, &x);

Lors du premier appel add(x,y,&z);, la variable z est modi Øe et elle prend la valeur 12 (5+7). Lors du second appel add(43,4,&x); la variable x est modi Øe et elle prend la valeur 47 (43+4).

6.4             Conversions de type des param?tres

Lors de l’appel d’une fonction, les param?tres subissent les conversions de type unaire telles que dØcrites dans la section 2.5. Les plus importantes sont les suivantes : ë les param?tres du type char et short sont transformØs en int; ë les param?tres du type float sont transformØs en double.

Les conversions de type unaire ayant lieu lors de l’appel d’une fonction sont dØcrites dans le tableau 6.2.

                                                              Tableau 6.2        Conversions de type unaire.

Type du param?tre

Type apr?s conversion

char

int

short

int

int

int

long

long

float

double

double

double

6.5          Retour de fonction

Toute fonction qui n’est pas de type void retourne un rØsultat. Le type de ce rØsultat est celui de la fonction. La gØnØration du retour de fonction est provoquØe par l’appel de l’instruction return expression . L’expression qui suit le return est ØvaluØe, et la valeur obtenue est retournØe. Au niveau de la fonction appellante, le retour de fonction peut Œtre utilisØ comme la valeur d’une expression. Si nous prenons le cas de la fonction plus() vue prØcØdemment, la valeur du retour de cette fonction peut Œtre utilisØe pour a ecter une variable (dans cet exemple la variable z).

int x=12, y=5, z; z = plus(x, y);

Nous pouvons utiliser ce retour de fonction dans toute expression telle que dØ nie dans le chapitre 4. Ce qui nous permet d’Øcrire par exemple : z = z * plus(x, y); ou z *= plus(x, y);.

6.6        RØcursivitØ

En C, toute fonction peut appeler toute fonction dont elle conna t le nom (nous reviendrons sur ces probl?mes dans le chapitre 8 sur la visibilitØ). En particulier, elle peut s’appeler elle-mŒme. Il est donc possible d’Øcrire des fonctions rØcursives.

Prenons l’exemple le plus connu en mati?re de rØcursivitØ : la factorielle. Cette fonction factorielle peut s’Øcrire de la mani?re suivante :

Prog. 6.1 Fonction factorielle.

int fac(int n) { if (n == 0) return 1;

                                                                          else                              return n?fac(n?1);

}

Les limites de cette rØcursivitØ sont imposØes par la taille de la pile d’exØcution, au chargement du programme en mØmoire si le syst?me utilise une gestion de pile statique. Si vous testez cette fonction, vous serez sßrement limitØ, non par la taille de la pile, mais par l’espace de valeur d’un entier. Pour montrer la concision du langage, voici une factorielle Øcrite en une seule ligne :

int fac(int n) { return n? n*fac(n-1) : 1; }

6.7             Param?tres de la fonction principale

La structure des arguments de la fonction main() re ?te la liaison entre le langage C et le syst?me d’exploitation (shell sous unix). Les param?tres de la fonction main() sont passØs par le shell dans la majoritØ des cas. Ces param?tres ont une structure prØ-dØ nie. Ils sont dØcrits de la mani?re suivante : int main(int argc, char** argv, char** envp)

Les noms argc, argv et envp sont des noms mnØmoniques. Ils signi ent argument count , argument values et environment pointeur .

La fonction main() dispose donc toujours de trois param?tres passØs par l’environnement syst?me. Ces param?tres sont un entier et deux tableaux de pointeurs sur des caract?res. La signi cation de ces arguments est la suivante : ë l’entier argc contient le nombre d’arguments qui ont ØtØ passØs lors de l’appel de l’exØcutable (nombre de mots dans la ligne de commande);

6.8.                TAPES D’UN APPEL DE FONCTION                                                                                                                    49

argc == 6

argv

argv[0]

e

c

h

o

\0

argv[1]

e

s

s

a

i

\0

argv[2]

d

e

\0

argv[3]

p

a

s

s

a

g

e

\0

argv[4]

d

e

\0

argv[5]

p

a

r

a

m

e

t

r

e

s

\0

                          Figure 6.2         Illustration d’un passage d’arguments         la fonction principale.

ë le premier tableau contient les arguments de la ligne de commande au niveau du shell. Ces arguments sont dØcoupØs en mots par le shell et chaque mot est rØfØrencØ par un pointeur dans le tableau. Il y a toujours au moins un argument qui correspond au nom de l’exØcutable appelØ;

ë le nombre de pointeurs valides dans le premier tableau est donnØ par argc;

ë le deuxi?me tableau contient les variables d’environnement du shell au moment de l’appel de l’exØcutable. Contrairement au premier tableau, la taille de ce deuxi?me tableau n’est pas donnØe par un nombre de mots valides. La n de ce deuxi?me tableau est dØterminØe par un marqueur.

Ce marqueur est un pointeur NULL, c’est- -dire, un pointeur qui contient l’adresse 0; ë la cha ne pointØe par la premi?re entrØe du tableau argv contient le nom de la commande elle-mŒme.

La            gure 6.2 donne la vision interne des param?tres argc et argv issus de la commande :

echo essai de passage de parametres.

6.8               tapes d’un appel de fonction

Pour rØsumer voyons les Øtapes lors d’un appel de fonction. Ces Øtapes sont au nombre de six et sont rØalisØes soit par la fonction appellante soit par la fonction appelØe :

Mise en pile des param?tres la fonction appellante empile les copies des param?tres. Si le param?tre est une constante (enti?re, ottante ou adresse), le programme exØcutable empile une copie de cette constante et l’on obtient une variable de type compatible. Ceci explique pourquoi vous pouvez passer une constante et manipuler cette constante comme une variable dans la fonction appelØe. En C ANSI, vous avez cependant la possibilitØ de dire que la fonction consid?re ses arguments comme constants (quali catif const). Si les param?tres sont des variables, la fonction appellante empile une copie constituØe partir de la valeur courante de la variable. Ceci explique pourquoi la valeur d’un argument formel peut Œtre di Ørente de celle de son argument rØel.

Saut la fonction appelØe La fonction appellante provoque un saut l’adresse de dØbut de la fonction appelØe en empilant l’adresse de retour.

Prologue dans la fonction appelØe La fonction appelØe prØpare son environnement en particulier, elle positionne son pointeur de contexte dans la pile en sauvegardant l’ancien pointeur de contexte. Ce pointeur de contexte servira pendant toute l’exØcution de la fonction retrouver d’un cotØ les arguments de la fonction, de l’autre les variables locales la fonction. Elle fait ensuite grandir la pile de mani?re pouvoir ranger une copie des registres qu’elle va utiliser et les variables locales qui ne sont pas en registre. Cette Øtape est appelØe le prologue de la fonction. Ce prologue dØpend du type du processeur et des choix de rØalisation faits par les concepteurs du compilateur.

La fonction appelØe s’exØcute jusqu’     rencontrer un return en langage C. Ce return provoque le passage         l’Øpilogue dans la fonction appelØe. Lorsque le return est associØ             une valeur cette valeur est conservØe dans un registre de calcul (qui sert aussi pour Øvaluer les expressions).

Epilogue dans la fonction appelØe L’Øpilogue fait le travail inverse du prologue savoir, il restitue le contexte de la fonction appellante au niveau des registres du processeur (sauf les registres scratch qui contiennent la valeur de retour de la fonction). Pour cela, l’Øpilogue restaure les registres qu’il avait sauvegardØs (correspondants aux registres demandØs par les dØ nitions de variables de type register), puis il restaure le contexte de pile en reprenant l’ancienne valeur dans la pile. En n, il fait diminuer la pile conformØment la rØservation qu’il avait fait dans le prologue. Finalement il retourne la fonction appellante.

RØcupØration du rØsultat et e acement des param?tres la fonction appellante dØpile les param?tres qu’elle avait empilØs au dØbut de l’appel et utilise la(es) valeur(s) de retour de la fonction pour calculer l’expression courante dans laquelle la fonction a ØtØ appelØe.

Chapitre 7

PrØprocesseur

Sommaire

                                                  7.1 Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                       51

                                                  7.2 Inclusion de chiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                       52

7.3 Variables de prØcompilation . . . . . . . . . . . . . . . . . . . . . . . . . . 52 7.3.1 DØ nitiondeconstantesdecompilation . . . . . . . . . . . . . . . . . . . . 52

7.3.2 DØ nitiondestinØe lasØlection . . . . . . . . . . . . . . . . . . . . . . . . 53

7.3.3 E acementd’unedØ nition . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

                                             7.4 DØ nition de macro-expressions. . . . . . . . . . . . . . . . . . . . . . . .                                   53

                                                7.5 SØlection de code. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                      53

Le prØprocesseur traite le chier source avant le compilateur. Il ne manipule que des cha nes de caract?res. Il retire les parties commentaires (entre /* et */). Il prend en compte les lignes commen ant par un # pour crØer le code que le compilateur analysera. Ses possibilitØs sont de 4 ordres : ë inclusion de chier : #include "nom de fichier" ou #include <nom de fichier> ë dØ nition de variables de preprocessing :

#define NOM valeur

#undef NOM ë dØ nition de macro-fonctions ou macro-expressions : #define m(x) (342*(x)*(x)) ë sØlection du code en fonction des variables du prØprocesseur :

#if

#ifdef (if defined )

#ifndef (if not defined )

#else

#endif

7.1         Commentaires

Les commentaires sont destinØs faciliter la comprØhension du source lors de la relecture. Ils ne sont d’aucune utilitØ au compilateur, et il est naturel qu’ils n’apparaissent pas dans le source qui lui est destinØ. Le prØprocesseur retire les caract?res compris entre /* et */. Il ne g?re pas les imbrications de commentaires. La mise en commentaire d’une section source peut alors crØer des probl?mes.


CHAPITRE 7. PR PROCESSEUR

7.2 Inclusion de chiers

L’inclusion de     chiers par le prØprocesseur est dØclenchØe par la rencontre de la directive : #include nom_de_fichier.

Par convention, les          chiers    inclure ont des noms terminØs par .h pour signi er            header . Il existe trois fa ons de nommer un  chier      inclure. Ces fa ons dØterminent l’endroit oø le prØprocesseur va chercher le        chier.

1.    partir du catalogue courant, si le nom de    chier est entourØ par des guillemets :

ë #include "header.h" ë #include "include\mem.h" ë #include "..\include\uucp.h" ë #include "d:\temp\essai\header.h"

2.    partir d’un catalogue prØdØ ni correspondant l’installation du compilateur, si le nom de chier est entourØ par un infØrieur et un supØrieur :

ë #include <stdio.h>

Les chiers inclus sont traitØs par le prØprocesseur. Ils peuvent contenir d’autres inclusions de chiers. Ce processus rØcursif est parfois limitØ quelques niveaux d’inclusion.

7.3           Variables de prØcompilation

Les variables de prØcompilation ont deux utilisations :

ë La dØ nition de constantes de compilation.

ë La dØ nition de variables de prØcompilation dont la prØsence permet la sØlection du code (cf section 7.5).

7.3.1             DØ nition de constantes de compilation

Comme le montre le tableau 7.1, l’usage le plus courant des constantes de compilation est associØ la manipulation de tableaux. Il est plus simple et plus sßr d’avoir une constante qui soit utilisØe lors de la dØ nition et lors de la manipulation du tableau. La dØ nition d’une constante de prØcompilation se fait par : #define nom_de_la_variable valeur.

                                              Tableau 7.1         Utilisation d’une constante de compilation.

Sans constante de prØcompilation

Avec constante de prØcompilation

#define LG 20

int tab[20];

int tab[LG]

for( i=0; i<20; i++ )

for( i=0; i<LG; i++ )

Ceci Øvite de rechercher dans le source les instructions qui font rØfØrence la taille du tableau lorsque cette taille change. Lorsqu’une constante de compilation a ØtØ dØ nie, le prØprocesseur change toutes les occurrences du nom de la constante par sa valeur, sauf lorsque le nom se trouve dans une cha ne de caract?res. Le changement ne se fait que lorsque le nom de la variable est isolØ. Le tableau 7.2 est un exemple d’utilisation des variables de prØcompilation.

7.4. D FINITION DE MACRO-EXPRESSIONS                                                                                                                            53

                                        Tableau 7.2       InterprØtation des variables par le prØprocesseur.

Avant prØcompilation

Apr?s prØcompilation

#define PI 3.14159

#define LG 20

int i,t[LG];

int i, t[20];

for( i=0; i<LG; i++)

for( i=0; i<20; i++)

t[i] = PI;

t[i] = 3.14159;

for( i=0; i<LG; i++ )

for( i=0; i<20; i++ )

printf("Valeur de PI %f", PI);

printf("Valeur de PI %f", 3.14159);

(PI)

(3.14159)

LG PI

20 3.14159

LGPI

LGPI

7.3.2         DØ nition destinØe          la sØlection

Le prØprocesseur permet de dØ nir des variables de prØcompilation qui permettent de rØaliser les tests de sØlection de parties de chier source. Ces variables n’ont pas besoin d’Œtre associØes une valeur. Elles jouent en quelque sorte le r le de boolØens puisque le prØprocesseur teste si elles sont dØ nies (#ifdef) ou non (#ifndef). Elles servent dØterminer les parties de codes compiler. La dØ nition d’une variable de prØcompilation se fait par : #define nom_de_la_variable

7.3.3           E acement d’une dØ nition

Il est possible d’e acer une dØ nition de variable par : #undef nom_de_la_variable. La variable est alors considØrØe comme non dØ nie. Dans le cas oø la variable est associØe une valeur, les occurrences de son nom ne sont plus remplacØes par sa valeur.

7.4            DØ nition de macro-expressions

Le prØcompilateur permet la dØ nition de macro-expressions encore appelØes macro-fonctions. La dØ nition d’une macro-expression se fait par la directive #define. Le nom de la macro est suivi d’une parenth?se ouvrante, de la liste des arguments, d’une parenth?se fermante et de la dØ nition du corps de la macro :

#define add(x1,x2) ((x1) += (x2)).

Lorsque le prØprocesseur rencontre une expression du type add(a,b), il gØn?re ((a) += (b)).

Il faut faire particuli?rement attention l’ordre d’Øvaluation pour des macro-expressions complexes, et une technique simple et systØmatique consiste encadrer le nom des pseudo-variables dans l’expression de la macro. Comme le montre l’exemple 7.3, le parenth?sage garantit que l’expression rØsultante est correctement interprØtØe.

7.5         SØlection de code

La sØlection de code permet de gØnØrer partir d’un mŒme chier source des chiers exØcutables pour des machines et des syst?mes di Ørents (Linux, Windows, ). Le principe de base consiste CHAPITRE 7. PR PROCESSEUR

                                              Tableau 7.3           valuation de macros par le prØprocesseur.

Avant prØcompilation

Apr?s prØcompilation

Bon/Faux

#define add(x1,x2) x1 += x2

#define m(x) 128*x+342*x*x

#define y(x) 128*(x)+342*(x)*(x)

int a,b,c,d;

int a,b,c;

int e,f,g;

int e,f,g;

int a = b = c = 1;

a = b = c = 1;

int d = e = f = 2;

d = e = f = 2;

add(a,b);

a += b;

bon

add(a,b+1);

a += b + 1;

bon

d = m(a);

d = 128*a+342*a*a;

bon

e = y(a);

e = 128*(a)+342*(a)*(a);

bon

d = m(a+b);

d = 128*a+b+342*a+b*a+b;

faux

d = y(a+b);

d = 128*(a+b)+342*(a+b) *(a+b);

bon

f = m(add(a,b));

f = 128*a += b+342* a += b* a+= b;

faux

f = y(add(a,b));

f = 128*(a+=b)+342* (a += b)*(a+= b);

bon

passer ou             supprimer des parties de code suivant des conditions      xØes      partir des variables de prØcompilation.

Ceci permet d’avoir un mŒme chier source destinØ Œtre compilØ sur plusieurs machines, ou plusieurs syst?mes, en tenant compte des di Ørences entre ces machines.

La sØlection est aussi utilisØe pour avoir un source avec des instructions qui donnent des informations sur les variables (traces), et pouvoir gØnØrer le chier exØcutable avec ou sans ces traces. La sØlection se fait partir des lignes de directives suivantes : ë #if ë #ifdef ë #ifndef ë #else ë #endif

Toute condition de sØlection de code commence par un #if[n[def]], et se termine par #endif, avec Øventuellement une partie #else.

Lorsque la condition est vraie, le code qui se trouve entre le #if[n[def]] et le #else est passØ au compilateur. Si elle est fausse, le code passØ est celui entre le #else et le #endif. Si il n’y a pas de partie #else aucun code n’est passØ.

La sØlection avec #if se fait par test d’une expression valide du langage C. Cette expression ne peut mettre en jeu que des constantes et des variables de prØcompilation.

Chapitre 8

Compilations sØparØes

Sommaire

                                              8.1 Programme et chiers sources . . . . . . . . . . . . . . . . . . . . . . . . .                                    55

                                                   8.2 VisibilitØ. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                         56

8.2.1 R?gledevisibilitØ. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

8.2.2 ExtensiondelavisibilitØ. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

                                               8.3 Prototypes des fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                     59

                                       8.4 Fonctions externes et fonctions dØ nies ultØrieurement. . . . . . . . . .                            60

                                            8.5 DØclarations et dØ nitions multiples . . . . . . . . . . . . . . . . . . . . .                                  60

8.5.1 Fichiersd’inclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

8.5.2 RØductiondelavisibilitØ(fonctionsprivØes) . . . . . . . . . . . . . . . . . . 61

8.5.3 VariableslocalesrØmanentes . . . . . . . . . . . . . . . . . . . . . . . . . . 62

La compilation sØparØe permet de fragmenter un grand programme en des parties qui peuvent Œtre compilØes indØpendamment les unes des autres. Dans ce chapitre, nous allons voir comment cette possibilitØ est exploitable en langage C. partir de maintenant, nous appellerons : dØclaration : une association de type avec un nom de variable ou de fonction (dans ce cas la dØclaration contient aussi le type des arguments de la fonction),

dØ nition : une dØclaration et si c’est une variable, une demande d’allocation d’espace pour cette variable, si c’est une fonction la dØ nition du corps de fonction contenant les instructions associØes cette fonction.

8.1         Programme et         chiers sources

Comme nous l’avons dØj Øcrit dans le chapitre 1, un programme en langage C est constituØ par un ensemble de chiers destinØs Œtre compilØs sØparØment. La structure d’un programme, Øcrit en langage C, est rØsumØe dans la gure 8.1.

Comme le montre schØmatiquement la gure 8.2, chaque chier source contient les ØlØments suivants dans un ordre quelconque :

ë des dØclarations de variables et de fonctions externes, ë des dØ nitions de types synonymes ou de mod?les de structures,


                                                                 Figure 8.1       Du source      l’exØcutable.

Déclaration d'objets externes

FICHIER SOURCE

Définition de variables

Définition de fonctions

Définition de variables

Déclaration des'objets externes

Définition de fonctions

+ directives de précompilation

                                                                 Figure 8.2       Survol d’un     chier source.

ë des dØ nitions de variables, ë des dØ nitions de fonctions, ë des directives de prØcompilation et des commentaires.

Les directives de prØcompilation et les commentaires sont traitØs par le prØprocesseur. Le compilateur ne voit que les quatre premiers types d’objets.

Les chiers inclus par le prØprocesseur ne doivent contenir que des dØclarations externes ou des dØ nitions de types et de mod?les de structures.

R?gle fondamentale Toute variable ou fonction doit Œtre dØclarØe avant d’Œtre utilisØe.

8.2        VisibilitØ

La compilation sØparØe des di Ørentes parties d’un programme implique le respect de certaines r?gles que nous appellerons R?gles de visibilitØ. Ces r?gles s’appliquent aux noms (de variables et de fonctions).

8.2. VISIBILIT

Fichier d'inclusion

FICHIER SOURCE

Déclaration de variables externes

Définition de variables globales

fonction f1 variables locales a f1

fonction f2 variables locales a f2

fonction f3 variables locales a f3

fonction f4 variables locales a f4

                                                                     Figure 8.3        Exemple de visibilitØ.

8.2.1         R?gle de visibilitØ

Le compilateur ne lit le   chier source qu’une seule fois, du dØbut               la             n. Lorsqu’il rencontre l’utilisation d’une variable ou d’une fonction, il doit conna tre son type et sa classe d’adresse.

Par convention, dans le cas oø une fonction n’est pas connue, le compilateur consid?re qu’elle retourne une valeur de type int et il essaye de deviner le type des param?tres partir de l’appel (les appels postØrieurs devront se conformer ce premier appel).

La fonction peut Œtre dØ nie plus loin dans le chier. Si l’interface est conforme ce que le compilateur a devinØ, le compilateur n’Ømet pas de message d’erreur et utilise l’adresse de la fonction pour les appels suivants, mais il ne peut pas modi er ce qu’il a dØj gØnØrØ. La liaison du premier appel avec la fonction est laissØe l’Øditeur de liens.

Nous allons nous servir de l’exemple de la gure 8.3 pour continuer expliquer la visibilitØ des noms. Pour cet exemple, les r?gles de visibilitØ de noms que nous venons d’Ønoncer nous permettent de dire :

1.    la fonction f4 peut appeler les fonctions f3, f2 et f1;

2.    la fonction f3 peut appeler les fonctions f2 et f1;

3.    la fonction f2 peut appeler la fonction f1;

4.    la fonction f1 ne peut appeler aucune autre fonction;

5.    les fonctions f1, f2, f3 et f4 peuvent appeler des fonctions inconnues. Dans ce cas le compilateur utilise la r?gle par dØfaut et suppose que le rØsultat de la fonction appelØe est un entier.

Une fonction peut utiliser les variables internes qu’elle a dØ nies et les variables globales qui ont ØtØ dØ nies avant la fonction. Les noms de variables internes masquent les noms de variables globales. Ainsi, dans la gure 8.4 : ë la dØ nition de la variable locale a de type long l’intØrieur de la fonction f1 masque dans cette fonction la variable globale de mŒme nom et de type int. L’a ectation a=50 rØalisØe dans la fonction f1 modi e la variable locale de type long et non la variable globale;

ë par contre, la modi cation a=10 rØalisØe dans la fonction f2 a ecte la variable globale de type entier;

int a;                  // variable globale void f1(void) {      long a;           // variable locale a f1      a = 50;          // modification locale a f1

}

void f2(void) {      a = 10;          // modification globale

}

void f3(float a) {      a = 10;          // modification du parametre local a f3

}

                                                                        Figure 8.4       Masquage de nom.

ë de mŒme, l’argument a de type float de la fonction f3 masque dans cette fonction la variable globale de mŒme nom et de type int. L’a ectation a=10 rØalisØe dans la fonction f3 modi e l’argument local de type float et non la variable globale.

8.2.2          Extension de la visibilitØ

Pour le moment, nous n’avons pris en compte que les variables dØ nies dans le module correspondant une compilation. Il est possible de demander au compilateur de manipuler un objet dØ ni dans un autre module, en lui prØcisant que l’objet est de classe extern. Ceci revient donc faire une dØclaration et non une dØ nition de l’objet.

Si l’objet est une variable, le compilateur accepte son utilisation et fait les contr les de cohØrence sur son type. Il ne rØserve pas de place mØmoire pour elle. Il passe des informations dans le chier gØnØrØ pour demander l’Øditeur de liens de retrouver la variable dans les autres modules. Ces dØclarations se placent aussi bien au niveau global qu’ l’intØrieur des blocs.

Bien entendu, une telle variable doit Œtre dØ nie dans un autre chier du programme et tous les chiers doivent Œtre associØs par une Ødition de liens, sinon celle-ci se terminera avec des rØfØrences non rØsolues.

La gure 8.5 donne un exemple de dØ nition et de dØclaration de variables entre deux modules d’un mŒme programme. Dans cet exemple :

ë les variables a et b sont dØ nies dans le chier . Ces variables peuvent Œtre utilisØes par les fonctions contenues dans ce chier. Ces variables sont Øgalement accessibles partir des autres modules du programme qui les auront dØclarØes puisqu’elles ne sont pas de type static; ë la variable a est dØclarØe en tŒte du chier ; elle peut donc Œtre utilisØe par toutes les fonctions dØ nies dans . Le compilateur fait la liaison avec cette dØclaration lors de l’utilisation de la variable dans les fonctions f2 et f3. Il demande l’Øditeur de liens de trouver la variable a;

ë de mŒme, la variable b est dØclarØe localement dans la fonction f3 et peut Œtre utilisØe par cette fonction. Le compilateur fait les vØri cations gr ce aux informations fournies par la dØclaration et demande l’Øditeur de liens de trouver la variable.

8.3. PROTOTYPES DES FONCTIONS

                                                    Figure 8.5        VisibilitØ des variables entre modules.

8.3           Prototypes des fonctions

Les r?gles de visibilitØ s’appliquent aux fonctions de la mŒme mani?re qu’elles s’appliquent aux variables globales. Il est possible de dØclarer une fonction extern a n de l’utiliser dans un module di Ørent de celui oø elle est dØ nie.

La norme ANSI a ajoutØ au langage C la possibilitØ de dØclarer les prototypes des fonctions, appelØs parfois signatures des fonctions, en incluant les types des arguments dans la ligne de dØclaration. Un prototype permet de vØri er que les arguments passØs une fonction sont corrects en nombre et en type. Ainsi, les interfaces des fonctions que nous avons vues dans le chapitre 6 sont dØcrites par les prototypes du programme 8.1.

                                                      Prog. 8.1        Exemples de prototypes de fonctions.

1.   extern int add(int , int);

2.   extern void add2(int, int, int?);

3.   extern long push(int, double);

4.   extern int fac(int);

5.   extern main(int, char??, char??);

6.   extern int scanf(char?, ) ;

7.   extern int printf(char?, ) ;

8.   extern int getchar(void);

Pour reprØsenter des fonctions ayant des arguments variables en nombre et en type (comme printf() et scanf()), le langage C utilise trois points dans la liste des arguments (cf. lignes 6 et 7 du programme 8.1). Pour dØcrire le prototype d’une fonction qui n’accepte pas d’argument, on utilise le type void dans la liste des arguments, comme le montre la derni?re ligne du programme 8.1.

                                                    Figure 8.6        VisibilitØ des fonctions entre modules.

extern int f2(void); // declaration de f2 int a;

int f1(void) { // definition f1      a = 50;      a = f2(void);

}

int f2(void) { // definition de f2      int b;

     b = f1();             // appel de f1 }

                                                  Figure 8.7         VisibilitØ des fonctions dans un module.

8.4               Fonctions externes et fonctions dØ nies ultØrieurement

Une fois connues, les fonctions des autres modules peuvent Œtre utilisØes. Comme le montre la gure 8.6, si la fonction f1 est dØ nie dans le chier et que l’on souhaite utiliser dans le chier , il faut mettre le prototype extern int f1(void); au dØbut du chier .

De mŒme, pour utiliser une fonction dØ nie plus loin dans le module, il faut faire une dØclaration de cette fonction. Le compilateur fait la mise- -jour de la rØfØrence lorsqu’il rencontre la dØ nition. Comme le montre la gure 8.7, la dØclaration extern int f2(void) permet l’utilisation d’une fonction sans en conna tre le corps.

8.5             DØclarations et dØ nitions multiples

Une dØclaration de variable globale qui n’est pas associØe avec une initialisation est candidate Œtre une dØ nition. Ainsi, il est possible de trouver plusieurs dØclarations de variables sans le mot extern dans plusieurs modules et mŒme dans un seul module. Le compilateur demande l’Øditeur de liens de rØsoudre les con its potentiels. Il ne peut bien sur n’y avoir qu’une seule initialisation qui transforme le dØclaration candidate en dØ nition.

Pour des questions de lisibilitØ, les compilateurs acceptent donc les dØ nitions candidates de variables,

8.5. D CLARATIONS ET D FINITIONS MULTIPLES

ils acceptent ces dØ nitions de mani?re multiple si le type est identique. Le compilateur ne fait la demande de rØservation d’espace qu’une seule fois. Il consid?re les dØ nitions ultØrieures comme des dØclarations. Cette facilitØ permet au programmeur de mettre des dØclarations de variables pr?s des endroits oø elles sont manipulØes. Nous recommandons :

ë d’Øviter les dØclarations candidates                la dØ nition, ë de toujours associer la dØ nition avec une initialisation, ë d’utiliser le mot extern devant les dØclarations.

L’exemple suivant montre la dØclaration multiple d’une variable. f1 et f4 manipulent la mŒme variable a.

int a;

void f1(void){ a=a+1; } int a; void f4(void){ a??;} }

Il est cependant plus acadØmique d’Øcrire une seule dØ nition et des dØclarations. f1 et f4 manipulent la mŒme variable a.

int a;

void f1(void){ a=a+1; } extern int a; void f4(void){ a?? ;} }

8.5.1         Fichiers d’inclusion

Les      chiers d’inclusion sont destinØs         contenir des dØclarationsd’objets des types suivants :

ë types non prØ-dØ nis, ë mod?les et noms de structures, ë types et noms de variables, ë prototypes de fonctions.

Les chiers d’inclusion contiennent les dØclarations des variables et fonctions utilisØes par plusieurs modules.

La       gure 8.8 est un exemple dans lequel :

       ë le               chier <defs.h> contient les dØclarations des variables globales et des fonctions qui peuvent

Œtre utilisØes dans plusieurs chiers sources participant au programme; ë les variables a et b, qui sont dØ nies dans le module , peuvent Œtre utilisØes par plusieurs autres modules;

ë les fonctions utilisables sont f1 qui est dØ nie dans le module et f3 qui est dØ nie dans le module ;

ë le module , demande l’inclusion du chier <defs.h> qui fournit la dØclaration de la fonction f3 lors de la compilation. Ceci permet l’utilisation de cette fonction dans la fonction f2 sans ambigu tØ.

8.5.2             RØduction de la visibilitØ (fonctions privØes)

Dans un environnement qui permet plusieurs programmeurs de constituer un seul programme, il est intØressant d’avoir une notion de variables privØes accessibles seulement l’intØrieur d’un

                                                       Figure 8.8        Utilisation d’un      chier d’inclusion.

module. Cette notion de restriction de visibilitØ peut aussi s’appliquer aux fonctions pour donner un mØcanisme d’encapsulation qui n’autorise pas directement l’acc?s des variables mais permet cet acc?s travers l’appel de fonctions.

En langage C, le prØdicat static permet de masquer des noms de donnØes ou de fonctions aux autres chiers du programme. Une variable de type global static n’est visible que depuis la partie du programme qui la dØclare. Elle n’est accessible qu’aux fonctions dØ nies apr?s elle dans le mŒme chier. Ceci permet, entre autre, de manipuler des donnØes qui ont le mŒme nom dans plusieurs chiers avec des dØ nitions di Ørentes dans chacun d’entre-eux.

Comme le montre la gure 8.9, une fonction peut aussi Œtre dØclarØe static, elle sera alors inaccessible aux fonctions des autres chiers constituant le programme. Une tentative de dØclaration extern d’une variable ou d’une fonction statique est ine cace.

8.5.3          Variables locales rØmanentes

Pendant l’exØcution, les variables locales sont normalement crØØes l’entrØe du bloc dans lequel elles sont dØ nies. Les variables locales un bloc, appelØes Øgalement variables automatiques, sont naturellement invisibles de l’extØrieur de la fonction.



Le langage C donne la possibilitØ d’avoir des variables internes   un bloc dont la durØe de vie est la mŒme que celle des variables globales. Le prØdicat static appliquØ            une variable locale, modi e le lieu oø cette variable est implantØe; elle est, alors, mise avec les variables globales.

Son nom reste invisible l’extØrieur de la fonction. Le prØdicat static peut Œtre utilisØ pour des structures ou des tableaux de grande taille internes une fonction. Ceci permet de minimiser l’ overhead causØ par la crØation ou la destruction de l’espace mØmoire pour stocker ces variables de

8.5. D CLARATIONS ET D FINITIONS MULTIPLES

                                                         Figure 8.9          RØduction de visibilitØ (static).

grande taille l’entrØe ou la sortie de la fonction. Trois points sont remarquer : ë si une fonction retourne un pointeur sur une variable statique, celui-ci peut Œtre utilisØ par le reste du programme. Le contr le d’acc?s la variable est vØri Ø la compilation mais non l’exØcution. Une variable locale statique peut aussi Œtre modi Øe par des e ets de bord;

ë la durØe de vie d’une variable locale statique est la mŒme que celle des variables globales. chaque appel, une fonction retrouve la valeur d’une variable locale statique qu’elle a modi Øe lors des appels prØcØdents; Nous pouvons donc avoir une variable interne une fonction qui compte le nombre d’appels cette fonction;

ë l’initialisation d’une variable statique interne une fonction est faite la compilation, et non l’entrØe dans la fonction.

Lorsque le programme de la gure 8.10 s’exØcute, la variable enti?re a de type global prend successivement les valeurs : 1,2,3,4,5,6,7,8,9 et la variable a locale f1 prend successivement les valeurs : 1,11,21,31,41,51,61,71,81,91.

int a;                              // variable globale void f1(void) {

     static long a=1;           // variable statique locale a f1      a += 10;                    // modification locale a f1

}

void f2(void) {      for (a=1; a<10; a++)

          f1();                     // modification globale

}

main() {

     f2();

}

                                                              Figure 8.10       Variables locales statiques.


Chapitre 9

Pointeurs et tableaux

Sommaire

                                                 9.1 Tableaux une dimension . . . . . . . . . . . . . . . . . . . . . . . . . . .                                      65

                                             9.2 ArithmØtique d’adresse et tableaux . . . . . . . . . . . . . . . . . . . . .                                  66

                                              9.3 Tableaux multi-dimensions . . . . . . . . . . . . . . . . . . . . . . . . . . .                                    67

                                                9.4 Pointeurs et tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                      67

                                                9.5 Tableau de pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                      69

Ce chapitre est consacrØ aux tableaux et leur manipulation travers l’utilisation de pointeurs. Cette utilisation des pointeurs pour accØder aux contenus des tableaux est une des di cultØs du langage pour les dØbutants. Elle s’av?re cependant l’une des techniques les plus utilisØes par les programmeurs expØrimentØs.

9.1        Tableaux          une dimension

La dØclaration d’un tableau une dimension rØserve un espace de mØmoire contigu dans lequel les ØlØments du tableau peuvent Œtre rangØs.

Le nom du tableau seul est une constante dont la valeur est l’adresse du dØbut du tableau. Les ØlØments sont accessibles par : le nom du tableau, un crochet ouvrant, l’indice de l’ØlØment et un crochet fermant.

L’initialisation d’un tableau se fait en mettant une accolade ouvrante, la liste des valeurs servant initialiser le tableau, et un accolade fermante. La gure 9.1 montre l’espace mØmoire correspondant la dØ nition d’un tableau de dix entiers avec une initialisation selon la ligne :

int tab[10] = { 9,8,7,6,5,4,3,2,1,0 };

Comme le montrent les exemples du programme 9.1, il est aussi possible de ne pas spØci er la taille du tableau ou de ne pas initialiser tous les ØlØments du tableau. Dans ce programme, tb1 est dØ ni comme un tableau de 6 entiers initialisØs, et tb2 est dØ ni comme un tableau de 10 entiers dont les 6 premiers sont initialisØs. Depuis la normalisation du langage, l’initialisation des premiers ØlØments d’un tableau provoque l’initialisation de l’ensemble des ØlØments du tableau. Les derniers ØlØments sont initialisØs avec la valeur 0.

tab

tab[0]

tab

9

tab[1]

8

tab[2]

7

tab[3]

6

tab[4]

5

tab[5]

4

tab[6]

3

tab[7]

2

tab[8]

1

tab[9]

0

                                                                    Figure 9.1        Tableau de dix entiers.

                                                  Figure 9.2        Adresses dans un tableau de dix entiers.

Prog. 9.1 DØ nition de tableaux et initialisations.

int tb1[] = {12,13,4,15,16,32000}; int tb2[10] = {112,413,49,5,16,3200};

Les noms de tableaux Øtant des constantes, il n’est pas possible de les a ecter.

9.2            ArithmØtique d’adresse et tableaux

Comme nous venons de l’Øcrire, le nom d’un tableau correspond              l’adresse du premier ØlØment du tableau. Les ØlØments d’un tableau sont tous du mŒme type. Ils ont donc tous la mŒme taille et ils ont tous une adresse qui correspond au mŒme type d’objet (par exemple une adresse d’entier pour chaque ØlØment du tableau tb1 du programme 9.1). La         gure 9.2 reprend l’exemple de la               gure 9.1 en le complØtant par les adresses de chaque ØlØment. Ces adresses sont exprimØes partir du dØbut du tableau.

Ceci nous am?ne        considØrer les opØrations possibles       partir d’une adresse :

9.3. TABLEAUX MULTI-DIMENSIONS                                                                                                                                       67

                                                               Figure 9.3       Tableau      deux dimensions.

ë il est possible d’additionner ou de soustraire un entier (n) une adresse. Cette opØration calcule une nouvelle adresse de la mani?re suivante :

L’opØration suppose que l’adresse de dØpart et l’adresse rØsultante sont les adresses de deux variables contenues dans le mŒme tableau.

L’opØration suppose aussi que le tableau est d’une taille su samment grande, c’est- -dire qu’elle suppose que le programmeur doit Œtre conscient des risques de dØpassement des bornes du tableau. Dans le cas du tableau tab pris en exemple dans la gure 9.2, les adresses doivent Œtre comprises entre &tab[0] et &tab[9]. Pour une question de test de borne, l’adresse immØdiatement supØrieure est calculable. Il est donc autorisØ d’Øcrire &tab[10]. La zone mØmoire correspondante ne doit pas Œtre accØdØe.

ë Selon ces conditions, l’addition d’un entier une adresse retourne une adresse qui est celle du ne objet contenu dans le tableau partir de l’adresse initiale. Dans ces conditions, tab + n est l’adresse du ne entier partir du dØbut du tableau. Dans notre exemple de tableau de dix ØlØments, n doit Œtre compris entre 0 et 10. L’opØrateur d’acc?s la variable partir de l’adresse (*) ne peut cependant s’appliquer que pour n valant entre 0 et 9. Ainsi, *(tab+n) n’est valide que pour n compris entre 0 et 9.

ë L’addition ou la soustraction d’un entier est possible pour toute adresse dans un tableau. Ainsi, &tab[3]+2 donne la mŒme adresse que &tab[5]. De mŒme, &tab[3]-2 donne la mŒme adresse que &tab[1].

ë il est aussi possible de rØaliser une soustraction entre les adresses de deux variables appartenant un mŒme tableau. Cette opØration retourne le nombre d’objets entre les deux adresses. Ainsi, &tab[5]-&tab[3] doit donner la valeur 2, et &tab[3]-&tab[5] doit donner la valeur -2.

9.3            Tableaux multi-dimensions

Les tableaux deux dimensions sont des tableaux de tableaux. Les indices de droite sont les plus internes. Les tableaux n dimensions sont des tableaux de tableaux n-1 dimensions. La gure 9.3 donne les adresses des sous-tableaux et les noms des di Ørents ØlØments constituØs par la dØ nition du tableau deux dimensions suivant : int tab[8][5];.

9.4           Pointeurs et tableaux

Le pointeur est une variable destinØe contenir une adresse mØmoire. Il est reconnu syntaxiquement par l’* lors de sa dØclaration. Comme les adresses, le pointeur est associØ un type d’objet. Ce type est celui des objets qui sont manipulØs gr ce au pointeur. L’objet peut Œtre une variable ou une fonction.

La dØclaration d’un pointeur n’implique pas la crØation de l’objet associØ et l’a ectation de l’adresse de l’objet au pointeur. Il faut donc dØclarer un objet du type correspondant et initialiser le pointeur avec l’adresse de cet objet. Par convention, l’adresse 0 est invalide et si l’on cherche l’accØder, on obtient une erreur d’exØcution du type bus-error sur UNIX. Les pointeurs dØclarØs en variables globales sont initialisØs 0. Les pointeurs dØclarØs en local ont des valeurs initiales dØpendantes du contenu de la pile cet instant.

Voici deux exemples de dØ nition de pointeurs : ë int* ptint; pointeur sur un entier, ë char* ptchar; pointeur sur un caract?re.

Le compilateur C vØri e le type des adresses qui sont a ectØes un pointeur. Le type du pointeur conditionne les opØrations arithmØtiques sur ce pointeur. Les opØrations possibles sur un pointeur sont les suivantes :

ë a ectation d’une adresse au pointeur;

        ë utilisation du pointeur pour accØder           l’objet dont il contient l’adresse;

ë addition d’un entier (n) un pointeur; la nouvelle adresse est celle du ie objet partir de l’adresse initiale;

ë soustraction de deux pointeurs du mŒme type; ce qui calcule le nombre d’objets entre les adresses contenues dans les pointeurs (cette valeur est exprimØe par un nombre entier). Le tableau 9.1 donne des exemples de manipulations en relation avec les pointeurs et les tableaux, partir des variables suivantes : long x[10], *px, y;.

                                                      Tableau 9.1        Addition d’un entier       un pointeur.

Instruction

InterprØtation

px = &x[0];

px re oit l’adresse du premier ØlØment du tableau.

y = *px;

y re oit la valeur de la variable pointØe par px.

px++;

px est incrØmentØ de la taille de l’objet pointØ (4 octets). Il contient &x[1].

px = px+i;

px re oit l’adresse du ie objet               partir de l’objet courant.

L’addition dØcrite dans la derni?re ligne du tableau 9.1 se traduit par les conversions suivantes : px = (long*) ( (int) px + i * sizeof (long)); La soustraction de deux pointeurs retourne le nombre d’objets contenus entre les deux pointeurs. Les deux pointeurs doivent Œtre du mŒme type, et ils doivent contenir des adresses d’objets qui sont dans un mŒme tableau.

Le tableau 9.2 est un exemple de soustraction partir des dØ nitions de variables suivantes : int i, tab[20], *pt1, *pt2;

Par convention, le nom d’une variable utilisØ dans une partie droite d’expression donne le contenu de cette variable dans le cas d’une variable simple. Mais un nom de tableau donne l’adresse du tableau qui est exactement l’adresse du premier ØlØment du tableau. Nous faisons les constatations suivantes :

ë un tableau est une constante d’adressage; ë un pointeur est une variable d’adressage.

Ceci nous am?ne regarder l’utilisation de pointeurs pour manipuler des tableaux, en prenant les variables : long i, tab[10], *pti; ë tab est l’adresse du tableau (adresse du premier ØlØment du tableau &tab[0];

9.5. TABLEAU DE POINTEURS                                                                                                                                                     69

                                                          Tableau 9.2        Soustraction de deux pointeurs.

Instruction

InterprØtation

pt1 = &tab[0];

pt1 re oit l’adresse du premier ØlØment du tableau.

pt2 = &tab[10];

pt2 re oit l’adresse du dixi?me ØlØment du tableau.

i = pt2-pt1;

i re oit la di Ørence des deux pointeurs pt1 et pt2. c.a.d le nombre d’objets entre pt2 et pt1. i contiendra 10           la             n de cette instruction.

                                                                       Figure 9.4       Pointeur et tableau.

ë pti = tab; initialise le pointeur pti avec l’adresse du dØbut de tableau. Le & ne sert rien dans le cas d’un tableau. pti = &tab est inutile et d’ailleurs non reconnu ou ignorØ par certains compilateurs;

ë &tab[1] est l’adresse du 2e ØlØment du tableau.

          ë pti = &tab[1] est Øquivalent           :

pti = tab; oø pti pointe sur le 1er ØlØment du tableau.

pti += 1; fait avancer le pointeur d’une case ce qui fait qu’il contient l’adresse du 2e ØlØment du tableau.

Nous pouvons dØduire de cette arithmØtique de pointeur que : tab[i] est Øquivalent *(tab+i). La gure 9.4 est un exemple dans lequel sont dØcrites les di Ørentes fa ons d’accØder au ØlØments d’un tableau apr?s la dØ nition suivante : int tab[8], *pt=tab;.

9.5          Tableau de pointeurs

La dØ nition d’un tableau de pointeurs se fait par : type* nom[taille];

Le premier cas d’utilisation d’un tel tableau est celui oø les ØlØments du tableau de pointeurs contiennent les adresses des ØlØments du tableau de variables. La gure 9.5 est un exemple d’utilisation partir des dØ nitions de variables suivantes :

int tab[8], *pt[8] = {tab,tab+1,tab+2,tab+3,tab+4,tab+5,tab+6,tab+7};

                                 Figure 9.5          Tableau de pointeurs sur des variables dans un tableau.

Chapitre 10

Structures, unions, ØnumØrations et types synonymes

Sommaire

                                                  10.1 Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                       71

10.1.1 DØ nitiond’unestructure . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

10.1.2 Utilisationd’unestructure. . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

10.1.3 StructuresetlisteschanØes . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

10.1.4 Champsdebits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

                                                  10.2 Unions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                        74

10.2.1 DØ nitiond’union . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

10.2.2 Acc?sauxchamps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

                                                 10.3 numØrations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                       76

10.3.1 DØ nition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 10.3.2 Utilisation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

10.3.3 LimitesdesØnumØrations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

                                                10.4 Types synonymes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                      77

10.1         Structures

Une structure est un objet composØ de plusieurs champs qui sert reprØsenter un objet rØel ou un concept.

10.1.1           DØ nition d’une structure

Le langage C permet de dØ nir des types de structures comme les autres langages ØvoluØs. Cela se fait selon la dØ nition donnØe par le programme 10.1.


Prog. 10.1  Syntaxe de la dØ nition d’une structure.

struct nom_de_structure { type1 nom_champ1; type2 nom_champ2; etc.

typeN nom_champ_N; } variables;

Une dØ nition de ce type sert dØ nir un mod?le de structure associØ un nom de mod?le, qui est optionnel, et dØ nir des variables construites partir de ce mod?le (cette dØ nition est optionnelle aussi). Comme le montre l’exemple suivant, il est possible de ne pas dØclarer de variable la dØ nition de la structure, dans ce cas, le mod?le de structure doit Œtre associØ un nom de mani?re pouvoir utiliser celui-ci pour dØ nir des variables ultØrieurement.

struct date { int jour; int mois; int annee;

};

La dØ nition d’une structure ne rØserve pas d’espace mØmoire. Il faut dØ nir les variables correspondant ce mod?le de structure : struct date obdate, *ptdate;.

10.1.2           Utilisation d’une structure

Les structures peuvent Œtre manipulØes champ par champ ou dans leur ensemble.

10.1.2.1             OpØrations sur les champs

L’acc?s aux ØlØments d’une structure, que nous appelons aussi champs, se fait selon la syntaxe : nom_de_variable.nom_du_champ. Lorsqu’on dispose d’un pointeur sur une structure, l’Øcriture di ?re un peu en s’Øcrivant : nom_de_variable->nom_du_champ, oø la ?che est construite avec le signe moins (-) et le signe supØrieur (>).

En prenant les dØ nitions de donnØes suivantes, le tableau 10.1 donne les di Ørentes possibilitØs d’a ectation pour chaque champ. struct date obdate, ?ptdate; ptdate = &obdate;

Une fois ces dØ nitions rØalisØes, nous pouvons utiliser les variables obdate et ptdate comme le montre le tableau 10.1.

                                                          Tableau 10.1       Adressage dans une structure.

Objet

Pointeur

= 1;

ptdate->jour = 1;

= 1;

ptdate->mois = 1;

obdate.annee = 85;

ptdate->annee = 85;

Nous remarquerons l’Øquivalence d’Øcriture entre : (*ptdate).jour et ptdate->jour.

10.1. STRUCTURES

Pour le compilateur, l’acc?s un champ d’une structure revient faire un calcul de dØplacement par rapport au dØbut de la structure.

10.1.2.2                 OpØrations sur la variable dans son ensemble

La normalisation du langage C a autorisØ une pratique qui consiste considØrer une variable construite partir d’un type structurØ comme une variable simple.

Ainsi, comme le montre les dØ nitions suivantes, il est possible de construire une fonction qui accepte en argument une variable structurØe ou qui retourne une variable de type structurØ.

struct date obdate, obdate2; struct date newdate(); int checkdate(struct date uneDate);

Les opØrations suivantes sont possibles sur les variables de type structurØ :

1.    l’a ectation d’un variable structurØe par une autre variable du mŒme type, comme obdate=obdate2;

2.    le test d’ØgalitØ ou d’inØgalitØ entre deux variables structurØes du mŒme type, comme obdate==obdate2; ou obdate!=obdate2;

3.    le retour d’une variable structurØe par une fonction, comme

obdate=newdate();

4.    le passage en argument d’une variable structurØe      une fonction, comme result=checkdate(date1);

10.1.3          Structures et listes cha nØes

La syntaxe du langage C autorise les dØ nitions de structure contenant des pointeurs sur ce type de structure. Ceci permet de faire des listes cha nØes comme le montre la structure suivante :

struct noeud { int val; struct noeud? suiv; struct noeud? pred;

} node, ?ptnoeud ;

Note sur les pointeurs de structures : nous avons vu que l’incrØmentation d’un pointeur fait passer le pointeur sur l’ØlØment suivant. Il en est de mŒme pour un pointeur sur une structure. Un pointeur incrØmentØ permet l’acc?s la structure suivante lorsque les structures sont dans un tableau. Reprenons la structure noeud prØcØdente et dØclarons un tableau de structures et un pointeur sur la structure noeud : struct noeud tab[100], *ptnoeud = tab;. La variable ptnoeud pointe alors sur tab[0] et si nous Øcrivons ptnoeud++;, alors ptnoeud pointera sur tab[1].

10.1.4         Champs de bits

Les structures donnent acc?s aux ØlØments binaires. Il est possible de dØcouper logiquement un ensemble d’octets en des ensembles de bits. La prØcision de la longueur de chaque champ est faite par l’ajout de : longueur chaque ØlØment de la structure. Les structures de champs de bits sont dØclarØes selon le mod?le du programme 10.2.

Prog. 10.2               DØ nition d’un mod?le de structure champs de bits.

struct nom_de_structure { unsigned nom_champ1 : longueur1; unsigned nom_champ2 : longueur2; etc.

unsigned nom_champ_N : longueurN;

} objets ;

Il est recommandØ de n’utiliser que des ØlØments de type unsigned. La norme X3J11 n’impose pas que d’autres types soient supportØs. Un champ sans nom, avec simplement la taille, est un champ de remplissage pour cadrer sur des fronti?res de mot machine.

Voici quelques exemples de dØ nitions de mod?les de structures correspondant            des champs de bits :

struct mot { unsigned sign : 1; unsigned val : 15;

};

struct flottant { unsigned exposant  : 7; unsigned signe        : 1; unsigned mantisse        : 24;

};

struct mixte { unsigned exposant : 7; unsigned signe : 1; unsigned mantisse : 24; unsigned comp : 7;

: 9; };

10.2        Unions

Les unions permettent l’utilisation d’un mŒme espace mØmoire par des donnØes de types di Ørents des moments di Ørents.

10.2.1          DØ nition d’union

La dØ nition d’une union respecte une syntaxe proche de celle d’une structure (cf. programme 10.3).

Prog. 10.3               Syntaxe de la dØ nition d’une union.

union nom_de_union { type1 nom_champ1; type2 nom_champ2; etc.

typeN nom_champ_N;

} variables;

10.2. UNIONS

L’exemple suivant dØ nit deux variables z1 et z2 construites sur le mod?le d’une zone qui peut contenir soit un entier, soit un entier long, soit un nombre avec point dØcimal, soit un nombre avec point dØcimal long.

union zone { int entier; long entlong;

float flottant;

double flotlong;

} z1,z2;

Lorsque l’on dØ nit une variable correspondant un type union, le compilateur rØserve l’espace mØmoire nØcessaire pour stocker le plus grand des champs appartenant l’union. Dans notre exemple, le compilateur rØserve l’espace mØmoire nØcessaire pour stocker un double pour chacune des variables z1 et z2.

10.2.2         Acc?s aux champs

La syntaxe d’acc?s aux champs d’une union est identique celle pour accØder aux champs d’une structure (voir section 10.1.2.1).

Une union ne contient cependant qu’une donnØe la fois et l’acc?s un champ de l’union pour obtenir une valeur, doit Œtre fait dans le mŒme type qui a ØtØ utilisØ pour stocker la valeur. Si cette uniformitØ n’est pas respectØe dans l’acc?s, l’opØration devient dØpendante de la machine.

Les unions ne sont pas destinØes faire des conversions. Elles ont ØtØ inventØes pour utiliser un mŒme espace mØmoire avec des types de donnØes di Ørents dans des Øtapes di Ørentes d’un mŒme programme. Elles sont tr?s utilisØes dans les compilateurs.

Les di Ørents champs d’une union sont la mŒme adresse physique. Ainsi, les ØgalitØs suivantes sont vraies :

&z1.entier == (int?)&z1.entlong

&z1.entier == (int?)&z1.flottant

&z1.entier == (int?)&z1.flotlong

Dans l’exemple 10.4, la zone mØmoire correspondant la variable lmot peut Œtre vue sans signe ou avec signe. Ainsi si la valeur -1 est a ectØe , alors cette valeur peut Œtre considØrØe comme plus grande que zØro (puisque Øgale USHRT_MAX ou 65535U) ou plus petite suivant qu’elle est vue travers ou lmot.ent1. Ainsi le programme 10.4 donne le rØsultat suivant : Valeur de : -1, Valeur de lmot.ent1 : 65535.

                                                                   Prog. 10.4        Utilisation d’une union.

#include <stdio.h> int main(int argc, char?? argv) { union etiq { short int ent; unsigned short int ent1;

} lmot= {?1,}; printf("Valeur de : \%hd\n", ); printf("Valeur de lmot.ent1 : \%hd\n", lmot.ent1); }

10.3            numØrations

Les ØnumØrations servent o rir des possibilitØs de gestion de constantes ØnumØrØes dans le langage C. Ces ØnumØrations sont un apport de la norme ANSI et permettent d’exprimer des valeurs constantes de type entier en associant ces valeurs des noms.

Les ØnumØrations o rent une alternative            l’utilisation du prØprocesseur (cf. chapitre 7).

10.3.1        DØ nition

La dØ nition d’une ØnumØration respecte la syntaxe donnØe dans le tableau 10.5.

                                                           Prog. 10.5           Syntaxe de la dØ nition d’une

ØnumØration.

enum nom_de_ØnumØration { enumerateur1, enumerateur2, etc.

enumerateurN } variables;

Les valeurs associØes aux di Ørentes constantes symboliques sont, par dØfaut, dØ nies de la mani?re suivante : la premi?re constante est associØe la valeur 0; les constantes suivantes suivent une progression de 1.

enum couleurs { rouge, vert , bleu

} rvb;

Ce premier exemple d’ØnumØration dØ nit les constantes symboliques en utilisant les possibilitØs standard des ØnumØrations :

1. rouge qui correspond la valeur 0; 2. vert qui correspond         la valeur 1;

3. bleu qui correspond            la valeur 2.

Il est possible de xer une valeur chaque ØnumØrateur en faisant suivre l’ØnumØrateur du signe Øgal et de la valeur enti?re exprimØe par une constante; la progression reprend avec +1 pour l’ØnumØrateur suivant :

enum autrescouleurs { violet=4,

orange,

jaune=8

};

Dans ce deuxi?me exemple d’ØnumØration, les constantes symboliques suivantes sont dØ nies :

1.    violet qui est associØe             la valeur 4;

2.    orange qui correspond            la valeur 5 (violet + 1);

3.    jaune qui est associØe             la valeur 8.

10.4. TYPES SYNONYMES

10.3.2        Utilisation

Les ØnumØrateurs peuvent Œtre utilisØs dans des expressions du langage la mŒme place que des constantes du type entier. Ainsi, ils peuvent se situer dans des calculs, pour a ecter des variables et pour rØaliser des tests.

10.3.3          Limites des ØnumØrations

Les ØnumØrations sont des types synonymes (cf. section 10.4) du type entier. Elles permettent d’utiliser des constantes symboliques. Les variables dØ nies partir de type ØnumØrØ sont cependant traitØes comme des entiers. Ceci prØsente l’avantage de pouvoir rØaliser des expressions combinant des variables de type ØnumØrØ avec des entiers. Le compilateur n’impose pas qu’une variable de type ØnumØrØ soit a ectØe avec une des valeurs correspondant aux constantes symboliques associØes avec ce type. Ainsi nous pouvons Øcrire en reprenant les dØ nitions prØcØdentes : rvb = 12;

arcenciel = violet + jaune; if (rvb == arcenciel) tfic = repertoire;

else tfic = normal;

rvb = sock_unix;

Dans cet exemple nous mØlangeons les constantes symboliques appartenant des types d’ØnumØration di Ørents sans avoir de message d’avertissement du compilateur.

10.4          Types synonymes

Il est possible gr ce au dØclarateur typedef de dØ nir un type nouveau qui est un type synonyme. L’introduction du mot rØservØ typedef comme premier mot d’une ligne de dØ nitions provoque le fait que les noms qui seraient des noms de variables sur la mŒme ligne sans le mot typedef deviennent des noms de types synonymes. Chaque nom de type synonyme correspond au type qu’aurait eu la variable sur la mŒme ligne sans typedef. Ainsi la dØ nition suivante :

typedef int entier;

dØ nit un type synonyme appelØ entier ayant les mŒmes caractØristiques que le type prØ-dØ ni int.

Une fois cette dØ nition rØalisØe, nous pouvons utiliser ce nouveau type pour dØ nir des variables et nous pouvons mØlanger les variables de ce type avec des variables enti?res pour rØaliser des expressions.

entier e1=23, e2=5, te[50]={1,2,3,4,5,6,7};

int i;

i = e1 + e2; te[20] = i ? 60;

Dans le cas de la dØclaration de tableaux, la taille du nouveau type se trouve apr?s le nouveau nom de type.

typedef int tab[10]; tab tt;

Dans cet exemple, tab devient un type synonyme correspondant au type tableau de dix entiers. La variable tt est donc un tableau de dix entiers. typedef est tr?s semblable une directive du prØprocesseur (cf. chapitre 7) mais il s’adresse au compilateur.

Attention : typedef ne rØserve pas d’espace mØmoire. Le nom est un type; il est donc inaccessible comme variable.

L’utilisation de typedef permet au compilateur de faire des vØri cations sur les types d’objets. En particulier sur les pointeurs.

Il est aussi possible de dØ nir un type Øquivalent             une structure :

typedef struct { int jour , mois, annee; } date;

et d’utiliser directement ce type. Ceci Øvite les rØpØtitions fastidieuses du mot rØservØ struct :

date obdate, *ptdate;.

Ainsi obdate est un objet correspondant au type date qui est synonyme d’une structure anonyme contenant trois variables enti?res appelØes jour, mois et annee. La variable ptdate est un pointeur qui peut contenir une adresse d’une variable du type date mais qui pour le moment n’est pas initialisØe.


Chapitre 11

EntrØes-sorties de la biblioth?que standard

Sommaire

11.1 EntrØes-sorties standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 11.1.1 changescaract?reparcaract?re . . . . . . . . . . . . . . . . . . . . . . . . 80

11.1.2 changesligneparligne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

11.1.3 changesavecformats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

                                                11.2 Ouverture d’un chier . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                      82

                                                11.3 Fermeture d’un chier . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                      84

11.4 Acc?s au contenu du chier . . . . . . . . . . . . . . . . . . . . . . . . . . 84 11.4.1 Acc?scaract?reparcaract?re . . . . . . . . . . . . . . . . . . . . . . . . . . 85

11.4.2 Acc?sparligne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

11.4.3 Acc?sparenregistrement . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

11.5 EntrØes-sorties formatØes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 11.5.1 Formats:casdelalecture. . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

11.5.2 Formats:casdel’Øcriture . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90

11.5.3 ConversionsurlesentrØes-sortiesstandards . . . . . . . . . . . . . . . . . . 91

11.5.4 ConversionenmØmoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

11.5.5 Conversiondansles chiers . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

11.6 DØplacement dans le chier . . . . . . . . . . . . . . . . . . . . . . . . . .                94 11.7 Gestion des tampons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                98

                                                11.8 Gestion des erreurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                     99

Comme nous l’avons vu, le langage C est de bas niveau. La biblioth?que du langage C fournit des traitements assez courants de plus haut niveau et permet, entre autres, la rØalisation des entrØessorties. La biblioth?que standard contient :

1.    les fonctions permettant la gestion des entrØes-sorties (E/S);

2.    des fonctions de manipulation de cha nes de caract?res;

3.    des fonctions d’allocation dynamique de mØmoire;

4.    des fonctions               caract?re gØnØral qui permettent l’acc?s au syst?me.

Ces fonctions et les structures associØes sont dØcrites dans le chier <stdio.h>. Tout programme dØsirant manipuler les E/S devra contenir la directive : #include <stdio.h>. Ce chier contient :

ë la dØ nition de macro-expressions;

ë le prototype des fonctions;

ë la dØ nition de constantes : EOF (cf. note de bas de page 1, page 39), etc; ë la dØ nition du tableau des    chiers ouverts.

Le langage C donne une vision    UNIXienne          des E/S. La mŒme interface d’acc?s aux chiers peut Œtre utilisØe sur les pØriphØriques; ainsi, mŒme l’Øcran et le clavier qui permettent la communication avec l’utilisateur, seront appelØs               chier ou                ux.

En langage C, un chier est une suite d’octets (un caract?re = 1 octet). Le chier n’a pas de structure propre qui lui est attachØe. Il est cependant possible au niveau programme de considØrer un chier comme une suite d’enregistrements correspondant une structure. Le principe de la manipulation d’un chier est le suivant :

1.    ouverture du               chier;

2.    lecture, Øcriture, et dØplacement dans le       chier;

3.    fermeture du              chier.

En ce qui concerne les ux de communication avec l’utilisateur, l’ouverture et la fermeture sont e ectuØes automatiquement.

11.1          EntrØes-sorties standard

Ce sont des fonctions prØ-dØ nies qui permettent de rØaliser les opØrations suivantes : ë lecture et Øcriture caract?re par caract?re; ë lecture et Øcriture ligne par ligne; ë lecture et Øcriture formatØes.

11.1.1              changes caract?re par caract?re

Dans ces Øchanges, l’unitØ de base est le caract?re que le syst?me consid?re comme un octet parfois rØduit une version de l’ASCII sur sept bits.

Les deux fonctions que nous prØsentons ici sont souvent des macro-expressions du prØprocesseur (voir chapitre 7), nous les considØrerons comme des fonctions pour simpli er la prØsentation.

int getchar(void); synopsis : cette fonction permet de lire un caract?re sur l’entrØe standard s’il y en a un. Ce caract?re est considØrØ comme Øtant du type unsigned char.

argument : rien.

retour : la fonction retourne un entier pour permettre la reconnaissance de la valeur de n de chier (EOF). L’entier contient soit la valeur du caract?re lu soit EOF.

            conditions d’erreur : en         n de       chier la fonction retourne la valeur EOF.

int putchar(int); synopsis : Cette fonction permet d’Øcrire un caract?re sur la sortie standard.

argument : Elle est dØ nie comme recevant un entierpour Œtre conforme getchar(). Ceci permet d’Øcrire putchar(getchar()).

retour : Elle retourne la valeur du caract?re Øcrit toujours considØrØ comme un entier. conditions d’erreur : en cas d’erreur la fonction retourne EOF.

11.1. ENTR ES-SORTIES STANDARD

LE programme 11.1 rØalise la lecture de son chier standard d’entrØe caract?re par caract?re et reproduit les caract?res sur son chier standard de sortie.

Prog. 11.1 Lecture et Øcriture caract?re par caract?re sur les E/S standard.

#include <stdio.h> void main(){ int c;

while((c=getchar()) != EOF) putchar(c); }

11.1.2             changes ligne par ligne

Dans ces Øchanges, l’unitØ de base est la ligne de caract?res. La ligne est considØrØe comme une suite de caract?res char terminØe par un caract?re de n de ligne ou par la dØtection de la n du chier. Le caract?re de n de ligne a une valeur enti?re Øgale 10 et est reprØsentØ par l’abrØviation ’\n’.

char* gets(char*); synopsis : lire une ligne sur l’entrØe standard; les caract?res de la ligne sont rangØs (un caract?re par octet) dans la mØmoire partir de l’adresse donnØe en argument la fonction. Le retour chariot est lu mais n’est pas rangØ en mØmoire. Il est remplacØ par le caract?re nul ’\0’ de mani?re ce que la ligne, une fois placØe en mØmoire, puisse Œtre utilisØe comme une cha ne de caract?res.

argument : l’adresse d’une zone mØmoire dans laquelle la fonction doit ranger les caract?res lus. retour : Si des caract?res ont ØtØ lus, la fonction retourne l’adresse de son argument pour permettre une imbrication dans les appels de fonction.

conditions d’erreur : Si la n de chier est atteinte, lors de l’appel (aucun caract?re n’est lu), la fonction retourne un pointeur de caract?re de valeur 0 ou NULL (dØ ni dans <stdio.h>). remarque : Cette fonction ne peut pas vØri er que la taille de la ligne lue est infØrieure la taille de la zone mØmoire dans laquelle il lui est demandØ de placer les caract?res. Il faut lui prØfØrer la fonction fgets() pour tout logiciel de qualitØ.

int puts(char*); synopsis : cette fonction permet d’Øcrire une cha ne de caract?res, suivie d’un retour chariot sur la sortie standard.

argument : l’adresse d’une cha ne de caract?res.

retour : une valeur enti?re non nØgative en cas de succ?s. conditions d’erreur : Elle retourne la valeur EOF en cas de probl?me.

Le programme 11.2 est une nouvelle version du programme prØcØdent, cette fois-ci ligne par ligne. Le choix d’un tableau de 256 caract?res permet d’espØrer qu’aucune ligne lue ne provoquera un dØbordement par la fonction gets(). Ceci reste un espoir et peut Œtre dØmenti si le programme est confrontØ une ligne de plus grande taille, dans ce cas le comportement du programme est non dØ ni.

Prog. 11.2 Lecture ligne par ligne sur les chiers standard.

#include <stdio.h> void main(){ char BigBuf[256]; while(gets(BigBuf)!=NULL) puts(BigBuf); }

11.1.3            changes avec formats

Les fonctions permettant de faire des E/S formatØes sur les chiers standard d’entrØe et de sortie sont les suivantes :

int scanf(const char*, ); synopsis : lecture formatØe sur l’entrØe standard. arguments : comme l’indique la spØci cation " ", cette fonction accepte une liste d’arguments variable partir du second argument.

1.    le premier argument est une cha ne de caract?res qui doit contenir la description des variables         saisir.

2.    les autres arguments sont les adresses des variables (conformØment la description donnØe dans le premier argument) qui sont a ectØes par la lecture.

retour : nombre de variables saisies.

conditions d’erreur : la valeur EOF est retournØe en cas d’appel sur un chier standard d’entrØe fermØ.

int printf(const char*, ); synopsis : Øcriture formatØe sur la sortie standard a

arguments : cha ne de caract?res contenant des commentaires et des descriptions d’arguments Øcrire, suivie des valeurs des variables.

retour : nombre de caract?res Øcrits.

conditions d’erreur : la valeur EOF est retournØe en cas d’appel sur un chier standard de sortie fermØ.

Des exemples de formats sont donnØs dans le tableau 3.1, page 23. Les possibilitØs en mati?re de format sont donnØes de mani?re exhaustive dans la partie sur les E/S formatØes (voir section 11.5).

11.2         Ouverture d’un        chier

Tout chier doit Œtre ouvert pour pouvoir accØder son contenu en lecture, Øcriture ou modi cation. L’ouverture d’un chier est l’association d’un objet extØrieur (le chier) au programme en cours d’exØcution. Une fonction d’ouverture spØci e le nom du chier l’intØrieur de l’arborescence du syst?me de chiers et des attributs d’ouverture.

L’ouverture d’un          chier est rØalisØe par la fonction fopen selon la description suivante :

FILE* fopen(const char*, const char* );

11.2. OUVERTURE D’UN FICHIER

synopsis : ouverture d’un chier rØfØrencØ par le premier argument (nom du chier dans le syst?me de chiers sous forme d’une cha ne de caract?res) selon le mode d’ouverture dØcrit par le second argument (cha ne de caract?res).

arguments :

1.    la premi?re cha ne de caract?res contient le nom du chier de mani?re rØfØrencer le chier dans l’arborescence. Ce nom est dØpendant du syst?me d’exploitation dans lequel le programme s’exØcute.

2.    Le deuxi?me argument est lui aussi une cha ne de caract?res. Il spØci e le type d’ouverture.

retour : pointeur sur un objet de type FILE (type dØ ni dans <stdio.h>) qui sera utilisØ par les opØrations de manipulation du chier ouvert (lecture, Øcriture ou dØplacement).

conditions d’erreur : le pointeur NULL est retournØ si le chier n’a pas pu Œtre ouvert (probl?mes d’existence du chier ou de droits d’acc?s).

Le type d’ouverture est spØci Ø partir d’un mode de base et de complØments. A priori, le chier est considØrØ comme une chier de type texte (c’est- -dire qu’il ne contient que des caract?res ASCII). Le type d’ouverture de base peut Œtre :

       ë "r" le         chier est ouvert en lecture. Si le        chier n’existe pas, la fonction ne le crØe pas.

ë "w" le chier est ouvert en Øcriture. Si le chier n’existe pas, la fonction le crØe. Si le chier existe la fonction le vide.

ë "a" le chier est ouvert en ajout. Si le chier n’existe pas, la fonction le crØe. Les Øcritures auront lieu la n du chier.

Le type d’ouverture peut Œtre agrØmentØ de deux caract?res qui sont : ë "b" le chier est considØrØ en mode binaire. Il peut donc contenir des donnØes qui sont transfØrØes sans interprØtation par les fonctions de la biblioth?que.

ë "+" le chier est ouvert dans le mode complØmentaire du mode de base. Par exemple s’il est ouvert dans le mode "r+" cela signi e qu’il est ouvert en mode lecture et plus, soit lecture et

Øcriture.

La combinaison des modes de base et des complØments donne les possibilitØs suivantes :

ë "r+" le chier est ouvert en lecture plus Øcriture. Si le chier n’existe pas, la fonction ne le crØe pas. Le chier peut Œtre lu, modi Ø et agrandi. ë "w+" le chier est ouvert en Øcriture plus lecture. Si le chier n’existe pas, la fonction le crØe. Si le chier existe, la fonction le vide. Le chier peut Œtre manipulØ en Øcriture et relecture.

ë "a+" le          chier est ouvert en ajout plus lecture. Si le            chier n’existe pas, la fonction le crØe. Les Øcritures auront lieu     la             n du       chier. Le               chier peut Œtre lu. ë "rb" le         chier est ouvert en lecture et en mode binaire. Si le      chier n’existe pas, la fonction ne le crØe pas.

ë "wb" le chier est ouvert en Øcriture et en mode binaire. Si le chier n’existe pas, la fonction le crØe. Si le chier existe, la fonction le vide.

ë "ab" le chier est ouvert en ajout et en mode binaire. Si le chier n’existe pas, la fonction le crØe. Les Øcritures auront lieu la n du chier. ë "r+b" ou "rb+"le chier est ouvert en lecture plus Øcriture et en mode binaire. Si le chier n’existe pas, la fonction ne le crØe pas. Le chier peut Œtre lu, modi Ø et agrandi.

ë "w+b" ou "wb+" le chier est ouvert en Øcriture plus lecture et en mode binaire. Si le chier n’existe pas, la fonction le crØe. Si le chier existe, la fonction le vide. Le chier peut Œtre Øcrit puis lu et Øcrit.

ë "a+b" ou "ab+" le chier est ouvert en ajout plus lecture et en mode binaire. Si le chier n’existe pas, la fonction le crØe. Les Øcritures auront lieu la n du chier.

La fonction fopen() retourne un descripteur de chier qui servira pour toutes les autres opØrations. Le descripteur de chier doit Œtre dØclarØ comme dans l’exemple : si le syst?me ne peut pas ouvrir le chier, il retourne un descripteur de chier Øgal au pointeur NULL. Si un            chier qui n’existe pas est

ouvert en mode Øcriture ou ajout, il est crØØ par le syst?me.

                                                                    Prog. 11.3       Ouverture d’un     chier.

#include <stdio.h> void main(){

FILE? MyFich;

MyFich = fopen("d:\temp\passwd","r"); }

11.3         Fermeture d’un        chier

Avant d’Øtudier les fonctions permettant d’accØder aux donnØes d’un chier ouvert, considØrons la fonction qui permet de terminer la manipulation d’un chier ouvert. Cette fonction rØalise la fermeture du chier ouvert. Elle a le prototype suivant :

int fclose(FILE*); synopsis : c’est la fonction inverse de fopen. Elle dØtruit le lien entre le descripteur de chier ouvert et le chier physique. Si le chier ouvert est en mode Øcriture, la fermeture provoque l’Øcriture physique des donnØes du tampon (voir 11.7).

arguments : un descripteur de            chier ouvert valide. retour : 0 dans le cas normal. conditions d’erreur : la fonction retourne EOF en cas d’erreur.

Tout programme manipulant un chier doit donc Œtre encadrØ par les deux appels de fonctions fopen() et fclose() comme le montre le programme 11.4.

                                                       Prog. 11.4       Ouverture et fermeture d’un      chier.

#include <stdio.h> void main(){

FILE? MyFich;

MyFich = fopen("d:\temp\passwd","r");

fclose(MyFich); }

11.4          Acc?s au contenu du         chier

Une fois le         chier ouvert, le langage C permet plusieurs types d’acc?s          un     chier :

ë par caract?re, ë par ligne, ë par enregistrement, ë par donnØes formatØes.

Dans tous les cas, les fonctions d’acc?s au chier (sauf les opØrations de dØplacement) ont un comportement sØquentiel. L’appel de ces fonctions provoque le dØplacement du pointeur courant relatif au chier ouvert. Si le chier est ouvert en mode lecture, les opØrations de lecture donnent une impression de consommation des donnØes contenues dans le chier jusqu’ la rencontre de la n du chier.

11.4. ACC¨S AU CONTENU DU FICHIER

11.4.1           Acc?s caract?re par caract?re

Les fonctions suivantes permettent l’acc?s caract?re par caract?re :

int fgetc(FILE*); synopsis : lit un caract?re (unsigned char) dans le chier associØ; argument : descripteur de chier ouvert; retour : la valeur du caract?re lu promue dans un entier;

conditions d’erreur : la rencontre de la             n de       chier, la fonction retourne EOF et positionne les indicateurs associØs (voir section 11.8).

int getc(FILE*);

synopsis : cette fonction est identique fgetc() mais peut Œtre rØalisØe par une macro dØ nie dans <stdio.h>.

int ungetc(int, FILE*);

synopsis : cette fonction permet de remettre un caract?re dans le bu er de lecture associØ un ux d’entrØe.

int fputc(int , FILE*);

synopsis : Øcrit dans le chier associØ dØcrit par le second argument un caract?re spØci Ø dans le premier argument. Ce caract?re est converti en un unsigned char;

argument : le premier argument contient le caract?re              Øcrire et le second contient le descripteur de      chier ouvert;

retour : la valeur du caract?re Øcrit promue dans un entier sauf en cas d’erreur;

conditions d’erreur : en cas d’erreur d’Øcriture, la fonction retourne EOF et positionne les indicateurs associØs (voir section 11.8).

int putc(int , FILE*);

synopsis : cette fonction est identique fputc() mais elle est rØalisØe par une macro dØ nie dans <stdio.h>.

Les fonctions (macros) getc() et putc() sont en fait la base de getchar() et putchar() que nous avons utilisØes jusqu’ici.

ë putchar(c) est dØ ni comme putc(c,stdout); ë getchar() est dØ ni comme getc(stdin).

Comme getchar(), la fonction getc() retourne un entier pour pouvoir retourner la valeur EOF en n de chier. Dans le cas gØnØral, l’entier retournØ correspond un octet lu dans le chier et rangØ

dans un entier en considØrant l’octet comme Øtant du type caract?re non signØ (unsigned char).

Voici un exemple d’Øcriture l’Øcran du contenu d’un chier dont le nom est donnØ en argument. Le chier est ouvert en mode lecture et il est considØrØ comme Øtant en mode texte.

                             Prog. 11.5         Lecture caract?re par caract?re d’un       chier apr?s ouverture.

#include <stdio.h> void main(int argc, char?? argv){ if(argc != 2) return 1;

FILE? MyFich=fopen(argv[1], "r"); if(MyFic == NULL) { printf("Ouverture impossible %s \n", argv[1]);

return 2;

}

int TheCar;

while((TheCar=fgetc(MyFic))!=EOF) fputc(TheCar,stdout);

fclose(MyFic); }

11.4.2         Acc?s par ligne

Comme pour les E/S standards, il est possible de rØaliser des opØrations de lecture et d’Øcriture ligne par ligne l’intØrieur de chiers ouverts. Cet acc?s ligne par ligne se fait gr ce aux fonctions :

char* fgets(char*, int , FILE*); synopsis : lit une ligne de caract?res dans le chier associØ, les caract?res de la ligne sont rangØs dans la mØmoire partir de l’adresse donnØe en argument la fonction. Le retour chariot est lu et rangØ en mØmoire. Il est suivi par le caract?re nul ’\0’ de mani?re ce que la ligne, une fois placØe en mØmoire, puisse Œtre utilisØe comme une cha ne de caract?res.

arguments :

1. adresse de la zone de stockage des caract?res en mØmoire, 2. nombre maximum de caract?res (taille de la zone de stockage), 3. et descripteur de           chier ouvert.

retour : adresse re ue en entrØe sauf en cas d’erreur;

conditions d’erreur : la rencontre de la n de chier, la fonction retourne NULL et positionne les indicateurs associØs (voir section 11.8).

int fputs(const char*, FILE*); synopsis : cette fonction permet d’Øcrire une cha ne de caract?res rØfØrencØe par le premier argument dans le chier dØcrit par le second argument.

argument :

1.    le premier argument contient l’adresse de la zone mØmoire qui contient les caract?res Øcrire. Cette zone doit Œtre un cha ne de caract?res (terminØe par le caract?re nul). Elle doit contenir un retour chariot pour obtenir un passage     la ligne suivante.

2.    le second argument contient le descripteur de chier ouvert dans lequel les caract?res seront Øcrits. retour : une valeur positive si l’Øcriture s’est correctement dØroulØe.

conditions d’erreur : en cas d’erreur d’Øcriture, la fonction retourne EOF et positionne les indicateurs associØs (voir section 11.8).

11.4. ACC¨S AU CONTENU DU FICHIER

Pour la lecture ligne ligne, il est nØcessaire de donner l’adresse d’un tableau pouvant contenir la ligne. De plus, il faut donner la taille de ce tableau pour que la fonction fgets ne dØborde pas du tableau. La fonction fgets() lit au plus n-1 caract?res et elle ajoute un caract?re nul (’\0’) apr?s le dernier caract?re qu’elle a mis dans le tableau. La rencontre du caract?re de n de ligne ou de la n de chier provoque la n de lecture. Le caract?re de n de ligne n’est pas mis dans le tableau avant le caract?re nul.

La fonction fputs() Øcrit une ligne dans le chier. Le tableau de caract?res doit Œtre terminØ par un caract?re nul (’\0’). Il faut mettre explicitement la n de ligne dans ce tableau pour qu’elle soit prØsente dans le chier.

Voici le programme prØcØdent Øcrit        l’aide de ces fonctions :

                                        Prog. 11.6       Lecture ligne       ligne d’un     chier apr?s ouverture.

#include <stdio.h> void main(int argc, char?? argv) { if(argc!=2) return 1;

FILE? TheFic=fopen(argv[1], "r"); if(TheFic == NULL) { printf("Ouverture impossible %s \n", argv[1]);

return 2;

} char BigBuf[256]; while( fgets(BigBuf,sizeof(BigBuf), TheFic) != NULL

)

fputs(BigBuf,stdout);

fclose(TheFic); }

Les di Ørences entre gets() et fgets(), d’une part, et puts() et fputs(), d’autre part, peuvent s’expliquer par le fait que les fonctions puts et gets agissent sur les E/S standards qui sont le plus souvent des terminaux (Øcran + clavier). Les di Ørences sont les suivantes :

ë la fonction gets() :

1.    ne nØcessite pas que lui soit fournie la taille du tableau de lecture. Il faut espØrer que les lignes saisies seront plus courtes que la taille du tableau. Comme ces lignes viennent a priori d’un terminal, elles font souvent moins de 80 caract?res.

2.    ne met pas le caract?re de n de ligne dans le tableau. Ce caract?re est remplacØ par le caract?re de n de cha ne.

            ë la fonction puts() : ne nØcessite pas la prØsence du caract?re de               n de ligne dans le tableau.

Ce caract?re est ajoutØ automatiquement lors de l’Øcriture.

11.4.3          Acc?s par enregistrement

L’acc?s par enregistrement permet de lire et d’Øcrire des objets structurØs dans un chier. Ces objets structurØs sont le plus souvent reprØsentØs en mØmoire par des structures. Pour ce type d’acc?s, le chier doit Œtre ouvert en mode binaire (voir exemple 11.7 ligne 11). Les donnØes ØchangØes ne sont pas traitØes comme des caract?res.

L’acc?s par enregistrement se fait gr ce aux fonctions :

ë size_t fread (void* Zone, size_t Taille, size_t Nbr, FILE* fp); ë size_t fwrite(void* Zone, size_t Taille, size_t Nbr, FILE* fp);

Ces fonctions ont une interface homog?ne. Elles acceptent une liste identique d’arguments, et retournent le mŒme type de rØsultat. Les arguments sont les suivants :

1.    le premier argument, que nous avons appelØ Zone, est l’adresse de l’espace mØmoire partir duquel l’Øchange avec le chier est fait. L’espace mØmoire correspondant re oit les enregistrements lus, ou fournit les donnØes Øcrire dans les enregistrements. Il faut, bien entendu, que l’espace mØmoire correspondant l’adresse soit de taille su sante pour supporter le transfert des donnØes, c’est- dire d’une taille au moins Øgale (Taille* Nbr).

2.    le deuxi?me argument, que nous avons appelØ Taille, est la taille d’un enregistrement en nombre d’octets.

3.    le troisi?me argument, que nous avons appelØ Nbr, est le nombre d’enregistrements que l’on dØsire Øchanger.

4.    le dernier argument, que nous avons appelØ fp, est un descripteur de chier ouvert dans un mode de transfert binaire.

Ces deux fonctions retournent le nombre d’enregistrements ØchangØs. Elles ont ØtØ con ues de mani?re permettre l’Øchange de plusieurs structures, ce qui explique la prØsence des deux arguments qui fournissent la taille totale des donnØes transfØrer.

Voici un exemple d’utilisation de la fonction fread(). Cet exemple est une lecture du contenu d’un chier appelØ FicParcAuto avec stockage du contenu de ce chier dans un tableau en mØmoire ParcAuto. Les cases du tableau sont des structures contenant un entier, une cha ne de vingt caract?res et trois cha nes de dix caract?res.

                                                Prog. 11.7        Lecture d’enregistrements dans un      chier.

#include <stdio.h>

#include <stddef.h>

struct auto { int age;

char couleur[20], numero[10], type[10], marque[10];

} ParcAuto[20];

void main(int argc, char?? argv) {

FILE? TheFic=fopen(argv[1], "rb+"); if (TheFic == NULL) { printf("Ouverture impossible %s \n", argv[1]);

return 1;

}

size_t fait; for(int i = 0; i<20; i++){ fait=fread(&ParcAuto[i], sizeof(struct auto), 1, TheFic); if(fait != 1) { printf("Erreur lecture %s \n", argv[1]);

return 2;

}

} fclose(TheFic);

}


Il est possible de demander la lecture des vingt enregistrements en une seule opØration, en rempla ant les lignes 18 24 par les lignes suivantes :

fait=fread(ParcAuto, sizeof(struct auto), 20, TheFic);

ou bien encore par :

fait=fread(ParcAuto, sizeof(ParcAuto), 1, TheFic);

11.5         EntrØes-sorties formatØes

Nous avons dØj vu comment utiliser printf et scanf. Nous allons approfondir nos connaissances sur ces fonctions et sur les autres fonctions qui font des conversions de format. Les fonctions que nous allons voir utilisent des formats de conversion entre le mod?le des donnØes machines et le mod?le nØcessaire la vision humaine (cha ne de caract?res). Les lectures formatØes nØcessitent :

ë le format de description des lectures              faire; ë une adressepour chaque variable simple ou pour un tableau de caract?res.

Les Øcritures formatØes nØcessitent :

        ë le format de description des Øcritures         faire;

ë les valeurs des variables simples Øcrire. Comme dans le cas de la lecture, l’Øcriture d’un ensemble de caract?res est une opØration particuli?re qui peut se faire partir de l’adresse du premier caract?re.

11.5.1           Formats : cas de la lecture

Les formats de conversion servent dØcrire les types externes et internes des donnØes lire. Les formats peuvent contenir :

1. des caract?res "blancs" (espace, tabulation). La prØsence d’un caract?re blanc provoque la lecture et la mise la poubelle des caract?res "blancs" (espace, tabulation, retour chariot) qui seront lus; 2. des caract?res ordinaires (ni blanc, ni %). Ces caract?res devront Œtre rencontrØs la lecture;

3. des spØci cations de conversion, commen ant par le caract?re %.

Une conversion consiste en :

1.    un caract?re de pourcentage (%);

2.    un caract?re (optionnel) d’e acement (*); dans ce cas la donnØe lue est mise  la poubelle;

3.    un champ (optionnel) dØ nissant la taille de la donnØe lire exprimØe par une valeur enti?re en base dix;

4.    un caract?re (optionnel) de prØcision de taille qui peut Œtre : l, h ou L. Ces caract?res agissent sur les modes de spØci cation de la mani?re suivante :

(a)  si le format initial est du type d, i ou n, les caract?res l et h prØcisent respectivement que la donnØe est du type entier long (long int) ou entier court (short int) plut t qu’entier ordinaire (int).

(b)  si le format initial est du type o, x ou u, les caract?res l et h prØcisent respectivement que la donnØe est du type entier long non signØ (unsigned long int) ou entier court non signØ (unsigned short int) plut t qu’entier non signØ (unsigned int).

(c)   si le format initial est du type e ou f ou g, les caract?res l et L prØcisent respectivement que la donnØe est du type nombre avec point dØcimal de grande prØcision (double) ou nombre avec point dØcimal de tr?s grande prØcision (long double) plut t que du type nombre avec point dØcimal (float).

(d)  dans tous les autres cas, le comportement est indØ ni.

5.    un code de conversion.

Les codes de conversion sont dØcrits dans le tableau 11.1.

                                                        Tableau 11.1        Code de conversion pour scanf.

Code

Conversion rØalisØe

%

lit un %

d

entier signØ exprimØ en base dØcimale

i

entier signØ exprimØ en base dØcimale

o

entier non signØ exprimØ en base octale

u

entier non signØ exprimØ en base dØcimale

x

entier non signØ exprimØ en hexadØcimal

e,f ou g

nombre avec partie dØcimale en notation point dØcimal, ou notation exponentielle

c

caract?re

s

mots ou cha ne de caract?res sans blanc

[spØcif.]

cha ne de caract?res parmi un alphabet

p

adresse, pour faire l’opØration inverse de l’Øcriture avec %p

n

permet d’obtenir le nombre d’octets lus dans cet appel

La spØci cation entre les crochets dØ nit un alphabetde caract?res. La donnØe lue doit Œtre conforme cette spØci cation. La lecture avec un format de spØci cation retourne une cha ne de caract?res.

11.5.2          Formats : cas de l’Øcriture

Les formats de conversion servent dØcrire les types externes et internes des donnØes Øcrire. Les formats peuvent contenir :

1. des caract?res qui sont recopiØs dans la cha ne engendrØe par l’Øcriture; 2. et des spØci cations de conversion. Une conversion consiste en :

1.    un caract?re de pourcentage (%);

2.    des drapeaux ( ags) qui modi ent la signi cation de la conversion;

3.    la taille minimum du champ dans lequel est insØrØe l’Øcriture de la donnØe;

(a)  la taille est donnØe en nombre de caract?res,

(b)  pour les chi res, si la taille commence par            la donnØe est cadrØe    gauche.

(c)   pour une cha ne de caract?re, si la taille est prØcØdØe de 0, la cha ne est cadrØe droite et est prØcØdØe de zØros;

4.    un point suivi de la prØcision. La prØcision dØ nit le nombre de chi res signi catifs pour une donnØe de type entier, ou le nombre de chi res apr?s la virgule (.!) pour une donnØe de type ottant. Elle indique le nombre de caract?res pour une cha ne;

5.    un h, un l ou un L signi ant court ou long et permettant de prØciser :

(a)  dans le cas d’une conversion d’un entier (format d, i, o, u, x, X) que l’entier Øcrire est un entier court (h) ou long (l);

(b)  dans le cas d’une conversion d’un nombre avec partie dØcimale (format e, f, g, E, G) que le nombre Øcrire est un nombre avec point dØcimal de tr?s grande prØcision (long double). 6. un code de conversion.

Les champs taille et prØcision peuvent contenir une *. Dans ce cas la taille doit Œtre passØe dans un argument [sf]printf. Par exemple les lignes suivantes d’appel            printf() sont Øquivalentes :

printf("Valeur de l’entier Indice : %*d\n", 6, Indice); printf("Valeur de l’entier Indice : %6d\n", Indice); Les codes de conversion sont dØcrits dans le tableau 11.2.

                                                       Tableau 11.2         Code de conversion pour printf.

Code

Conversion rØalisØe

%

lit un %

d

entier signØ exprimØ en base dØcimale

i

entier signØ exprimØ en base dØcimale

o

entier non signØ exprimØ en base octale

u

entier non signØ exprimØ en base dØcimale

x,X

entier non signØ exprimØ en hexadØcimal

e,E

nombre avec partie dØcimale en notation exponentielle

f

nombre avec partie dØcimale en notation point dØcimal

g

nombre avec partie dØcimale, printf choisit le format f ou e

c

caract?re

s

cha ne de caract?res

p

la valeur passØe est une adresse

n

permet d’obtenir le nombre d’octets Øcrits

La di Ørence entre x et X vient de la forme d’Øcriture des valeurs dØcimales entre 10 et 15. Dans le premier cas, elles sont Øcrites en minuscule (a-f), dans le second cas, elles sont Øcrites en majuscule (A-F). De mŒme, le caract?re E de la notation exponentielle est mis en minuscule par les formats e et g. Il est mis en majuscule par les formats E et G. Le nombre maximum de caract?res qui peuvent Œtre construits dans un appel aux fonctions de type fprintf() ne doit pas dØpasser 509. Les drapeaux sont dØcrits dans le tableau 11.3.

11.5.3             Conversion sur les entrØes-sorties standards

int printf(const char? format, ) ; int scanf (const char? format, ) ;

11.5.4         Conversion en mØmoire

Les deux fonctions de conversion en mØmoire s’appellent sprintf et sscanf. Les appels sont les suivants :

                                                  Tableau 11.3          Modi cateurs de format pour printf.

Drapeau

Modi cation apportØe

-

la donnØe convertie est cadrØe      gauche.

+

si la donnØe est positive le signe + est mis.

blanc

si le rØsultat de la conversion ne commence pas par un signe, un blanc.

est ajoutØ.

#

pour format o augmente la prØcision de mani?re               forcer un 0 devant la donnØe.

pour format x et X force 0x devant la donnØe.

pour format e, E, f, g, et G force le point dØcimal. pour format g et G les zØros apr?s le point dØcimal sont conservØs.

int sprintf(char* string, const char* format, ); synopsis : conversion de donnØes en mØmoire par transformation en cha ne de caract?res.

arguments :

1. zone dans laquelle les caract?res sont stockØs; 2. format d’Øcriture des donnØes; 3. valeurs des donnØes.

retour : nombre de caract?res stockØs. int sscanf(char* string, const char* format, ); synopsis : lecture formatØe partir d’une zone mØmoire. arguments :

1.    zone dans laquelle les caract?res sont acquis;

2.    format de lecture des donnØes;

3.    adresse des variables          a ecter  partir des donnØes.

retour : nombre de variables saisies. conditions d’erreur : la valeur EOF est retournØe en cas d’erreur empŒchant toute lecture.

sprintf convertit les arguments arg1, , argn suivant le format de contr le et met le rØsultat dans string (cha ne de caract?res).

Inversement, sscanf extrait d’une cha ne de caract?res des valeurs qui sont stockØes dans des variables suivant le format de contr le.

11.5.5         Conversion dans les        chiers

Deux fonctions fprintf et fscanf permettent de rØaliser le mŒme travail que printf et scanf sur des chiers ouverts en mode texte :

int fprintf(FILE*, const char*, ); synopsis : Øcriture formatØe sur un chier ouvert en mode texte.

arguments :

1. descripteur de         chier ouvert valide dans lequel les caract?res sont rangØs; 2. format d’Øcriture des donnØes;

3. valeurs des donnØes.

retour : nombre de caract?res Øcrits.

conditions d’erreur : une valeur nØgative est retournØe en cas d’erreur d’Øcriture. int fscanf(FILE*, const char*, );

synopsis : lecture formatØe dans un chier ouvert en mode texte. arguments :

1.    descripteur de        chier ouvert dans lequel les caract?res sont lus;

2.    format de lecture des donnØes;

3.    adresse des variables          a ecter  partir des donnØes.

retour : nombre de variables saisies.

conditions d’erreur : la valeur EOF est retournØe en cas d’appel sur un chier standard d’entrØe fermØ.

Le programme 11.8 ouvre le chier d:\temp\passwd ( chier contenant les identi cations des utilisateurs) et qui extrait les di Ørents champs dont les numØros d’utilisateur et de groupe, en mettant ces numØros dans des variables de type entier. Rappelons qu’une ligne de ce chier contient les champs suivants sØparØs par le caract?re deux points ’ :’. ë le nom de l’utilisateur, ë le mot de passe (cryptØ), ë le numØro d’utilisateur,

        ë le numØro de groupe dans lequel l’utilisateur est placØ         la connexion,

ë un champ servant mettre des informations complØmentaires appelØ champ GCOS (ce champ peut contenir des caract?res blancs);

ë le rØpertoire de connexion de l’utilisateur, ë le           chier binaire exØcutable               charger lors de la connexion (le plus souvent un shell).

                                                Prog. 11.8        Lecture avec format dans un      chier texte.

#include <stdio.h>

void main() { int i ,res;

char Nom[10], Passwd[16], Gcos[128], Rep[255], Shell[255];

int Uid, Gid;

FILE? TheFic=fopen("\verb"d:\temp\passwd"","r"); if(TheFic == NULL) { printf("Ouverture impossible %s \n","passwd");

return 1;

}

while(!feof(TheFic)){ res = fscanf(TheFic,"%[^:]:",Nom); if(res != 1) break;

res = fscanf(TheFic,"%[^:]:",Passwd); if(res != 1) break; res = fscanf(TheFic,"%d:",&Uid); if(res != 1) break; res = fscanf(TheFic,"%d:",&Gid); if(res != 1) break;

for(i=0; i<128; i++){ res = fgetc(TheFic); if(res == ’:’) {

Gcos[i] = ’\0’; break;

} else

Gcos[i] = res;

}

res = fscanf(TheFic,"%[^:]:",Rep); if(res != 1) break; res = fgetc(TheFic); if(res != ’\n’) {

ungetc(res ,TheFic); res = fscanf(TheFic,"%s",Shell); if(res != 1) break;

} else

Shell[0] = ’\0’; printf("%s %s %d %d\n", Nom, Passwd, Uid, Gid,); printf("%s %s %s\n", Gcos, Rep ,Shell);

}

fclose(TheFic); }

11.6         DØplacement dans le        chier

Jusqu’ maintenant nous avons vu des fonctions qui modi aient de mani?re automatique le pointeur courant dans le chier correspondant (adresse de l’octet dans le chier partir duquel se fera la


11.6. D PLACEMENT DANS LE FICHIER

prochaine opØration d’E/S). Nous allons voir les fonctions qui permettent de conna tre la valeur de cette position courante dans le               chier et de la modi er. Ces fonctions associØes   la position dans le chier sont :

int fseek(FILE*, long, int); synopsis : change la position courante dans le chier.

arguments :

1.    descripteur de        chier ouvert;

2.    dØplacement         l’intØrieur du     chier en nombre d’octets;

3.    point de dØpart du dØplacement. Cet argument peut prendre les valeurs suivantes qui selon la norme doivent Œtre dØ nies dans le chier <stdio.h> mais sont souvent dans le chier <unistd.h> sur les machines de type "UNIX system V" :

                                   SEEK_SET : le dØplacement est alors relatif au dØbut du          chier;

                                   SEEK_CUR : le dØplacement est alors relatif            la position courante;

SEEK_END : le dØplacement est alors relatif            la             n du       chier; retour : 0 en cas de succ?s conditions d’erreur : une valeur di Ørente de zØro est retournØe si le dØplacement ne peut pas

Œtre rØalisØ.

long ftell(FILE*); synopsis : retourne la valeur de la position courante dans le chier.

argument : descripteur de    chier ouvert; retour :

1.    sur les        chiers binaires : nombre d’octets entre la position courante et le dØbut du            chier.

2.    sur les chiers texte : une valeur permettant fseek de re-positionner le pointeur courant l’endroit actuel.

conditions d’erreur : la valeur -1L est retournØe, et la variable errno est modi Øe.

int fgetpos(FILE*, fpos_t*); synopsis : acquiert la position courante dans le chier.

arguments :

1.    descripteur de        chier ouvert;

2.    rØfØrence d’une zone permettant de conserver la position courante du chier (le type fpos_t est souvent un type Øquivalent du type entier long);

retour : 0 en cas de succ?s conditions d’erreur : une valeur di Ørente de 0 est retournØe, et la variable errno est modi Øe.

int fsetpos(FILE*, const fpos_t*); synopsis : change la position courante dans le chier.

arguments :

1.    descripteur de        chier ouvert;

2.    rØfØrence d’une zone ayant servi  conserver la position courante du             chier par un appel prØcØdent     fgetpos();

retour : 0 en cas de succ?s conditions d’erreur : une valeur di Ørente de 0 est retournØe, et la variable errno est modi Øe. void rewind(FILE*);

synopsis : si le descripteur de              chier ouvert fp est valide, rewind(fp) est Øquivalent        (void) fseek(fp,0L,0).

Pour illustrer le dØplacement l’intØrieur d’un chier, nous allons prendre pour exemple la modi cation de l’ ge des voitures dans le chier FicParcAuto vu prØcØdemment. Le programme 11.9 rØalise la modi cation d’un enregistrement dans un chier en procØdant de la mani?re suivante :

1.    il lit un enregistrement du     chier dans une zone en mØmoire;

2.    il modi e la zone en mØmoire;

3.    il replace le pointeur courant du chier sur le dØbut de l’enregistrement pour pouvoir rØØcrire cet enregistrement;

4.    il Øcrit la zone mØmoire dans le          chier.

                                           Prog. 11.9         Modi cations par dØplacement dans un      chier.

#include <stdio.h>

#include <stddef.h> struct auto { int age;

char couleur[20], numero[10], type[10], marque[10];

} UneAuto;

void main(int argc, char?? argv) {

FILE? TheFic=fopen(argv[1], "rb+"); if (TheFic == NULL) { printf("Ouverture impossible %s \n", argv[1]);

return 1;

}

size_t fait; for(int i=0; i<20; i++){ fait=fread(&UneAuto, sizeof(UneAuto), 1, TheFic); if(fait != 1){ printf("Erreur lecture fichier parcauto\n");

return 2;

}

++; fait=fseek(TheFic, ?sizeof(UneAuto), SEEK_CUR); if(fait != 0){ printf("Erreur deplacement fichier parcauto\n");

return 3;

} fait=fwrite(&UneAuto, sizeof(UneAuto), 1, TheFic); if(fait != 1) { printf("Erreur ecriture ; fait=%d \n", fait);

return 4;

}

} fclose(TheFic); }

Cette modi cation est rØalisØe par le programme 11.9 selon les instructions C suivantes :

ë la ligne 18 correspond une lecture d’un enregistrement du chier dans la zone mØmoire UneAuto du type struct auto.

11.6. D PLACEMENT DANS LE FICHIER

ë la ligne 23 modi e la valeur du champ age dans la structure en mØmoire;

ë la ligne 24 modi e la position courante du chier pour positionner le pointeur courant l’adresse de dØbut de l’enregistrement qui est en mØmoire.

ë la ligne 27 Øcrit dans le chier le contenu de la zone mØmoire UneAuto. Cette Øcriture provoque la modi cation de l’enregistrement sur disque.

Ce mŒme exemple peut aussi Œtre rØalisØ avec les fonctions fgetpos() et fsetpos() comme le montre le programme 11.10. La di Ørence majeure entre ces deux exemples vient du fait que, dans la version 11.10, le programme conserve l’information permettant de re-positionner le pointeur courant dans le chier, alors que dans le programme 11.9 le programme revient en arri?re de la taille d’un enregistrement. Les fonctions fgetpos() et fsetpos() sont plus appropriØes pour des dØplacements dans un chier avec des tailles d’enregistrement variables.

                                          Prog. 11.10       DØplacements dans un       chier avec fgetpos().

void main(int argc, char?? argv) {



FILE? TheFic=fopen(argv[1], "rb+"); if (TheFic == NULL) { printf("Ouverture impossible %s \n", argv[1]);

return 1;

}

size_t fait; fpos_t CurPos; for(int i=0; i<20; i++) { fait=fgetpos(TheFic, &CurPos); if(fait != 0){ printf("Erreur acquisition Position\n");

return 2;

}

fait=fread(&ParcAuto[i], sizeof(struct auto), 1, TheFic); if(fait != 1){ printf("Erreur lecture fichier parcauto\n");

return 3;

}

ParcAuto[i ].age++; fait=fsetpos(TheFic, &CurPos); if(fait != 0){ printf("Erreur restitution Position\n");

return 4;

}

fait=fwrite(&ParcAuto[i], sizeof(struct auto), 1, TheFic); if(fait != 1){ printf("Erreur ecriture ; fait=%d \n", fait);

return 5;

}

} fclose(TheFic);

}

Les E/S sont en gØnØral bu erisØes. Dans le cas gØnØral, l’allocation du bu er se fait de mani?re automatique lors de la premi?re entrØe-sortie. Il est cependant possible d’associer un bu er avec un chier ouvert par les fonctions dØcrites ci-apr?s. Cette association doit Œtre faite avant tout Øchange dans le chier ouvert. Le bu er se trouve dans l’espace adressable de l’utilisateur. Les appels de fonctions associØes la prØsence d’un bu er sont :

void setbuf(FILE*, char*); synopsis : associe un bu er un chier ouvert, dans le cas oø le pointeur est NULL, les E/S du chier sont non bu erisØes (chaque Øchange donne lieu un appel syst?me).

arguments :

1.    descripteur de        chier ouvert;

2.    adresse d’une zone mØmoire destinØe devenir le bu er d’E/S associØ au chier ouvert, cette zone doit avoir une taille prØ-dØ nie dont la valeur est BUFSIZE. Elle peut Œtre Øgale au pointeur NULL, ce qui rend les E/S du chier non bu erisØes.

int setvbuf(FILE*, char*, int, size_t); synopsis : contr le la gestion de la bu erisation d’un chier ouvert avant son utilisation.

arguments :

1.    descripteur de        chier ouvert;

2.    adresse d’une zone mØmoire destinØe devenir le bu er d’E/S associØ au chier ouvert, cette zone doit avoir la taille donnØe en quatri?me argument. Si l’adresse est Øgale NULL, la fonction alloue de mani?re automatique un bu er de la taille correspondante.

3.    le type de bu erisation, ce param?tre peut prendre les valeurs suivantes dØ nies dans <stdio.h> :

_IOFBF : signi e que les E/S de ce  chier seront totalement bu erisØes (par exemple les Øcritures n’auront lieu que lorsque le tampon sera plein).

_IOLBF : signi e que les E/S seront bu erisØes ligne par ligne (i.e. dans le cas de l’Øcriture, un retour chariot provoque l’appel syst?me). _IONBF : les E/S sur le chier sont non bu erisØes.

4.    la taille de la zone mØmoire (bu er).

int flush(FILE*); synopsis : vide le bu er associØ au chier;

argument : descripteur de chier ouvert en mode Øcriture ou en mode mise- -jour. Ce descripteur peut Œtre Øgal NULL auquel cas l’opØration porte sur l’ensemble des chiers ouverts en

Øcriture ou en mise- -jour; retour : 0 dans le cas normal et EOF en cas d’erreur. conditions d’erreur : la fonction retourne EOF si l’Øcriture physique s’est mal passØe.

Les E/S sur les terminaux sont bu erisØes ligne par ligne. La fonction fflush() permet de forcer l’Øcriture des derni?res informations.

11.8. GESTION DES ERREURS

Les erreurs des fonctions d’E/S peuvent Œtre rØcupØrØes par le programme. Des variables sont associØes aux erreurs sur chaque ux d’entrØe-sortie. Ces variables ne sont pas directement modi ables mais elles sont accessibles travers un ensemble de fonctions qui permettent de les tester ou de les remettre zØro.

De plus, pour donner plus d’informations sur les causes d’erreur, les fonctions d’E/S utilisent une variable globale de type entier appelØ errno. Cette variable est aussi utilisØe par les fonctions de biblioth?que servant rØaliser les appels syst?me. La valeur de errno n’est signi cative que lorsqu’une opØration a ØchouØ et que l’appel de fonction correspondant a retournØ une valeur spØci ant l’Øchec. Les fonctions associØes la gestion des erreurs sont :

int ferror(FILE*);

synopsis : Cette fonction retourne une valeur di Ørente de zØro si la variable qui sert mØmoriser les erreurs sur le chier ouvert correspondant a ØtØ a ectØe lors d’une opØration prØcØdente.

argument : le descripteur de chier ouvert pour lequel la recherche d’erreur est faite. retour : une valeur di Ørente de zØro si une erreur s’est produite. int feof(FILE*);

synopsis : Cette fonction teste si l’indicateur de n de chier a ØtØ a ectØ sur le chier ouvert correspondant au descripteur passØ en argument. argument : le descripteur de chier ouvert sur lequel le test de n de chier est dØsirØ. retour : retourne vrai si la n de chier est atteinte.

void clearerr(FILE*);

synopsis : Cette fonction e ace les indicateurs de n de chier et d’erreur du chier ouvert correspondant au descripteur passØ en argument.

argument : le descripteur de chier ouvert pour lequel on dØsire e acer les valeurs de la variable mØmorisant les erreurs et de la variable servant mØmoriser la rencontre de la n de chier.

retour : conditions d’erreur : void perror(const char*);

synopsis : Cette fonction fait la correspondance entre la valeur contenue dans la variable errno et une cha ne de caract?res qui explique de mani?re succincte l’erreur correspondante. argument : Cette fonction accepte un argument du type cha ne de caract?res qui permet de personnaliser le message.

Pour illustrer cette gestion d’erreur, nous allons ajouter des tests d’erreur dans l’exemple de parcours du chier FicParcAuto avec modi cation de l’ ge dans les di Ørents champs (Prog. 11.11).

Prog. 11.11 Gestion des cas d’erreurs pendant la manipulation d’un chier. int main() {

FILE? TheFic = fopen("FicParcAuto", "r+b"); if(TheFic == NULL) { perror("Ouverture impossible FicParcAuto");

return 1;

}

size_t fait;

while( 1 ){ fait=fread(&ParcAuto, sizeof(ParcAuto), 1, TheFic); if(fait != 1) {

if(feof(TheFic)) fprintf(stderr , "Fin de fichier FicParcAuto\n");

else fprintf(stderr , "Erreur lecture FicParcAuto\n");

break;

}

++; fait=fseek(TheFic, ?sizeof(ParcAuto), SEEK_CUR); if (fait != 0){

perror("Erreur deplacement FicParcAuto");

break;

} fait=fwrite(&ParcAuto, sizeof(ParcAuto), 1, TheFic); if(fait != 1){ fprintf(stderr , "Erreur ecriture ; fait=%d \n",fait);

break;

} fflush(TheFic);

} clearerr(TheFic); fclose(TheFic);

return 0;

}


Chapitre 12

Sommaire

12.1 Fonctions de manipulation de chanes de caract?res. . . . . . . . . . . . 101

12.2 Types de caract?res . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

12.3 Fonctions mathØmatiques. . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

12.4 Fonctions utilitaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103

12.5 Fonctions de dates et heures . . . . . . . . . . . . . . . . . . . . . . . . . . 105

12.6 Messages d’erreur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

La biblioth?que standard, par le biais de la librairie <string.h>, fournit des fonctions de manipulation de cha nes de caract?res. Rappelons qu’une cha ne de caract?res est un tableau de caract?res contenant des caract?res quelconques et terminØe par le caract?re ’\0’.

int strlen(char* s); renvoie la longueur de la cha ne s (le caract?re de terminaison de cha ne n’est pas comptØ).

strcpy(char* chdest, char* chsource); copie chsource dans chdest.

strcnpy(char* chdest, char* chsource, int len); copie les len premiers caract?res de chsource dans chdest.

char* strcat(char* ch1, char* ch2); concatØne ch2 apr?s ch1.

char* strncat(char* ch1, char* ch2, int len); concatØne ch2 apr?s len caract?res de ch1. char* strchr(char* ch, char c); dØlivre un pointeur sur la premi?re apparition de c dans la cha ne ch ou NULL.

char* strrchr(char* ch, char c); dØlivre un pointeur sur la derni?re occurence de c dans la cha ne ch ou NULL.

char* strstr(char* ch, char* ssch); dØlivre un pointeur sur la premi?re sous-cha ne ssch contenue dans la cha ne ch ou NULL.

int strcmp(char* ch1, char* ch2); renvoie zØro si ch1==ch2, un nombre strictement supØrieur zØro si ch1>ch2 et un nombre strictement infØrieur zØro sinon.

int strncmp(char* ch1, char* ch2, int len); mŒme chose que prØcØdemment mais la comparison se fait sur len premiers caract?res uniquement.

Ces deux derni?res fonctions font la di Ørence entre majuscules et minuscules, si l’on veut faire abstraction des maj/min utiliser int stricmp(char* ch1, char* ch2) et int strncmp(char* ch1, char* ch2, int len). Il existe Øgalement d’autres fonctions, mais elles sont en gØnØral tr?s peu utilisØes.

Il existe des macros expressions dØ nies dans <ctype.h> qui permettent de dØterminer ou de changer le type d’un caract?re. Ces macros expressions de test retournent un rØsultat non nul si le test est vrai.

ë isalpha(c) est vrai si c est une lettre (alphabØtique), ë isupper(c) est vrai si c est une majuscule (upper case), ë islower(c) est vrai si c est une minuscule (lower case), ë isdigit(c) est vrai si c est un chi re,

ë isspace(c) est vrai si c est un blanc, une interligne ou une tabulation, ë ispunct(c) est vrai si c est un caract?re de ponctuation, ë isalnum(c) est vrai si c est un caract?re alphabØtique ou numØrique, ë isprint(c) est vrai si c est un caract?re imprimable de 040           0176, ë isgraph(c) est vrai si c est un caract?re graphique de 041 0176,

ë iscntrl(c) est vrai si c est le caract?re del ou suppr(0177), ou un caract?re de contr le (<040), ë isascii(c) est vrai si c est un caract?re ASCII (<0200, cf. table en annexe A).

Les macros qui transforment un caract?re :

ë toupper(c) retourne le caract?re majuscule correspondant   c, ë tolower(c) retourne le caract?re minuscule correspondant             c, ë toascii(c) masque c avec 0x7f.

Le       chier d’en-tŒte <math.h> contient des dØclarations de fonctions et de macros mathØmatiques.

Les macros EDOM et ERANGE (qui se trouvent dans <errno.h>) sont des constantes enti?res non nulles utilisØes pour signaler des erreurs de domaine et d’intervalle pour les fonctions; HUGE_VAL est une valeur positive de type double :

ë Il se produit une erreur de domaine si un argument n’est pas dans le domaine sur lequel la fonction est dØ nie. En cas d’erreur de domaine, errno re oit EDOM, la valeur de retour dØpend de l’implØmentation.

ë Il se produit une erreur d’intervalle si le rØsultat de la fonction ne peut pas Œtre reprØsentØ par le type double. Si le rØsultat est trop grand, la fonction retourne HUGE_VAL avec le signe appropriØ, et errno re oit ERANGE. Si le rØsultat est trop petit, la fonction retourne 0, c’est l’implØmentation qui dØtermine si errno re oit ERANGE ou pas.

Dans le tableau 12.1, x et y sont de type double, n de type int, et toutes les fonctions retournent un double. Pour les fonction trigonomØtriques, les angles sont exprimØs en radians.

                                                              Tableau 12.1      Fonctions mathØmatiques.

Fonction

DØ nition

sin(x)

sinus de x

cos(x)

cosinus de x

tan(x)

tangente de x

asin(x)

arc sinus de x, dans l’intervalle [??/2,?/2],x ? [?1,1]

acos(x)

arc cosinus de x, dans l’intervalle [0,?],x ? [?1,1]

atan(x)

arc tangente de x, dans l’intervalle [??/2,?/2]

atan2(x,y)

arc tangente de y/x, dans l’intervalle [??,?]

sinh(x)

sinus hyperbolique de x

cosh(x)

cosinus hyperbolique de x

tanh(x)

tangente hyperbolique de x

exp(x)

fonction exponentielle ex

log(x)

logarithme nØpØrien : ln(x),x > 0

log10(x)

logarithme de base 10 : log10(x),x > 0

pow(x,y)

xy. Il se produit une erreur de domaine si x = 0 et y ? 0, n’est pas un entier

sqrt(x) ceil(x)

le plus petit entier supØrieur ou Øgal            x, exprimØ en double

floor(x)

le plus grand entier infØrieur ou Øgal            x, exprimØ en double

fabs(x) ldexp(x,n)

valeur absolue de x, |x| x 2n

frexp(x, int* exp)

sØpare x en une fraction normalisØe dans l’intervalle [1/2,1], qui est retournØe, et une puissance de 2, qui est placØe dans *exp. Si x est nul, les deux parties du rØsultat sont nulles.

modf(x, double* ip)

sØpare x en ses parties enti?re et fractionnaire, toutes deux du mŒme signe que x. Cette fonction place la partie enti?re dans *ip et retourne la partir fractionnaire.

fmod(x, y)

reste de x/y, exprimØe en virgule            ottante, de mŒme signe que x. Si y est nul, le rØsultat dØpend de l’implØmentation.

12.4           Fonctions utilitaires

Le chier d’en-tŒte <stdlib.h> contient des dØclarations de fonctions traitant la conversion de nombres, l’allocation de mØmoire, et des opØrations similaires.

double atof(const char* s); atof convertit s en un double; Øquivaut                                                 strod(s, (char**)

NULL).

double atoi(const char* s); atoi convertit s en un int; Øquivaut (int) strol(s, (char**) NULL), 10.

double atol(const char* s); atol convertit s en un long; Øquivaut                                          strol(s, (char**) NULL),

10.

double strtod(const char* s, char** endp); strtod convertit s en un double sans tenir compte des caract?res d’espacement de tŒte. Elle place dans *endp un pointeur sur la partir non convertie de s, si elle existe, sauf si endp vaut NULL. Si la rØponse est trop grande, la fonction retourne HUGE_VAL avec le signe appropriØ. Si al rØponse est trop petite, la fonction retourne 0. Dans les deux cas, errno re oit ERANGE.

double strtol(const char* s, char** endp, int base); strtol convertit s en un long sans tenir compte des caract?res d’espacement de tŒte. Elle place dans *endp un pointeur sur la partir non convertie de s, si elle existe, sauf si endp vaut NULL. Si la base est comprise entre 2 et 36, la conversion s’e ectue en considØrant que l’entrØe est Øcrite dans cette base. Si base vaut 0, la base est 8, 10 ou 16; un 0 en tŒte indique le format octal, et un 0x ou un 0X, le format hexadØcimal. Des lettres majuscules ou minuscules reprØsentent des chi res compris entre 10 et base-1. En base 16, on peut placer un 0x ou 0X en tŒte du nombre. Si la rØponse est trop grande, la fonction retourne LONG_MAX ou LONG_MIN, selon le signe du rØsultat, et errno re oit ERANGE.

unsigned long strtoul(const char* s, char** endp, int base); strtoul est la mŒme fonction que strtol, mis part que le rØsultat est de type unsigned long, et que la valeur de retour en cas d’erreur est ULONG_MAX.

int rand(void); rand retourne un entier pseudo-alØatoire compris entre 0 et RAND_MAX, RAND_MAX vaut au minimum 32767.

void srand(unsigned int seed); srand prend seed comme amorce de la nouvelle sØquence de nombres pseudo-alØatoires. L’amorce intiale vaut 1.

void* calloc(size_t nobj, size_t size); calloc retourne un espace mØmoire rØservØ un tableau de nobj objets, tous de taille size, ou bien NULL si cette demande ne peut pas Œtre satisfaite. La mØmoire allouØe est initialisØe par des 0.

void* malloc(size_t size); malloc retourne un espace mØmoire rØservØ un objet de taille size, ou bien NULL si cette demande ne peut pas Œtre satisfaite. La mØmoire allouØe est n’est pas initialisØe.

void* realloc(void* p, size_t size); realloc change en size la taille de l’objet pointØ par p. Si la nouvelle taille est plus petite que l’ancienne, seul le dØbut du contenu de l’objet est conservØ. Si la nouvelle taille est plus grande, le contenu de l’objet est conservØ, et l’espace mØmoire supplØmentaire n’est pas initialisØ. realloc retourne un pointeur sur le nouvel espace mØmoire, ou bien NULL si cette demande ne peut Œtre satisfaite, auquel cas *p n’est pas modi Ø.

void free(void* p);                               free lib?re l’espace mØmoire pointØ par p. Elle ne fait rien si p vaut NULL.

p doit Œtre un pointeur sur l’espace allouØ par calloc, malloc ou realloc.

void abort(void);                 abort provoque un arrŒt anormal du programme.

void exit(int status); exit provoque l’arrŒt normal du programme. Les fonctions atexit dans l’ordre inverse de leur enregistrement, l’Øcriture des tampons associØs aux chiers ouverts est forcØe, les ots ouverts sont fermØs et le contr le est rendu l’environnement. La fa on dont status est retournØ l’environnement dØpend de l’implØmentation, mais la valeur 0 indique que le programme qui s’arrŒte a rempli sa mission. On peut aussi utiliser les valeurs EXIT_SUCCESS et EXIT_FAILURE, pour indiquer respectivement la rØussite ou l’Øchec du programme.

int atexit(void (*fcn)(void)); atexit enregistre que la fonction fcn devra Œtre appelØe lors de l’arrŒt normal du programme. Elle retourne une valeur non nulle si cet enregistrement n’est pas rØalisable.

int system(const char* s); system passe la cha ne s l’environnement pour que celui-ci l’exØcute. Si s vaut NULL, system retourne une valeur non nulle si un interprØteur de commandes est prØsent. Si s ne vaut pas NULL, la valeur de retour dØpend de l’implØmentation. Exemple : system("dir");.

char* getenv(const char* name); getenv retourne la cha ne d’environnement associØe) name ou NULL si cette cha ne n’existe pas. Les dØtails dØpendent de l’implØmentation. int abs(int i); abs retourne la valeur absolue de son argument de type int. long labs(long n); labs retourne la valeur absolue de son argument de type long.

div_t div(int num, int denom); div calcul le quotient et le reste de la division de num par denom. Le quotient et le reste sont placØs respectivement dans les champs quot et rem, de type int, d’une structure de type div_t.

ldiv_t ldiv(long num, long denom); ldiv calcul le quotient et le reste de la division de num par denom. Le quotient et le reste sont placØs respectivement dans les champs quot et rem, de type long, d’une structure de type ldiv_t.

Bien que n’Øtant pas une instruction du langage mais un appel une fonction du syst?me, il est intØressant de considØrer le exit() comme une rupture de sØquence. L’instruction return provoque la n d’une fonction; de mŒme l’appel la fonction exit() provoque la n du programme. Cette fonction exit() peut Œtre aussi associØe une expression. Cette expression est ØvaluØe et la valeur obtenue est retournØe au processus p?re. La valeur 0 signi e que le programme s’est bien passØ.

Prenons l’exemple du programme 12.1 qui est sensØ ouvrir un chier. Si ce chier est absent, la fonction impose l’arrŒt brutal du programme.

                                                    Prog. 12.1          Utilisation de l’appel syst?me exit().

FILE ouvre(const char? nom_fichier) {

FILE fid = fopen(nom_fichier, ’r’); if( fid == NULL ) {

printf("Ouverture impossible %s\n", nom_fichier); exit(1);

}

return fid; }

12.5           Fonctions de dates et heures

Le chier d’en-tŒte <time.h> contient des dØclarations de types et de fonctions servant manipuler la date et l’heure. Certaines fonctions traitent l’heure locale, qui peut Œtre di Ørente de l’heure calendaire (fuseaux horaires). clock_t et time_t sont des types arithmØtiques qui reprØsentent des instants, et struct tm contient les composantes d’une heure calendaire :

int tm_sec;

// seconde (0?59)

int tm_min;

// minute (0?59)

int tm_hour;

// heure (0?23)

int tm_mday;

// jour (1?31)

int tm_mon;

// mois (0?11)

int tm_year;

// annØe

int tm_wday;

// jour depuis dimanche (0?6)

int tm_yday;

// jour depuis le 1er janvier (0?365)

int tm_isdst;

// drapeau d’heure d’ØtØ (0 ou 1)

tm_isdst est positif si l’heure d’ØtØ est en vigueur, nul sinon, et nØgatif si cette information n’est pas disponible.

clock_t clock(void); clock retourne le temps d’utilisation du processeur par le programme depuis le dØbut de son exØcution, ou bien -1 si cette information n’est pas disponible. clock() / CLK_TK est une durØe en secondes.

time_t time(time_t* tp); time retourne l’heure calendaire actuelle, ou bien -1 si l’heure n’est pas disponible. Si tp est di Ørent de NULL, *tp re oit aussi cette valeur de retour.

double difftime(time_t time2, time_t time1); difftime retourne la durØe time2-time1, exprimØe en secondes.

time_t mktime(struct tm* tp); mktime convertit l’heure locale contenue dans la structure *tp en heure calendaire, exprimØe suivant la mŒme reprØsentation que celle employØe par time. Les valeurs des composantes de l’heure seront comprises dans les intervalles donnØs ci-dessus. La fonction mktime retourne l’heure calendaire, ou bien -1 si celle-ci ne peut pas Œtre reprØsentØe.

Les quatre fonctions suivantes retournent des pointeurs sur des objets statiques qui peuvent Œtre ØcrasØs par d’autres appels.

char* asctime(const struct tm* tp); asctime convertit l’heure reprØsentØe dans le structure *tp en une cha ne de la forme : Sun Jan 3 15 :14 :13 1998\n\0.

char* ctime(const time_t* tp); ctime convertit l’heure calendaire *tp en heure locale. Elle Øquivaut : asctime(localtime(tp)).

struct tm* gmtime(const time_t* tp); gmtime convertit l’heure calendaire *tp en temps universel (TU). Elle retourne NULL si le TU n’est pas disponible.

struct tm* localtime(const time_t* tp); localtime convertit l’heure calendaire *tp en heure locale.

size_t strftime(char* s, size_t max, const char* fmt, const struct tm* tp); strftime transforme les informations de date et d’heure contenue dans *tp suivant le format fmt, qui est analogue un format de printf, et place le rØsultat dans s (cf. [KR94] pour le dØtail des formats).

On utilise la macro assert (dans le chier <assert.h>) pour insØrer des messages d’erreur dans les programmes : void assert(int exp). Si exp vaut 0 au moment oø assert(exp) est exØcutØe, la macro insert imprime sur la console un message de la forme :

Assertion failed : exp, file nom_de_fichier, line nn

Puis, elle appelle abort pour arrŒter l’exØcution du programme. Le nom du chier source et le numØro de ligne sont donnØs par les macros __FILE__ et __LINE__ du prØprocesseur.

Si NDEBUG est dØ ni au moment oø <assert.h> est inclus, la macro assert n’est pas prise en compte.


Table des caract?res ASCII

b7 b6

b5 Bits

b4 b3 b2 b1

0

0

0

0

0

1

0

1

0

0

1

1

1

0

0

1

0

1

1

1

0

1

1

1

Controle

Symbole Nombres

s

Majuscu

le

Minuscule

0 0 0 0

0 NUL

0                        0

16 DLE

10                     20

32 SP

20

40

48

30

0

60

64

40

@

100

80

50

P

120

96

60

140

112

p

70

160

0 0 0 1

1 SOH

1                        1

17 DC1

11                     21

33 !

21

41

49

31

1

61

65

41

A

101

81

51

Q

121

97

61

a

141

113

q

71

161

0 0 1 0

2 STX

2                        2

18 DC2

12                     22

34 ”

22

42

50

32

2

62

66

42

B

102

82

52

R

122

98

62

b

142

114 r

72

162

0 0 1 1

3 ETX

3                        3

19 DC3

13                     23

35 #

23

43

51

33

3

63

67

43

C

103

83

53

S

123

99

63

c

143

115

s

73

163

0 1 0 0

4 EOT

4                        4

20 DC4

14                     24

36 $

24

44

52

34

4

64

68

44

D

104

84

54

T

124

100

d

64

144

116

t

74

164

0 1 0 1

5 ENQ

5                        5

21 NAK

15                     25

37 %

25

45

53

35

5

65

69

45

E

105

85

55

U

125

101

e

65

145

117

u

75

165

0 1 1 0

6 ACK

6                        6

22 SYN

16                     26

38 &

26

46

54

36

6

66

70

46

F

106

86

56

V

126

102 f

66

146

118

v

76

166

0 1 1 1

7 BEL

7                        7

23 ETB

17                     27

39 ’

27

47

55

37

7

67

71

47

G

107

87 W

57                    127

103

g

67

147

119

w

77

167

1 0 0 0

8 BS

8                      10

24 CAN

18                     30

40 (

28

50

56

38

8

70

72

48

H

110

88 X

58                    130

104

h

68

150

120

x

78

170

1 0 0 1

9 HT

9                      11

25 EM

19                     31

41 )

29

51

57

39

9

71

73

49

I

111

89 Y

59                    131

105 i

69

151

121

y

79

171

1 0 1 0

10 LF

A                      12

26 SUB

1A                    32

42 *

2A

52

58 3A

:

72

74 4A

J

112

90 Z

5A                   132

106 j

6A

152

122

z

7A

172

1 0 1 1

11 VT

B                      13

27 ESC

1B                    33

43 +

2B

53

59 3B

;

73

75 K

4B                   113

91

[

5B                   133

107

k

6B

153

123 {

7B

173

1 1 0 0

12 FF

C                      14

28 FS

1C                     34

44 ,

2C

54

60 3C

<

74

76 L

4C                    114

92 \

5C                    134

108 l

6C

154

124 |

7C

174

1 1 0 1

13 CR

D                      15

29 GS

1D                    35

45 ?

2D

55

61 =

3D

75

77 M

4D                   115

93

]

5D                   135

109

m

6D

155

125 }

7D

175

1 1 1 0

14 SO

E                      16

30 RS

1E                    36

46 .

2E

56

62 >

3E

76

78 N

4E                   116

94 ˆ

5E                   136

110

n

6E

156

126 ˜

7E

176

1 1 1 1

15 SI

F                      17

31 US

1F                     37

47 /

2F

57

63 ?

3F

77

79 O

4F                    117

95 ??

5F                    137

111

o

6F

157

127 DEL

7F                   177

                                     Legende :        decCHAR

                                                                     hex              oct

Mots rØservØs du C

                                                                  Tableau B.1      Liste des mots rØservØs.

Type

Classe

Instruction

OpØrateur

tiquette

int

auto

if

sizeof

case

char

extern

else

default

short

static

while

long

register

do

unsigned

typedef

for

float

switch

double

break

struct

continue

union

goto

enum void

return

Quelques pointeurs sur Internet

Sommaire

C.1 Quelques cours de programmation . . . . . . . . . . . . . . . . . . . . . . 109

C.2 Librairies scienti ques et graphiques . . . . . . . . . . . . . . . . . . . . . 110

C.3 Sources et sites de programmeurs . . . . . . . . . . . . . . . . . . . . . . 110

Voici quelques pointeurs sur le langage C qui peuvent Œtre bien utiles. consulter Je ne garantit ni la pØrennitØ de ces sites, ni l’exhaustivitØ des adresses proposØes! Je maintiens Øgalement une page Web contenant une collection d’adresses sur des cours et des librairies en langage C et C++ : .

C.1             Quelques cours de programmation

Les quelques liens proposØs ici permettent d’accØder un grand nombre de support de cours tØlØcharger (formats postscript ou pdf), ou a consulter on line (html).

ë , Guides de programmation tous langages : C, C++, Java, Javascript, VRML,

ë , Guides de programmation, langages C, C++, Java, Javascript, VRML, , M. Toura vane, Esil (UniversitØ de Marseille).

ë , Cours de langage C, G.H. Lamot.

ë , Cours d’informatique, UniversitØ de Marne-La-VallØe.

ë , Cours de programmation (en anglais).

ë Liste de pointeurs sur des cours, notamment en C (en anglais).

ë , Yahoo : Langages de programmation.

ë C++ Programming tutorial (en anglais).

ë The GNU C Library, compatible C ANSI.


110                                                                                                           ANNEXE C. QUELQUES POINTEURS SUR INTERNET

C.2               Librairies scienti ques et graphiques

Les librairies proposØes ci-dessous ne sont pas forcØment toutes compatibles avec la programmation sous VC++, vØri er. L’installation de ces librairies n’est pas forcØment aisØe et nØcessite sans doute un peu d’expØrience

ë , The Fast Light Tool Kit, pour dØvelopper une interface graphique, indØpendante de la plateforme de dØveloppement (Unix, Linux, Windows, ). ë , VTK, The Visualization toolkit (graphisme - 3D).

ë , Numerical recipes, algorithmes scienti ques (optimization, tri, transformØe de Fourier rapide, ).

ë , The GNU Scienti c Library, algorithmes scienti que : transformØe de Fourier, pseudo-gØnØrateurs de nombres alØatoires, fonctions spØciales,

C.3             Sources et sites de programmeurs

Quelques sites oø vous pourrez trouver du code source et des astuces intØressantes.

ë , Programmation scienti que en Matlab, C, C++, Java, ë , SourceBank, banque de codes tous langages ë , Code Guru (visual C++, Microsoft Foundation classes)

Liste des       gures

       1.1                       Les Øtapes de compilation d’un programme. . . . . . . . . . . . . . . . . . . . . . . .                             11

       5.1       Organigramme du while.                             . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                 37

       5.2       Organigramme du for.                               . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                 38

       5.3        Organigramme du do while.                            . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                               39

       5.4               Break et continue dans (a) un for, (b) un while et (c) un do while.                   . . . . . . . .               42

       6.1      Structure d’une fonction.                             . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                 46

       6.2       Illustration d’un passage d’arguments         la fonction principale.                . . . . . . . . . . . .                  49

       8.1     Du source      l’exØcutable.                            . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                 56

       8.2      Survol d’un                             chier source. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                  56

       8.3      Exemple de visibilitØ.                               . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                  57

       8.4     Masquage de nom.                              . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                   58

       8.5      VisibilitØ des variables entre modules.                        . . . . . . . . . . . . . . . . . . . . . . . . . .                            59

       8.6      VisibilitØ des fonctions entre modules.                        . . . . . . . . . . . . . . . . . . . . . . . . . .                            60

       8.7       VisibilitØ des fonctions dans un module.                       . . . . . . . . . . . . . . . . . . . . . . . . .                           60

       8.8      Utilisation d’un      chier d’inclusion.                        . . . . . . . . . . . . . . . . . . . . . . . . . . . .                             62

       8.9         RØduction de visibilitØ (static).                           . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                              63

         8.10 Variables locales statiques.                              . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                64

       9.1      Tableau de dix entiers.                               . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                 66

       9.2                         Adresses dans un tableau de dix entiers. . . . . . . . . . . . . . . . . . . . . . . . . .                              66

       9.3     Tableau                              deux dimensions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                 67

       9.4      Pointeur et tableau.                               . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                   69

       9.5        Tableau de pointeurs sur des variables dans un tableau.                   . . . . . . . . . . . . . . . .                     70

Liste des tableaux

       2.1                   Longueur des types de base sur un processeur Intel i686. . . . . . . . . . . . . . . . .                         15

       2.2      Cha ne de caract?res constante                          . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                              19

       2.3       Exemples d’initialisation de cha nes.                         . . . . . . . . . . . . . . . . . . . . . . . . . . .                            19

       2.4                           Exemples de conversion implicite. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                20

       3.1                               Exemples de printf() et scanf(). . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                    23

       4.1                            Liste des opØrateurs unaires. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                 26

       4.2                            Liste des opØrateurs binaire. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                  27

       4.3                        Liste des opØrateurs binaires d’a ectation. . . . . . . . . . . . . . . . . . . . . . . . .                              29

       4.4                        Exemple d’opØrateurs binaires d’a ectation. . . . . . . . . . . . . . . . . . . . . . . .                             29

       4.5              PrØcØdence des opØrateurs (prioritØ dØcroissante de haut en bas). . . . . . . . . . . .                   30

       5.1                                  Syntaxes du if. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                        33

       5.2        Syntaxe classique du switch.                            . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                               35

       6.1       Exemples de dØ nition de fonctions.                         . . . . . . . . . . . . . . . . . . . . . . . . . . .                            46

       6.2                             Conversions de type unaire. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                  47

       7.1       Utilisation d’une constante de compilation.                       . . . . . . . . . . . . . . . . . . . . . . .                         52

       7.2      InterprØtation des variables par le prØprocesseur.                   . . . . . . . . . . . . . . . . . . . .                       53

       7.3                          valuation de macros par le prØprocesseur. . . . . . . . . . . . . . . . . . . . . . . . .                            54

       9.1      Addition d’un entier                           un pointeur. . . . . . . . . . . . . . . . . . . . . . . . . . . . .                              68

       9.2                            Soustraction de deux pointeurs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                 69

                               10.1 Adressage dans une structure. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                  72

                               11.1 Code de conversion pour scanf. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                  90

                                11.2 Code de conversion pour printf. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                   91

             11.3 Modi cateurs de format pour printf.                           . . . . . . . . . . . . . . . . . . . . . . . . . .                            92

                                12.1 Fonctions mathØmatiques. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                 103

LISTE DES TABLEAUX                                                                                                                                                                     113

        B.1     Liste des mots rØservØs.                            . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                               108

Liste des programmes

       1.1      Hello World!                                 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                        9

       3.1                        Lecture et Øcriture de cha ne par scanf() et printf() . . . . . . . . . . . . . . . . .                              21

       3.2        Lectures multiples avec scanf()                            . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                              22

       5.1                              Premier exemple de test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                   34

       5.2                              Second exemple de test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                   34

       5.3                             Premier exemple de switch. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                   36

       5.4       Second exemple de switch.                            . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                36

       5.5                        Recopie d’une cha ne avec une boucle while(). . . . . . . . . . . . . . . . . . . . . .                             37

       5.6       Lecture d’une ligne avec for.                            . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                               39

       5.7                                DivisibilitØ par trois. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                     39

       5.8                          Utilisation du continue dans une boucle for() . . . . . . . . . . . . . . . . . . . . .                               40

       5.9                   Utilisation des ruptures de sØquence dans une boucle for(). . . . . . . . . . . . . . .                        41

           5.10 Lecture d’une ligne avec for et break                           . . . . . . . . . . . . . . . . . . . . . . . . . .                            41

                                 5.11 Utilisation de l’inf me goto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                    41

                                5.12 Utilisation de plusieurs return. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                   43

       6.1                               Fonction factorielle. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                    48

       8.1                         Exemples de prototypes de fonctions. . . . . . . . . . . . . . . . . . . . . . . . . . . .                               59

       9.1        DØ nition de tableaux et initialisations.                         . . . . . . . . . . . . . . . . . . . . . . . . .                           66

                             10.1 Syntaxe de la dØ nition d’une structure. . . . . . . . . . . . . . . . . . . . . . . . . .                                72

           10.2 DØ nition d’un mod?le de structure champs de bits.                     . . . . . . . . . . . . . . . . . .                      74

                              10.3 Syntaxe de la dØ nition d’une union. . . . . . . . . . . . . . . . . . . . . . . . . . . .                                 74

          10.4 Utilisation d’une union.                              . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                 75

                           10.5 Syntaxe de la dØ nition d’une ØnumØration. . . . . . . . . . . . . . . . . . . . . . . .                             76

                     11.1 Lecture et Øcriture caract?re par caract?re sur les E/S standard. . . . . . . . . . . . .                       81

          11.2 Lecture ligne par ligne sur les                       chiers standard. . . . . . . . . . . . . . . . . . . . . .                          82

         11.3 Ouverture d’un                               chier. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                   84

         11.4 Ouverture et fermeture d’un                          chier. . . . . . . . . . . . . . . . . . . . . . . . . . . . .                             84

          11.5 Lecture caract?re par caract?re d’un                  chier apr?s ouverture. . . . . . . . . . . . . . .                     86

         11.6 Lecture ligne        ligne d’un                    chier apr?s ouverture. . . . . . . . . . . . . . . . . . . . .                         87

         11.7 Lecture d’enregistrements dans un       chier.                    . . . . . . . . . . . . . . . . . . . . . . . .                          88

LISTE DES PROGRAMMES                                                                                                                                                            115

11.8 Lecture avec format dans un chier texte. . . . . . . . . . . . . . . . . . . . . . . . . 94 11.9 Modi cations par dØplacement dans un chier. . . . . . . . . . . . . . . . . . . . . . 96

        11.10DØplacements dans un                       chier avec fgetpos(). . . . . . . . . . . . . . . . . . . . . . .                            97

           11.11Gestion des cas d’erreurs pendant la manipulation d’un        chier.            . . . . . . . . . . . .               100

             12.1 Utilisation de l’appel syst?me exit().                           . . . . . . . . . . . . . . . . . . . . . . . . . .                         105

Bibliographie

[98990] ISO/IEC 9899. Programming language C. ISO/IEC, 1990.

[Dij68] E.W. Dijkstra. GO TO statements considered harmful. Communications of the ACM, 11(3) :147 148, Mars 1968.

[JR78] S.C. Johnson and D.M. Ritchie. Portability of C programs and the UNIX operating system. Bell System Technical Journal, 57(6) :2021 2048, July/August 1978.

[KR94] B.W. Kernighan and D.M. Ritchie. Le langage C ANSI, version fran aise. Manuels informatiques. Masson, Paris, deuxi?me edition, 1994. Les solutions aux exercices sont proposØes chez le mŒme Øditeur par C.L. Tondo et S.E. Gimpel [TG95].

[RK78] D.M. Ritchie and B.W. Kernighan. The C programming language. Prentice Hall Inc., Englewood cli s, New Jersey, Mars 1978.

[Str86] B. Stroustrup. The C++ programming language. Prentice Hall Inc., Englewood cli s, New Jersey, 1986.

[TG95] C.L. Tondo and S.E. Gimpel. Le langage C ANSI - Solutions. Manuels informatiques. Masson, Paris, deuxi?me edition, 1995. Solutions aux exercices proposØs dans le livre de B.W. Kernighan et D.M. Ritchie [KR94].

[Wir74] N. Wirth. On the composition of well structured programs. ACM computing survey, 6(4) :247 259, DØcembre 1974.



1. American National Standard Institute .

2. International Standard Organization .

. Il faut noter que les langages Fortran et Cobol gardent une place prØpondØrante, respectivement, dans les domaines du calcul scienti que et de la gestion.

. ASCII : American Standard Code for Information Interchange , cf. annexe A. Ensemble de caract?res codØs sur 7 bits. En consØquence, les accents et les caract?res spØciaux comme le ne sont pas permis!

[5] . Ce type de commentaire a ØtØ introduit avec le langage C++ et n’est pas purement C-ANSI. Il est cependant supportØ par tous les compilateurs actuels. PrØcisons que dans ce cas, le commentaire doit se limiter une unique ligne.

. La commande printf ne se limite pas l’a chage d’une cha ne de caract?res, mais est beaucoup plus puissante comme nous le verrons dans le chapitre 3.

[7] . Les di Ørentes Øtapes de la compilation sont plus complexes que cela. Toutes ces opØrations sont relativement transparentes avec le logiciel Visual C++ de Microsoft, puisqu’il su t de cliquer sur un bouton pour rØaliser l’ensemble des Øtapes aboutissant l’exØcutable!

[8] . Il n’y a pas de type boolØen en C. Celui-ci a ØtØ introduit plus tard avec le langage C++, l’extension orientØe objet du langage C.

. Le quali cateur signed est appliquØ par dØfaut. Ainsi, il n’y a pas de di Ørence entre une variable de type int et une variable de type signed int.

. Ces dØ nitions peuvent aussi s’Øcrire de mani?re abrØgØe : short ou long, le type de base int Øtant inclus implicitement.

[11] . Attention, mŒme si la notation de ces caract?res particuliers semble contenir deux caract?res (une barre inversØe et un caract?re), il faut bien considØrer qu’il n’y en a qu’un seul!

5. Le nombre 100 correspond au code ASCII dØcimale du caract?re ’d’.

. Une valeur la fois mais comme le pointeur est une variable cette valeur peut changer au cours de l’exØcution du programme.

. dans le sens oø la variable tt est dØ nie en dehors de toute fonction. La plupart du temps, nous utiliserons des variables locales, c’est- -dire des variables dØ nies l’intØrieur d’une fonction.

1. Un entier long sur les machines les plus courantes est reprØsentØ sur 4 octets, soit 32 bits.

. EOF est le caract?re qui marque la n d’un chier : End Of File . Il est obtenu par la frappe du caract?re Contr le D sur un syst?me de type UNIX, et par la frappe du caract?re Contr le Z sur un syst?me Windows.

. Vous trouvez ici l’explication rationnelle de la formule magique qui consiste mettre un et commercial devant les noms de variables lors de l’appel de la fonction scanf(). Il faut en e et passer l’adresse de la variable modi er cette fonction.

. Ces dØ nitions de variables globales sont en fait des demandes de rØservation mØmoire destinØes l’Øditeur de liens.

. Lorsque le compilateur rencontre un appel une fonction qui n’est pas dØclarØe ou dØ nie, il consid?re que la fonction retourne une valeur enti?re.

. Ce sont des dØclarations et non des dØ nitions. En e et, les chiers d’inclusion sont associØs plusieurs chiers source et si ces chiers contiennent des dØ nitions, les variables risquent d’Œtre dØ nies plusieurs fois.

1. L’instruction tb1= est une hØrØsie qui mØrite l’exclusion de la communautØ des utilisateurs du langage C.

2. Ce qui, en gØnØral, est Øgal                  n’importe quoi.

.      La promotion unaire fait que de toute fa on la valeur d’un caract?re est convertie en une valeur d’entier lors du passage d’argument                une fonction.

. Ce qui explique pourquoi il faut un & devant les noms de donnØes lire quand ces noms ne sont pas des tableaux.

3. Des formats particuliers permettent la lecture de plusieurs caract?res                             l’intØrieur d’un tableau de caract?res.

. Cet alphabet est dØ ni soit par la liste des caract?res signi catifs, soit par le premier caract?re suivi d’un tiret et le dernier caract?re dans l’ordre croissant de la table ASCII (cf. annexe A). La nØgation de l’alphabet peut Œtre obtenue en mettant un ? apr?s le crochet ouvrant. Pour que le crochet fermant soit dans l’alphabet, il faut qu’il suive immØdiatement le crochet ouvrant.

[27] . Anglicisme que l’on peut traduire par tamponnØes. Prenons le cas des Øcritures. Elles sont d’abord rØalisØes dans un espace mØmoire que l’on appelle tampon (bu er en anglais). Les caract?res sont ensuite transfØrØs au syst?me en bloc. Ceci permet de minimiser les appels au syst?me (car le tampon de caract?res est dans l’espace mØmoire du programme) et a priori d’amØliorer les performances. Cette gestion de tampon intermØdiaire peut se traduire par une reprØsentation non exacte de l’Øtat du programme par les Øcritures. En e et, les Øcritures sont di ØrØes et le tampon d’Øcriture n’est vidØ que lorsqu’une n de ligne est transmise.



247