Universit´ de la M´diterran´e e e e Facult´ des Sciences de Luminy e

Le langage C
Licences Maths & Informatique – Master Math´matiques – Master C.C.I. e

Henri Garreta D´partement d’Informatique - LIF e

` TABLE DES MATIERES

` TABLE DES MATIERES

Table des mati`res e
´ e 1 El´ments de base 1.1 Structure g´n´rale d’un programme . . . . . . . e e 1.2 Consid´rations lexicales . . . . . . . . . . . . . e 1.2.1 Pr´sentation du texte du programme . . e 1.2.2 Mots-cl´s . . . . . . . . . . . . . . . . . e 1.2.3 Identificateurs . . . . . . . . . . . . . . 1.2.4 Op´rateurs . . . . . . . . . . . . . . . . e 1.3 Constantes litt´rales . . . . . . . . . . . . . . . e 1.3.1 Nombres entiers . . . . . . . . . . . . . 1.3.2 Nombres flottants . . . . . . . . . . . . 1.3.3 Caract`res et chaˆ e ınes de caract`res . . . e 1.3.4 Expressions constantes . . . . . . . . . . 1.4 Types fondamentaux . . . . . . . . . . . . . . . 1.4.1 Nombres entiers et caract`res . . . . . . e 1.4.2 Types ´num´r´s . . . . . . . . . . . . . e ee 1.4.3 Nombres flottants . . . . . . . . . . . . 1.5 Variables . . . . . . . . . . . . . . . . . . . . . 1.5.1 Syntaxe des d´clarations . . . . . . . . . e 1.5.2 Visibilit´ des variables . . . . . . . . . . e 1.5.3 Allocation et dur´e de vie des variables e 1.5.4 Initialisation des variables . . . . . . . . 1.5.5 Variables locales statiques . . . . . . . . 1.5.6 Variables critiques . . . . . . . . . . . . 1.5.7 Variables constantes et volatiles . . . . . 1.6 Variables, fonctions et compilation s´par´e . . . e e 1.6.1 Identificateurs publics et priv´s . . . . . e 1.6.2 D´claration d’objets externes . . . . . . e . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5 5 5 6 6 6 7 7 7 7 8 9 9 11 11 11 11 12 13 13 14 14 15 15 15 16 18 18 18 19 19 19 20 21 21 22 22 22 23 23 24 24 25 27 28 28 29 30 31 31 32 33 34 34 34 34

2 Op´rateurs et expressions e 2.1 G´n´ralit´s . . . . . . . . . . . . . . . . . . . . . . e e e 2.1.1 Lvalue et rvalue . . . . . . . . . . . . . . . 2.1.2 Priorit´ des op´rateurs . . . . . . . . . . . . e e 2.2 Pr´sentation d´taill´e des op´rateurs . . . . . . . . e e e e 2.2.1 Appel de fonction () . . . . . . . . . . . . . 2.2.2 Indexation [] . . . . . . . . . . . . . . . . . 2.2.3 S´lection . . . . . . . . . . . . . . . . . . . e 2.2.4 S´lection dans un objet point´ -> . . . . . . e e 2.2.5 N´gation ! . . . . . . . . . . . . . . . . . . . e 2.2.6 Compl´ment ` 1 ~ . . . . . . . . . . . . . . e a 2.2.7 Les c´l`bres ++ et -- . . . . . . . . . . . . . ee 2.2.8 Moins unaire - . . . . . . . . . . . . . . . . 2.2.9 Indirection * . . . . . . . . . . . . . . . . . 2.2.10 Obtention de l’adresse & . . . . . . . . . . . 2.2.11 Op´rateur sizeof . . . . . . . . . . . . . . e 2.2.12 Conversion de type (“cast” operator) . . . . 2.2.13 Op´rateurs arithm´tiques . . . . . . . . . . e e 2.2.14 D´calages << >> . . . . . . . . . . . . . . . e 2.2.15 Comparaisons == != < <= > >= . . 2.2.16 Op´rateurs de bits & | ^ . . . . . . . . . . . e 2.2.17 Connecteurs logiques && et || . . . . . . . 2.2.18 Expression conditionnelle ? : . . . . . . . . 2.2.19 Affectation = . . . . . . . . . . . . . . . . . 2.2.20 Autres op´rateurs d’affectation += *= etc. e 2.2.21 L’op´rateur virgule , . . . . . . . . . . . . e 2.3 Autres remarques . . . . . . . . . . . . . . . . . . . 2.3.1 Les conversions usuelles . . . . . . . . . . . 2.3.2 L’ordre d’´valuation des expressions . . . . e 2.3.3 Les op´rations non abstraites . . . . . . . . e 2

c H. Garreta, 2004

` TABLE DES MATIERES

` TABLE DES MATIERES

3 Instructions 3.1 Syntaxe . . . . . . . . . . . . . . . . . . . 3.2 Pr´sentation d´taill´e des instructions . . e e e 3.2.1 Blocs . . . . . . . . . . . . . . . . 3.2.2 Instruction-expression . . . . . . . 3.2.3 Etiquettes et instruction goto . . . 3.2.4 Instruction if...else... . . . . 3.2.5 Instructions while et do...while 3.2.6 Instruction for . . . . . . . . . . . 3.2.7 Instruction switch . . . . . . . . . 3.2.8 Instructions break et continue . 3.2.9 Instruction return . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

36 36 37 37 37 38 38 39 40 41 42 43 44 44 44 44 45 46 46 46 47 48 48 48 48 49 50 52 52 52 52 53 54 54 56 56 57 58 58 60 61 62 64 64 64 65 66 66 68 69 70 72 73 74 76 77 77 78 3

4 Fonctions 4.1 Syntaxe ANSI ou “avec prototype” . . . . . . 4.1.1 D´finition . . . . . . . . . . . . . . . . e 4.1.2 Type de la fonction et des arguments . 4.1.3 Appel des fonctions . . . . . . . . . . 4.1.4 D´claration “externe” d’une fonction . e 4.2 Syntaxe originale ou “sans prototype” . . . . 4.2.1 D´claration et d´finition . . . . . . . . e e 4.2.2 Appel . . . . . . . . . . . . . . . . . . 4.2.3 Coexistence des deux syntaxes . . . . 4.3 Arguments des fonctions . . . . . . . . . . . . 4.3.1 Passage des arguments . . . . . . . . . 4.3.2 Arguments de type tableau . . . . . . 4.3.3 Arguments par adresse . . . . . . . . . 4.3.4 Arguments en nombre variable . . . . 5 Objets structur´s e 5.1 Tableaux . . . . . . . . . . . . . . . . 5.1.1 Cas g´n´ral . . . . . . . . . . . e e 5.1.2 Initialisation des tableaux . . . 5.1.3 Chaˆ ınes de caract`res . . . . . e 5.2 Structures et unions . . . . . . . . . . 5.2.1 Structures . . . . . . . . . . . . 5.2.2 Unions . . . . . . . . . . . . . . 5.2.3 Champs de bits . . . . . . . . . 5.3 Enum´rations . . . . . . . . . . . . . . e 5.4 D´clarateurs complexes . . . . . . . . e 5.4.1 Cas des d´clarations . . . . . . e 5.4.2 Pointeurs et tableaux constants 5.4.3 La d´claration typedef . . . . e 5.4.4 Cas des types d´sincarn´s . . . e e

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . et volatils . . . . . . . . . . . .

6 Pointeurs 6.1 G´n´ralit´s . . . . . . . . . . . . . . . . . . . . . . . . . . . . e e e 6.1.1 D´claration et initialisation des pointeurs . . . . . . . e 6.1.2 Les pointeurs g´n´riques et le pointeur NULL . . . . . . e e 6.2 Les pointeurs et les tableaux . . . . . . . . . . . . . . . . . . 6.2.1 Arithm´tique des adresses, indirection et indexation . e 6.2.2 Tableaux dynamiques . . . . . . . . . . . . . . . . . . 6.2.3 Tableaux multidimensionnels . . . . . . . . . . . . . . 6.2.4 Tableaux multidimensionnels dynamiques . . . . . . . 6.2.5 Tableaux de chaˆ ınes de caract`res . . . . . . . . . . . e 6.2.6 Tableaux multidimensionnels formels . . . . . . . . . . 6.2.7 Tableaux non n´cessairement index´s ` partir de z´ro e e a e 6.2.8 Matrices non dynamiques de taille inconnue . . . . . . 6.3 Les adresses des fonctions . . . . . . . . . . . . . . . . . . . . 6.3.1 Les fonctions et leurs adresses . . . . . . . . . . . . . . 6.3.2 Fonctions formelles . . . . . . . . . . . . . . . . . . . .
c H. Garreta, 2004

. . . . . . . . .2. . . . . .3 Deux ou trois choses bien pratiques. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . Structures r´cursives . . . . . . . . .h . . . . . . . . . . . . . . . . . . 7. . . . . . . . . . . . . . . . . . . . . . . . .2 Exemple : stdio. . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . .1 Flots .4. . . . . . . . . . . . . . 7. . . . . . . . . e e 7. . . . . . . . e 8. . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .h . . .` TABLE DES MATIERES ` TABLE DES MATIERES 6. . . . . . . . . . . . . . . . e 8. . . . . . . . . . . . . . . . . . . . . . . . . . . . e e 7. . . . . . . . . Imprim´ le 27 f´vrier 2005 e e Henri.1 Fonctions g´n´rales sur les flots . . . . . . . . . . . . . . . . . . . . .3. . .h . . . . .4. . . .fr 4 c H. . . . . . . . . . . . . . . . . . . . .h . . . . . .2. . . . . . e 7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . 8. . . . . . . . . . .1 Les arguments du programme principal . . .1. . . . . . . 7. . . . .2. . . . . . . . . . . . . . . . . . . . . . . 8. . . . . . . .h . . . . . . . . . . .3 Tableaux de fonctions . . . . .h. . . . . . . . . . . . . . . .5 Fonctions math´matiques : math. . . . .1. . . . . . . . .4 A propos de la fonction scanf et des lectures interactives 7. . . . . . . . . . . . . . . . . e 6. . . . . . . . . . . . . . .2 Lecture et ´criture textuelles . . .1 Inclusion de fichiers . . . .2 Branchements hors fonction : setjmp. . . . . . . e 8. . 7.4. . . . . . . . . . . . . . . . . . . . . . .2 Ecriture avec format printf . . . . . . . . . . . . . . . . . . . . . . . . r´cursives e . . . . . . . . . . . . . . . . . . . . .3 Fichiers en acc`s relatif . . 7. . .1 Le pr´processeur . . . . .h . .1. . . . . . . . . . . . . .4. . . . . .2 Exemple . . . . . . . . . . .4. . . . . . . . .2 D´finition et appel des “macros” . . . . . .2 Fichiers binaires et fichiers de texte . . . . . . . . . e 8. . . e 7. . . . . . . . . . .1. . . . . . . . 6. . . . . . . .3 Traitement de chaˆ ınes : string.4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1 Fichiers “en vrac” . . . . . . . . . . . . . . . . .1 Lecture et ´criture de caract`res et de chaˆ e e ınes . . . . . . . . . . . . . . . . . 8. . . . . . . . . . . . . . .4 Classification des caract`res : ctype. . . . .2 Positionnement dans les fichiers . . e 8. . . . . . . . . . . . . . . . . . . . . . . . .4. . . e 8. . . . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . . .3 Lecture avec format scanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6 Limites propres ` l’impl´mentation : limits. . e 7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . e 6. . . . . . . . . . . 7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . e 8. . . . . . . . . . . . . . . . . . . . 8 Autres ´l´ments du langage C ee 8. . . . . .2. . . . . . . . . . . . . . . .4. . . . . . . . . .3 Compilation conditionnelle . . .3. . . . . . . float. . . . . . . . . . . . . . . . . . . . . . . . . . . a 8. . . . 2004 . 8. . . . . . . . . . . . . . . . .3 Structures mutuellement . . . . .3. . . .1 Lecture-´criture . . a e . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . 7. . . . . . . . .2 Fonctions utilitaires : stdlib. . . 7. . . . . . . . . . .4 6. . .4 Flou artistique . . . . . . .3. . . . . . . . . . . . . .2. . . . e 7. . .3. .1 Aide ` la mise au point : assert. . .5 Les fichiers de bas niveau d’UNIX . . . .3 Op´rations en mode binaire . . . . . . . . . . . . . . . . . . . . . . . . .univ-mrs. . . . . . . . . . . . . . . Garreta. . . . . . . . . . . . . 7. . . . . . . .2 La modularit´ de C . . . . 8. . . . . . . . . . . . . 8. . . . . 8. .4 La biblioth`que standard . . . . . . . .Garreta@luminy. . . .3. . . . . . . . . .2 Les unit´s standard d’entr´e-sortie . . . . . . . .1 D´claration . . . . . . . . . . . . . . . 8. . . . 8.2.4. . . . 79 80 81 81 81 82 84 84 85 87 87 87 89 92 94 96 96 96 97 98 98 99 100 101 103 103 103 104 105 107 108 109 111 111 113 114 115 116 117 119 120 120 120 7 Entr´es-sorties e 7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6. . . . . . . . . . . . . . . . .4 Exemples . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .h . . .1 Fichiers en-tˆte .3 Interruptions : signal. . . . .5 Les variantes de printf et scanf . . . . . . . . . . . .h . . . . . . . . . . . . . . .h . . . . . . . . . . . . . .

sans aucune autre propri´t´ a ee sp´cifique . En fait. la plupart des compilateurs acceptent que main soit d´clar´e e e e e e void au lieu de int. c H. section 8.  n’apparaisse pas explicitement. les variables internes ` main sont locales. insuffisant pour ˆtre ex´cut´.h> int main() { printf("Bonjour\n"). car il contient des appels de fonctions ou des e e e r´f´rences ` des variables qui ne sont pas d´finies dans le mˆme fichier. comme la construction  Program . d´clarations de variables et de fonctions externes. et que l’instruction  return 0 . return 0. les directives pour le pr´processeur (cf. enum. voici la version C du c´l`bre programme-qui-dit-bonjour.2 1. tout comme celles des autres fonctions. typedef). appel´ fichier source. car le pr´processeur n’est pas le compilateur C et ne travaille pas sur la a e e e syntaxe du langage. L’´dition e ea e e e de liens est l’op´ration par laquelle plusieurs fichiers objets sont mis ensemble pour se compl´ter mutuellee e ment : un fichier apporte des d´finitions de fonctions et de variables auxquelles un autre fichier fait r´f´rence e ee et r´ciproquement. C’est par elle e que l’ex´cution commencera .2. La compilation est la traduction des fonctions ´crites en C en des e e e proc´dures ´quivalentes ´crites dans un langage dont la machine peut ex´cuter les instructions. D’o` la question : par o` l’ex´cution doit-elle commencer ? La r`gle g´n´ralement suivie par l’´diteur u u e e e e e de liens est la suivante : parmi les fonctions donn´es il doit en exister une dont le nom est main. dit fichier objet. ` part cela. end. main est une fonction comme les autres. } 1. 2 N´anmoins. tandis que les d´finitions de variables se traduisent par des e r´servations d’espace. le premier programme que ee a e e vous ´crirez contiendra d´j` la fonction printf que vous n’aurez certainement pas ´crite vous-mˆme. e e Chaque fichier source entrant dans la composition d’un programme ex´cutable est fait d’une succession d’un e nombre quelconque d’´l´ments ind´pendants.. Notez bien que. ´ventuellement garni de valeurs initiales. et produit un fichier.1 Consid´rations lexicales e Pr´sentation du texte du programme e Le programmeur est maˆ ıtre de la disposition du texte du programme.1) doivent comporter un # dans la premi`re position de la ligne. Par exemple. e 1 Le programme montr´ ici est ´crit selon des r`gles strictes. En C on n’a donc pas une structure syntaxique englobant tout. le lancement du programme ´quivaut ` l’appel de cette fonction par le syst`me e e a e d’exploitation. un nombre ou un a e e a u symbole compos´2 . un programme n’est qu’une collection de fonctions assortie d’un ensemble de variables globales. des tabulations et des sauts ` la ligne peuvent ˆtre plac´s ` tout endroit o` cela ne coupe pas un identificateur. L’´diteur de liens (ou linker ) prend en entr´e plusieurs fichiers objets et biblioth`ques (une e e e e e vari´t´ particuli`re de fichiers objets) et produit un unique fichier ex´cutable. Le compilateur e e e e lit toujours un fichier. e e e Cela ne constitue pas une exception ` la r`gle donn´e ici. 2004 5 . Garreta. e a Pour finir cette entr´e en mati`re. en particulier. e d´finitions de variables et e d´finitions de fonctions. union. L’´diteur de liens est largement ee e e ind´pendant du langage de programmation utilis´ pour ´crire les fichiers sources. qui peuvent mˆme avoir ´t´ e e e e ee ´crits dans des langages diff´rents.1 ´ e El´ments de base Structure g´n´rale d’un programme e e La transformation d’un texte ´crit en langage C en un programme ex´cutable par l’ordinateur se fait en deux e e ´tapes : la compilation et l’´dition de liens. Les autres directives et d´clarations s’adressent e e e au compilateur et il n’en reste pas de trace lorsque la compilation est finie. Des blancs.  du langage Pascal . e Seules les expressions des deux derni`res cat´gories font grossir le fichier objet : les d´finitions de fonce e e tions laissent leur traduction en langage machine. ou que ce type ne figure pas.. e Chaque fichier objet est incomplet.1 ´ ´ ELEMENTS DE BASE 1 1. sans lequel on e e ee ne saurait commencer un cours de programmation1 : #include <stdio. e c constructions de types (struct. qui sont : ee e – – – – – des des des des des directives pour le pr´processeur (lignes commen¸ant par #).

[ + ] . e Attention. Garreta. ces derniers acceptent ´galement e comme commentaire tout texte compris entre le signe // et la fin de la ligne o` ce signe apparaˆ : u ıt // Ceci est un commentaire ` la mode C++. il peut donc figurer ` n’importe e e e ee a quelle place dans un identificateur.h ) pour les besoins des biblioth`ques.1. En outre.2. Lorsque seul le compilateur est concern´. * ! / ~ % < | > & ? ^ : Symboles compos´s : e -> ++ -<= >= == != && || << >> += -= *= /= %= <<= >>= |= &= ^= Tous ces symboles sont reconnus par le compilateur comme des op´rateurs. y compris ee e e e a a ` l’int´rieur d’une chaˆ de caract`res. Dans certaines circonstances cette r`gle est plus qu’un conseil. dont le premier est une lettre.2. a Le caract`re anti-slash \ pr´c´dant imm´diatement un saut ` la ligne masque ce dernier : la ligne suivante e e e e a est consid´r´e comme devant ˆtre concat´n´e ` la ligne courante. le texte  /* voici un grand e e e /* et un petit */ commentaire */  est erron´. est compris comme ceci :  message = "anti 1.4 Op´rateurs e Symboles simples : ( = ) . car sa non-observance e e cr´e une expression ambigu¨. il est conseill´ d’encadrer par des blancs toute e a e e e utilisation d’un op´rateur. e e 6 c H. Par exemple.3 break else long switch case enum register typedef char extern return union const float short unsigned continue for signed void default goto sizeof volatile do if static while Identificateurs Un identificateur est une suite de lettres et chiffres contigus. lorsqu’il s’agit d’identificateurs externes.2. car ces noms commencent par un tel blanc a e soulign´. 2004 . Leur fonction est pr´vue par la syntaxe de C et ils ne peuvent pas ˆtre e e e e utilis´s dans un autre but : e auto double int struct 1. le nombre de caract`res discriminants est au moins de 31. le texte e ıne e message = "anti\ constitutionnellement". Les langages C et C++ cohabitant dans la plupart des compilateurs actuels. ♥ e 1. par convention un programmeur ne doit pas utiliser des identificateurs qui commencent par ce caract`re. c’est-`-dire partag´s par plusieurs fichiers sources. Cela est vrai en toute circonstance. Cependant.  Les mots suivants sont r´serv´s. car seul  /* voici un grand /* et un petit */  sera e vu comme un commentaire par le compilateur. il est a e possible que sur un syst`me particulier l’´diteur de liens sous-jacent soit trop rustique pour permettre le respect e e de ces deux prescriptions. e – dans les identificateurs.2 Consid´rations lexicales e ´ ´ 1 ELEMENTS DE BASE Les commentaires commencent par /* et se terminent par */ : /* Ce texte est un commentaire et sera donc ignor´ par le compilateur */ e Les commentaires ne peuvent pas ˆtre imbriqu´s : ´crit dans un programme. c’est-`-dire lorsqu’il s’agit d’identificateurs dont la port´e est incluse dans un seul e a e fichier (nous dirons de tels identificateurs qu’ils sont priv´s) : e – en toute circonstance une lettre majuscule est tenue pour diff´rente de la lettre minuscule correspondante . Le caract`re (appel´  blanc soulign´ ) est consid´r´ comme une lettre . Cela assure qu’il n’y aura jamais de conflit avec les noms introduits e (` travers les fichiers  . Il est interdit d’ins´rer des e e caract`res blancs ` l’int´rieur d’un symbole compos´.2 Mots-cl´s e constitutionnellement" .

5e6 Une constante flottante est suppos´e de type double. e e Voici par exemple trois mani`res d’´crire le mˆme nombre : e e e 27 033 0x1B D´tail ` retenir : on ne doit pas ´crire de z´ro non significatif ` gauche d’un nombre : 0123 ne repr´sente e a e e a e pas la mˆme valeur que 123. qui joue le rˆle de virgule d´cimale. e – une suite de chiffres d´cimaux. e e – un point. mais pas les deux. la e e notation +123 est interdite. – les suffixes L ou l indiquent qu’elle est du type long double. Une constante de type e e e chaˆ de caract`res se note en ´crivant ses caract`res entre guillemets. sinon unsigned int. Exemples : . u : indique que la constante est d’un type unsigned . Exemple : 123. e Les constantes litt´rales sont sans signe : l’expression −123 est comprise comme l’application de l’op´rateur e e unaire − ` la constante 123 . e Le type d’une constante enti`re est le plus petit type dans lequel sa valeur peut ˆtre repr´sent´e. Ou. 5000000. l : indique que la constante est d’un type long. mˆme non imprimable. 1.3. – L. e e – une constante ´crite en hexad´cimal (base 16) commence par 0x ou 0X. – ´ventuellement un signe + ou -. o e – une suite de chiffres d´cimaux (la partie fractionnaire). ` moins de comporter un suffixe explicite : e a – les suffixes F ou f indiquent qu’elle est du type float .0L. 5. ee On peut omettre : – la partie enti`re ou la partie fractionnaire. comme il n’existe pas d’op´rateur + unaire. cette subtilit´ n’a aucune a e cons´quence pour le programmeur. mais puisque le calcul est fait pendant la compilation. mais pas les deux. plus e e e e exactement : – si elle est d´cimale : si possible int.2 Nombres flottants Une constante litt´rale est l’expression d’un nombre flottant si elle pr´sente. 0x7FFFU..5e7. sinon long. On peut combiner ces deux suffixes : 16UL.3 Caract`res et chaˆ e ınes de caract`res e Une constante de type caract`re se note en ´crivant le caract`re entre apostrophes.456E-78. Garreta. dans l’ordre : e e – une suite de chiffres d´cimaux (la partie enti`re). sinon unsigned long. Exemples.e6. e – si elle est octale ou hexad´cimale : si possible int. e Certains suffixes permettent de changer cette classification : – U.0e4f 1. e – le point ou l’exposant. e Les trois derniers ´l´ments forment l’exposant.3 1.3. Les constantes litt´rales enti`res peuvent aussi s’´crire en octal et en hexad´cimal : e e e e – une constante ´crite en octal (base 8) commence par 0 (z´ro) . 5. Notez aussi qu’en C original.1 Constantes litt´rales e Nombres entiers Les constantes litt´rales num´riques enti`res ou r´elles suivent les conventions habituelles. trois caract`res : ıne e e e e ’A’ "A" ’2’ ’"’ "" "’" Quatre chaˆ ınes de caract`res : e "Bonjour ` tous !" a On peut faire figurer n’importe quel caract`re.1 ´ ´ ELEMENTS DE BASE 1. e – une des deux lettres E ou e. sinon unsigned long . avec quelques e e e e particularit´s. Exemples : 1L.3. dans une constante caract`re ou e e e chaˆ de caract`res en utilisant les combinaisons suivantes. appel´es s´quences d’´chappement : ıne e e e e c H. Exemples : 1.3 Constantes litt´rales e 1. 2004 7 .

e e e ’\0’ est toujours interchangeable avec 0. c’est-`-dire entier repr´sent´ sur un octet. B. D. logique. – la constante chaˆ repr´sente alors. on peut aussi le noter \d2 d1 ou \d1 e ıt´ Par exemple. e e 8 c H. ` l’endroit o` elle est ´crite. 2004 . exemples : -1. "HELLO". 1. voyez la section 2. ’A’ . Exemples : sizeof(int). il est utilis´ tr`s fr´quemment en C.2. de nos jours il e e e e s’agit presque toujours du code ASCII4 . de mani`re contigu¨. une constante chaˆ de caract`res a pour type celui d’un tableau de caract`res (c’est-`-dire e ıne e e a  char[] ) et pour valeur l’adresse d’une cellule de la m´moire.3. l’adresse de la cellule o` a ´t´ rang´ le ıne e a u e u ee e premier caract`re de la chaˆ e ıne. ıne e e e Le codage interne d’une chaˆ de caract`res est le suivant (voyez la figure 1) : ıne e – les caract`res constituant la chaˆ sont rang´s en m´moire. e e e – l’expression constitu´e par l’application de l’op´rateur & (op´rateur de calcul de l’adresse. Garreta. ` un champ d’une variable statique de type structure ou ` un ´l´ment a a a ee d’un tableau statique dont le rang est donn´ par une expression constante . escape (de code ASCII 27).’a’.1. La e a e e valeur d’une constante caract`re est le nombre qui repr´sente le caract`re de mani`re interne . sizeof(2 * x + 3). dans l’ordre o` ils e ıne e e e e u figurent dans la chaˆ . de longueur quelconque.14159265. e e Les expressions constantes peuvent ˆtre ´valu´es pendant la compilation. on peut le noter indiff´remment 0. 1. dans une expression.10) ` une variable statique.4 Expressions constantes Une expression constante est une expression de l’un des types suivants : – toute constante litt´rale . Notez que. e &table[50] . e e a – l’expression constitu´e par l’application de l’op´rateur sizeof ` un descripteur de type. exemples : 1.5e-2 . ”. S’il commence par un ou deux z´ros e e et si cela ne cr´e pas une ambigu¨ e. C. etc. "Bonjour" B o n j o u r\ 0 Fig. e – une expression correcte form´e par l’application d’un op´rateur courant (arithm´tique. ıne – un caract`re nul est ajout´ imm´diatement apr`s le dernier caract`re de la chaˆ pour en indiquer la fin . Cela est fait ` titre facultatif par e e e a les compilateurs de certains langages. exemples : &x. 2 * 3. ’A’. la chaˆ suivante d´finit la suite des 9 caract`res3 A. ’\000’ e e e ou ’\0’ (mais certainement pas ’0’) .) ` une e e e a ou deux expressions constantes . exemples : sizeof x. "HELLO" + 6 . e Par caract`re nul on entend le caract`re dont le code interne est 0 . &fiche. \ et E : "A\033B\"C\fD\\E" Une constante de type caract`re appartient au type char. e e e e e ıne. saut ıne e e de page. En C ce n’est pas facultatif : il est garanti que toute expression constante (et donc toute sous-expression constante d’une expression quelconque) sera effectivement ´valu´e avant que e e 3 Nous 4 En verrons qu’en fait cette chaˆ comporte un caract`re de plus qui en marque la fin. 1 – Repr´sentation de la chaˆ "Bonjour" e ıne Par cons´quent. e e a – l’expression constitu´e par l’application de l’op´rateur sizeof ` une expression quelconque.3 Constantes litt´rales e ´ ´ 1 ELEMENTS DE BASE \n \t \b \r \f \a \\ ’ " \d3 d2 d1 nouvelle ligne (LF) tabulation (HT) espace-arri`re (BS) e retour-chariot (CR) saut de page (FF) signal sonore (BELL) \ ’ " le caract`re qui a pour code le nombre octal d3 d2 d1 . qui ne sera pas ´valu´e .nom. Une constante de type chaˆ de caract`res repr´sente une suite finie de caract`res. ıne e standard le langage C ne pr´voit pas le codage Unicode des caract`res. sizeof(char *) .

le nombre 123 consid´r´ comme un ee e ee ´l´ment de l’ensemble {0 . C’est un abus de langage : le caract`re sign´ e e e e e ou non sign´ n’est pas un attribut d’un nombre (un nombre donn´ est positif ou n´gatif.. Par exemple.4 Types fondamentaux l’ex´cution ne commence.. l’implantation des types de e e a e niveau sup´rieur. 65535} est plus encombrant que le mˆme nombre 123 quand il est consid´r´ ee e ee comme un ´l´ment de l’ensemble {0 . Dans d’autres langages. e – Le deuxi`me crit`re de classification des donn´es num´riques est la taille requise par leur repr´sentation.. par extension. 255}. et d’une e famille infinie de types d´riv´s obtenus en appliquant quelques proc´d´s r´cursifs de construction soit ` des e e e e e a types fondamentaux soit ` des types d´riv´s d´finis de la mˆme mani`re. 1 – Les types du langage C Le tableau 1 pr´sente l’ensemble des types connus du compilateur C.. etc. e e e e e Comme pr´c´demment. les nombres entiers et les nombres flottants. qui reste oblig´ e e e de consid´rer ces donn´es comme appartenant ` des ensembles disjoints.1 Nombres entiers et caract`res e La classification des types num´riques ob´it ` deux crit`res : e e a e a e e – Si on cherche ` repr´senter un ensemble de nombres tous positifs on pourra adopter un type non sign´ . ` l’aide des seuls types num´riques. les caract`res. e 1. les constantes symboliques. et donc d’une variable devant repr´senter tout e e e ´l´ment de l’ensemble. de toute variable cens´e pouvoir repr´senter n’importe quelle valeur de cet ensemble. c’est tout) mais de l’ensemble de nombres e e e qu’on a choisi de consid´rer et. non d’une valeur particuli`re. l’´valuation des expressions constantes est donc e e e enti`rement  gratuite . En termes de temps d’ex´cution. e e e c H. En C on a fait le choix oppos´. 2004 9 . c’est un attribut d’un ensemble.4.. sont cod´s e e e de mani`re interne par des nombres. Garreta. a e e e e e Cette organisation r´v`le un trait de l’esprit de C : le pragmatisme l’emporte sur l’esth´tisme. les bool´ens.4 Types fondamentaux Types de base Nombres entiers Anonymes Petite taille – sign´s e – non sign´s e Taille moyenne – sign´s e – non sign´s e Grande taille – sign´s e – non sign´s e Nomm´s e Nombres flottants Simples Grande pr´cision e Pr´cision encore plus grande e ´ ´ Types derives Tableaux Fonctions Pointeurs Structures Unions char unsigned char short unsigned short long unsigned long enum float double long double [ ] ( ) * struct union Tab. laissant e e a e au programmeur le soin de r´aliser lui-mˆme. parfois mˆme e e e e sur la rigueur.1 ´ ´ ELEMENTS DE BASE 1. e 1. au contraire si on doit repr´senter un ensemble contenant des nombres positifs et des nombres n´gatifs on e e devra utiliser un type sign´ 5 . mais ce fait est officiellement ignor´ par le programmeur. ee 5 On dit parfois qu’une donn´e  est un entier sign´  ou  est un entier non sign´ . L’organisation g´n´rale de cet ensemble e e e est ´vidente : on dispose de deux sortes de types de base.

. 2N −1 − 1) co¨ ıncident.. c’est la constante symbolique EOF. mais la taille e exacte des donn´es de ces types n’est pas fix´e par la norme du langage. Ainsi. On invoque l’efficacit´ pour justifier l’´criture de programmes non e e e e e portables . 1. Or la plupart des programmes qui lisent des caract`res doivent e e e ˆtre capables de manipuler une valeur suppl´mentaire. e e 10 c H. ´ A propos des booleens. 7 Si on consid`re un type comme l’ensemble de ses valeurs. Pour cette raison.. Lorsque les e e char sont par d´faut non sign´s. la taille (au sens de l’op´rateur sizeof.2. e e De plus. l’ensemble des caract`res et celui des nombres sont disjoints). Quelle que soit la machine utilis´e.) produit une valeur bool´enne. la repr´sentation sign´e et la repr´sentation non sign´e des ´l´ments communs aux deux domaines e e e e ee (les nombres 0.483. les variables et fonctions qui repr´sentent ou renvoient des caract`res sont e e e souvent d´clar´es int. section 2. etc. lorsque son ´criture est possible. e ` Le caractere  impossible . signifiant  la e e e fin des donn´es . dans des instructions comme if ou while) on u e peut faire figurer n’importe quelle expression . distincte de tous les  vrais  caract`res..768 et 32. Le type int peut donc poser un probl`me de portabilit´8 : le mˆme programme. Etre e e e portable est un crit`re de qualit´ et de fiabilit´ important.967.4 Types fondamentaux ´ ´ 1 ELEMENTS DE BASE Avec N chiffres binaires (ou bits) on peut repr´senter : e – soit les 2N nombres positifs 0.1. comme e e – un nombre entier pouvant repr´senter n’importe quel caract`re du jeu de caract`res de la machine utilis´e . Dans les autres cas il vaut mieux expliciter char. au choix. les unit´s de e e e e ee e taille et d’adressage). Il faut savoir qu’` tout e e a endroit o` une expression bool´enne est requise (typiquement.147. une telle valeur est d´finie dans le e e fichier stdio. e e e e e On notera que la signification d’un char en C.. En C. . tandis e e e que  putchar(65) . e e e e e 6 A retenir : un objet de type char est  unitaire  aussi bien du point de vue des tailles que de celui des adresses. Inversement. Les entiers courts et longs. c’est-`-dire la plus a a adapt´e ` la machine utilis´e. ` Le type caractere.535 nombre entier compris entre -32. elle sera consid´r´e ee fausse sinon.767 nombre entier entre 0 et 4. puisque a e a e enti`rement faite de nombres.483. Il est garanti que toute donn´e repr´sentable dans le type short est e e repr´sentable aussi dans le type long7 (en bref : un long n’est pas plus court qu’un short !). e – soit les 2N nombres positifs et n´gatifs −2N −1 . non char : n’importe quelle valeur appartenant au type int mais n’appartenant pas au e e type char peut alors servir d’indicateur de fin de donn´es. un char est un entier sign´ . rien ne s’oppose ` l’´criture de l’expression ch . En C il n’existe donc pas de type bool´en sp´cifique. elle sera tenue pour vraie si elle est non nulle.  est (sur un syst`me utilisant le code ASCII) une mani`re non portable d’obtenir le mˆme affichage.. Garreta. comparaison..  putchar(’A’) . 2N −1 − 1 (cas sign´). on a donc les inclusions larges char ⊆ short ⊆ long (et aussi float e ⊆ double ⊆ long double). un unsigned char est alors un entier non sign´.296 entier entre -2. cf. un programme portable est toujours meilleur qu’un programme e e non portable pr´tendu ´quivalent. sur d’autres il e a e e est synonyme de long. e e e e – un nombre entier occupant la plus petite cellule de m´moire adressable s´par´ment6 . peut avoir des comportements diff´rents. Sur les machines e e e actuelles les plus r´pandues cela signifie g´n´ralement un octet (8 bits). . Par exemple. le type int correspond ` la taille d’entier la plus efficace. si vous pr´f´rez.648 et 2. Sur certains syst`mes et compilateurs int est synonyme de short. dans un contexte conditionnel. e e e Le plus souvent. 2004 . lorsqu’un op´rateur (´galit´.147. Un objet de type char peut ˆtre d´fini. Toutes les valeurs qu’il est possible de ranger dans une variable de type char sont en principe des caract`res l´gaux. est tr`s diff´rente de celle d’un char en Pascal e e (dans ce langage. On peut dire que ces propri´t´s d´finissent le type char (ou. l’exp´rience prouve que.11) de t[0] vaut une unit´ de taille.294. expr (c’est-`-dire expr  vraie ) ´quivaut ` a e a expr != 0 (expr diff´rente de 0). 8 Un programme ´crit pour une machine ou un syst`me A est dit portable s’il suffit de le recompiler pour qu’il tourne correctement e e sur une machine diff´rente B. un entier petit. e e De nos jours on trouve souvent : unsigned short : 16 bits short : 16 bits unsigned long : 32 bits long : 32 bits pour pour pour pour repr´senter e repr´senter e repr´senter e repr´senter e un un un un nombre entier compris entre 0 et 65. 1. short ou long selon le besoin.’A’ + 32 qui est tout ` fait homog`ne. ch ´tant une variable de e e type char. compil´ sur deux machines e e e e distinctes. le compilateur C fera en sorte que le programmeur voie ces objets de la mani`re suivante : si t est un tableau e e de char. Par exemple. la norme ANSI pr´voit la possibilit´ de d´clarer des signed char. D’o` un conseil important : n’utilisez le type int que pour e u des variables locales destin´es ` contenir des valeurs raisonnablement petites (inf´rieures en valeur absolue ` e a e a 32767) . En principe. 2N − 1 (cas non sign´) . et l’´cart entre les adresses de e e e t[1] et t[0] vaut une unit´ d’adressage.h.647 Le type int.  est une mani`re portable d’obtenir l’affichage du caract`re A.

3. e e 1. e Les long double correspondent g´n´ralement aux flottants de grande pr´cision manipul´s par certains e e e e coprocesseurs arithm´tiques ou les biblioth`ques de sous-programmes qui les simulent. e e Les valeurs d’un type ´num´r´ se comportent comme des constantes enti`res . samedi. double (double pr´cision) e e e et long double (pr´cision ´tendue). true }. Garreta. var-init .90E308 avec 15 chiffres a a d´cimaux significatifs. e 1. est constitu´ par une famille finie de nombres entiers.1. sur des syst`mes de taille moyenne.29E-38 et de 0. mardi e ee e e valant 1. 2004 .29E-38 ` 1. mercredi valant 2. typedef unsigned char Boolean. la constante true valant 1 et le type Boolean comme le type le moins encombrant dans lequel on peut repr´senter ces deux valeurs.5 Variables il rend 0 pour faux et 1 pour vrai.90E308 ` -0. introduit un type ´num´r´. mais ce n’est pas exig´ par la norme. vendredi. Leur unique avantage r´side dans le fait que e a e certains compilateurs d´tectent parfois. section 8. e o` sp´cification est de la forme : u e  auto     register  static   extern    rien et chaque var-init est de la forme : identificateur = expression rien 11        unsigned   char     short   signed  const       long  rien  volatile int     rien float        double    long double                                 c H. . qui introduisent la constante false valant 0. un float occupe 32 bits et un double 64. Par a a e e e e a exemple.1 ´ ´ ELEMENTS DE BASE 1. Ainsi.70E38 ` -0. l’´nonc´ : e e enum jour_semaine { lundi.70E38 avec 7 chiffres d´cimaux a a e significatifs. les expressions mardi + 2 et jeudi repr´sentent la mˆme valeur.4. Signalons aux esth`tes que le fichier <types.5 1.3. ce qui donne e par exemple des float allant de -1. e Typiquement.3 Nombres flottants La norme ANSI pr´voit trois types de nombres flottants : float (simple pr´cision).5. Mais il n’est pas exclu e e que sur un syst`me particulier un long double soit la mˆme chose qu’un double.1 Variables Syntaxe des d´clarations e La forme compl`te de la d´claration d’une variable sera expliqu´e ` la section 5.56E-308 et de 0. e ee e e A propos des types ´num´r´s voyez aussi la section 5.56E-308 ` 0. var-init . ou ´num´ration. dimanche }. Il est garanti e e e e que toute valeur repr´sentable dans le type float est repr´sentable sans perte d’information dans le type double.2 Types ´num´r´s e e e Un type ´num´r´. e e et toute valeur repr´sentable dans le type double l’est dans le type long double. etc. e ee 1. chacun associ´ e ee e e e e a ` un identificateur qui en est le nom. jeudi. appel´ enum jour semaine.4.. La norme ne sp´cifie pas les caract´ristiques de tous ces types.2). Dans le cas le plus simple e e e a on trouve sp´cification var-init . et des double allant de -0. La syntaxe de la d´claration des ´num´rations est expliqu´e ` la section 5. les m´langes entre objets de types e e e ´num´r´s distincts .h> comporte les d´clarations : e e enum { false. il n’y a pas a a e grand-chose ` dire ` leur sujet. elles font donc double emploi e ee e avec celles qu’on d´finit ` l’aide de #define (cf. mercredi. constitu´ par les constantes lundi valant 0.4.. mardi. Mis ` part ce qui touche ` la syntaxe de leur d´claration. ces types sont alors le moyen d’augmenter la s´curit´ des programmes.

. Un argument formel est accessible de l’int´rieur de la fonction. Avec prototype : long i = 1. partout o` une variable locale plus e e u profonde ne le masque pas.. Dans le bloc o` il e ee e a u est d´clar´.6. et qu’elle concerne l’´dition de liens. k une variable locale et j un argument formel de une fonction. non la compilation. int une_fonction(int j) { short k. e Exemple. i est une variable globale.2 Visibilit´ des variables e La question de la visibilit´ des identificateurs (c’est-`-dire  quels sont les identificateurs auxquels on peut e a faire r´f´rence en un point d’un programme ? ) est r´gl´e en C comme dans la plupart des langages comportant ee e e la structure de bloc. z. on ne doit pas d´clarer un argument formel et une variable locale du niveau le plus haut avec le mˆme nom. 2004 . sous r´serve de ne pas ˆtre masqu´e par une variable locale ou un argument u e e e e formel de mˆme nom. c’est-`-dire des variables d´clar´es au d´but du bloc le plus a e e e ext´rieur9 . y = 0. Une variable locale ne peut ˆtre r´f´renc´e que depuis l’int´rieur du bloc o` e ee e e u elle est d´finie . avant la premi`re e a e e e e instruction.. Le corps d’une fonction est lui-mˆme un bloc. Tout bloc peut comporter un ensemble de d´clarations de variables. qui sont alors e dites locales au bloc en question. il s’agit alors d’arguments formels. Arguments formels. en aucun cas on ne peut y faire r´f´rence depuis un point ext´rieur ` ce bloc. e a Variables locales. En aucun cas on ne peut y faire r´f´rence depuis l’ext´rieur de la fonction. . int une_fonction(j) int j. } Sans prototype : long i = 1. e La question de la visibilit´ inter-fichiers est examin´e ` la section 1. les arguments formels des fonctions sont consid´r´s e ee comme des variables locales du niveau le plus haut. 1. et une complication : tout bloc peut comporter ses propres d´finitions de variables locales. ee e Variables globales. On peut noter d’ores et d´j` qu’elle ne e e a ea se pose que pour les variables globales et les fonctions. e 9 Par cons´quent.1.5. – ` l’int´rieur d’un bloc.. Le nom d’une variable globale ou d’une fonction peut ˆtre utilis´ depuis n’importe e e quel point compris entre sa d´claration (pour une fonction : la fin de la d´claration de son en-tˆte) et la fin e e e du fichier o` la d´claration figure.5 Variables ´ ´ 1 ELEMENTS DE BASE Exemples : int x. extern float a. e Un bloc est une suite de d´clarations et d’instructions encadr´e par une accolade ouvrante “{” et l’accolade e e fermante “}” correspondante. static unsigned short cpt = 1000. { short k. } Ci-dessus. Les d´clarations de variables peuvent se trouver : e – en dehors de toute fonction. avec une simplification : les fonctions ne peuvent pas ˆtre imbriqu´es les unes dans les e e autres. il s’agit alors de variables globales . . e e e – soit entre le nom de la fonction et le { initial (fonction d´finie en syntaxe originale ou sans prototype). a e – dans l’en-tˆte d’une fonction. Toutes les d´clarations de variables locales ` un bloc doivent ˆtre ´crites au d´but du bloc. mais d’autres blocs peuvent ˆtre e e imbriqu´s dans celui-l`. b. Pour ce qui concerne leur visibilit´. e e e 12 c H. Garreta. plac´s e e – soit dans les parenth`ses de l’en-tˆte (fonction d´finie en syntaxe ANSI avec un prototype). il s’agit alors de variables locales . le nom d’une variable locale masque toute variable de mˆme nom d´finie dans un bloc englobant le e e e e bloc en question.

elle n’a aucune incidence ni sur la taille ni sur la dur´e du e e programme ex´cutable produit. e e e Variables automatiques.1 ´ ´ ELEMENTS DE BASE 1. Ainsi. puisqu’elle est ´valu´e ` l’ex´cution.5. e Remarque. On note une grande similitude entre les variables locales et les arguments formels des fonctions : ils ont la mˆme visibilit´ et la mˆme dur´e de vie. une variable automatique ne peut ˆtre initialis´e que si elle est simple e e (c’est-`-dire autre que tableau ou structure). Exemple : double x = 0.5. Dans le C original. En toute circonstance la d´claration d’une variable statique peut indiquer une valeur e initiale ` ranger dans la variable. Le syst`me d’exploitation se charge. pendant la traduction d’un fichier. Les arguments formels des fonctions sont automatiquement initialis´s lors de e leur cr´ation (au moment de l’appel de la fonction) par les valeurs des arguments effectifs. Bien que la syntaxe soit analogue. 22. Garreta. Cette limitation ne fait pas partie du C ANSI. e La d´claration d’une variable locale peut elle aussi comporter une initialisation. Cela est vrai y compris pour des variables de types complexes (tableaux ou a structures). voir les sections 1. Par cons´quent : e e e ea e e – la valeur initiale doit ˆtre d´finie par une expression constante (calculable durant la compilation) . 1. register. 33. la construction e a e int i = exp. ´ventuellement garni de valeurs initiales. il ne  voit  a pas les autres.5e3.5. avant que l’ex´cution ne commence. Certains qualifieurs (static. c’est-`-dire le temps d’´valuation de l’expression qui d´finit la valeur initiale. e e e A l’oppos´.5. e Les variables statiques pour lesquelles aucune valeur initiale n’est indiqu´e sont remplies de z´ros. . une telle initialisation n’a rien en commun avec une affectation comme celles qui sont faites durant l’ex´cution du programme. Cela est la d´finition e e mˆme des arguments des fonctions. Mais il ne s’agit pas de la e mˆme sorte d’initialisation que pour les variables statiques : l’initialisation repr´sente ici une affectation tout ` e e a fait ordinaire. e e Les variables automatiques pour lesquelles aucune valeur initiale n’est indiqu´e sont allou´es avec une valeur e e impr´visible. a e e e a e chaque fois que la fonction ou le bloc est activ´ . L’ine e terpr´tation de ces z´ros d´pend du type de la variable. e e – une telle initialisation est enti`rement gratuite. 1. de e e e e les allouer dans un espace m´moire de taille ad´quate. imm´diatement avant l’activation du programme.. plac´e ` l’int´rieur d’un bloc. a c H. c’est-`-dire permanentes : elles existent pendant toute la a dur´e de l’ex´cution.3 Allocation et dur´e de vie des variables e Les variables globales sont toujours statiques.6) permettent de modifier l’allocation et la dur´e de vie des variables locales. 44. 55 }. 2004 13 . Il s’agit ici uniquement de pr´ciser la valeur qui doit e e ˆtre d´pos´e dans l’espace allou´ ` la variable.5 et o 1. e u e a – une telle initialisation  coˆte  le mˆme prix que l’affectation correspondante. ´quivaut au couple e int i. e Remarque.4 Initialisation des variables Variables statiques. En r´alit´ c’est presque la mˆme chose : les arguments formels e e e e e e e sont de vraies variables locales avec l’unique particularit´ d’ˆtre automatiquement initialis´s (par les valeurs des e e e arguments effectifs) lors de l’activation de la fonction. i = exp .. int t[5] = { 11. les variables locales et les arguments formels des fonctions sont automatiques : l’espace correse pondant est allou´ lors de l’activation de la fonction ou du bloc en question et il est rendu au syst`me lorsque e e le contrˆle quitte cette fonction ou ce bloc.5 Variables car le compilateur ne traduit qu’un fichier source ` la fois et. /* d´claration */ e /* affectation */ /* d´claration + initialisation */ e Par cons´quent : e – l’expression qui donne la valeur initiale n’a pas ` ˆtre constante.

cpt). locale .. ou entrer e e ee en conflit avec un autre objet de mˆme nom. printf("%d ". cpt++. printf("%d ". Elles sont automae e e tiquement initialis´es ` z´ro chaque fois qu’elles sont cr´´es. e e a Les variables ainsi d´clar´es doivent ˆtre locales et d’un type simple (nombre.6 Variables critiques Le qualifieur register pr´c´dant une d´claration de variable informe le compilateur que la variable en e e e question est tr`s fr´quemment acc´d´e pendant l’ex´cution du programme et qu’il y a donc lieu de prendre e e e e e toutes les dispositions utiles pour en acc´l´rer l’acc`s. β . plac´ devant la d´claration d’une variable locale. Ainsi. e e 1. pointeur). cpt). Cela est tout ` fait e e e e a a inhabituel pour une variable locale.. 1001. . car la deuxi`me activation de fonction suspecte acc`de aussi ` i. cpt).. e – pour sa dur´e de vie.1. e a Elle n’est accessible que depuis l’int´rieur du bloc o` elle est d´clar´e. D’autre part. } En effet. On aurait pu obtenir un effet analogue avec le programme int cpt = 1000. produit une variable qui est e e – pour sa visibilit´. les variables locales statiques ont une particularit´ e e potentiellement fort dangereuse : il en existe une seule instance pour toutes les activations de la fonction dans laquelle elles sont d´clar´es. cpt++. de e e o e cette mani`re l’acc`s ` leur valeur ne met pas en œuvre le bus de la machine. α fonction_suspecte(). Cons´quence ` retenir : les variables locales statiques se marient mal avec e a la r´cursivit´. une variable e locale statique conserve sa valeur entre deux activations cons´cutives de la fonction. etc. Malgr´ tout le bien qu’on vient d’en dire. 1002. dans certains calculateurs de telles variables ee e sont log´es dans un registre de l’unit´ centrale de traitement (CPU) plutˆt que dans la m´moire centrale . Le compilateur accorde ce traitement sp´cial aux e a e ee e 14 c H. il s’agit de l’initialisation d’une vae riable statique : elle est effectu´e une seule fois avant l’activation du programme. } mais ici la variable cpt est globale et peut donc ˆtre modifi´e inconsid´r´ment par une autre fonction. Exemple : e void bizarre1(void) { static int cpt = 1000.5 Variables locales statiques Le qualifieur static. } la valeur de la variable i avant et apr`s l’appel de fonction suspecte (c’est-`-dire aux points α et β) peut e a ne pas ˆtre la mˆme. Ainsi. mais elle est cr´´e au d´but de e u e e ee e l’activation du programme et elle existe aussi longtemps que dure l’ex´cution de celui-ci. e Attention. } Lorsque la d´claration d’une telle variable comporte une initialisation. void bizarre2(void) { printf("%d ". des appels successifs e de la fonction ci-dessus produisent l’affichage des valeurs 1000. On notera e a pour finir que la version suivante est erron´e : e void bizarre3(void) { int cpt = 1000.5.5 Variables ´ ´ 1 ELEMENTS DE BASE 1. 2004 . dans l’exemple suivant : e e void fonction_suspecte(void) { static int i. statique (c’est-`-dire permanente). cpt++. Par exemple. tandis que dans la premi`re version elle n’est visible que depuis e e l’int´rieur de la fonction et donc ` l’abri des manipulations maladroites et des collisions de noms. tous les appels de bizarre3 afficheront la mˆme valeur 1000.. Garreta.5.

peu  optimisateur . Un identificateur qui n’est pas public est appel´ e e e priv´. Lorsque cela n’est plus possible (par exemple.6. ne peut pas ˆtre utilis´e pour indiquer le nombre d’´l´ments dans une d´claration de e e e e ee e tableau. Ce renseignement permet au compilateur d’optimiser la gestion de la variable.2. etc. La question ne e e e concerne que les noms de variables et de fonctions. 1.3. En particulier. Il est conseill´ de toujours d´clarer const les variables et les arguments formels qui peuvent l’ˆtre. } Attention. parce u e que tous les registres de la CPU sont pris) les d´clarations register restantes sont ignor´es. 2004 15 . parmi lesquels la d´termination des variables critiques et leur allocation e e dans les registres de la CPU.) n’existent que pendant la compilation et ne peuvent pas ˆtre partag´s par deux fichiers. C’est regrettable mais. 10 La d´claration d’une variable const doit n´cessairement comporter une initialisation car sinon. Un nom de variable ou de fonction d´fini dans un fichier source et e e pouvant ˆtre utilis´ dans d’autres fichiers sources est dit public. y compris dans une section du programme qui ne e comporte aucune r´f´rence ` cette variable. la nature exacte d’une telle optimisation n’´tant pas sp´cifi´e. while ((*d++ = *s++) != 0) . sur les syst`mes o` cela a un sens. elle n’aurait jamais de valeur d´finie. Par exemple un compilateur peut juger utile de ne pas allouer e e e du tout une variable qualifi´e const et de remplacer ses occurrences par la valeur initiale10 indiqu´e lors de la e e d´claration.) doit ˆtre qualifi´e volatile. Or de nos jours les compilateurs de C ont fini par devenir tr`s perfectionn´s et e e int`grent des algorithmes d’optimisation. en appliquant le qualifieur register ` ses e a variables pr´f´r´es (qu’il croit critiques alors qu’elles ne le sont pas r´ellement). fonctions et compilation s´par´e e e variables dans l’ordre o` elles figurent dans les d´clarations. return dest. L’utilisation du qualifieur register est int´ressante lorsque l’on doit utiliser un compilateur e rustique. pour la plupart des compilateurs.1 ´ ´ ELEMENTS DE BASE 1. dont la visibilit´ se r´duit ` l’´tendue de la fonction ou du bloc contenant e e e a e leur d´finition.4. fonctions et compilation s´par´e e e Identificateurs publics et priv´s e Examinons maintenant les r`gles qui r´gissent la visibilit´ inter-fichiers des identificateurs. Mis e a ` part cela.1 Variables. e e e e Note. Il convient donc e e d’appliquer ce qualifieur aux variables les plus critiques d’abord. g`ne le travail du compilateur eee e e et obtient un programme moins efficace que s’il n’avait jamais utilis´ ce qualifieur. Cela pr´vient e e e e u e le compilateur que sa valeur peut changer myst´rieusement. e Jargon.4. de types. une telle variable ne pouvant e e pas ˆtre affect´e par la suite. Garreta. etc. car les autres identificateurs (noms de structures. Le sens du qualifieur volatile d´pend lui aussi de l’impl´mentation.6 Variables. une variable qualifi´e const n’est pas tout e a ` fait une expression constante au sens de la section 1. Identificateurs publics et priv´s.5.6 1. ee a Les compilateurs sont tenus de signaler toute tentative d´celable de modification d’une variable const. e Regle 1. Il diminue le nombre d’hypoth`ses. Exemple : char *strcpy(char *dest. pour ces compilateurs une variable. e e e c H. Il ne sera donc question que des noms des variables globales et des noms des fonctions.7 Variables constantes et volatiles Le qualifieur const plac´ devant une variable ou un argument formel informe le compilateur que la variable e ou l’argument en question ne changera pas de valeur tout au long de l’ex´cution du programme ou de l’activation e de la fonction. char *srce) { register char *d = dest. Le C ANSI introduit aussi les notions de pointeur constant et de pointeur sur constante. Par exemple toute variable e dont la valeur peut ˆtre modifi´e de mani`re asynchrone (dans une fonction de d´tection d’interruption. que le compilateur peut faire sur une variable ainsi qualifi´e. ou par e e e e un canal d’entr´e-sortie. Ils n’appartiennent e pas au C original. expliqu´es ` la e a section 5. Il s’av`re alors que le programmeur. *s = srce. e 1. et e e e donc d’optimisations. mˆme qualifi´e const. Il n’y a pas de e e probl`me pour les variables locales. sur un syst`me particulier ces deux qualifieurs peuvent n’avoir aucun autre effet.

e a 1. Exemple : d´finition de la fonction e 16 c H. Un identificateur r´f´renc´ dans un fichier alors e e ee e qu’il est d´fini dans un autre fichier est appel´ externe. a e – sans prototype (cf. – par cons´quent. sans modifier la dur´e de vie). e e e a ue ee – une d´claration se limite ` indiquer que l’objet en question a dˆ ˆtre cr´´ dans un autre fichier qui sera fourni lors de l’´dition de liens. Exemple. e e – dans une d´claration externe de tableau. etc. mˆme si elle est ult´rieurement d´finie dans le fichier o` figure l’appel. si une fonction n’est pas ` r´sultat entier alors elle doit ˆtre soit d´finie soit d´clar´e e a e e e e e externe avant son appel. La d´claration externe d’une fonction s’obtient en ´crivant l’en-tˆte de la fonction. Garreta. En plus de cela : – une d´finition produit la cr´ation de l’objet dont l’identificateur est le nom .) de l’identificateur en question. uniquement parce qu’ils partagent ` tort des identificateurs publics. fonctions et compilation s´par´e e e ´ ´ 1 ELEMENTS DE BASE – Sauf indication contraire. – ` r´sultat entier (int). d’utiliser le qualifieur static pour rendre priv´s tous les identificateurs qui peuvent l’ˆtre. il est inutile d’indiquer la taille de celui-ci (puisque la d´claration externe n’alloue pas le tableau). D´finition et d´claration d’une variable ou d’une fonction. sans toucher ` la visibilit´) et quand il s’applique e a e a ` un identificateur global (static change la visibilit´. elle est alors suppos´e ˆtre e e – externe.6. e Jargon. extern int table[]. section 4. Dans le fichier o` sont d´finies les variables n et table. Regle 2. – une fonction peut ˆtre r´f´renc´e alors qu’elle n’a encore fait l’objet d’aucune d´finition ni d´claration e ee e e e externe . d’automatique en statique. pr´c´dant la d´claration d’un identificateur global. Dans un autre fichier. Sauf pour les e e e a e deux points suivants : e e – une d´claration externe ne doit pas comporter d’initialisateur (puisque la d´claration externe n’alloue pas la variable). valeur initiale. ` sa d´finition. les propri´t´s de l’objet externe doivent ˆtre e a ee e indiqu´es pour que la compilation puisse avoir lieu correctement. de publique en priv´e.6 Variables. – le qualifieur static. pour ne pas dire e e e obligatoire. e ( Cr´er  une variable ou une fonction c’est r´server l’espace correspondant.1. e e e Lorsqu’un programme est d´compos´ en plusieurs fichiers sources il est fortement conseill´. rempli par l’´ventuelle valeur e e e initiale de la variable ou par le code de la fonction). rend celui-ci priv´. La d´claration externe d’une variable doit ˆtre identique. – Toute variable doit avoir ´t´ d´finie (c’est-`-dire d´clar´e normalement) ou d´clar´e externe avant son ee e a e e e e utilisation . En g´n´ral. on ´crira : u e e unsigned long n = 1000. o` ces variables sont uniquement r´f´renc´es. les noms externes doivent faire l’objet d’une e e e e d´claration : le compilateur ne traitant qu’un fichier ` la fois. int table[100]. on ´crira : u ee e e extern unsigned long n.2) .2 D´claration d’objets externes e Nous ne consid´rons donc d´sormais que les noms publics. le mot extern est facultatif. 2004 . Exemple : extern unsigned long n. e e e e On prendra garde au fait que le qualifieur static n’a pas le mˆme effet quand il s’applique ` un identificateur e a local (static change la dur´e de vie. tout identificateur global est public . Aussi bien une d´claration qu’une e e e d´finition d’un nom de variable ou de fonction est une formule qui sp´cifie la classe syntaxique (variable ou e e fonction) et les attributs (type. au mot extern pr`s. Si on ne e e suit pas cette recommandation on verra des fichiers qui ´taient corrects s´par´ment devenir erron´s lorsqu’ils e e e e sont reli´s. e e e u La d´claration externe d’une variable s’obtient en faisant pr´c´der une d´claration ordinaire du mot-cl´ e e e e e extern. pr´c´d´ du mot extern e e e e e e et suivi d’un point-virgule . Dans un autre fichier cette variable aura ´t´ d´finie : ee e unsigned long n.

a e D´finition : e double carre(x) double x.2. Dans l’ensemble des fichiers qui constituent un programme. e e e – il peut y avoir au plus une d´claration-d´finition comportant un initialisateur. Regle 3. e e e e u e – peut ˆtre d´clar´ externe (y compris dans le fichier o` il est d´fini) un nombre quelconque de fois. pr´c´d´ du mot extern. ou double carre(double).6 Variables.1 ´ ´ ELEMENTS DE BASE 1. chaque nom public peut faire l’objet d’un nombre quelconque de d´clarations-d´finitions. Cette r`gle volontariste est simple et elle exprime la meilleure fa¸on de programmer. e e e e e En syntaxe originale (c’est-`-dire  sans prototype ) il faut en outre ne pas ´crire les arguments formels. ou l’un ou l’autre de ces ´nonc´s. } D´claration externe dans un autre fichier : e double carre(double x). e e Des techniques et conseils pour ´crire des programmes modulaires en C sont expos´s ` la section 8. La clart´ e e e e e e e e des concepts et la fiabilit´ des programmes y perdent beaucoup. mais : e e – il doit y avoir au moins une d´claration-d´finition sans le mot-cl´ extern . chaque nom public : – doit faire l’objet d’une et une seule d´finition . fonctions et compilation s´par´e e e double carre(double x) { return x * x. e Un comportement fr´quent est le suivant : appelons momentan´ment  d´claration-d´finition  une expression e e e e g´n´rale de la forme e e externopt declaration Nous pouvons donner la r`gle relˆch´e : e a e Regle 3’. Il faut savoir cependant e c que chaque syst`me tol`re des ´carts. Garreta. c H. } D´claration externe dans un autre fichier : e double carre(). { return x * x. 2004 17 . e e a = initialisateur rien . Dans l’ensemble des fichiers qui constituent un programme. qui r´v`lent surtout la rusticit´ de l’´diteur de liens sous-jacent.

) et les noms de e variables (x. On peut distinguer les expressions pures des expressions avec effet de bord 11 . a ıtre a Cela justifie les appellations lvalue ( left value ) et rvalue ( right value ). tandis que l’affectation x = y + 1 (qui en C est une expression) est ` effet a de bord. 0. etc. pour chaque sorte d’expression complexe. ). mais on peut signaler d’ores et d´j` que toutes ces constructions ob´issent aux e ea e mˆmes r`gles de syntaxe.1 Lvalue et rvalue Toute expression poss`de au moins deux attributs : un type et une valeur. c’est-`-dire les constantes litt´rales et les identificateurs. mais pas d’adresse. l’expression 2 * i + 3 poss`de le type entier et la valeur 23. Les op´rateurs servent ` construire des expressions complexes. des composantes des enregistrements et des tableaux.). Les propri´t´s d’une expression complexe d´coulent essentiellement de la nature de l’op´rateur ee e e qui chapeaute l’expression. – les noms des variables d’un type struct ou union 11 Effet de bord est un barbarisme ayant pour origine l’expression anglaise side effect qu’on peut traduire par effet secondaire souvent un peu cach´. Les op´rateurs dont il sera question ici peuvent aussi apparaˆ dans les d´clarations. C’est une originalit´ de C que de consid´rer les  d´signateurs  complexes (les objets point´s. e e e e les ´l´ments des tableaux. Certaines expressions sont repr´sent´es par une formule e e qui d´termine un emplacement dans la m´moire . pointeurs et fonctions) comme dans la d´claration complexe : e e e char (*t[20])(). les constantes symboliques (lundi. un type et une valeur. D’autres expressions ne sont pas associ´es ` un emplacement de la m´moire : elles ont un type et une valeur. Garreta. l’expression y + 1 est pure. Bien sˆr. etc. false. alors qu’en C de telles expressions sont habituelles. la valeur de l’expression est alors d´finie comme le e e e contenu de cet emplacement. notamment pour ce qui est des priorit´s. On se souvient qu’en Pascal les signes e a e qui permettent d’´crire de tels d´signateurs (c’est-`-dire les s´lecteurs []. n’importe quelle rvalue peut apparaˆ ` droite. il suffit d’ignorer le contenant (adresse) pour ne voir que le e contenu (type. Au contraire. Exemples : 12. Une rvalue ne poss`de que deux e e e e e attributs : type. La signification des expressions ainsi ´crites est alors tr`s diff´rente de celle des expressions figurant dans la e e e partie ex´cutable des programmes. qui est une expression ´galement l´gitime. parfois dangereux. Comme nous le verrons. etc. Par exemple.31416e1. e a – Rvalue (expressions signifiant  la valeur de. La raison principale de la distinction entre lvalue et rvalue est la suivante : seule une lvalue peut figurer ` gauche du signe = dans une affectation . si i est une variable e enti`re valant 10. comme 2 * x + 3 ou e a sin(0. ^ et . e e Dans la partie ex´cutable des programmes on trouve deux sortes d’expressions : e – Lvalue (expressions signifiant  le contenu de. e e e e e La question des d´clarations complexes sera vue ` la section 5. 2 * i + 3.. C’est le cas des constantes et e des expressions d´finies comme le r´sultat d’une op´ration arithm´tique. C’est le cas des noms des variables.2 ´ OPERATEURS ET EXPRESSIONS 2 2. pour e ıtre e la construction des types d´riv´s (tableaux. une expression ` effet de bord modifie le contenu d’une ou plusieurs variables.1.numero. e Pour les expressions simples. valeur). Les expressions simples sont e e les constantes litt´rales (0. les champs des structures. Les sections suivantes pr´ciseront. e a Remarque 2. etc. fiche. etc. 2004 . e e 2.) comme des expressions construites avec des op´rateurs ee e et ob´issant ` la loi commune. e 18 c H. Il e e a e e ne viendrait pas ` l’id´e d’un programmeur Pascal d’´crire 2 + (t[i]) afin de lever une quelconque ambigu¨ e a e e ıt´ sur la priorit´ de + par rapport ` celle de []. car la priorit´ de l’op´rateur [] est sup´rieure ` celle de +.. Par e a exemple. Une lvalue poss`de trois attributs : une adresse. car elle modifie la valeur de la variable x. notamment pour ce qui concerne la priorit´ des op´rateurs et l’usage des parenth`ses. nombre.) n’ont pas statut d’op´rateur. la situation est la suivante : a e – sont des lvalue : – les noms des variables simples (nombres et pointeurs). dans e a u 2 + (t[i]) les parenth`ses sont superflues. ’A’. Toute lvalue peut ˆtre vue comme une rvalue . s’il s’agit ou non d’une lvalue. Exemples : x.4.. e table[i].1 Op´rateurs et expressions e G´n´ralit´s e e e Dans cette section nous ´tudions les op´rateurs et les expressions du langage C. Une expression pure ne fait que cela : l’´tat du syst`me est le mˆme avant et apr`s son e e e e e ´valuation. Remarque 1. valeur.).31416e1). mais ce e e e e a n’est pas le cas dans (2 + t)[i]. en C un grand nombre d’expressions ont de tels effets. ). Dans tous les cas une expression repr´sente une valeur..

mais en aucun e e e cas la chronologie de l’´valuation des sous-expressions. exp n ) poss`de le type T. exp n ) n’est pas une lvalue. La plupart des caract`res sp´ciaux d´signent des op´rations et e e e e e e subissent les mˆmes r`gles syntaxiques. . e 12 Cette double possibilit´ est comment´e ` la section 6. l’expression carre(2 * x) a le type double. Garreta. 2 – Op´rateurs du langage C e Remarque. ++ % -> -- -un *un &un > >= /= %= += -= <<= Tab. 2004 19 . e 2.2. e e a c H. les suffixes un et bin pr´cisent de quel op´rateur a e e il s’agit..6.´ 2 OPERATEURS ET EXPRESSIONS 2. section 1. l’expression x . – les noms des fonctions.z signifie (x e a y) . Le signe → indique l’associativit´  de gauche ` droite  ..1 Pr´sentation d´taill´e des op´rateurs e e e e Appel de fonction () Op´ration : application d’une fonction ` une liste de valeurs.2 Priorit´ des op´rateurs e e C comporte de tr`s nombreux op´rateurs. – les noms des variables de type tableau. Format : e a exp 0 ( exp 1 . Un coup d’œil au corps de la fonction (cf. Exemple : y = carre(2 * x) + 3. sachant que carre est le nom d’une fonction rendant un double. Le signe ← indique l’associativit´ de  droite ` gauche  . exp n sont appel´s les arguments effectifs de l’appel de la fonction. par exemple. . sens de l’associativit´ e → sizeof (type) ← → → → → → → → → → → ← >>= &= ^= |= ← → .. comme -. Dans certains cas un mˆme signe. soit ici la valeur 4x2 . d´signe deux op´rateurs. exp 0 doit ˆtre de type fonction e e rendant une valeur de type T ou bien12 adresse d’une fonction rendant une valeur de type T. notamment pour ce qui concerne le jeu des priorit´s et la possibilit´ de e e e e parenth´sage. e e L’expression exp 0 ( exp 1 . Alors exp 0 ( exp 1 ...1. l’autre binaire (` deux arguments). class´s par ordre de priorit´.y .3. ).2 Pr´sentation d´taill´e des op´rateurs e e e e – ne sont pas des lvalue : – les constantes. . La table 2 montre l’ensemble de tous les op´rateurs. l’un unaire (` un argue e e a ment).. exp n ) exp 1 . 2...2 2. par exemple. Dans le tableau 2.z. cette valeur est pr´cis´e par une ou plusieurs instructions  return exp .4. . e e e e prior 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 op´rateurs e () [] ! ~ *bin / + -bin << >> < <= == != &bin ^ | && || ? : = *= . La valeur de cette expression d´coule de la d´finition de la fonction (` l’int´rieur e e e a e de la fonction. l’expression x = y = z signifie e a x = (y = z). Notez que le sens de l’associativit´ des op´rateurs pr´cise la signification d’une expression.2) pourrait nous apprendre que la valeur de cette expression n’est autre que le carr´ de son argument.

Toutes ces questions sont reprises plus en d´tail ` la section 4. expr k ). si m a ´t´ d´clar´e par une expression de ee e e la forme (NL et NC sont des constantes) : double m[NL][NC]. 2004 . les tableaux sont toujours ` un seul indice .. – tout argument effectif de type float est converti en double .2. etc. Leur absence ne provoque pas d’erreur.1) : ee e e e – le nombre des arguments effectifs doit correspondre ` celui des arguments formels13 . Bien noter que les parenth`ses doivent apparaˆ e ıtre. expr k sont trait´s e e e selon les r`gles de la section B. mais leurs composantes peuvent ˆtre ` leur tour des a e a tableaux. – la valeur de chaque argument effectif subit ´ventuellement les mˆmes conversions qu’elle subirait dans e e l’affectation argument formel = argument effectif B.2.3.4) e e 20 c H. section 4. c’est-`-dire selon les r`gles de la section B ci-dessus. – les autres arguments effectifs ne subissent aucune conversion.. il e e ee e e n’y a jamais de  test de d´bordement . e Remarque 2. e 2. Garreta. Format : ee e e ee exp 0 [ exp 1 ] exp 0 doit ˆtre de type  tableau d’objets de type T . il v´rifie que expr 0 est bien de type char *. En C original.6. exp 1 doit ˆtre d’un type entier. Op´ration : acc`s au ieme ´l´ment d’un tableau. a e Cette remarque est loin d’ˆtre marginale car elle concerne. l’appel e a y = carre(2). section 4. Alors exp 0 [exp 1 ] est de e e type T . mˆme lorsque la liste des arguments est e vide.. mais les autres param`tres effectifs expr 1 . Par exemple. quand le compilateur rencontre l’instruction e printf(expr 0 . 13 Il existe n´anmoins un moyen pour ´crire des fonctions avec un nombre variable d’arguments (cf.2. e Exemple. expr 1 . . e ee e Deux d´tails auxquels on tient beaucoup en C : e – le premier ´l´ment d’un tableau a toujours l’indice 0 . e ee En C.3.N − 1 d´termin´ par le nombre N d’´l´ments allou´s par la d´claration du tableau. avec la fonction carre d´finie ` la section 1.. e a Remarque 1.2 Pr´sentation d´taill´e des op´rateurs e e e e 2 ´ OPERATEURS ET EXPRESSIONS Les contraintes support´es par les arguments effectifs ne sont pas les mˆmes dans le C ANSI et dans le C e e original (ceci est certainement la plus grande diff´rence entre les deux versions du langage) : e A. section 4. L’expression t[0] d´signe le premier ´l´ment du tableau t. ee e e ee a a – il n’est jamais v´rifi´ que la valeur de l’indice dans une r´f´rence ` un tableau appartient ` l’intervalle 0. C’est le cas.2 Indexation [] ´ Definition restreinte (´l´ment d’un tableau)..4) les arguments correspondant ` la partie variable sont trait´s comme les arguments a e des fonctions sans prototype. t[1] le second. les deux fonctions les plus e utilis´es : printf et scanf. excusez du peu. Ainsi. avec l’argument formel correspone dant . cette expression d´signe l’´l´ment du tableau dont l’indice est donn´ par la valeur de exp 1 . Par exemple. au sens de l’affectation. Cela constitue e un pi`ge assez vicieux tendu aux programmeurs dont la langue maternelle est Pascal. mais change compl`tement le sens de l’expression. . En C ANSI.. e – tout argument effectif de type char ou short est converti en int . a – chaque argument effectif doit ˆtre compatible. Autrement dit. si la fonction a ´t´ d´finie ou d´clar´e avec prototype (cf. un ´l´ment d’une matrice rectangulaire sera not´ : ee e m[i][j] Une telle expression suppose que m[i] est de type  tableau d’objets de type T  et donc que m est de type  tableau de tableaux d’objets de type T . par exemple. Il faut savoir que lorsqu’une fonction a ´t´ d´clar´e comme ayant des arguments en nombre ee e e variable (cf. est erron´ en C original (la fonction re¸oit la repr´sentation interne de la valeur enti`re 2 en  croyant  que e c e e c’est la repr´sentation interne d’un double) mais il est correct en C ANSI (l’entier 2 est converti en double au e moment de l’appel). ou en C ANSI si la fonction n’a pas ´t´ d´finie ou d´clar´e avec prototype : ee e e e – aucune contrainte (de nombre ou de type) n’est impos´e aux arguments effectifs .

´ 2 OPERATEURS ET EXPRESSIONS

2.2

Pr´sentation d´taill´e des op´rateurs e e e e

´ ` Definition complete (indexation au sens large). Op´ration : acc`s ` un objet dont l’adresse est donn´e e e a e par une adresse de base et un d´placement. Format : e exp 0 [ exp 1 ] exp 0 doit ˆtre de type  adresse d’un objet de type T , exp 1 doit ˆtre de type entier. Alors exp 0 [exp 1 ] d´signe e e e l’objet de type T ayant pour adresse (voir la figure 2) : valeur(exp 0 ) + valeur(exp 1 ) × taille(T)

exp0 exp1

Fig. 2 – L’indexation Il est clair que, si exp 0 est de type tableau, les deux d´finitions de l’indexation donn´es co¨ e e ıncident. Exemple : si t est un tableau d’entiers et p un  pointeur vers entier  auquel on a affect´ l’adresse de t[0], alors les e expressions suivantes d´signent le mˆme objet : e e t[i] p[i] *(p + i) *(t + i) Dans un cas comme dans l’autre, l’expression exp 0 [exp 1 ] est une lvalue, sauf si elle est de type tableau. 2.2.3 S´lection . e

Op´ration : acc`s ` un champ d’une structure ou d’une union. Format : e e a exp . identif exp doit poss´der un type struct ou union, et identif doit ˆtre le nom d’un des champs de la structure ou de e e l’union en question. En outre, exp doit ˆtre une lvalue. Alors, exp.identif d´signe le champ identif de l’objet e e d´signe par exp. e Cette expression est une lvalue, sauf si elle est de type tableau. Exemple. Avec la d´claration e struct personne { long int num; struct { char rue[32]; char *ville; } adresse; } fiche; les expressions fiche.num fiche.adresse fiche.adresse.rue etc. d´signent les divers champs de la variable fiche. Seules les deux premi`res sont des lvalue. e e 2.2.4 S´lection dans un objet point´ -> e e

Op´ration : acc`s au champ d’une structure ou d’une union point´e. Format : e e e exp->identif exp doit poss´der le type  adresse d’une structure ou d’une union  et identif doit ˆtre le nom d’un des champs e e de la structure ou de l’union en question. Dans ces conditions, exp->identif d´signe le champ identif de la e structure ou de l’union dont l’adresse est indiqu´e par la valeur de exp. e Cette expression est une lvalue, sauf si elle est de type tableau. Ainsi, exp->identif est strictement synonyme de (*exp).identif (remarquez que les parenth`ses sont indise pensables). Par exemple, avec la d´claration : e
c H. Garreta, 2004

21

2.2 Pr´sentation d´taill´e des op´rateurs e e e e

2

´ OPERATEURS ET EXPRESSIONS

struct noeud { int info; struct noeud *fils, *frere; } *ptr; les expressions suivantes sont correctes : ptr->info ptr->fils->frere ptr->fils->frere->frere

2.2.5

N´gation ! e

Op´ration : n´gation logique. Format : e e !exp Aucune contrainte. Cette expression d´signe une valeur de l’ensemble {0, 1}, d´finie par : e e !exp ≡ 1, si exp = 0 0, si exp = 0

Cette expression n’est pas une lvalue. Remarque. Bien que cela ne soit pas exig´ par le langage C, on ´vitera de  nier  (et plus g´n´ralement e e e e de comparer ` z´ro) des expressions d’un type flottant (float, double). A cause de l’impr´cision inh´rente ` la a e e e a plupart des calculs avec de tels nombres, l’´galit´ ` z´ro d’un flottant n’est souvent que le fruit du hasard. e ea e

2.2.6

Compl´ment ` 1 ~ e a

Op´ration : n´gation bit ` bit. Format : e e a ~exp exp doit ˆtre d’un type entier. Cette expression d´signe l’objet de mˆme type que exp qui a pour codage interne e e e la configuration de bits obtenue en inversant chaque bit du codage interne de la valeur de exp : 1 devient 0, 0 devient 1. Cette expression n’est pas une lvalue. Remarque. Le compl´ment ` un n’est pas une op´ration abstraite (cf. section 2.3.3). La portabilit´ d’un e a e e programme o` cet op´rateur figure n’est donc pas assur´e. u e e

2.2.7

Les c´l`bres ++ et -ee

Il existe deux op´rateurs unaires ++ diff´rents : l’un est postfix´ (´crit derri`re l’op´rande), l’autre pr´fix´ e e e e e e e e (´crit devant). e 1. Op´ration : post-incr´mentation. Format : e e exp++ exp doit ˆtre de type num´rique (entier ou flottant) ou pointeur. Ce doit ˆtre une lvalue. Cette expression est e e e caract´ris´e par : e e – un type : celui de exp ; – une valeur : la mˆme que exp avant l’´valuation de exp++ ; e e e – un effet de bord : le mˆme que celui de l’affectation exp = exp + 1. 2. Op´ration : pr´-incr´mentation. Format : e e e ++exp exp doit ˆtre de type num´rique (entier ou flottant) ou pointeur. Ce doit ˆtre une lvalue. Cette expression est e e e caract´ris´e par : e e – un type : celui de exp ; – une valeur : la mˆme que exp apr`s l’´valuation de exp++ ; e e e e – un effet de bord : le mˆme que celui de l’affectation exp = exp + 1. Les expressions exp++ et ++exp ne sont pas des lvalue. 22
c H. Garreta, 2004

´ 2 OPERATEURS ET EXPRESSIONS

2.2

Pr´sentation d´taill´e des op´rateurs e e e e

Exemple. L’affectation y = x++ ; y = ++x ; ´quivaut ` e a y = x; x = x + 1; x = x + 1; y = x;

L’op´rateur ++ b´n´ficie de l’arithm´tique des adresses au mˆme titre que +. Ainsi, si exp est de type e e e e e  pointeur vers un objet de type T , la quantit´ effectivement ajout´e ` exp par l’expression exp++ d´pend de e e a e la taille de T. Il existe de mˆme deux op´rateurs unaires -- donnant lieu ` des expressions exp-- et --exp. L’explication e e a est la mˆme, en rempla¸ant +1 par −1. e c Application : r´alisation d’une pile. Il est tr`s agr´able de constater que les op´rateurs ++ et -- et la e e e e mani`re d’indexer les tableaux en C se combinent harmonieusement et permettent par exemple la r´alisation e e simple et efficace de piles par des tableaux, selon le sch´ma suivant : e – d´claration et initialisation d’une pile (OBJET est un type pr´d´fini, d´pendant du probl`me particulier e e e e e consid´r´ ; MAXPILE est une constante repr´sentant un majorant du nombre d’´l´ments dans la pile) : ee e ee OBJET espace[MAXPILE]; int nombreElements = 0; e – op´ration  empiler la valeur de x  : if (nombreElements >= MAXPILE) erreur("tentative d’empilement dans une pile pleine"); espace[nombreElements++] = x; – op´ration  d´piler une valeur et la ranger dans x  : e e if (nombreElements <= 0) erreur("tentative de depilement d’une pile vide"); x = espace[--nombreElements]; On notera que, si on proc`de comme indiqu´ ci-dessus, la variable nombreElements poss`de constamment e e e la valeur que son nom sugg`re : le nombre d’´l´ments effectivement pr´sents dans la pile. e ee e

2.2.8

Moins unaire -

Op´ration : changement de signe. Format : e -exp exp doit ˆtre une expression num´rique (enti`re ou r´elle). Cette expression repr´sente l’objet de mˆme type e e e e e e que exp dont la valeur est l’oppos´e de celle de exp. Ce n’est pas une lvalue. e

2.2.9

Indirection *

Op´ration : acc`s ` un objet point´. On dit aussi  d´r´ference . Format : e e a e ee *exp exp doit ˆtre une expression de type  adresse d’un objet de type T . *exp repr´sente alors l’objet de type T e e ayant pour adresse la valeur de exp. L’expression *exp est une lvalue. Remarque 1. On prendra garde au fait qu’il existe un bon nombre d’op´rateurs ayant une priorit´ sup´rieure e e e a ` celle de *, ce qui oblige souvent ` utiliser des parenth`ses. Ainsi par exemple les expressions Pascal e↑[i] et a e e[i]↑ doivent respectivement s’´crire, en C, (*e)[i] et *(e[i]), la deuxi`me pouvant aussi s’´crire *e[i]. e e e Remarque 2. L’expression *p signifie acc`s ` la m´moire dont p contient l’adresse. C’est une op´ration e a e e  sans filet . Quel que soit le contenu de p, la machine pourra toujours le consid´rer comme une adresse e et acc´der ` la m´moire correspondante. Il appartient au programmeur de prouver que cete m´moire a ´t´ e a e e ee effectivement allou´e au programme en question. Si c’est le pas on dit que la valeur de p est une adresse valide ; e dans le cas contraire, les pires erreurs sont ` craindre ` plus ou moins court terme. a a
c H. Garreta, 2004

23

Plus exactement. Exemple 2.. alors ` la suite a de l’instruction p = &i. si i est une variable de type int et p une variable de type  pointeur vers un int . struct en_tete *suivant. &t[j].11 Op´rateur sizeof e Op´ration : calcul de la taille correspondant ` un type. } en_tete_fixe = { 0. Ce doit ˆtre une lvalue. En particulier. e 24 c H. Le programme suivant d´clare une liste chaˆ ee circulaire repr´sent´e par le pointeur ın´ e ın´ e e entree et r´duite pour commencer ` un unique maillon qui est son propre successeur (voir figure 3) . ce n’est pas une lvalue. Une autre utilisation ´l´gante de cet op´rateur est la cr´ation de composantes  fixes  dans ee e e les structures chaˆ ees. 2004 . struct en_tete *entree = &en_tete_fixe.2 Pr´sentation d´taill´e des op´rateurs e e e e 2 ´ OPERATEURS ET EXPRESSIONS 2. Une expression r´duite ` un nom de tableau. l’unit´ choisie est telle que la valeur e e de sizeof(char) soit 1 (on peut aussi voir cela comme une d´finition du type char). e e L’expression &exp a pour type  adresse d’un objet de type T  et pour valeur l’adresse de l’objet repr´sent´ e e par exp.2. L’expression &exp n’est pas une lvalue. 3 – Maillon fixe en tˆte d’une liste chaˆ ee e ın´ struct en_tete { long taille. Format : e e &exp exp doit ˆtre une expression d’un type quelconque T. e e a Deuxi`me forme : e sizeof exp exp est une expression quelconque (qui n’est pas ´valu´e par cette ´valuation de sizeof). &en_tete_fixe }. Ainsi.2.4). ` un nom de fonction ou ` une constante chaˆ e a a a ıne de caract`res est consid´r´e comme une constante de type adresse . Remarque. i et *p d´signent le mˆme objet. Un tel emploi de & devrait ˆtre consid´r´ comme erron´. l’op´rateur & appliqu´ ` de telles expressions e ee e ea est donc sans objet. e e Exemple 1. est une expression constante. La taille des objets est exprim´e en nombre d’octets. Premi`re forme : e a e sizeof ( descripteur-de-type ) Cette expression repr´sente un nombre entier qui exprime la taille qu’occuperait en m´moire un objet poss´dant e e e le type indiqu´ (les descripteurs de types sont expliqu´s ` la section 5..2. &i. Une utilisation fr´quente de cet op´rateur est l’obtention de l’adresse d’une variable en vue de e e la passer ` une fonction pour qu’elle modifie la variable : a scanf("%d%lf%s". d’autres e a maillons seront cr´´s dynamiquement durant l’ex´cution : ee e entree en_tete_fixe Fig. L’expression sizeof e e e exp repr´sente la taille qu’occuperait en m´moire un objet poss´dant le mˆme type que exp. 2. &p->nom). e e e e Dans un cas comme dans l’autre sizeof. Garreta.10 Obtention de l’adresse & Op´ration : obtention de l’adresse d’un objet occupant un emplacement de la m´moire. mais beaucoup de compilateurs se e ee e contentent de l’ignorer.

De plus. mˆme si cela paraˆ en contradiction avec le fait que tampon peut ˆtre vu e ıt e comme de type  adresse d’un char . l’´valuation de l’expression (type)exp ne change ni le type ni la valeur de exp. N’oubliez pas que. char → int). est rarement utile). est int. long.. Les conversions l´gitimes (et utiles) sont14 : e – Entier vers un entier plus long (ex. alors la conversion effectu´e ` l’occasion de l’´valuation e e a e de l’expression ( type 2 ) exp est la mˆme que celle qui est faite lors d’une affectation e x = expr . avec la d´claration e char tampon[80]. C’est encore une conversion sans travail : la valeur de exp est interpr´t´e comme un pointeur. 2004 25 .14 devient l’entier 3. le fait que l’expression donn´e par un op´rateur de conversion de type ne soit e e pas une lvalue interdit des expressions qui auraient pourtant ´t´ pratiques. la valeur de exp est e e purement et simplement tronqu´e (une telle troncation. le r´sultat est impr´visible. le type de l’expression sizeof.2 Pr´sentation d´taill´e des op´rateurs e e e e Remarque 1. Si type 2 et le type de exp sont num´riques. sans effectuer aucune transformation de son codage interne. si e e e la repr´sentation interne de exp n’a pas la taille voulue pour un pointeur.. Il est recommand´ de d´clarer de type size t toute variable (resp. En toute rigueur. l’expression sizeof tampon vaut 80. Le codage de exp est ´tendu de telle mani`re que la e e valeur repr´sent´e soit inchang´e. dans le fichier stddef. Attention. a e – Flottant vers entier : la partie fractionnaire de la valeur de exp est supprim´e. le flottant e 3.2. sans transformation de son codage interne. ee 2. long → short). e – Entier sign´ vers entier non sign´. la formule sizeof t / sizeof t[0] exprime toujours le nombre d’´l´ments de t. sans signification abstraite. Il en d´coule une propri´t´ bien utile : quel que soit le type du tableau e ee t. Lorsque son op´rande est un tableau (ou une expression de type tableau) la valeur rendue e par sizeof est l’encombrement effectif du tableau. toute e e fonction) devant contenir (resp. Par exemple. ee Danger ! Une telle conversion est enti`rement plac´e sous la responsabilit´ du programmeur. e e e – Entier vers un entier plus court (ex. mais uniquement pour les nombres positifs : la conversion en entier de e e -3. ou le contraire. Format : e ( type 2 ) exp type 2 repr´sente un descripteur de type . u e Note 2. – Adresse d’un objet de type T1 vers adresse d’un objet de type T2 . ou les deux. Sinon. sans effectuer aucune transformation de e son codage interne. Sauf cas de d´bordement (le r´sultat est alors impr´visible). sa valeur est la mˆme apr`s conversion. Si la valeur de exp est assez petite pour ˆtre repr´e e sentable dans le type de destination. il y a quelque chose de trompeur dans la phrase  conversion de exp . contrairement ` ce que a sugg`re une certaine mani`re de parler. unsigned.). sous l’appellation e a e size t. sont des types struct ou union sont u e interdites. o` x repr´sente une variable de type type 2 . Pour cette raison il est d´fini.0.h. – Entier vers adresse d’un objet de type T. e e e Toutes les conversions o` l’un des types en pr´sence. Danger ! Une telle conversion est enti`rement plac´e sous la responsabilit´ du programmeur. e Remarque 2. comme ee ((int) x)++. /* DANGER ! */ 14 Attention. Par exemple. exp est une expression quelconque. on peut voir cette conversion comme une r´alisation de la fonction e math´matique partie enti`re. – Entier vers flottant. Dans le C ANSI.12 Conversion de type (“cast” operator) Op´ration : conversion du type d’une expression. ee e ee e e L’expression (type 2 )exp n’est pas une lvalue. Note 1. e e e c H. l’entier 123 devient le flottat 123.´ 2 OPERATEURS ET EXPRESSIONS 2.14 donne -3. Par exemple. etc. Dans le C original. L’expression ci-dessus d´signe un e e ´l´ment de type type 2 qui est le r´sultat de la conversion vers ce type de l’´l´ment repr´sent´ par exp. ce type peut changer d’un syst`me ` un autre (int. Garreta. le flottant obtenu est e e e celui qui approche le mieux l’entier initial. devant renvoyer) des nombres qui repr´sentent des tailles. C’est une conversion sans travail : le compilateur se e e borne ` interpr´ter autrement la valeur de exp. le compilateur e e e l’accepte toujours. non -4. C’est une conversion sans travail : le compilateur donne une autre interpr´tation de la valeur de exp.

une telle conversion de type est faite sous la responsabilit´ du programmeur.. mais le probl`me n’est pas r´solu. mais ailleurs la e u e valeur rendue par malloc sera endommag´e lors de son affectation ` p.h>. – imaginez que –pour des raisons non d´taill´es ici– ptr poss`de ` un endroit donn´ une valeur qu’il est e e e a e l´gitime de consid´rer comme l’adresse d’un objet de type struct en tete (alors que ptr n’a pas ce e e type-l`). l’expression i / j repr´sente leur division enti`re (ou e e e euclidienne. En effet. e e Exemple typique : si on a oubli´ de mettre en tˆte du programme la directive #include <stdlib.c: In function ‘main’ monProgramme. mais son utilisation diminue toujours la qualit´ du programme qui l’emploie.B..2.. Par exemple (avec gcc) : monProgramme. . A ce sujet. Bien entendu. L’affectation e p = malloc(.. /* ERREUR */ x = (float)(i / j). Garreta. si expr est d’un type poina e teur et type 2 est un autre type pointeur. }.. e e e e 26 c H. voyez la remarque de la page e a 65. et il est vrai que la compilation a lieu maintenant en silence. a e ´ N. p = malloc(sizeof(MACHIN)). /* ERREUR */ Exemple 2. p = (MACHIN *) malloc(sizeof(MACHIN)). Exemple 1. struct en_tete *suivant..2 Pr´sentation d´taill´e des op´rateurs e e e e 2 ´ OPERATEURS ET EXPRESSIONS Cependant. . L’appel d’une fonction bien ecrite ne requiert jamais un  cast . seul capable de e garantir qu’` tel moment de l’ex´cution du programme ptr pointe bien un objet de type struct en tete. e e pour une raison facile ` comprendre : cet op´rateur fait taire le compilateur. – d´claration d’un pointeur  g´n´rique  : e e e void *ptr. de l’op´rateur de conversion de e e type entre types pointeurs consiste ` s’en servir pour  voir  un espace m´moire donn´ par son adresse comme a e e poss´dant une certaine structure alors qu’en fait il n’a pas ´t´ ainsi d´clar´ : e ee e e – d´claration d’une structure : e struct en_tete { long taille. Voici un exemple de manipulation cet objet : a ((struct en_tete *) ptr)->taille = n. ou encore leur quotient par d´faut). Sur un ordinateur o` les entiers et les pointeurs ont la mˆme taille cela peut marcher. la traitant comme x = (le type de x)((int) x + 1) . mais le fait que le r´sultat de malloc soit tenu e e pour un entier 15 . /* CECI NE REGLE RIEN! */ .. x = i / (float) j..) lui fait dire qu’on fabrique un pointeur (p) ` partir d’un entier (le r´sultat de malloc) sans a e op´rateur cast. Et voici deux mani`res de se tromper (en n’obtenant que le r´sultat de la conversion vers le type float de e e leur division enti`re) e x = i / j. mais parfois n´cessaire.. il est seulement e e cach´. e e l’utilisation de la fonction malloc de la biblioth`que standard soul`ve des critiques : e e . 2004 . et il n’y a aucun moyen de rendre cette affectation juste aussi longtemps que le compilateur 15 Rappelez-vous que toute fonction appel´e et non d´clar´e est suppos´e de type int. Une utilisation pointue et dangereuse. certains compilateurs acceptent cette expression.. Si i et j sont deux variables enti`res.c:9:warning: assignment makes pointer from integer without a cast On croit r´soudre le probl`me en utilisant l’op´rateur de changement de type e e e . Ce qui est anormal n’est pas l’absence de cast. C’est donc une mani`re de cacher des erreurs sans les r´soudre.. Il suffit pourtant de bien lire l’avertissement affich´ par le compilateur pour trouver la solution. l’expression (type 2 )expr est toujours accept´e par le compilateur sans e le moindre avertissement.. Voici deux mani`res de ranger dans x (une variable de type e e float) leur quotient d´cimal : e x = (float) i / j. L’op´rateur de changee ment de type est parfois n´cessaire. A la compilation on a des avertissements. MACHIN *p.

la valeur de (−17)/5 ou de 17/(−5) est −3 alors que le quotient par d´faut de −17 par 5 est plutˆt −4 (−17 = 5 × (−4) + 3). dans e e e e int somme. Avant l’´valuation de l’expression. e Ici. les op´randes subissent les conversions  usuelles  (cf. nombre. d’une directive ad hoc : #include <stdlib. car somme et 100 sont tous deux entiers et l’expression e somme / 100 est enti`re. alors l’op´ration faite est la division e e flottante des valeurs de expr 1 et expr 2 toutes deux converties dans le type double. section 6. e A propos de la division.5 mais 0. e expr2 Il r´sulte de cette r`gle un pi`ge auquel il faut faire attention : 1/2 ne vaut pas 0. long. etc. si q est le quotient de a par b alors |q| est le quotient de |a| par |b|.. qui prend deux op´randes e e e entiers (short..1). soustraction. 2004 27 .. 2.1)..1). et notamment la r`gle dite  du plus fort . Le r´sultat est une e valeur double qui approche le rationnel expr1 du mieux que le permet la la pr´cision du type double.0. Par exemple. e o c H.) et donne un r´sultat entier. et e e e le r´sultat est entier. soit. p = malloc(sizeof(MACHIN)). et la division flottante dans laquelle le r´sultat est e e flottant (float. comme dans e e e ee e moyenne = somme / nombre. D’autre part.2. Le e e langage C supporte l’arithm´tique des adresses (cf. De mˆme..3. En C on note par le mˆme signe la division enti`re. Format : e    +      . 16 On prendra garde au fait que si les op´randes ne sont pas tous deux positifs la division enti`re du langage C ne coincide pas e e avec le  quotient par d´faut  (quotient de la  division euclidienne  des matheux). la valeur de moyenne n’est pas ce que ce nom sugg`re. La solution est donc d’informer ce dernier ` propos du type de malloc. Garreta.´ 2 OPERATEURS ET EXPRESSIONS 2.2. donc tronqu´e (et il ne faut pas croire que l’affectation ult´rieure ` la variable flottante e e e a moyenne pourra retrouver les d´cimales perdues). int. la solution est simple : e moyenne = somme / 100. ici la solution est un peu plus compliqu´e : e moyenne = somme / (double) nombre.    * exp1 exp2   /         % Aucune de ces expressions n’est une lvalue.. .2 Pr´sentation d´taill´e des op´rateurs e e e e fera une telle hypoth`se fausse.3. Dans ce cas particulier. c’est mieux. section 2. double). e – si au moins une des expressions expr 1 ou expr 2 n’est pas enti`re. moyenne = somme / 100. multiplication. ont la cons´quence e e e importante suivante : dans l’expression expr 1 / expr 2 – si expr 1 et expr 2 sont toutes deux enti`res alors  /  est traduit par l’op´ration  division enti`re 16 . Mˆme probl`me si le d´nominateur avait ´t´ une variable enti`re. en e a faisant pr´c´der l’affectation litigieuse soit d’une d´claration de cette fonction e e e void *malloc (size_t).h> .13 Op´rateurs arithm´tiques e e Ce sont les op´rations arithm´tiques classiques : addition. section 2. division et modulo e e (reste de la division enti`re). float moyenne. . les r`gles qui commandent les types des op´randes et du r´sultat des e e e expressions arithm´tiques (cf.

contrairement ` ce qui se passe en Pascal.3. section 1. section 2.).3. aussi bien si exp 1 est sign´e que si elle est non sign´e a e e e 28 c H. L’expression exp 1 << exp 2 (resp. exp 1 >> e exp 2 ) repr´sente l’objet de mˆme type que exp 1 dont la repr´sentation interne est obtenue en d´calant les bits e e e e de la repr´sentation interne de exp 1 vers la gauche17 (resp. long.3. int. e e e e e e e e Format :    ==      !=         < exp1 exp2  <=       >        >= exp 1 et exp 2 doivent ˆtre d’un type simple (nombre ou pointeur).. si on suppose que la valeur de exp 1 est cod´e sur huit bits.4. Par exemple. des copies du bit de signe18 . 2004 .1). section 2. les op´randes subissent les conversions usuelles (cf. e ˆ ˆ A propos de  etre vrai  et  etre non nul .15 Comparaisons == != < <= > >= Il s’agit des comparaisons usuelles : ´gal.2. sup´rieur. ce qui ´vite beaucoup de parenth`ses disgracieuses. Ainsi. La e e e portabilit´ d’un programme o` ils figurent n’est donc pas assur´e. en C. inf´rieur ou ´gal. e a Les bits sortants sont perdus. diff´rent. si exp 1 est d’un type sign´. e u e 2. des z´ros . des z´ros . << 1 Remarque analogue pour le d´calage ` droite >>. Remarque.2 Pr´sentation d´taill´e des op´rateurs e e e e 2 ´ OPERATEURS ET EXPRESSIONS 2... l’expression e e 0 <= x && x < 10 est correctement ´crite.1).. exp 1 << exp 2 est la mˆme chose (si << 1 apparaˆ exp 2 fois) que e ıt ((exp 1 << 1) << 1) . Notez que. le type bool´en e 17 Par convention la droite d’un nombre cod´ en binaire est le cˆt´ des bits de poids faible (les unit´s) tandis que la gauche est e o e e celui des bits de poids forts (correspondant aux puissances 2k avec k grand). alors exp 1 << 1 exp 1 >> 1 exp 1 >> 1 ≡ ≡ ≡ b6b5b4b3b2b1b00 0b 7 b 6 b 5 b 4 b 3 b 2 b 1 b7b7b6b5b4b3b2b1 (cas non sign´) e (cas sign´) e Avant l’´valuation du r´sultat. Les bits entrants sont : – dans le cas du d´calage ` gauche. Comme on a dit (cf. Garreta. ces op´rateurs ont une priorit´ sup´rieure ` celle des a e e e a connecteurs logiques.2. e e e Avant la prise en compte de l’op´rateur. not´s e e b7b6b5b4b3b2b1b0 (chaque bi vaut 0 ou 1). section 2. vers la droite) d’un nombre de positions ´gal ` la e e a valeur de exp 2 . sup´rieur ou ´gal. e a e e a e e e – dans le cas du d´calage ` droite : si exp 1 est d’un type non sign´. 18 De cette mani`re le d´calage ` gauche correspond (sauf d´bordement) ` la multiplication par 2exp2 . inf´rieur.2. short. e e Ces expressions ne sont pas des lvalue. Format : e e exp1 << >> exp2 exp 1 et exp 2 doivent ˆtre d’un type entier (char. Cette expression repr´sente l’un des ´l´ments e e ee (de type int) 1 ou 0 selon que la relation indiqu´e est ou non v´rifi´e par les valeurs de exp 1 et exp 2 .14 D´calages << >> e Op´ration : d´calages de bits.3). Les op´rateurs de d´calage de bits ne sont pas des op´rations abstraites (cf. Ces exe e e pressions ne sont pas des lvalue. tandis que le d´calage ` e a e e a e a droite correspond ` la division enti`re par 2exp2 . Autrement dit.1). les op´randes subissent les conversions usuelles (cf.

N − 1 ]. 2004 29 . italique. on peut ´crire respectivement e if (i) etc. c’est-`-dire des programmes qui re¸oivent les informations transmises par les composants mat´riels ou qui ee a c e commandent le fonctionnement de ces derniers. Cette expression repr´sente un objet de type entier dont le codage e e interne est construit bit par bit ` partir des bits correspondants des valeurs de exp 1 et exp 2 . etc. En tant e qu’expression. Exemple.1). Garreta. On admet g´n´ralement qu’` cause de propri´t´s techniques des processeurs. capitalis´. Cela ressemble ` ceci : e e e a void afficher(char *texte. Format : e   & | exp1  ^ exp 1 et exp 2 doivent ˆtre d’un type entier. p != NULL. pour vraie dans tous les autres cas.2. N’importe quelle expression peut occuper la place d’une condition . Bien sˆr. while (*ptchar != ’\0’) etc.). a Par exemple.2 Pr´sentation d´taill´e des op´rateurs e e e e n’existe pas en C. les op´randes subissent les conversions usuelles (cf. for (p = liste. p = p->suiv) etc. p = p->suiv) etc. qui ne e disposaient que de compilateurs simples. comme effet de bord. c’est-`-dire des sous-ensembles de l’intervalle d’entiers [ 0 . Attention. pour traduire le bout de Pascal if a = 0 then etc. La relation d’´galit´ se note ==. Du point de vue syntaxique cette construction est correcte. while (*ptchar) etc. Voici une faute possible chez les nouveaux venus ` C e e a en provenance de Pascal qui.3. Dans de telles applications on est souvent aux pries avec des nombres dont chaque bit a une signification propre.16 Op´rateurs de bits & | ^ e et ou exclusif sur les bits des repr´sentations internes e    exp2 Ces op´rateurs d´signent les op´rations logiques et... 16 ou 32. ces expressions raccourcies e e a ee repr´sentent une certaine optimisation des programmes. la mise ` z´ro de a). non =. unsigned long attributs). ces op´rateurs sont principalement destin´s ` l’´criture de logiciel de bas niveau. soulign´. a = 0 vaut 0 (avec. les biblioth`ques graphiques comportent souvent une fonction qui effectue l’affichage d’un texte e affect´ d’un ensemble d’attributs (gras.´ 2 OPERATEURS ET EXPRESSIONS 2. 2. ´crivent e if (a = 0) etc. ind´pendante des autres. ont pris l’habitude de les utiliser largement. selon la table a suivante. Pour faire en sorte que l’argument attributs puisse repr´senter un ensemble d’attributs quelconque. Il n’y a pas beaucoup d’exemples  de haut niveau 19 d’utilisation de ces op´rateurs. for (p = liste. mais elle est loin d’avoir l’effet escompt´. ou e e e des valeurs des op´randes. Le plus e utile est sans doute la r´alisation d’ensembles de nombres naturels inf´rieurs ` une certaine constante pas trop e e a grande. les compilateurs actuels sont suffisamment perfectionn´s pour effectuer e spontan´ment cette sorte d’optimisations et il n’y a plus aucune justification de l’emploi de ces comparaisons e implicites qui rendent les programmes bien moins expressifs. N valant 8. Ces programmes sortent du cadre de ce cours. section 2. On ne peut plus aujourd’hui conseiller cette pratique. p. il fallait ´crire a e u e if (a == 0) etc. C’est pourquoi les premiers programmeurs C. Une cons´quence de ce fait est la suivante : ` la place de e a if (i != 0) etc. Ces exe e e pressions ne sont pas des lvalue. En effet. comme les pilotes de p´riph´rique e e a e e e (les c´l`bres  drivers ). on e associe les attributs ` des entiers conventionnels : a 19 En effet. e c H.. elle sera tenue pour fausse si elle est nulle. dans laquelle (exp)i signifie  le ieme bit de exp  : (exp 1 )i 0 0 1 1 (exp 2 )i 0 1 0 1 (exp 1 & exp 2 )i 0 0 0 1 (exp 1 ^ exp 2 )i 0 1 1 0 (exp 1 | exp 2 )i 0 1 1 1 Avant l’´valuation du r´sultat.

Ainsi. e e – sinon exp 2 est ´valu´e et exp 1 && exp 2 vaut 0 ou 1 selon que la valeur de exp 2 est nulle ou non. l’ensemble d’attributs e qu’il souhaite : afficher("Bonjour".17 Connecteurs logiques && et || Op´rations : conjonction et disjonction. e e Ces expressions ne sont pas des lvalue. /* affichage en gras et italique */ (avec les constantes de notre exemple.2.000100 */ #define CAPITALISE 8 /* en binaire: 00.000001 */ #define ITALIQUE 2 /* en binaire: 00. c’est-`-dire des nombres e e a qui. cela est une bonne nouvelle.. En effet.. 2004 . i = 0.. while (i < nombre && strcmp(table[i]. Format : e exp1 && || exp2 exp 1 et exp 2 sont deux expressions de n’importe quel type.2 Pr´sentation d´taill´e des op´rateurs e e e e 2 ´ OPERATEURS ET EXPRESSIONS #define GRAS 1 /* en binaire: 00.2. Pour le e e e e e programmeur. exp 2 n’est pas ´valu´e et exp 1 || exp 2 vaut 1 ..000101).. e e Pour ´valuer exp 1 || exp 2 : exp 1 est ´valu´e d’abord et e e e – si la valeur de exp 1 est non nulle. C garantit que le premier op´rande sera ´valu´ d’abord et que.. Cette expression repr´sente un ´l´ment de type int e ee parmi { 0.. ch) != 0 n’est ´valu´e qu’apr`s avoir v´rifi´ e e e e e que la condition i < nombre est vraie (un appel de strcmp(table[i]. e e ..000010 */ #define SOULIGNE 4 /* en binaire: 00.. Garreta.. e e – sinon exp 2 est ´valu´e et exp 1 || exp 2 vaut 0 ou 1 selon que la valeur de exp 2 est nulle ou non. ´crits en binaire. En C la conjonction et la e e disjonction s’´valuent dans l’esprit des instructions plus que dans celui des op´rations.. e De son cˆt´.. e e e a L’utilisateur d’une telle fonction emploie l’op´rateur | pour composer... en binaire. ch) avec un premier argument table[i] invalide peut avoir des cons´quences tout ` fait d´sastreuses). comportent un seul 1 plac´ diff´remment de l’un ` l’autre.. Applications. le programme r´sultant peut ˆtre faux. le concepteur de la fonction utilise l’op´rateur & pour savoir si un attribut appartient ou non ` oe e a l’ensemble donn´ : e .001000 */ etc. 2. soit ch une variable e chaˆ L’op´ration  recherche de ch dans table  peut s’´crire : ıne. il n’est pas rare qu’on ´crive des conjonctions dont le e premier op´rande  prot`ge  (dans l’esprit du programmeur) le second .. GRAS | SOULIGNE). Les valeurs utilis´es pour repr´senter les attributs sont des puissances de 2 distinctes. e a e Une autre cons´quence de cette mani`re de concevoir && et || est stylistique. lors de l’appel. s’´crit 00. si cette protection ne fait pas partie e e de la s´mantique de l’op´rateur pour le langage utilis´. L’application pratique de e e cela est la possibilit´ d’´crire sous une forme fonctionnelle des algorithmes qui dans d’autres langages seraient e e 30 c H. Consid´rons e e e e e e l’exemple suivant : un certain tableau table est form´ de nombre chaˆ e ınes de caract`res . s’il suffit ` d´terminer e e e a e le r´sultat de la conjonction ou de la disjonction. exp 2 n’est pas ´valu´e et exp 1 && exp 2 vaut 0 .. ch) != 0) i++. alors le second op´rande ne sera mˆme pas ´valu´. if ((attributs & GRAS) != 0) prendre les dispositions n´cessaires pour afficher en gras e else if ((attributs & ITALIQUE) != 0) prendre les dispositions n´cessaires pour afficher en italique e . .. 1 } d´fini de la mani`re suivante : e e Pour ´valuer exp 1 && exp 2 : exp 1 est ´valu´e d’abord et e e e – si la valeur de exp 1 est nulle. la valeur de l’expression GRAS | SOULIGNE est un nombre qui. Ce programme est juste parce que la condition strcmp(table[i].

2.. .2 Pr´sentation d´taill´e des op´rateurs e e e e s´quentiels. } 2. Version e e e e ee ın´ (r´cursive) habituelle : e int present(INFO x. else texte = "non". else return present(x.. printf("la r´ponse est %s".then. if (reponse) texte = "oui". Cette expression est ´valu´e de e e e la mani`re suivante : e La condition exp 0 est ´valu´e d’abord e e – si sa valeur est non nulle. L->suivant)). L’op´rateur conditionnel n’est pas forc´ment plus facile ` lire que l’instruction conditionnelle. Imaginons un programme devant afficher un des textes non e e ou oui selon que la valeur d’une variable reponse est nulle ou non...19 Affectation = Op´ration : affectation.. Voici un exemple : le pr´dicat qui caract´rise la pr´sence d’un ´l´ment dans une liste chaˆ ee. } Version dans un style fonctionnel. 2004 31 . LISTE L) { /* l’information x est-elle dans la liste L ? */ return L != NULL && (x == L->info || existe(x.. exp 1 est ´valu´e et d´finit la valeur de l’expression conditionnelle. Dans ce cas. e e L’expression exp 0 ?exp 1 :exp 2 n’est pas une lvalue. e Avec l’op´rateur conditionnel c’est bien plus compact : e printf("la r´ponse est %s". Solutions classiques de ce micro-probl`me : e if (reponse) printf("la r´ponse est oui"). Garreta. e else printf("la r´ponse est non"). L->suivant).´ 2 OPERATEURS ET EXPRESSIONS 2. exp 2 est ´valu´e et d´finit la valeur de l’expression conditionnelle. e e e a Format : exp 0 ? exp 1 : exp 2 exp 0 est d’un type quelconque. e 2. Format : e ee exp 1 = exp 2 exp 1 doit ˆtre une lvalue. c’est-`-dire renvoyant une valeur. e e – sinon. texte).. Exemple. l’affectation ci-dessus repr´sente le mˆme objet que e e e ( type 1 ) exp 2 c H.else. Dans ce cas.. else if (L->info == x) return 1. exp 1 et exp 2 doivent ˆtre de types compatibles. e ou bien.2. consid´r´e comme une expression. mais e e a permet quelquefois de r´els all´gements du code. reponse ? "oui" : "non"). exp 1 n’est pas e e e ´valu´e. LISTE L) { /* l’information x est-elle dans la liste L ? */ if (L == NULL) return 0. permise par la s´mantique des op´rateurs && et || : e e int existe(INFO x. avec une variable auxiliaire : char *texte. Soit type 1 le type de exp 1 . e e e exp 2 n’est pas ´valu´e .18 Expression conditionnelle ? : Op´ration : sorte de if. pr´sent´ sous forme d’expression.

2.2 Pr´sentation d´taill´e des op´rateurs e e e e

2

´ OPERATEURS ET EXPRESSIONS

(la valeur de exp 2 convertie dans le type de exp 1 ), avec pour effet de bord le rangement de cette valeur dans l’emplacement de la m´moire d´termin´ par exp 1 . e e e L’expression exp 1 = exp 2 n’est pas une lvalue. Contrairement ` ce qui se passe dans d’autres langages, une affectation est donc consid´r´e en C comme a ee une expression : elle  fait  quelque chose, mais aussi elle  vaut  une certaine valeur et, ` ce titre, elle peut a figurer comme op´rande dans une sur-expression. On en d´duit la possibilit´ des affectations multiples, comme e e e dans l’expression : a = b = c = 0; comprise comme a = (b = (c = 0)). Elle aura donc le mˆme effet que les trois affectations a = 0 ; b = 0 ; e c = 0 ; Autre exemple, lecture et traitement d’une suite de caract`res dont la fin est indiqu´e par un point : e e ... while ((c = getchar()) != ’.’) exploitation de c ... Des contraintes p`sent sur les types des deux op´randes d’une affectation exp 1 = exp 2 . Lorsqu’elles sont e e satisfaites on dit que exp 1 et exp 2 sont compatibles pour l’affectation. Essentiellement : – deux types num´riques sont toujours compatibles pour l’affectation. La valeur de exp 2 subit ´ventuellement e e une conversion avant d’ˆtre rang´e dans exp 1 . La mani`re de faire cette conversion est la mˆme que dans e e e e le cas de l’op´rateur de conversion (cf. section 2.2.12) ; e – si exp 1 et exp 2 sont de types adresses distincts, certains compilateurs (dont ceux conformes ` la norme a ANSI) les consid´reront comme incompatibles tandis que d’autres compilateurs se limiteront ` donner un e a message d’avertissement lors de l’affectation de exp 2 ` exp 1 ; a – dans les autres cas, exp 1 et exp 2 sont compatibles pour l’affectation si et seulement si elles sont de mˆme e type. D’autre part, de la signification du nom d’une variable de type tableau (cf. 5.1.1) et de celle d’une variable de type structure (cf. 5.2.1) on d´duit que : e – on ne peut affecter un tableau ` un autre, mˆme s’ils sont d´finis de mani`re rigoureusement identique a e e e (un nom de tableau n’est pas une lvalue) ; – on peut affecter le contenu d’une variable de type structure ou union ` une autre, ` la condition qu’elles a a aient ´t´ explicitement d´clar´es comme ayant exactement le mˆme type. ee e e e

2.2.20

Autres op´rateurs d’affectation += *= etc. e

Op´ration binaire vue comme une modification du premier op´rande. Format : e e                 exp1                += -= *= /= %= >>= <<= &= ^= |=                                exp2

exp 1 doit ˆtre une lvalue. Cela fonctionne de la mani`re suivante : si • repr´sente l’un des op´rateurs + - * / % e e e e >> << & ^ |, alors exp 1 • = exp 2 peut ˆtre vue comme ayant la mˆme valeur et le mˆme effet que e e e exp 1 = exp 1 • exp 2 mais avec une seule ´valuation de exp 1 . L’expression r´sultante n’est pas une lvalue. e e ´ Exemple. Ecrivons la version it´rative usuelle de la fonction qui calcule xn avec x flottant et n entier non e n´gatif : e 32
c H. Garreta, 2004

´ 2 OPERATEURS ET EXPRESSIONS

2.2

Pr´sentation d´taill´e des op´rateurs e e e e

double puissance(double x, int n) { double p = 1; while (n != 0) { if (n % 2 != 0) /* n est-il impair ? */ p *= x; x *= x; n /= 2; } return p; } Remarque. En examinant ce programme, on peut faire les mˆmes commentaires qu’` l’occasion de plusieurs e a autres ´l´ments du langage C : ee – l’emploi de ces op´rateurs constitue une certaine optimisation du programme. En langage machine, la e suite d’instructions qui traduit textuellement a += c est plus courte que celle qui correspond ` a = b a + c. Or, un compilateur rustique traitera a = a + c comme un cas particulier de a = b + c, sans voir l’´quivalence avec la premi`re forme. e e – h´las, l’emploi de ces op´rateurs rend les programmes plus denses et moins faciles ` lire, ce qui favorise e e a l’apparition d’erreurs de programmation. – de nos jours les compilateurs de C sont devenus assez perfectionn´s pour d´celer automatiquement la pose e sibilit´ de telles optimisations. Par cons´quent, l’argument de l’efficacit´ ne justifie plus qu’on obscurcisse e e e un programme par l’emploi de tels op´rateurs. e Il faut savoir cependant qu’il existe des situations o` l’emploi de ces op´rateurs n’est pas qu’une question u e d’efficacit´. En effet, si exp 1 est une expression sans effet de bord, alors les expressions exp 1 • = exp 2 et exp 1 e = exp 1 • exp 2 sont r´ellement ´quivalentes. Mais ce n’est plus le cas si exp 1 a un effet de bord. Il est clair, e e par exemple, que les deux expressions suivantes ne sont pas ´quivalentes (la premi`re est tout simplement e e erron´e)20 : e nombre[rand() % 100] = nombre[rand() % 100] + 1; nombre[rand() % 100] += 1; /* ERRONE ! */ /* CORRECT */

2.2.21

L’op´rateur virgule , e

Op´ration : ´valuation en s´quence. Format : e e e exp 1 , exp 2 exp 1 et exp 2 sont quelconques. L’´valuation de exp 1 , exp 2 consiste en l’´valuation de exp 1 suivie de l’´valuation e e e de exp 2 . L’expression exp 1 , exp 2 poss`de le type et la valeur de exp 2 ; le r´sultat de l’´valuation de exp 1 est e e e  oubli´ , mais non son ´ventuel effet de bord (cet op´rateur n’est utile que si exp 1 a un effet de bord). e e e L’expression exp 1 , exp 2 n’est pas une lvalue. Exemple. Un cas fr´quent d’utilisation de cet op´rateur concerne la boucle for (cf. section 3.2.6), dont la e e syntaxe requiert exactement trois expressions ` trois endroits bien pr´cis. Parfois, certaines de ces expressions a e doivent ˆtre doubl´es : e e ... for (pr = NULL, p = liste; p != NULL; pr = p, p = p->suivant) if (p->valeur == x) break; ... Remarque syntaxique. Dans des contextes o` des virgules apparaissent normalement, par exemple lors u d’un appel de fonction, des parenth`ses sont requises afin de forcer le compilateur ` reconnaˆ e a ıtre l’op´rateur e virgule. Par exemple, l’expression uneFonction(exp 1 , (exp 2 , exp 3 ), exp 4 ); repr´sente un appel de uneFonction avec trois arguments effectifs : les valeurs de exp 1 , exp 3 et exp 4 . Au passage, e l’expression exp 2 aura ´t´ ´valu´e. Il est certain que exp 2 aura ´t´ ´valu´e avant exp 3 , mais les sp´cifications du eee e eee e e langage ne permettent pas de placer les ´valuations de exp 1 et exp 4 par rapport ` celles de exp 2 et exp 3 . e a
20 La

fonction rand() renvoie un entier al´atoire distinct chaque fois qu’elle est appel´e. e e

c H. Garreta, 2004

33

2.3 Autres remarques

2

´ OPERATEURS ET EXPRESSIONS

2.3
2.3.1

Autres remarques
Les conversions usuelles

Les r`gles suivantes s’appliquent aux expressions construites ` l’aide d’un des op´rateurs *, /, %, +, -, <, <=, e a e >, >=, ==, !=, &, ^, |, && et ||. Mutatis mutandis, elles s’appliquent aussi ` celles construites avec les op´rateurs a e *=, /=, %=, +=, -=, <<=, >>=, &=, ^= et |=. Dans ces expressions, les op´randes subissent certaines conversions avant que l’expression ne soit ´valu´e. e e e En C ANSI ces conversions, dans l’ ordre  logique  o` elles sont faites, sont les suivantes : u – Si un des op´randes est de type long double, convertir l’autre dans le type long double ; le type de e l’expression sera long double. – Sinon, si un des op´randes est de type double, convertir l’autre dans le type double ; le type de l’expression e sera double. e – Sinon, si un des op´randes est de type float, convertir l’autre dans le type float ; le type de l’expression sera float21 . – Effectuer la promotion enti`re : convertir les char, les short, les ´num´rations et les champs de bits en e e e des int. Si l’une des valeurs ne peut pas ˆtre repr´sent´e dans le type int, les convertir toutes dans le e e e type unsigned int. – Ensuite, si un des op´randes est unsigned long, convertir l’autre en unsigned long ; le type de l’exprese sion sera unsigned long. – Sinon, si un des op´randes est long et l’autre unsigned int22 : e – si un long peut repr´senter toutes les valeurs unsigned int, alors convertir l’op´rande de type unsigned e e int en long. Le type de l’expression sera long ; e – sinon, convertir les deux op´randes en unsigned long. Le type de l’expression sera unsigned long. – Sinon, si un des op´randes est de type long, convertir l’autre en long ; le type de l’expression sera long. e – Sinon, si un des op´randes est de type unsigned int, convertir l’autre en unsigned int ; le type de l’exe pression sera unsigned int. – Sinon, et si l’expression est correcte, c’est que les deux op´randes sont de type int ; le type de l’expression e sera int. 2.3.2 L’ordre d’´valuation des expressions e

Les seules expressions pour lesquelles l’ordre (chronologique) d’´valuation des op´randes est sp´cifi´ sont les e e e e suivantes : –  exp 1 && exp 2  et  exp 1 || exp 2  : exp 1 est ´valu´e d’abord ; exp 2 n’est ´valu´e que si la valeur de exp 1 e e e e ne permet pas de conclure ; –  exp 0 ? exp 1 : exp 2  exp 0 est ´valu´e d’abord. Une seule des expressions exp 1 ou exp 2 est ´valu´e e e e e ensuite ; –  exp 0 , exp 0  : exp 1 est ´valu´e d’abord, exp 2 est ´valu´e ensuite. e e e e Dans tous les autres cas, C ne garantit pas l’ordre chronologique dans lequel les op´randes intervenant e dans une expression ou les arguments effectifs d’un appel de fonction sont ´valu´s ; il ne faut donc pas faire e e d’hypoth`se ` ce sujet. La question ne se pose que lorsque ces op´randes et arguments sont ` leur tour des e a e a expressions complexes ; elle est importante dans la mesure o` C favorise la programmation avec des effets de u bord. Par exemple, si i vaut 1, l’expression a[i] + b[i++] peut aussi bien additionner a[1] et b[1] que a[2] et b[1]. L’ordre d’´valuation des op´randes d’une affectation n’est pas fix´ non plus. Pour ´valuer exp 1 = exp 2 , on e e e e peut ´valuer d’abord l’adresse de exp 1 et ensuite la valeur de exp 2 , ou bien faire l’inverse. Ainsi, le r´sultat de e e l’affectation a[i] = b[i++] est impr´visible. e 2.3.3 Les op´rations non abstraites e

Beaucoup d’op´rateurs ´tudi´s dans cette section (op´rateurs arithm´tiques, comparaisons, logiques, etc.) e e e e e repr´sentent des op´rations abstraites, c’est-`-dire poss´dant une d´finition formelle qui ne fait pas intervenir e e a e e les particularit´s de l’implantation du langage. Bien sˆr, les op´randes sont repr´sent´s dans la machine par des e u e e e
21 Cette r`gle est apparue avec le C ANSI : le compilateur accepte de faire des calculs sur des flottants en simple pr´cision. Dans e e le C original, elle s’´nonce plus simplement :  sinon, si l’un des op´randes est de type float, convertir les deux op´randes dans le e e e type double ; le type de l’expression sera double . 22 Cette r`gle compliqu´e est apparue avec le C ANSI. En C original, le type unsigned  tire vers lui  les autres types. e e

34

c H. Garreta, 2004

n’ont pas forc´ment de signification abstraite.´ 2 OPERATEURS ET EXPRESSIONS 2. qui n’est pas r´dhibitoire dans l’´criture de fonctions e e e e e de bas niveau (ces fonctions ne sont pas destin´es ` ˆtre port´es). e e e flottants. 2004 35 . Les transformations qu’ils effectuent sont a e d´finies au niveau des bits constituant le codage des op´randes. e ee – documenter soigneusement ces fonctions . un petit nombre d’op´rateurs.) est utile pour d´finir l’effet de l’op´ration en question ou les contraintes qu’elle subit. c’est-`-dire qui sont aux points de contact entre la composante logicielle et la composante mat´rielle a e d’un syst`me informatique. De telles op´rations sont r´serv´es aux programmes qui remplissent des fonctions de tr`s bas e e e e e niveau. doit rendre le programmeur tr`s pr´cautionneux e ae e e e d`s qu’il s’agit d’utiliser ces op´rateurs dans des programmes de niveau sup´rieur.3 Autres remarques configurations de bits. Ce d´faut.. e L’aspect de cette question qui nous int´resse le plus ici est celui-ci : la portabilit´ des programmes contenant e e des op´rations non abstraites n’est pas assur´e. ^ et |). et le pousser ` : e e e a – isoler les op´rations non abstraites dans des fonctions de petite taille bien rep´r´es . e e A l’oppos´. – constituer des jeux de tests validant chacune de ces fonctions. c H. mais seule leur interpr´tation comme des entit´s de niveau sup´rieur (nombres entiers. les d´calages (<< et >>) et les op´rations e e e a e e bit-`-bit (&. Garreta. non au niveau des nombres que ces op´randes e e e repr´sentent.. le compl´ment ` un (~).

. Comme l’indique la syntaxe de l’instruction-bloc. instruction-for → for ( expression opt . instruction-if → if ( expression ) instruction else instruction → if ( expression ) instruction instruction-while → while ( expression ) instruction instruction-do → do instuction while ( expression ) . instruction-goto → goto identif .. comme  ´l´ment .3 INSTRUCTIONS 3 3. de fois. instruction } instruction-expression → expression . Une formule avec des points de suspension.... instruction-ou-case } instruction-ou-case → case expression-constante : instruction opt → default : instruction → instruction instruction-return → return expression opt . expression opt ) instruction instruction-break → break . Garreta. expression opt . d´claration e e instruction . ´l´ment . C et le point-virgule. ´ventuellement nul. indique un ´l´ment pouvant ee ee ee apparaˆ un nombre quelconque. ıtre e instruction → instruction-bloc → instruction-expression → instruction-goto → instruction-if → instruction-while → instruction-do → instruction-for → instruction-break → instruction-continue → instruction-switch → instruction-return → instruction-vide → identificateur : instruction instruction-bloc → { d´claration .. 2004 . en C le point-virgule n’est pas 36 c H. instruction-switch → switch ( expression ) { instruction-ou-case . instruction-vide → .. instruction-continue → continue .1 Instructions Syntaxe Dans les descriptions syntaxiques suivantes le suffixe  opt  indique que la formule qu’il qualifie est optionnelle..

x = 2 * x + 3.3 INSTRUCTIONS 3.. une variable d´finie dans un bloc est locale ` ce bloc et donc inconnue ` l’ext´rieur.. En particulier.) { type 1 n. quelle que soit a la situation de cette instruction. Par exemple. e e Exemples : 123. Un surnombre de points-virgules cr´e des instructions vides.. tout autre objet de mˆme nom connu u e e e a ` l’ext´rieur du bloc.2. – toute fonction peut ˆtre appel´e  comme une proc´dure  (exemple23 d ). on retrouve bien l’instruction d’affectation. printf("%d\n".2 Instruction-expression Format : expression . n). le compilateur peut donc leur allouer le mˆme emplacee e ment de la m´moire. e 3.2 Pr´sentation d´taill´e des instructions e e e un s´parateur d’instructions mais un terminateur de certaines instructions. Avec deux cas particuliers tr`s int´ressants : e e – puisque l’affectation est une expression. } else { type 2 x.. c’est-`-dire en ignorant la valeur e e e a qu’elle rend. Les variables locales aux blocs permettent d’optimiser la gestion de l’espace local.1 Pr´sentation d´taill´e des instructions e e e Blocs Un bloc est une suite de d´clarations et d’instructions encadr´e par les deux accolades { et }.. de telles variables sont cr´´es et ´ventuellement initialis´es e ee e e lors de l’activation du bloc . e c H. une instruction-expression repr´sente l’ordre  ´valuez cette expression. Sauf e si elle est d´clar´e extern. un bloc peut comporter ses propres d´clarations de variables. 23 On verra le moment venu (cf. L’oubli du point-virgule ` la fin d’une instruction qui en requiert un est toujours une erreur. elles sont d´truites d`s que le contrˆle quitte le bloc. ensuite oubliez le e e r´sultat . Garreta. e e e a a e Dans le bloc o` elle est d´finie. fondamentale dans tous les langages de programmation (exemple c) . L’aspect e a utile de cette notion est : toute expression avec effet de bord pourra ˆtre ´valu´e uniquement pour son effet de e e e bord (exemple b). dans un programme tel que if (.2. } les variables n et x n’existeront jamais simultan´ment . Le bloc le plus ext´rieur d’une fonction et les autres blocs plus profonds ont le mˆme statut.. sans le d´truire. une telle variable masque. e Sauf si elles sont qualifi´es static (ou extern). Autrement dit. e 3. . il appartient ` la e a syntaxe de chaque instruction de pr´ciser si elle doit ou non ˆtre termin´e par un point-virgule. ind´pendamment e e e e de ce par quoi l’instruction est suivie dans le programme. cela n’aura servi ` rien (exemple a). section 7. a b c d Intuitivement. Mais oui.5 d) que printf rend un r´sultat parfois utile.2. 2004 37 .2 3. Il est clair que si l’expression n’a pas d’effet de bord. . il suffit d’´crire un point-virgule derri`re n’importe quelle expression pour en faire une instruction. Du point de e e vue de la syntaxe il se comporte comme une instruction unique et peut figurer en tout endroit o` une instruction u simple est permise. e e quelle que soit sa position dans le programme. Il ne faut donc pas esp´rer e e o e qu’une telle variable conserve sa valeur entre deux passages dans le bloc. i++.

2 Pr´sentation d´taill´e des instructions e e e 3 INSTRUCTIONS Remarque. grande_boucle: /* ici on a quitt´ les deux boucles internes (sur j et k) */ e .2.. . } . e e Imaginons que lirecaractere soit le nom d’une fonction sans argument. Elle est alors utilisable a e partout dans la fonction o` elle apparaˆ (avant et apr`s l’instruction qu’elle pr´fixe) et elle reste inconnue en u ıt e e dehors de la fonction. Une cons´quence regrettable de tout cela est un pi`ge assez vicieux tendu aux pascaliens. puisque le nom d’une fonction est une expression (une constante valant l’adresse de la fonctio) et que toute expression suivie d’un point-virgule est une instruction correcte.. e e e e e si elle est fausse (nulle) instruction 2 est ex´cut´e. Un tel emploi de goto est avantageusement remplac´ en C par l’utilisation des e e instructions return. Cependant. e 3. sera trouv´ l´gal par le compilateur. non nulle) instruction 1 est ex´cut´e . break et continue. Exemple : o e for (i = 0. k++) { .. Elle ne se r´v`le utile que lorsqu’il faut e e abandonner plusieurs structures de contrˆle (if.3.4 Instruction if. j <= N2. s´par´e de celle-ci par un e e e e e caract`re deux points. Il est donc rare que l’on ait besoin de l’instruction goto en C. Dans certains langages e e e comme Pascal. while. if (... elle doit ˆtre plac´e devant une fonction. k <= N2. rien n’est ex´cut´..... expression est ´valu´e : si elle est vraie (i.. e e e e 38 c H. il suffit de l’´crire devant une e a e e instruction pour qu’elle soit automatiquement connue comme un nom ` port´e locale. Garreta. /* mais on est toujours dans la boucle la plus externe (sur i) */ } 3. Or cette expression (tout ` fait analogue ` l’exemple a ci-dessus) ne produit e e a a pas l’appel de la fonction et ne traduit donc probablement pas la pens´e du programmeur.) goto grande_boucle. e transf`re le contrˆle ` l’instruction pr´fix´e par l’´tiquette en question.3 Etiquettes et instruction goto Format : ´tiquette : instruction e Une ´tiquette est un identificateur .. 2004 .. L’appel correct de cette fonction s’´crit : e lirecaractere(). i < N1. expression est ´valu´e : si elle est e e e e e vraie instruction 1 est ex´cut´e . elle est utilis´e pour obtenir l’abandon d’une structure de contrˆle (exemple : une boucle) depuis e o l’int´rieur de la structure. sinon. l’´nonc´ e e lirecaractere.. Dans la deuxi`me forme.2. Formats : if (expression) instruction 1 else instruction 2 et if (expression) instruction 1 Dans la premi`re forme... L’instruction goto ´tiquette . j++) for (k = 0. i++) { for (j = 0. Elle n’a pas ` faire l’objet d’une d´claration explicite .else. e o a e e e Th´oriquement. for.e.. tout algorithme peut ˆtre programm´ sans utiliser l’instruction goto.) imbriqu´es les unes dans les autres.

soit... reste vraie. else printf("Il n’y a personne!").. Fondamentalement.. et repeat. son indentation ne traduit pas convenablement les rapports entre les if et les else : if (nombrePersonnes != 0) if (nombrePersonnes != nombreAdultes) printf("Il y a des enfants!"). Notez que la syntaxe exige que la condition figure entre parenth`ses. du langage Pascal. Voici une faute qu’on peut faire quand e a on d´bute : e if (nombrePersonnes != 0) { if (nombrePersonnes != nombreAdultes) printf("Il y a des enfants!"). Ce probl`me se pose dans tous les langages qui offrent deux vari´t´s d’instruction conditionnelle. en tout cas. } et une instruction vide  . Remarque. La syntaxe pr´voit exactement une instruction entre la condition et le else.while on la v´rifie apr`s. non de celle de l’expression. Le compilateur signalera donc une erreur sur le else....... en utilisant des blocs : if (nombrePersonnes != 0) { if (nombrePersonnes != nombreAdultes) printf("Il y a des enfants!"). else . Formats : e while (expression) instruction et do instruction while (expression). un e e exc`s de points-virgules ` la suite de instruction 1 constitue une erreur. Celles-ci font partie de la syntaxe e du if. else printf("Il n’y a personne!"). Garreta. vue comme une e e e condition.. plus simplement.. e il s’agit de r´it´rer l’ex´cution d’une certaine instruction tant qu’une certaine instruction. . Dans la structure while on v´rifie la condition avant d’ex´cuter l’instruction. Par exemple.5 Instructions while et do. e e c H.while Ces instructions correspondent respectivement aux instructions while. 2004 39 .. Lorsque plusieurs instructions if sont imbriqu´es.until. il est convenu que chaque else se rapporte au dernier e if pour lequel le compilateur a rencontr´ une condition suivie d’exactement une instruction. tandis que e e dans la structure do.2.. } else printf("Il n’y a personne!").2 Pr´sentation d´taill´e des instructions e e e On notera que l’expression conditionnelle doit figurer entre parenth`ses. Il y a maintenant deux instructions entre la ligne du if et celle du else : une instruction-bloc { .3 INSTRUCTIONS 3.do. Le fonctionnement de ces instructions est d´crit par les organigrammes de la figure 4. 3. On le r´sout e ee e soit par l’utilisation d’instructions vides : if (nombrePersonnes != 0) if (nombrePersonnes != nombreAdultes) printf("Il y a des enfants!"). }. Par cons´quent. Le listing du e programme peut (et doit !) traduire cela par une indentation (marge blanche) expressive. le programme suivant est probablement incorrect . else printf("Il n’y a personne!"). mais il ne faut pas oublier que le compilateur ne tient pas compte des marges.

dans la construction for(expr 1 ... 4 – Instruction while (` gauche) et do. .... e e e e e – expr 2 est le test de continuation de la boucle .until du e Pascal. On consid`re alors qu’elle est toujours vraie. ex´cute donc au moins une fois l’instruction qui constitue son corps avant e d’´valuer la condition de continuation. Exemple : e 40 c H. la boucle  ind´finie  peut se programmer : e for ( . 2004 .2. C’est en cela qu’elle est l’analogue de l’instruction repeat. cette construction ´quivaut strictement ` celle-ci : e e a expr 1 . il est ´valu´ avant l’ex´cution du corps de la boucle ... expr 2 . i++) t[i] = 0. il faut dans ce cas que l’abandon de la boucle soit programm´ ` l’int´rieur de son corps (sinon ea e cette boucle ind´finie devient infinie !). – expr 3 est une expression (par exemple une incr´mentation) ´valu´e ` la fin du corps de la boucle. e e e a Par exemple. 3.. ) instruction ´quivaut ` e a while (expr 2 ) instruction La condition de continuation expr 2 peut elle aussi ˆtre absente. ) instruction Bien entendu..while (` droite) a a L’instruction do.while ( faire tant que.... l’instruction Pascal : for i:=0 to 9 do t[i]:=0 se traduit tout naturellement en C for (i = 0. Par exemple for ( .3. expr 3 ) instruction Par d´finition.. Les expressions expr 1 et expr 3 peuvent ˆtre absentes (les points-virgules doivent cependant apparaˆ e ıtre). } Ainsi.while. Garreta.until ´quivalente ( r´p´ter jusqu’` ce que. Mais on notera que la condition figurant dans une instruction do. expr 2 . expr 2 .2 Pr´sentation d´taill´e des instructions e e e 3 INSTRUCTIONS expression != 0 oui non oui instruction instruction expression != 0 non suite du programme suite du programme Fig. ) et celle qui figurerait dans une instruction repeat.. i < 10.6 Instruction for Format : for ( expr 1 . while (expr 2 ) { instruction expr 3 . e e Ainsi.. ) sont inverses l’une e e e a de l’autre.. expr 3 ) : – expr 1 est l’expression qui effectue les initialisations n´cessaires avant l’entr´e dans la boucle .

... on doit utiliser l’instruction a break. e e – s’il existe un ´nonc´ case avec une constante qui ´gale la valeur de expression. et si l’´nonc´ default existe...2 Pr´sentation d´taill´e des instructions e e e for (. le contrˆle est transf´r´ ` e e e o eea l’instruction qui suit cet ´nonc´ . switch (i) { case 3: j++. case 2: j++.) { printf("donne un nombre (0 pour sortir): "). e e Attention. break est avantaa e geusement remplac´e par return comme dans : e char *adresse_de_fin(char *ch) { /* renvoie adresse du caract`re qui suit la cha^ne ch */ e ı for (. toutes les instructions qui le suivent sont e a e e ex´cut´es. 3.2. alors aucune a e e instruction n’est ex´cut´e. e e – si un tel case n’existe pas. if (n == 0) break.. Exemple (idiot) : j = 0. Garreta.) if (*ch++ == 0) return ch.. l’instruction e e a a switch s’apparente beaucoup plus ` une sorte de goto  param´tr´  (par la valeur de l’expression) qu’` a e e a l’instruction case.7 Instruction switch Format : switch ( expression ) corps Le corps de l’instruction switch prend la forme d’un bloc {.. 2004 41 .. exploitation de la donn´e n e ... Autrement dit. . de Pascal. scanf("%d". jusqu’` la fin du bloc ou jusqu’` une instruction de rupture (break). comme dans l’exemple suivant.3 INSTRUCTIONS 3. } Si on suppose que i ne peut prendre que les valeurs 0 . Lorsqu’il y a branchement r´ussi ` un ´nonc´ case.of. Pour obtenir un comportement similaire ` celui du case.. case 1: j++. e e – si la valeur de expression ne correspond ` aucun case et s’il n’y a pas d’´nonc´ default. alors l’instruction ci-dessus a le mˆme effet que e l’affectation j = i. &n)..... } Lorsque l’abandon de la boucle correspond aussi ` l’abandon de la proc´dure courante.} renfermant une suite d’instructions entre lesquelles se trouvent des constructions de la forme case expression-constante : ou bien default : Le fonctionnement de cette instruction est le suivant : expression est ´valu´e .. } 3.of. dans lequel on compte la fr´quence de chaque chiffre et des caract`res e e blancs dans un texte : c H.. de Pascal. alors le contrˆle est transf´r´ ` l’instruction qui e e o eea suit l’´nonc´ default .

case ’ ’: case ’\n’: case ’\t’: nb_blancs++. . break. Dans la port´e d’une structure de contrˆle (instructions for. while. Dans la port´e d’une structure de contrˆle de type boucle e e o (while. . a e Par exemple la construction for (expr 1 .. for (i = 0. ) nb_chiffre[i++] = 0. Garreta.. expr 3 ) { . continue. Dans ce cas.8 Instructions break et continue Formats : break. e 42 c H. expr 2 . break. } sortie: .3. ee e e e e e e dans le cas de for. par l’ex´cution de l’expression d’incr´mentation).. goto sortie. do ou for).....’0’]++. break. } ´quivaut ` e a { for (expr 1 . default: nb_autres++. expr 3 ) { . le d´marrage de l’it´ration e e e e e suivante. do et switch). l’instruction break e o provoque l’abandon de la structure et le passage ` l’instruction ´crite imm´diatement derri`re. 2004 . elle produit l’abandon de l’it´ration courante et. i < 10. le cas ´ch´ant. while ((c = getchar()) != EOF) switch (c) { case ’0’: case ’1’: case ’2’: case ’3’: case ’4’: case ’5’: case ’6’: case ’7’: case ’8’: case ’9’: nb_chiffre[c . Elle agit comme si les instructions qui se trouvent entre l’instruction continue et la fin du corps de la boucle avaient ´t´ supprim´es pour l’it´ration en cours : l’ex´cution continue par le test de la boucle (pr´c´d´. } L’instruction continue est moins souvent utilis´e. L’utilisation de a e e e break ailleurs qu’` l’int´rieur d’une de ces quatre instructions constitue une erreur (que le compilateur signale).2 Pr´sentation d´taill´e des instructions e e e 3 INSTRUCTIONS nb_blancs = nb_autres = 0. l’emploi de ces instructions ne nous semble pas œuvrer pour la clart´ des programmes. c’est la plus profonde qui est concern´e par les instructions break e e et continue. } 3. e e Lorsque plusieurs boucles sont imbriqu´es.. expr 2 .2.

9 Instruction return Formats : return expression . return.3 INSTRUCTIONS 3. e e a Dans la deuxi`me forme. la valeur retourn´e par la fonction reste ind´termin´e. les conversions autoris´es e e e e ´tant les mˆmes que celles faites ` l’occasion d’une affectation. c’est donc la valeur que l’appel de la fonction repr´sente dans l’expression o` il figure. On suppose dans ce cas que e e e e la fonction appelante n’utilise pas cette valeur . le contrˆle est rendu ´galement ` la proc´dure appelante.2. son r´sultat est la valeur que la fonction renvoie ` la fonction e e e e a appelante . } c H. Dans la premi`re forme expression est ´valu´e . e Absence d’instruction return dans une fonction.2 Pr´sentation d´taill´e des instructions e e e 3... et return . 2004 43 . Si n´cessaire. Garreta. e u e la valeur de expression est convertie dans le type de la fonction (d´clar´ dans l’en-tˆte). Lorsque la derni`re instruction (dans l’ordre de e l’ex´cution) d’une fonction est termin´e. il est donc prudent de d´clarer void cette fonction. Tout se passe e e o e a e comme si l’accolade fermante qui termine le corps de la fonction ´tait en r´alit´ ´crite sous la forme e e ee . Dans un cas comme dans l’autre l’instruction return provoque l’abandon de la fonction en cours et le retour a ` la fonction appelante.

int combien) { /* copie dans dest les combien premiers caract`res de srce */ e /* renvoie le nombre de caract`res effectivement copi´s e e */ int compteur. Ainsi. mais du point de la syntaxe le langage e ne connaˆ que les fonctions. l’en-tˆte de la fonction a e extract donn´e en exemple ne peut pas s’´crire sous la forme char *extract(char *dest. C’est pourquoi on ne parlera ici que de fonctions.1 Syntaxe ANSI ou “avec prototype” D´finition e Une fonction se d´finit par la construction : e type opt ident ( d´claration-un-ident . rassemblant dans une e section sp´cifique (cf.2 Type de la fonction et des arguments L’en-tˆte de la fonction d´finit le type des objets qu’elle renvoie. l’exemple pr´c´dent aurait aussi pu ˆtre ´crit de mani`re ´quivalente (mais cette pratique n’est e e e e e e pas conseill´e) : e 24 La notion de sous-programme (comme les procedures de Pascal. int n). char *srce. e e e par un identificateur. e e 4.1 4. L’appel e d’une fonction est une expression.) est suppos´e ici connue du e lecteur.1) la mani`re de d´clarer des fonctions rendant e e e un r´sultat d’un type plus complexe. elle ne convient qu’aux fonctions dont le type est d´fini simplement. ee e e C’est dans la syntaxe et la s´mantique de la d´finition et de l’appel des fonctions que r´sident les principales e e e diff´rences entre le C original et le C ANSI. le compilateur suppose qu’il s’agit d’une fonction ` r´sultat e a e entier. int extract(char *dest. c’est-`-dire globales. en C on ne peut pas d´finir une fonction ` l’int´rieur d’une autre : toutes a e a e les fonctions sont au mˆme niveau. *dest = ’\0’. Nous expliquons principalement le C ANSI. Ainsi. un sous-programme est toujours suppos´ renvoyer une valeur. e La premi`re ligne de la d´finition d’une fonction s’appelle l’en-tˆte. e e section 4. – tout type struct ou union. 25 Restriction : on n’a pas le droit de  mettre en facteur  un type commun ` plusieurs arguments. La pr´sence d’un point-virgule e e e a ` cet endroit provoquerait des erreurs bien bizarres. Ce peut ˆtre : e e e e – tout type num´rique .4. tandis que l’appel d’une proc´dure est une instruction.1. e e e e Exemple. etc. section 5. } Contrairement ` d’autres langages.4).. for (compteur = 0. e e e Chaque formule d´claration-un-ident poss`de la mˆme syntaxe qu’une d´claration de variable25 . *srce. compteur < combien && *srce != ’\0’. section 4. qui est une fonction e a comme les autres ayant pour seule particularit´ un nom convenu. compteur++) *dest++ = *srce++. return compteur. e ee l’appel d’une fonction renvoie un r´sultat. de la fonction. e e En C on retrouve ces deux mani`res d’appeler les sous-programmes.. car la d´finition serait prise pour une d´claration (cf. Garreta. alors que l’appel d’une proc´dure ne renvoie rien. On verra ult´rieurement (cf. mˆme ıt e e lorsque celle-ci n’a pas de sens ou n’a pas ´t´ sp´cifi´e. ou parfois le prototype. 2004 . Ou. – tout type pointeur .4 FONCTIONS 4 Fonctions Beaucoup de langages distinguent deux sortes de sous-programmes24 : les fonctions et les proc´dures. e e 44 c H.1.1. si on pr´f`re. Autrement dit. les subroutines de Fortran. Si le type de la fonction n’est pas indiqu´.2) la mani`re de faire du C original. C’est le cas notamment de main. La syntaxe indiqu´e ici est incompl`te . d´claration-un-ident ) e e instruction-bloc Notez qu’il n’y a pas de point-virgule derri`re le “)” de la premi`re ligne. e 4. .

compteur < combien && *srce != ’\0’. il est prudent de la d´clarer comme rendant un objet de type a e a e voidvoid.. sa d´finition prend la forme e type opt ident ( void ) instruction-bloc On notera que. int combien) { /* copie dans dest les combien premiers caract`res de srce */ e /* maintenant cette fonction ne renvoie rien */ int compteur.1. compteur++) *dest++ = *srce++. *dest = ’\0’. type k arg formel k ) etc. int combien) etc. Lorsqu’une fonction ne renvoie pas une valeur. Exemple : e void extract(char *dest. } Fonctions sans arguments. Lorsqu’une fonction n’a pas d’arguments. Cela signifie que les arguments formels26 de la fonction repr´sentent d’authentiques variables locales initialis´es.. mais (cf. Notez bien que les parenth`ses doivent toujours apparaˆ e ıtre. e e par les valeurs des arguments effectifs27 correspondants. Garreta. Le programmeur se trouve ainsi ` l’abri d’une a a utilisation intempestive du r´sultat de la fonction. Supposons qu’une fonction ait ´t´ d´clar´e ainsi ee e e type fonction ( type 1 arg formel 1 .4 FONCTIONS 4. lors d’un appel tel que fonction ( arg effectif 1 . alors il a la valeur d’une constante adresse ıt e (l’adresse de la fonction) et aucun appel de la fonction n’est effectu´. c’est-`-dire lorsqu’elle corresa pond plus ` une proc´dure qu’` une vraie fonction. e e e e – les valeurs des arguments effectifs subissent les conversions n´cessaires avant d’ˆtre rang´es dans les arguments formels correspondants (exactement les mˆmes conversions qui sont faites lors des affectations). le passage des arguments se fait par valeur. Ce type est garanti incompatible avec tous les autres types : une tentative d’utilisation du r´sultat e de la fonction provoquera donc une erreur ` la compilation... sauf cas exceptionnel. ´ Fonctions sans resultat. char *srce...2. lors de l’appel de la fonction. suivi d’une paire de parenth`ses contenant ´ventuellement e e e une liste d’arguments effectifs. oe 4. d´clar´s ` e e e a l’int´rieur de la paire de parenth`ses. e 26 Les arguments formels d’une fonction sont les identificateurs qui apparaissent dans la d´finition de la fonction. arg formel k = arg effectif k Cela a des cons´quences tr`s importantes : e e e e e – les erreurs dans le nombre des arguments effectifs sont d´tect´es et signal´es. on ne doit pas ´crire une paire de parenth`ses vide dans la d´claration ou e e e la d´finition d’une fonction. En effet. . e e c H.2) que le programmeur ne souhaite pas que ses appels soient contrˆl´s par le compilateur. char *srce. – si le type d’un argument effectif n’est pas compatible avec celui de l’argument formel correspondant. En C. 2004 45 . ´crites ` l’int´rieur e a e de la paire de parenth`ses caract´ristiques de l’appel. for (compteur = 0. e e 27 Les arguments effectifs d’un appel de fonction sont les expressions qui apparaissent dans l’expression d’appel. . une erreur est signal´e. mˆme si la liste d’arguments est vide : si un e nom de fonction apparaˆ dans une expression sans les parenth`ses.3 Appel des fonctions L’appel d’une fonction se fait en ´crivant son nom. section 4. alors. e Passage des arguments. arg effectif k ) la transmission des valeurs des arguments se fait comme si on ex´cutait les affectations : e arg formel 1 = arg effectif 1 .1 Syntaxe ANSI ou “avec prototype” extract(char *dest. un en-tˆte de la forme e e type ident() ne signifie pas que la fonction n’a pas d’arguments.

Par exemple. suivi d’un point-virgule . t[i0 + 1]) que par une fonction(t[i0 + 1]. e Lorsque les hypoth`ses ci-dessus ne sont pas justes.1. unsigned long n. les noms des arguments formels sont des identificateurs sans e aucune port´e. car le programmeur. Garreta. c e e e e e e 46 c H. c’est-`-dire les instructions e a qui la composent. Ces derniers doivent donc ˆtre sans effets de bord les uns sur les autres. En syntaxe originale la d´finition d’une fonction prend la forme e type opt ident ( ident . Toute d´finition ou d´claration ult´rieure tant soit peu diff´rente sera qualifi´e de  red´claration e e e e e e ill´gale 28 . . 4.4 D´claration “externe” d’une fonction e Une d´claration externe d’une fonction est une d´claration qui n’est pas en mˆme temps une d´finition. avant l’appel de la fonction. t[i0 ]).. en particulier lorsque la fonction ne renvoie pas un int. ident ) d´claration . unsigned long n. 2004 . ces hypoth`ses sur la fonction constituent pour le compilateur une premi`re d´claration de la e e e fonction.4. Ce n’est sˆrement pas indiff´rent ! u e Appel d’une fonction inconnue.4. une d´claration externe de cette derni`re. ın´ e con¸oit sa d´finition ou d´claration ult´rieure comme ´tant la premi`re d´claration de la fonction. comme expliqu´ ` la e e e ea section 4. en y supprimant les noms des arguments formels. e il faut : – soit ´crire la d´finition de la fonction appel´e avant celle de la fonction appelante. si une fonction a ´t´ d´finie ainsi ee e void truc(char dest[80].1. – soit la formule obtenue ` partir de l’en-tˆte pr´c´dent. Ces expressions sont appel´es des prototypes de la fonction. char *srce. e e e – soit ´crire. en particulier. d´claration e e instruction-bloc 28 C’est une erreur surprenante. En C une fonction peut ˆtre appel´e alors qu’elle n’a pas ´t´ d´finie e e ee e (sous-entendu : entre le d´but du fichier et l’endroit o` l’appel figure). /* ERREUR !!! */ Si i0 est la valeur de i juste avant l’ex´cution de l’instruction ci-dessus. char *. Le compilateur suppose alors e u – que la fonction renvoie un int. alors cet appel peut aussi bien e se traduire par une fonction(t[i0 ]. On e e e e  annonce  l’existence de la fonction (d´finie plus loin. ou ou extern void machin(char [80]. float). oubliant que l’appel de la fonction a entraˆ e une d´claration implicite. t[i0 ]) ou mˆme par e une fonction(t[i0 ].. mais on ne donne pas son corps. il est e impossible de pr´voir quelles sont les valeurs effectivement pass´es ` une fonction lors de l’appel : e e a x = une_fonction(t[i++].2 Syntaxe originale ou “sans prototype” 4 FONCTIONS Remarque. e o e De plus. unsigned long. char *srce.2. Dans la premi`re forme. e 4. Le langage ne sp´cifie pas l’ordre chronologique des ´valuations des arguments effectifs d’un e e appel de fonction. t[i++]).1 Syntaxe originale ou “sans prototype” D´claration et d´finition e e ´ Definition. – que le nombre et les types des arguments formels de la fonction sont ceux qui correspondent aux arguments effectifs de l’appel (ce qui. float x). ou dans un autre fichier) tout en pr´cisant le type de son e e r´sultat et le nombre et les types de ses arguments. Le mot extern est facultatif. a e e e Par exemple. Cela se fait en ´crivant e – soit un en-tˆte identique ` celui qui figure dans la d´finition de la fonction (avec les noms des arguments e a e formels)... empˆche les contrˆles et conversions mentionn´s plus haut). mais le pointe virgule est essentiel. float x) { corps de la fonction } alors des d´clarations externes correctes sont : e extern void truc(char dest[80].2 4.

4. char *srce. ou int extract(). e Juste avant le transfert du contrˆle ` la fonction. car la fonction appelante d´pose la valeur enti`re 2 ` l’adresse du premier argument formel (qu’elle e e e a  croit  entier). des e e arguments formels et des arguments effectifs. Les d´clarations e e e de ces arguments se trouvent imm´diatement apr`s l’en-tˆte. avant l’accolade qui commence le corps de la e e e fonction. Exemple : int extract(dest. int combien. vous avez bien lu ! C’est l` la principale caract´ristique de la s´mantique d’appel des fonctions du C original. { return x * x. la e fonction carre ayant ´t´ ainsi d´finie ee e double carre(x) double x. 2004 47 .2. puis e e – les valeurs des arguments effectifs de type char ou short sont converties dans le type int . for (compteur = 0. indiqu´s lors de la d´finition de la fonction. Ou plus exactement. ni les types. – les valeurs des arguments effectifs de type float sont converties dans le type double . la d´claration externe de la fonction pr´c´dente prend une e e e des formes suivantes : extern int extract(). } l’appel x = carre(2). ni mˆme les noms. } ´ Declaration externe. combien) /* copie dans dest les combien premiers caract`res de srce */ e /* renvoie le nombre de caract`res effectivement copi´s e e */ char *dest. lors d’un appel de la fonction le compilateur ne tient aucun compte ni du nombre ni des types des arguments formels 30 . { int compteur. Des appels corrects auraient ´t´ ee x = carre((double) 2). ces valeurs sont copi´es dans les arguments formels coro a e respondants.4 FONCTIONS 4. ou x = carre(2. e e 30 Mais oui. cons´quent. a e e 29 Par c H. puisque int est le type suppos´ des fonctions e e e e e non d´clar´es. ni en nombre ni en type. Garreta. Par exemple. des arguments formels n’apparaissent dans une d´claration e e externe29 .0). Or la fonction appel´e croit recevoir le codage d’un nombre flottant double. – les valeurs des arguments effectifs d’autres types sont laiss´es telles quelles. dans ce que la fonction appelante croit ˆtre les emplacements des arguments e formels de la fonction appel´e : il n’y a aucune v´rification de la concordance. les d´clarations externes montr´es ici sont sans aucune utilit´. Comme on le voit. compteur++) *dest++ = *srce++. Le r´sultat n’a e e aucun sens.2 Syntaxe originale ou “sans prototype” Les parenth`ses de l’en-tˆte ne contiennent ici que la liste des noms des arguments formels. *dest = ’\0’. return compteur. Chaque argument est ´valu´ e e e e s´par´ment. srce. En syntaxe originale. compteur < combien && *srce != ’\0’. est erron´.2 Appel En syntaxe originale. Cela est une source d’erreurs tr`s importante.

c’est-`-dire quel que soit le nombre e a d’octets qu’il faut recopier.3 Arguments des fonctions 4 FONCTIONS 4. est consid´r´e comme ayant pour type  adresse d’un T  et pour valeur e e ee l’adresse du premier ´l´ment du tableau. l’objet effectivement pass´ ` la fonction est uniquement l’adresse du premier ea ´l´ment et il suffit que l’espace r´serv´ dans la fonction pour un tel argument ait la taille. 2004 . /* une fonction sans arguments dont les appels seront contrˆl´s */ oe /* une fonction avec ou sans arguments. o` la taille du tableau ne peut pas ˆtre ignor´e (elle est indispensable pour e e u e e l’allocation de l’espace m´moire). ee e e D’autre part. les appels de ee e e e fonction sont faits sans v´rification ni conversion des arguments effectifs.. si une fonction n’a pas d’argument. Lorsque la fonction a ´t´ d´finie ou d´clar´e sous la syntaxe originale. quelle que soit sa taille.3. while (s[i] != 0) i++. e a Si l’argument effectif est d’un type simple (nombre.3 4. le nom d’un tableau. la d´finition de son en-tˆte en C ANSI doit e e s’´crire sous la forme bien peu heureuse : e type opt ident ( void ) car une paire de parenth`ses sans rien dedans signifierait non pas que la fonction n’a pas d’arguments. sa valeur est recopi´e dans l’argument formel correspondant. en C il n’est jamais v´rifi´ que les indices des tableaux appartiennent bien ` l’intervalle 0. Ainsi lorsqu’un ee argument effectif est un tableau. ee e – les formules  type t[ ]  et  type *t  sont tout ` fait ´quivalentes . 4. c’est-`-dire que ses appels doivent ˆtre trait´s sans contrˆle des arguments.4. e e e Exemple..1 Arguments des fonctions Passage des arguments L’id´e maˆ e ıtresse est qu’en C le passage des arguments des fonctions se fait toujours par valeur.2. e 32 Attention. return i. et plus g´n´ralement toute e e e expression d´clar´e  tableau de T . Garreta. double moyenne(). cette question se complique notablement dans le cas des tableaux multidimensionnels. a e – la fonction pourra ˆtre appel´e indiff´remment avec un tableau ou un pointeur pour argument effectif. d’un pointeur. la syntaxe originale pour la d´finition et la d´claration oe e e e externe des fonctions fait partie elle aussi du C ANSI . de nos jours les programmeurs ont donc le choix entre l’une ou l’autre forme. 6.2 Arguments de type tableau Apparaissant dans la partie ex´cutable d’un programme.N − 1 e e a d´termin´ par le nombre d’´l´ments indiqu´ dans la d´claration. } peut indiff´remment ˆtre concr´tis´e de l’une des trois mani`res suivantes : e e e e e int strlen(char s[80]) int strlen(char s[]) int strlen(char *s) 31 Il n’en est pas de mˆme lors de la d´finition. Cf.3. ces contrˆles et conversions sont effectu´s. mais e qu’elle est d´clar´e sans prototype. Au contraire.3 Coexistence des deux syntaxes A cˆt´ de ce que nous avons appel´ la syntaxe ANSI. pointeur) ou d’un type struct ou union. fixe.2. Il en d´coule la propri´t´ suivante : e e ee e e e ee Dans la d´claration d’un argument formel t de type  tableau de T  : e – l’indication du nombre d’´l´ments de t est sans utilit´32 . lorsque la fonction a ´t´ e ee sp´cifi´e sous la syntaxe ANSI. e e a e e o Exemples : int getchar(void). aux appels incontrˆl´s */ oe 4.3 48 c H. Apr`s avoir fait e les conversions opportunes la valeur de chaque argument effectif est affect´e ` l’argument formel correspondant. e e o e A cause de cette coexistence. Cela ne fait pas intervenir la taille effective du tableau31 . L’en-tˆte  vague  de la fonction suivante e int strlen(chaˆ de caract`res s) { ıne e int i = 0.

e ee a plus g´n´ralement. b repr´sente un entier . Cependant. une lvalue) appartenant ` la proc´dure appelante. un appel correct de la e e fonction pr´c´dente sera e e z = quotient(&x. ce qui prouve que la fonction appel´e n’a modifi´ qu’une copie locale du tableau e e e envelopp´. } main() { struct enveloppe x. e a ee e a 4. l3 = strlen("Bonjour"). void possible_modification(struct enveloppe x) { x.1). e e a e L’argument effectif correspondant ` un tel argument formel doit ˆtre une adresse. les tableaux sont pass´s par adresse.. Exemple : a struct enveloppe { int t[100]. y). si t est un tableau de char et p l’adresse d’un char. Le sch´ma e e e ea e suivant r´sume les principales situations possibles. x. }. supposons avoir ` ´crire une fonction ae int quotient(a.2. bien que les occasions o` cela est n´cessaire u e semblent assez rares. A retenir : parce que les arguments effectifs sont pass´s par valeur. possible_modification(x). si x. mais aussi ` son adresse. x. e a a C’est ce qui s’appelle le passage par adresse des arguments.) ou bien des struc ou des union sont toujours pass´s par valeur. puisque les structures sont pass´es par valeur.3 Arguments des fonctions Dans les trois cas. les trois appels suivants sont corrects : l1 = strlen(t).. printf("%d\n".3 Arguments par adresse Les arguments qui sont d’un type simple (char.t[50]). elle requiert que la e e e fonction puisse acc´der non seulement ` la valeur de la variable. ce m´canisme empˆche qu’elle puisse e e e ˆtre modifi´e par la fonction appel´e. l2 = strlen(p). Bien entendu. Garreta. Soient les fonctions e c H. e e Passage par valeur des tableaux. l’op´rateur & ne doit pas ˆtre utilis´ si l’argument effectif est d´j` une adresse. b) cens´e renvoyer le quotient de la division euclidienne de l’entier a par l’entier b et devant en plus remplacer a e par le reste de cette division. Par exemple. 2004 49 . La notation *a fait r´f´rence ` une variable (ou. a l’adresse d’un entier. c’est-`-dire que celui-ci a bien ´t´ pass´ par valeur (les structures seront vues ` la section 5. On devra la d´finir : e int quotient(int *a. Il suffit de d´clarer le tableau comme le champ unique d’une structure  enveloppe  ne e servant qu’` cela. } Ci-dessus. *a = *a % b. y et z sont des variables enti`res.4 FONCTIONS 4. pointeur. Or une telle modification est parfois souhaitable . int b) { int r = *a / b. Souvent. C ne fournit aucun m´canisme sp´cifique : le passage de l’adresse de l’argument doit ˆtre programm´ e e e e explicitement en utilisant comme argument un pointeur vers la variable. } La valeur affich´e est 1. int. cela est obtenu a e par l’utilisation de l’op´rateur &. Lorsque l’argument effectif est une variable. Par exemple.t[50] = 1. elles e fournissent un moyen pour obtenir le passage par valeur d’un tableau.t[50] = 2. Alors que certains langages le prennent en charge.3. return r.

4. x[1]. Pour les arguments anonymes elle s’effectue comme pour une fonction sans prototype : les char et les short sont convertis en int. au e e moins un.. va_start(magik.. } Des a b c appels corrects de cette fonction seraient : = max(3. x[5]. x[7]). 0). comme le montre l’exemple suivant : e #include <stdarg. } h(int c. int)) > m) m = x. A l’int´rieur de la fonction on acc`de aux arguments anonymes au moyen des macros va start et va arg e e d´finies dans le fichier stdarg.  . ` g(&*d). i++) if ((x = va_arg(magik. Exemple : int max(short n. i <= n. . un peu plus fiable. Garreta. p. m. ici deux. suivis d’une virgule et de trois points :  .. Le programmeur choisit le nom de cette variable. 2004 . return m. Mais tout cela est suffisamment casse-cou pour que nous pr´f´rions n’expliquer que la mani`re ANSI ee e de faire. r).. Pour les arguments nomm´s. q. i.3 Arguments des fonctions 4 FONCTIONS f(int a) { .3. int *d) { .. bien sˆr. mais il faut qu’il y en e e ait au moins autant qu’il y a d’arguments formels nomm´s.. f(*d). elle consiste ` utiliser. g(&c). Une fonction avec un nombre variable d’arguments est d´clar´e en explicitant quelques arguments fixes. g(d). = max(2. Nous exposons ici la mani`re dont cette facilit´ est r´alis´e dans le C ANSI. Les ´l´ments d´finis dans le fichier stdarg.. p. x[4]. mais il n’en fait rien d’autre que la mettre comme argument dans les appels des macros va start et va arg. . int x. int x0. q. m = x1. u.h sont : ee e va list pointeur D´claration de la variable pointeur.h.. = max(8.h> int max(short n. x[6]. l’affectation des valeurs des arguments effectifs aux arguments formels est faite e comme d’ordinaire. x[0]. } g(int *b) { .. for (i = 2. . qui sera automatiquement g´r´e par le dispositif d’acc`s e ee e aux arguments. x[3].. une faiblesse du langage (la non-v´rification du a e nombre d’arguments effectifs) et une caract´ristique du syst`me sous-jacent (le sens d’empilement des arguments e e effectifs). cette derni`re expression correspondant.. les float en double et il n’y a aucune autre conversion. 50 c H. des appels corrects de f et g sont : e f(c). } A l’int´rieur de la fonction h.. Dans le C a e e e e original elle existe aussi . r). x1). sans garde-fou. x[2].4 Arguments en nombre variable Le langage C permet de d´finir des fonctions dont le nombre d’arguments n’est pas fix´ et peut varier d’un e e appel ` un autre. e u a 4. int x1.) Une telle fonction peut ˆtre appel´e avec un nombre quelconque d’arguments effectifs. Exemple d’appel : e m = max(3.) { va_list magik.

). dont la fonction peut d´duire l’adresse des autres. le principe des fonctions avec un nombre variable d’arguments est de d´clarer e effectivement au moins un argument.3 Arguments des fonctions va start(pointeur .. le premier argument (le format) renseigne sur le nombre et la nature des autres arguments. Elle est d´clar´e (dans le fichier stdio. Ici encore.h) : e e int printf(char *. e e ee e Comme l’exemple le montre.4 FONCTIONS 4. type) Parcours des arguments anonymes : le premier appel de cette macro donne le premier argument anonyme . La question des fonctions formelles (fonctions arguments d’autres fonctions) et d’autres remarques sur les fonctions et leurs adresses sont trait´es ` la section 6. Il faut aussi choisir le e moyen d’indiquer ` la fonction le nombre de valeurs r´ellement fournies. dernier argument) Initialisation de la variable pointeur . ea type doit d´crire le type de l’argument qui est en train d’ˆtre r´f´renc´. e e va arg(pointeur . Chaque fois.. La fonction printf est un autre exemple de fonction avec un nombre variable d’arguments. dernier argument doit ˆtre le dere nier des arguments explicitement nomm´s dans l’en-tˆte de la fonction. 2004 51 . Garreta. une autre technique consiste ` utiliser une valeur  intruse  (ex : le pointeur e a NULL parmi des pointeurs valides).1. . chaque appel suivant donne l’argument suivant le dernier d´j` obtenu.3. Dans l’exemple ci-dessus ce nombre est a e pass´ comme premier argument . e a c H.

un tableau t d´clar´ de taille N poss`de les ´l´ments ee e e e e e ee t0 . tN −1 .. c’est-`-dire une expression dont tous les ´l´ments sont connus au moment de la compilation. e Les exceptions ` cette r`gle. ee 5. e – l’apparition d’un nom du tableau comme argument de sizeof.. t1 .. les deux expressions t sont ´quivalentes.. sont ee – l’occurrence du nom du tableau dans sa propre d´claration .1 5. La d´claration d’un tableau avec un nombre quelconque d’indices a e e suit la syntaxe : type nom [ expression opt ] [ expression opt ] . la d´claration e e e a e double matrice[10][20].2 Initialisation des tableaux &t[0] Une variable de type tableau peut ˆtre initialis´e lors de sa d´claration. la coutume est d’appeler ces ´l´ments les lignes de la matrice. e Tableaux multidimensionnels. Si a et b sont deux tableaux. Ainsi la d´finition pr´c´dente alloue un tableau de 10 ´l´ments. Cela se fait par une expression ayant e e e la syntaxe : { expression .. 2004 . e – lors de la d´claration d’un tableau externe (d´fini dans un autre fichier)..´ 5 OBJETS STRUCTURES 5 5. . table[9]. Autrement dit.1. De a ee cette mani`re le compilateur peut l’´valuer et connaˆ la quantit´ d’espace n´cessaire pour loger le tableau. L’expression optionnelle qui figure entre les crochets doit ˆtre de type entier et constante au sens de la e section 1. si t est de type tableau. Cela permet de dire alors :  en C les ee matrices sont rang´es par lignes . c’est-`-dire les expressions de type tableau qui ne sont pas ´quivalentes ` a e a e a l’adresse du premier ´l´ment du tableau. Exemple : unsigned long table[10]. car il y a alors allocation effective du tableau. . a e l’affectation b = a sera – comprise comme l’affectation d’une adresse ` une autre.3. Elle est facultative e dans le cas des d´clarations qui ne sont pas des d´finitions. e 52 c H. [ expression opt ] qui est un cas particulier de formules plus g´n´rales expliqu´es ` la section 5. Elle e e ıtre e e est obligatoire lorsqu’il s’agit d’une d´finition. nomm´s respectivement table[0]. Garreta. c’est-`-dire : e e a – lors de la d´claration d’un tableau qui est un argument formel d’une fonction . introduit matrice comme ´tant le nom d’un tableau de 10 ´l´ments33 qui. car b n’est pas modifiable.4. est un tableau de e ee a 20 ´l´ments de type double.. expression . e e e ee e table[1]. En g´n´ral lorsque le nom d’un tableau apparaˆ dans une expression e e ıt il y joue le mˆme rˆle qu’une constante de type adresse ayant pour valeur l’adresse du premier ´l´ment du e o ee tableau.4.1. Le premier e ee ´l´ment poss`de toujours l’indice 0. e e De tout cela.1 Objets structur´s e Tableaux Cas g´n´ral e e Dans le cas le plus simple la d´claration d’un tableau se fait par une formule comme : e type-de-base nom [ expression opt ] . chacun ` son tour. Ce sont les seules occasions o` le compilateur veut bien se souvenir qu’un nom de tableau repr´sente aussi un u e espace m´moire poss´dant une certaine taille. Par exemple. et a – rejet´e. e e ´ Semantique du nom d’un tableau. C ne pr´voit que les tableaux ` un seul indice.1. mais les ´l´ments des e a ee tableaux peuvent ` leur tour ˆtre des tableaux. L’expression optionnelle qui figure entre les crochets sp´cifie le nombre d’´l´ments du tableau. Par cons´quent. . il faut retenir en tout cas que le nom d’un tableau n’est pas une lvalue et donc qu’il n’est pas possible d’affecter un tableau ` un autre. expression } 33 Dans le cas d’une matrice. mˆme ayant des types identiques..

En particulier. 30. Garreta. mais e a ce n’est pas une obligation. ainsi que par les fonctions de la librairie standard qui e construisent des chaˆ ınes. e D’autre part. 2004 53 . la r`gle de compl´tion par des z´ros s’applique aussi aux sous-listes. 5. 2. 8. 40.3 Chaˆ ınes de caract`res e Les chaˆ ınes de caract`res sont repr´sent´es comme des tableaux de caract`res. les tableaux sont  tass´s  : chaque e composante occupe le moins de place possible. 6 }. les ´l´ments restants sont remplis de z´ros (cas des variables globales) ee ee e ou bien restent ind´termin´s (cas des variables locales). { 7. 7.1 Tableaux Exemple : int t[5] = { 10. 1 4 7 0 2 5 8 0 3 6 9 0 0 0 0 0 1 5 9 0 2 6 0 0 3 7 0 0 4 8 0 0 t1 t2 Fig. allouent et garnissent les deux tableaux de la figure 5. la a a liste des expressions d’initialisation peut comporter des sous-listes indiqu´es ` leur tour avec des accolades. Le cas ´ch´ant. 5. Un caract`re nul suit le dernier e e e e e caract`re utile de la chaˆ et en indique la fin. 5 – Tableaux initialis´s e Remarque.4.1. Elle est suppos´e v´rifi´e par les fonctions de la librairie standard qui exploitent des e e e chaˆ ınes. chaque composante occupe un (resp. { 4. dans un tableau de char (resp. 3. 5. o e t1 1 2 3 0 4 5 6 0 7 8 9 0 0 0 0 0 t2 1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 0 Fig. une erreur est signal´e. int t2[4][4] = { 1. e e e e e Par exemple. 4. Les expressions indiqu´es doivent ˆtre des expressions constantes au sens de la section 1. deux) octet(s). 8. Par cons´quent. S’il y a plus d’expressions d’initialisation que d’´l´ments e e ee dans le tableau. e a c H. 2.3. 3 }. alloue un tableau ` 5 composantes garni avec les valeurs indiqu´es. S’il y a moins e e d’expressions que le tableau a d’´l´ments. Cette convention est suivie par le compilateur (qui range les e ıne chaˆ ınes constantes en leur ajoutant un caract`re nul). 50 }. Rappelons que ces repr´sentations rectangulaires sont tr`s conventionnelles. Dans la m´moire e e e de l’ordinateur ces deux tableaux sont plutˆt arrang´s comme sur la figure . 6. 6 – Tableaux  ` plat  a Si la taille d’une composante est un diviseur de la taille d’un entier. 30. de short).´ 5 OBJETS STRUCTURES 5. 9 }. Par exemple. avant de passer une chaˆ ` une telle fonction. a e Lorsque le tableau comporte des sous-tableaux. 9 } }. la pr´sence d’un initialisateur dispense d’indiquer la taille du tableau. les d´clarations e int t1[4][4] = { { 1. 20. 40. 50 }. il faut s’assurer qu’elle comporte bien e ıne a un caract`re nul ` la fin. 20. Il est convenu que ce e dernier doit avoir alors pour taille le nombre effectif de valeurs initiales. c’est-`-dire lorsque c’est un tableau ` plusieurs indices. la d´finition e int t[] = { 10.

’t’. Elles se d´clarent sous la syntaxe e e e suivante : struct nomopt ’{’ declaration .1. Elle affecte ` p l’adresse de la zone de la m´moire o` le compilateur a rang´ le texte en question (l’adresse du ’B’ a e u e de "Bonjour. l’expression  struct nom  a pourra ˆtre employ´e comme une sorte de nom de type .. Exemple. Selon le syst`me sous-jacent. return i . ’u’. char message2[] = "Salut". bien que l’expression *p soit une lvalue (ce qui est une propri´t´ syntaxique). Heureusement. ’u’. e e Ici. l’affectation suivante est tout ` fait l´gitime : a e p = "Bonjour. ’t’.1. Chaque champ peut e avoir un type quelconque (y compris struct. prenom[32]. la liste des d´clarations des champs de la structure. Dans ce cas. De ce qui a ´t´ dit pour les tableaux en g´n´ral il d´coule que les expressions suivantes sont e ee e e e correctes : char message1[80] = { ’S’.5. char nom[32]. (la deuxi`me formule d´finit un tableau de taille 6). } Une constante-chaˆ de caract`res apparaissant dans une expression a le mˆme type qu’un tableau de ıne e e caract`res c’est-`-dire.2 Structures et unions 5 ´ OBJETS STRUCTURES Donnons un exemple classique de traitement de chaˆ ınes : la fonction strlen (extraite de la biblioth`que e standard) qui calcule le nombre de caract`res utiles d’une chaˆ : e ıne int strlen(char s[]) { register int i = 0. A ce propos. Par la suite. u e e e e – facultativement. declaration ’}’ rien nom ’. ’a’. constituerait une tentative de modification d’un objet constant. Il faut bien noter cependant que l’expression "Salut" qui apparaˆ dans ces deux exemples n’est pas du tout ıt trait´ par le compilateur comme les autres chaˆ e ınes de caract`res constantes rencontr´es dans les programmes. ee elle ne doit pas ˆtre consid´r´e comme telle car *p repr´sente un objet m´moris´ dans la zone des constantes. e ee e e e et toute occurrence de *p comme op´rande gauche d’une affectation : e *p = expression.. provoquant l’abandon e e a e imm´diat du programme.. si p a ´t´ d´clar´ e a e ee e e char *p. Comment allez-vous?".. bien sˆr) . 54 c H. 5. entre accolades. char message2[] = { ’S’. ’l’. while (s[i++] != ’\0’) . Initialisation. Cette erreur est impossible ` d´celer ` la a e a compilation. soit une erreur sera signal´e ` l’ex´cution. ’a’.2. la liste des variables que l’on d´finit ou d´clare comme poss´dant la structure ici d´finie. pour l’essentiel. 2004 .2."). soit aucune erreur ne sera signal´e mais la validit´ de la suite du traitement sera e e e compromise.2 5. les variables-chaˆ ınes de caract`res peuvent ˆtre initialis´es lors e e e de leur d´claration. nom rien ’ . Comme tous les tableaux.1 Structures et unions Structures Les structures sont des variables compos´es de champs de types diff´rents.’ . ’\0’ }. le nom que l’on souhaite donner ` la structure.’ A la suite du mot-cl´ struct on indique : e – facultativement.. struct fiche { int numero. le type  adresse d’un caract`re . Par exemple. C offre un raccourci : les deux expressions e e pr´c´dentes peuvent s’´crire de mani`re tout ` fait ´quivalente : e e e e a e char message1[80] = "Salut". Garreta.. il ne s’agit que d’un moyen commode pour indiquer une collection de valeurs initiales ` ranger dans un a tableau. ’l’. voir aussi la remarque 2 de la section 6. } a. c. e e – facultativement. ’\0’ }. b.

e et enfin x. b. e e Voici quelques informations sur la disposition des champs des structures.a. on pourra d´clarer de nouvelles e a e variables analogues ` a. b et c. b. lui.b. qui engendrent des trous anonymes entre les champs. prenom[32]. c H. x. . c.). y. Deux cas relativement fr´quents sont les suivants : e e a a – les champs de type char sont ` n’importe quelle adresse.a. x. D’autre part il est facile de e e voir que -sauf dans le cas de structures vraiment tr`s simples. Elles sont sans importance lorsque les structures sont exploit´es par le programme qui les cr´e. ou encore struct { int numero. C garantit que les champs d’une structure sont allou´s dans l’ordre o` ils apparaissent dans la e u d´claration.numero = 1234. type 2 c. b. Contigu¨ e. prenom[32]. e Comme dans beaucoup de langages. beaucoup de compilateurs alloueraient les champs e a dans l’ordre : x. – les structures sont pass´es par valeur lors de l’appel des fonctions . b´n´ficie e e a e e de la s´mantique des valeurs. dans le sens des adresses croissantes : x. Ainsi. chacune constitu´e des trois champs numero. Nous aurions pu aussi bien faire les e d´clarations : e struct fiche { int numero.f.c. y. La syntaxe est la mˆme que pour un tableau. x. struct fiche a. mais deviennent indispensables lorsque les structures e e sont partag´es entre sous-programmes ´crits dans divers langages ou lorsqu’elles servent ` d´crire les articles e e a e d’un fichier existant en dehors du programme. est assujetti ` la s´mantique des valeurs. x. type 3 f.´ 5 OBJETS STRUCTURES 5. char nom[32]. e e La possibilit´ pour une fonction de rendre une structure comme r´sultat est officielle dans le C ANSI . a Initialisation. Exemple : e a. en mˆme temps elle donne ` cette structure le nom fiche. avec la d´claration correspondante en Pascal (facile ` imaginer).2 Structures et unions Cette d´claration introduit trois variables a. Position. x. sur e e ce point les compilateurs plus anciens pr´sentent des diff´rences de comportement. tous les autres champs devant commencer ` une adresse paire . e – le r´sultat d’une fonction peut ˆtre une structure. Une variable de type structure peut ˆtre initialis´e au moment de sa d´claration. Les champs des structures subissent en g´n´ral des contraintes d’alignement d´pendant du ıt´ e e e syst`me sous-jacent. e a a e 35 Alors que. x. Plus loin. char nom[32]. }. du moins e e e dans le cas d’une variable globale.f35 .d. y. x. les long ` une adresse multiple de quatre. comme une expression d’un type primitif.d. Garreta. c. Les structures supportent les manipulations  globales 34 : – on peut affecter une expression d’un type structure ` une variable de type structure (pourvu que ces deux a types soient identiques) . avec la d´finition e e struct modele { type 1 a. Il s’agit souvent des mˆmes r`gles e e e que celles qui p`sent sur les variables simples. nom et e e prenom . b et c en ´crivant simplement a e struct fiche x.les transmettre comme r´sultats des fonctions est e e en g´n´ral peu efficace. etc. } a. } on trouve. Exemple : e 34 L’expression correcte de cette propri´t´ consisterait ` dire qu’une structure. (remarquez qu’il faut ´crire  struct fiche  et non pas  fiche ). e. d.e. x. par opposition ` un tableaux qui. on acc`de aux champs d’une variable de type structure au moyen de e l’op´rateur  . 2004 55 . x. mais cette derni`re forme nous aurait empˆch´ par la suite de d´clarer aussi facilement d’autres variables de e e e e mˆme type.b. – les champs d’un type simple doivent commencer ` une adresse multiple de leur taille (les short ` une a a adresse paire. x.c et enfin x.

une valeur vraisemblable et utile. Par e exemple. D’une part. La taille d’une union est celle du plus volumineux de ses e e champs. cet e op´rateur ne supporte pas les structures. Par exemple. sous le nouveau type. donc. les op´rateurs binaires de bits (&. lorsqu’on fait r´f´rence ` un champ d’une union. prenom[32]. le programme suivant teste si les deux bits hauts d’une e e certaine variable m.3 Champs de bits Le langage C permet aussi d’utiliser des structures et des unions dont les champs. 2004 . } mix. struct adresse_virt adrv.2. et qu’ensuite on ex´cute les affectations : e x. e e avec le mot union ` la place du mot struct. tandis qu’un champ d’une union peut en ˆtre une. Donner plus de valeurs qu’il n’y a de champs constitue une erreur.tp == 0) etc. "Pierre" }. i = x.5. int entier. e e D’autre part. (l’´criture binaire du chiffre hexa C est 1100). Ainsi. Bien sˆr. si x est une variable r´elle (float).3) : a e a union mot_machine { unsigned long mot. tous ou seulement certains. A l’oppos´. e e u de la valeur de x. ´ventuellement assez coˆteuse. Ce n’est pas le cas. La syntaxe de la d´claration et de l’utilisation d’une union est exactement la mˆme que pour les structures. } x. |. Exemple (la struct adresse virt est d´finie ` la section 5.2.2 Unions Tandis que les champs des structures se suivent sans se chevaucher. char *ptr.mot = m. ` des moments e a diff´rents. tantˆt e o o e o comme la structure bizarre d´finie ci-apr`s. la donn´e ne subit aucune e ee a e transformation. C e convertit la donn´e. ces e u e deux mani`res de faire peuvent ˆtre ´quivalentes sur une machine et ne pas l’ˆtre sur une autre. L’instruction pr´c´dente pourrait aussi s’´crire (sur une machine 32 bits. Par exemple. sont nuls : mix. une variable de type union peut contenir. des objets de types et de tailles diff´rents. alors les champs restants sont initialis´s par des z´ros. les champs d’une union commencent tous au mˆme endroit et. Les unions permettent de consid´rer un mˆme objet comme poss´dant plusieurs types .reel = 12. cet entier n’a aucune raison d’ˆtre voisin de 12. sont faits d’un nombre quelconque de bits. Garreta. Remarque 1. if (mix.entier.2 Structures et unions 5 ´ OBJETS STRUCTURES struct fiche { int numero. elles e e e semblent donc faire double emploi avec l’op´rateur de conversion de type. char nom[32]. "DURAND". ^) s’av`rent ´galement tr`s e e e e e utiles. de type unsigned long. e e 5. se superposent. } u = { 1234. e 5. si on indique moins de valeurs que la structure ne comporte de champs. Comme pour les tableaux. Cela implique une transformation.2. mˆme si elle a ´t´ affect´e en faisant r´f´rence ` un autre champ. si l’on d´clare e ee e ee a e union { float reel.adrv. afin que l’expression ait. tantˆt comme l’adresse d’un caract`re. o` int est synonyme de long) : e e e u if (m & 0xC0000000 == 0) etc. l’entier i obtenu sera celui dont la repr´sentation binaire co¨ e ıncide avec la repr´sentation binaire du nombre e flottant 12. Dans ce genre de probl`mes. De tels champs doivent ˆtre ainsi d´clar´s : e e e unsignedopt intopt identificateur rien 56 ’:’ constante-entiere c H. lorsque l’on change le type d’un objet au moyen de l’op´rateur de changement de type. La variable mix pourra ˆtre vue tantˆt comme un entier long.5. e e e e Remarque 2.5 . les programmes de cette sorte n’´tant pas portables. l’expression  (int) x  convertit x en l’entier dont la valeur est e la plus voisine de la valeur qu’avait le r´el x.

b. mardi. a.). cette e e syntaxe est la suivante : enum nomopt ’{’ id-valeur ’. le nombre indiqu´ de bits est quand mˆme r´serv´ : on suppose e e e e qu’il s’agit de bits de  rembourrage  entre deux champs nomm´s. d’autres de droite ` gauche.. vendredi }. Dans tous les cas. le type enum jourouvrable est connu . etc. e e 29 24 8 0 tp numpag Fig. A la suite d’une telle d´claration. nom rien ’ . unsigned numpagv : 16. vendredi } x. mardi. mercredi.’ . }. comme ceux qui communiquent avec leur machine-hˆte ` un niveau tr`s bas (noyau d’un o a e syst`me d’exploitation. c. mercredi.. 7 – Champs de bits depl Attention.’ o` chaque id-valeur est ` son tour de la forme u a identificateur Exemple : enum jourouvrable { lundi. le champ est log´ au d´but du mot suivant. b. Si elles figurent ici c’est uniquement parce que la e e e syntaxe de leur d´claration poss`de des points communs avec celle des structures. a. : 5. vendredi } x. ou encore enum { lundi. y. e e certaines machines rangent les champs de gauche ` droite. On aurait aussi bien pu introduire toutes ces variables par les d´clarations e enum jourouvrable { lundi. c. jeudi. e e Exemple : la structure struct adresse_virt { unsigned depl : 9. e u a c H. id-valeur ’}’ rien nom ’. gestion des p´riph´riques.’ . . mercredi. d´coupe un mot de 32 bits en en quatre champs. y. ce qui est interdit). etc. jeudi. Dans les cas simples. Le type doit obligatoirement ˆtre entier. comme indiqu´36 sur la figure 7. enum jourouvrable x. 36 Sur ’=’ expression-constante rien un syst`me o` un mot est fait de 32 bits (sinon un champ serait ` cheval sur deux mots. e e Lorsque le type et le nom du champ sont absents.. e e e e a mardi (´gale ` 1). e Chacun de ces champs est log´ imm´diatement apr`s le champ pr´c´dent. la portabilit´ a a e des structures contenant des champs de bits n’est pas assur´e . b. 2004 57 .. c.´ 5 OBJETS STRUCTURES 5. e e Ce type est form´ d’une famille finie de symboles qui repr´sentent des constantes enti`res : lundi (´gale ` 0). jeudi. les variables x et y le poss`dent.3 Enum´rations e Chaque constante pr´cise le nombre de bits qu’occupe le champ. Garreta. dans ce cas. y.. cela limite leur usage ` une certaine cat´gorie e a e de programmes. Dans ce programme on pourra ´crire : e a e enum jourouvrable a..3 Enum´rations e Les ´num´rations ne constituent pas un type structur´. mardi. Ainsi. Ces programmes sont rarement destin´s ` ˆtre port´s e e e e ae e d’un syst`me ` un autre. sauf si cela le met ` cheval sur e e e e e a deux mots (de type int) . les champs de bits sont tr`s d´pendants de l’implantation. e a 5. Par exemple. unsigned tp : 2.

Ils sont utilis´s e e e – par l’op´rateur de changement de type lorsque le type de destination n’est pas d´fini par un identificateur : e e  (char *) expression  . les types ´num´r´s ne sont qu’une e ee deuxi`me mani`re de donner des noms ` des nombres entiers (la premi`re mani`re ´tant l’emploi de la directive e e a e e e #define.3) : e u e e e apr`s la d´claration e e typedef enum jourouvrable { lundi. des  fonctions rendant des adresses de tableaux . decembre }. char *) . qui apparaissent dans trois sortes d’´nonc´s : e e – les d´finitions de variables (globales et locales). Autrement dit. u 58 c H. Si elles sont faciles ` lire et ` traiter par le compilateur. les d´clarations des param`tres formels des fonctions. (fevrier vaut 2. etc. – dans les prototypes des fonctions :  strcpy(char *. octobre vaut 10. la valeur de chacune de ces constantes est ´gale au rang de celle-ci dans l’´num´ration (lundi e e e e vaut 0. section 5.4. etc. – la formule  adresse d’un T  (ou  pointeur sur un T ). Tous les objets ´taient soit simples. o` T est la description naturelle d’un type .5.4 D´clarateurs complexes e 5 ´ OBJETS STRUCTURES mais cette derni`re forme nous aurait empˆch´ par la suite de d´clarer aussi facilement d’autres variables du e e e e mˆme type. mars vaut 3. e e – les d´clarations de types (typedef) e La mˆme question r´apparaˆ dans des constructions qui d´crivent des types sans les appliquer ` des identie e ıt e a ficateurs. e Ces formules ont mauvaise r´putation.4 D´clarateurs complexes e Jusqu’ici nous avons utilis´ des formes simplifi´es des d´clarations. Par d´faut. octobre. etc. mardi vaut 1. des fonctions ou des adresses d’objets simples. voir section 8. les expressions  enum jourouvrable  et  JOUROUVRABLE  sont ´quivalentes. d’une struct ou union ou le nom d’un type baptis´ par typedef . – comme argument de sizeof (rare) :  sizeof(int [100])  .  5. septembre = 9. d’un type enum. mars. ` partir d’une sorte de description  ` e e e a a l’endroit . avril. – l’identificateur qu’il s’agit de d´clarer. les e e e d´clarations des variables externes et les d´clarations des champs des structures et des unions . vendredi } JOUROUVRABLE.). o` T est la description naturelle d’un type. [] et * qui repr´sentent des proc´d´s r´cursifs de construction de types complexes . e a e Pour cette raison nous allons donner un proc´d´ m´canique qui. u – la formule  fonction rendant un T . novembre. e e e e e e e – un certain nombre d’occurrences des op´rateurs (). On peut alt´rer cette s´quence en associant explicitement des valeurs ` certains e e a ´l´ments : ee enum mois_en_r { janvier = 1. elles s’av`rent e a a e difficiles ` composer et ` lire par le programmeur. Appelons description naturelle d’un type une a e e expression de l’un des types suivants : – la description correcte en C d’un type de base.1. Ainsi. Il nous reste ` voir comment d´clarer des  tableaux a e de pointeurs  . mardi. La question concerne au premier chef les descripteurs de types. struct ou union ou bien un type auquel on a donn´ un nom par une d´claration typedef . les ´num´rations peuvent se combiner avec la d´claration typedef (cf. e Les nombres entiers et les valeurs de tous les types ´num´r´s sont totalement compatibles entre eux.4. Bien sˆr. soit e e e e des tableaux.2). facile ` concevoir. Par e ee cons´quent. des  tableaux d’adresses de fonctions . jeudi. enum. Garreta. 2004 . e u – la formule  tableau de T . fabrique la d´claration C souhait´e.1 Cas des d´clarations e Un d´clarateur complexe se compose de trois sortes d’ingr´dients : e e e – un type  terminal  qui est un type de base (donc num´rique).) 5. nous devons expliquer la syntaxe des formules qui permettent de d´crire des types de complexit´ e e quelconque. mercredi. l’affectation d’un entier ` une variable d’un type ´num´r´ ou l’affectation r´ciproque ne provoquent e a e ee e en principe aucun avertissement de la part du compilateur . en C. o` T est la description naturelle d’un type . Appelons cela des types d´sincarn´s. car les symboles qui repr´sentent les proc´d´s r´cursifs de a a e e e e construction sont peu expressifs et se retrouvent plac´s ` l’envers de ce qui aurait sembl´ naturel. fevrier. e e – les d´finitions des fonctions et les d´clarations des fonctions externes .

Quand on ´crit des programmes relevant du calcul num´rique on a souvent besoin de variables e e de type  adresse d’une fonction R → R . nous ´crirons successivement : e e tableau de pointeur sur int pointeur sur int pointeur sur int int tabPtr (tabPtr)[] tabPtr[] *tabPtr[] La partie gauche de la ligne ci-dessus se r´duisant ` un type simple. nous pouvons ´noncer la  recette de cuisine  du tableau 3. cherchons ` ´crire maintenant la d´claration de ptrTab. e Exemple 3. 3 – Construction d’un d´clarateur complexe e Exemple 1. struct ou union. Voici comment ´crire un tel type e 37 La notion de pointeur sur un tableau est assez acad´mique. Nos pouvons ´crire successivement : a e e tableau de 10 tableau de 20 double tableau de 20 double tableau de 20 double double double matrice (matrice)[10] matrice[10] (matrice[10])[20] matrice[10][20] Exemple 4. les d´clarations de tabPtr et ptrTab peuvent ˆtre ´crites ensemble : e e e e int *tabPtr[]. car la description naturelle e e du type d’une fonction inclut en g´n´ral la sp´cification des types des arguments (le prototype de la fonction). . e Tab. (*ptrTab)[]. Garreta. alors les parenth`ses e e autour de exp d peuvent ˆtre omises. e e e et la description naturelle d’un type tableau comporte l’indication du nombre de composantes de celui-ci. e e Exemple 2. e Pour obtenir la d´claration d’une variable ou d’une fonction : e 1. a – ` droite. car elles encadrent une expression qui commence par * e (dit autrement. a transformez les deux expressions de la mani`re suivante : e (a) fonction rendant un exp g exp d → exp g (exp d )() (b) tableau de exp g exp d → exp g (exp d )[] (c) pointeur sur exp g exp d → exp g *exp d De plus. e e e a Les d´clarations pour lesquelles le membre gauche est le mˆme (` la fin des transformations) peuvent ˆtre e e a e regroup´es. si exp d n’est pas de la forme *exp. a e e e Appliquant les r`gles du tableau 3.´ 5 OBJETS STRUCTURES 5. cette ligne constitue la d´claration C e a e cherch´e. comme il faudra l’´crire dans un programme :  int *tabPtr[] . car [] a une priorit´ sup´rieure ` *). enum. En pratique on n’en a presque jamais besoin. Tant que l’expression ` gauche n’est pas un type de base. dans les r`gles (a) et (b). En r´alit´ ces indications s’ajoutent aux r`gles donn´es ici. c H. elles sont n´cessaires. puisqu’une variable de type tableau repr´sente d´j` l’adresse de e e ea celui-ci (dans la notion d’ adresse d’un tableau  il y a une double indirection). a a e 2.4 D´clarateurs complexes e Dans ces conditions. 2004 59 . un a e e ae e pointeur sur un tableau37 d’entiers : pointeur sur un tableau de int tableau de int int ptrTab *ptrTab (*ptrTab)[] Les parenth`ses ci-dessus ne sont pas facultatives. Ainsi. Ecrivez : – ` gauche. des informations annexes sont ´crites ` l’int´rieur des crochets des tableaux et e e a e des parenth`ses des fonctions. Soit ` d´clarer une matrice de 10 lignes et 20 colonnes. presque sans modifier le processus que ces r`gles e e e e e ´tablissent : tout simplement. la description naturelle du type. l’identificateur ` d´clarer (un seul). En toute rigueur les r`gles a et b de ce tableau devraient ˆtre plus complexes. Pour la comparer ` la pr´c´dente. Soit ` d´clarer tabPtr comme un tableau (de dimension ind´termin´e) de pointeurs d’entiers.

5. les expressions finales obtenues sont assez lourdes ` dig´rer pour un humain. c’est-`-dire d’une fonction rendant void. c’est-`-dire sans sp´cifier les arguments de la fonction signal (cette d´claration est fournie a e e dans le fichier <signal. nous fera reconnaˆ imm´diatement l’expression (*signal(2. Attaquons-nous ` signal : a fonction (avec arguments int sig et int (*fon)(int)) rendant un pointeur sur fonction (avec argument int) rendant void signal pointeur sur fonction (avec argument int) rendant void (signal)(int sig. signal rend un pointeur vers une telle fonction. ` la ee e e a vue de l’expression float matrice[10][20]. int (*fon)(int)) pointeur sur fonction (avec argument int) rendant void signal(int sig. 2004 . Les cas les plus utiles sont les suivants : 1.4 D´clarateurs complexes e 5 ´ OBJETS STRUCTURES adresse de fonction (avec argument double) rendant double fonction (avec argument double) rendant double double La d´claration cherch´e est donc  double (*adrFon)(double) . Plutˆt que de ıtre e o rendre encore plus lourdes des explications qui le sont d´j` beaucoup.4.5. La fonction signal renvoie e l’adresse d’une  proc´dure . int (*fon)(int)))(int) . func))().h>) : fonction pointeur pointeur fonction void rendant un pointeur sur fonction rendant void sur fonction rendant un void sur fonction rendant un void rendant un void signal (signal)() signal() *signal() (*signal())() En syntaxe ANSI il faut en plus donner les types des arguments des fonctions. e e 2. Ainsi. int (*fon)(int)) fonction (avec argument int s) rendant void *signal(int sig. Comme annonc´. *nom. Voici comment il faudrait la d´clarer en e a e syntaxe originale. Garreta. l’´nonc´ e e e e void (*signal(sig. on comprend sans effort suppl´mentaire qu’un ´l´ment de la matrice s’´crira matrice[i][j] et que cette formule e ee e repr´sentera un objet de type float. On nous dit que les arguments de la fonction signal sont un int et un pointeur sur une fonction prenant un int et rendant un void. e e adrFon *adrFon (*adrFon)(double) Exemple 5. Cet exemple abominable est extrait de la biblioth`que UNIX. contentons-nous de montrer des exemples ea significatifs. la d´claration cherch´e est e e void (*signal(int sig. int (*fon)(int)) void (*signal(int sig.2 Pointeurs et tableaux constants et volatils Les qualifieurs const et volatile peuvent aussi apparaˆ dans les d´clarateurs complexes. A leur d´charge e a e e on peut tout de mˆme dire qu’une fois ´crites elles se r´v`lent bien utiles puisqu’elles font apparaˆ les types e e e e ıtre de leurs ´l´ments terminaux comme ils seront employ´s dans la partie ex´cutable des programmes. De mˆme. ne doit e e e pas ˆtre modifi´e). La formule : const type *nom d´clare nom comme un pointeur vers un objet constant ayant le type indiqu´ (la variable point´e. int (*fon)(int)))(int) Finalement. Nous savons d´j` ´crire le type  pointeur sur une fonction ea e prenant un int et rendant un void  (ce n’est pas tr`s diff´rent de l’exemple pr´c´dent) : e e e e void (*fon)(int). La formule type *const nom 60 c H.f))(n) comme un appel correct de la  proc´dure  ıtre e e rendue par l’appel de signal. En outre.

Voici un autre exemple : typedef struct noeud { int info. Garreta. etc. Cette expression d´clare l’identificateur NOEUD comme le nom du type  la structure noeud  . Syntaxe : e u typedef d´claration e Pour d´clarer un nom de type.4 D´clarateurs complexes e d´clare nom comme un pointeur constant vers un objet ayant le type indiqu´ (c’est la variable nom qui ne doit e e pas ˆtre modifi´e). c. a c H.´ 5 OBJETS STRUCTURES 5. *pcec ou tec[i] est ill´gale. Par e e e e tradition. la seule vraie nouveaut´ introduite ici est la notion de pointeur constant et la syntaxe de e e e sa d´claration  type-point´ *const pointeur . volatils ou non. te[100]. Ainsi. e En r´sum´. On a donc la possibilit´ de d´clarer des tableaux d’objets volatils.5. *pec. Elle permet de donner un nom e a ` un type dans le but d’all´ger par la suite les expressions o` il figure. b. d´clarent successivement : e – e : un entier – pe : un pointeur vers un entier – pce : un pointeur constant vers un entier (qui doit ˆtre obligatoirement initialis´.4. ` la suite de e e a 38 Notez qu’il n’y a pas besoin d’une notation sp´ciale pour d´clarer un tableau constant d’objets (variables) puisque la signification e e ordinaire des tableaux est exactement celle-l`. MATRICE mat. } NOEUD. struct noeud *fils_gauche. que par NOEUD a. *fils_droit. Signalons enfin que toute cette explication reste valable en e e rempla¸ant le mot const et la notion d’objet constant par le mot volatile et le concept d’ objet volatil (cf. tec[100]. b. e e 3. e a e e L’ensemble des r`gles de syntaxe qui s’appliquent aux d´clarations de variables s’appliquent ´galement ici.. c. pcec. . Remarquez l’utilisation diff´rente d’un nom de structure (pr´c´d´ du mot struct) et d’un nom de type. dans laquelle le rˆle du nom de la variable est jou´ par le nom du type qu’on veut e o e d´finir. on peut introduire plusieurs noms de types dans un seul ´nonc´ typedef.. les noms des types d´finis ` l’aide de la d´claration typedef sont ´crits en majuscules. ec. la formule const type nom[expr opt ] d´clare nom comme un tableau d’objets constants38 (nom[i] ne doit pas ˆtre modifi´). c de ce type pourront par la suite ˆtre d´clar´es aussi bien par l’expression e e e struct noeud a. e e e Ainsi par exemple les d´clarations e int e. Exemple : les d´clarations e e typedef float MATRICE[10][20]. b.3 La d´claration typedef e Cette construction est l’homologue de la d´claration type du langage Pascal. Plus simplement. *pec. on fait suivre le mot r´serv´ typedef d’une expression identique ` une e e e a d´claration de variable. *const pcec = &ec. e e e Par exemple.7). *pe. c section 1. *const pce = &e. sont ´quivalentes ` e a float mat[10][20]. des pointeurs volatils vers des e e objets. puisqu’il est constant) e e – te : un tableau de 100 entiers – ec : un entier constant – pec : un pointeur vers un entier constant – pcec : un pointeur constant vers un entier constant – tec : un tableau de 100 entiers constants Toute affectation avec pour membre gauche pce. 2004 61 . 5. const int ec = 0. Des variables e a.

dont elle renvoie l’adresse. size_t. Par exemple : e e typedef float LIGNE[20]. e e a Le principe est alors le suivant : construisez la d´claration correcte d’un nom quelconque X.  sizeof(double [N]) .. est une autre mani`re (nettement plus facile ` lire. est une autre mani`re d’introduire le type MATRICE d´j` vu.  double e e ee e e *ptr  en effa¸ant le nom d´clar´. qui impl´mente une m´thode de tri rapide d’un tableau (algorithme Quicksort) programm´ en e e e toute g´n´ralit´. 3. double *p. *ARBRE. des indications et mises en garde ` propos de l’op´rateur de changement de type) : a e p = malloc(N * sizeof(double)). cette fonction e e e est d´clar´e comme rendant l’adresse d’un char. c e e Rappelons qu’en C ANSI l’expression ci-dessus s’´crit plus simplement (voyez ` la section 2. Par exemple. *fils_droit. int (*)(const void*.5. Essayons par exemple d’´crire autrement l’affectation pr´c´dente. e e e Ce type ´tant compatible avec tous les autres types pointeur. typedef LIGNE MATRICE[10].2. le nom d’un type qui a fait l’objet d’une d´claration e e typedef peut ˆtre utilis´ comme type de base. e a e e e 5. Dans les prototypes des fonctions. Comme argument de l’op´rateur sizeof. e De telles expressions sont utiles dans trois circonstances : 1. ee e La fonction qsort est donc ainsi d´clar´e dans le fichier stdlib. const void*)). Exemple : la fonction malloc alloue un bloc de e m´moire de taille donn´e. 2.4 D´clarateurs complexes e 5 ´ OBJETS STRUCTURES typedef struct noeud { int info.h : e e void qsort(const void*. nul ou positif exprimant la comparaison. puis effacez X. Les arguments de cette fonction sont le tableau ` trier. Utilisation : p = (double *) malloc(N * sizeof(double)). en C ANSI.h) la fonction malloc est d´clar´e comme rendant une valeur de type  void * .4 Cas des types d´sincarn´s e e Tout cela devient encore plus ´sot´rique lorsqu’on doit construire un type sans l’appliquer ` un identificateur. Supposons avoir besoin d’espace pour ranger un tableau de N r´els en double pr´cision. e e e 40 Le type size t (d´fini dans <stddef. est franchement ´sot´rique. Dans les biblioth`ques du C original39 . ptr. et l’utilisateur doit convertir le r´sultat de malloc dans le e e e type qui l’int´resse. Dans la construction des d´clarateurs complexes. 62 c H. il reste  double [N] . size_t. /* D´CONSEILL´ EN C ANSI */ E E Le type d´sincarn´  double *  apparaissant ci-dessus a ´t´ d´duit d’une d´claration de pointeur.12. Pour construire un op´rateur de changement de type. PROCEDURE *signal(int sig. e e e e D´clarons un tableau T de N doubles : e double T[N] Supprimons T. Garreta. la biblioth`que standard C comporte la e fonction qsort. le probl`me expos´ ici ne se pose pas.4. struct noeud *fils_gauche. les identificateurs NOEUD et ARBRE sont des synonymes pour  struct noeud  et  struct noeud * . 39 Dans la biblioth`que ANSI (fichier stdlib.. PROCEDURE *func). Par cons´quent. . e e e D´clarations40 : e char *malloc(size_t). } NOEUD. l’appel de la fonction malloc montr´ ci-dessus peut e e s’´crire aussi41 : e p = (double *) malloc(sizeof(double [N])). La notation  N * sizeof(double)  est bien plus e e e raisonnable. De mˆme e ea e typedef void PROCEDURE(int). le nombre de ses ´l´ments. 2004 . n’est-ce pas ?) de d´clarer la fonction signal vue pr´c´demment. la taille de e e e a ee chacun et une fonction qui repr´sente la relation d’ordre utilis´e (le  crit`re de tri ) : elle re¸oit deux adresses e e e c d’´l´ments du tableau et rend un nombre n´gatif. notamment e a a ` la page 26.h>) repr´sente un entier sans signe e e 41 L’exemple montr´ ici.

size_t nombre.´ 5 OBJETS STRUCTURES 5. les noms des arguments sont une aide pour le programmeur. Garreta. size_t taille. const void *adr2)). int (*compare)(const void *adr1. c H. Exemple : void qsort(const void *tableau. lorsqu’ils sont bien choisis. Ce n’est pas requis par la syntaxe mais. 2004 63 .4 D´clarateurs complexes e Note.

car une adresse e n’est en fin de compte rien d’autre qu’un nombre entier (l’indice d’un ´l´ment du tableau qu’est la m´moire de ee e l’ordinateur). Garreta. En plus de cet aspect. en incorporant au langage des techniques d’adressage indirect e ee couramment utilis´es en assembleur. Prendre l’adresse d’une variable existant par ailleurs. en C les pointeurs sont le moyen d’am´liorer e e l’acc`s aux ´l´ments des tableaux ordinaires. Mais ils y sont pr´sent´s comme des e e e e e entit´s fort myst´rieuses. Initialisation : p = &x. e e Un pointeur n’est pas un concept unique. la variable e a e point´e. Le langage Pascal a introduit les pointeurs e e parmi les types de donn´es disponibles dans des langages g´n´ralistes. accessible ` partir du contenu de p. l’expression char *p. Apr`s cette instruction. e a Dans tous les langages o` ils existent. e e 2. auquel on pourra faire r´f´rence par l’expression e ee *p Initialisation des pointeurs. on fait r´f´rence ` ce dernier par a ee a l’expression : *variable Par exemple. *p en d´signe le contenu.6 POINTEURS 6 6. 2004 . Par la suite.1. l’adresse de la variable x. Dans le cas le plus e e a e e e simple. d´claration : e type *p.1 D´claration et initialisation des pointeurs e Nous avons ´tudi´ ` la section pr´c´dente la syntaxe des d´clarations des pointeurs. e a a e e Comme dans l’exemple pr´c´dent. dont l’utilisateur n’est cens´ connaˆ ni la structure ni le comportement. elle contiendra e ee e l’adresse d’un caract`re. de telles op´rations ´tant e e e e obligatoirement confin´es dans les programmes ´crits en assembleur. Provoquer l’allocation d’un nouvel espace. e Les premiers langages ´volu´s ne permettaient pas les manipulations de pointeurs. Il existe trois mani`res e sˆres de donner une telle valeur ` un pointeur : u a 1. d´finit la variable p comme  pointeur vers un char . Un pointeur devrait donc supporter toutes les op´rations qu’on peut faire subir ` un nombre et e a qui gardent un sens lorsque ce nombre exprime un rang. La diff´rence est qu’ici elle n’a pas de nom propre : e e e e 64 c H. une variable tout ` fait analogue ` la variable x de l’exemple pr´c´dent existe. *p. Lorsqu’elle aura ´t´ correctement initialis´e. e 6. le principal int´rˆt des pointeurs r´side dans la possibilit´ de r´aliser u ee e e e des structures de donn´es r´cursives (listes et arbres) : des structures dans lesquelles un champ est virtuellement e e aussi complexe que la structure elle-mˆme. Ce qui est tout ` fait sp´cifique d’un pointeur p est la possibilit´ de mentionner la variable dont la valeur a e e de p est l’adresse. c’est-`-dire allouer dynamiquement une variable anonyme. cette syntaxe est type *variable Si par la suite on affecte ` une telle variable l’adresse d’un certain objet. Exemple. Cela n’en e e e ıtre facilite pas le maniement aux programmeurs d´butants.1 Pointeurs G´n´ralit´s e e e Un pointeur est une variable dont la valeur est l’adresse d’une cellule de la m´moire. Initialisation : p = malloc(sizeof(type)). Un pointeur correctement initialis´ est toujours le renvoi ` une deuxi`me variable. a Exemple. Un pointeur contient en principe une adresse valide. p contient maintenant une adresse valide. les expressions x et *p d´signeront le mˆme objet (le contenu de x). d´claration : e type x. sans ressemblance avec les autres types de donn´es. Beaucoup des difficult´s ` comprendre les pointeurs e e a proviennent plus du secret qui les entoure que de leur r´elle difficult´ d’emploi.

peut-ˆtre sans s’en rendre compte. en C ANSI. etc. Note. A propos de la taille des pointeurs et celle des entiers. e l’utilisation de variables enti`res pour conserver ou transmettre des pointeurs paraissant bien marginale.2. e o avec les d´clarations e type t[10]. de donner une valeur ` un pointeur : lui affecter e e a une constante ou une expression num´rique. e e a ` charge pour l’utilisateur de la convertir en l’adresse sp´cifique qui lui convient.). il postule que cette fonction renvoie un int et ins`re e e a ` cet endroit le code qui convertit un entier en un pointeur. Le compilateur rencontrant malloc pour la premi`re fois. ) Certaines fonctions de la biblioth`que. comme on le verra bientˆt. Obtenir une valeur par des calculs l´gitimes sur des pointeurs. Garreta. la variable ae ee ee e point´e deviendrait inaccessible et serait d´finitivement perdue. les deux octets e e u hauts du r´sultat de malloc seront purement et simplement remplac´s par z´ro. c’est-`-dire &t[3]. rendent comme r´sultat une  adresse quelconque . e e e e e e e c H. 2004 65 . affecte ` q l’adresse du quatri`me ´l´ment du tableau t.h>) on ´crive l’affectation e p = (struct machin *) malloc(sizeof(struct machin)). Cette question peut sembler tr`s secondaire. Par exemple. En effet. Une expression comme l’affectation ci-dessus soul`ve un autre probl`me : existe-t-il une relation entre la taille d’un nombre entier (types int et e e unsigned int) et celle d’un pointeur ? Le langage ne prescrit rien ` ce sujet .1 G´n´ralit´s e e e si la valeur de p venait ` ˆtre alt´r´e (sans avoir ´t´ auparavant sauvegard´e dans un autre pointeur). l’expression q = p + 3. indispensables dans certaines fonctions de bas niveau (contrˆle d’organes mat´riels.h> contient la d´claration suivante e e de la fonction malloc : void *malloc(size_t size). Par exemple. en tout cas non portable. Or e c’est ce qu’on fait. A la section 2. Appel : p = malloc(sizeof(struct machin)).1.12 (page 26) nous expliquons pourquoi l’emploi d’un op´rateur de changement de e type en association avec malloc est dangereux et d´conseill´. les long aux adresses multiples de quatre.). Par exemple. supposons que sans avoir d´clar´ la fonction malloc e e e e e (ni inclus le fichier <stdlib. sur un syst`me o` les entiers occupent deux octets et les pointeurs quatre. *q. etc. il est donc parfois erron´ et toujours a e dangereux de supposer qu’un entier puisse contenir un pointeur. la tradition s’´tait cr´´e de prendre le type char * pour  pointeur e a e ee vers n’importe quoi . e e 3. De telles expressions. le fichier standard <stdlib.2 Les pointeurs g´n´riques et le pointeur NULL e e (ou tout simplement p = t. o e rendent non portables les programmes o` elles figurent. La question est d’autant plus e e e vicieuse que le programme ci-dessus se comportera correctement sur tous les syst`mes dans lesquels les entiers e et les pointeurs ont la mˆme taille. e e 42 Avec une protection suppl´mentaire : le type void ´tant incompatible avec tous les autres types. Elles doivent donc ˆtre ´vit´es chaque fois que cela est u e e e possible. La question est : comment e d´clarer de tels pointeurs peu typ´s ou pas typ´s du tout ? e e e Le C original n’ayant rien pr´vu ` ce sujet. Notamment. l’objet point´ par une variable e e e d´clar´e de type void * ne pourra pas ˆtre utilis´ tant que la variable n’aura pas ´t´ convertie en un type pointeur pr´cis. Cette conversion peut fournir un r´sultat tout ` fait e a erron´. sur beaucoup de syst`mes les adresses des objets de taille non unitaire supportent e des contraintes d’alignement (les short aux adresse paires.6 POINTEURS 6. dans le style de : e p = (struct machin *) 0x2A3E. alors que les adresses des caract`res sont les seules ` ne subir aucune restriction. *p. comme malloc. lorsqu’on appelle une fonction qui renvoie un pointeur e sans avoir pr´alablement d´clar´ la fonction. e 6. e a Le C ANSI introduit un type sp´cifique pour repr´senter de telles adresses g´n´riques : le type void *. Ce e e e e type est compatible avec tout autre type pointeur : n’importe quel pointeur peut ˆtre affect´ ` une variable de e ea type void * et r´ciproquement42 . suivies de l’initialisation p = &t[0]. a e ee a Il existe aussi une mani`re risqu´e.

1 Les pointeurs et les tableaux Arithm´tique des adresses. etc. si exp 1 et exp 2 sont les adresses respectives de deux objets O1 et O2 de mˆme type et e e contigus (O2 apr`s O1 ). e De mani`re analogue. a – la soustraction de deux adresses (de types rigoureusement identiques). il semble naturel de donner ` exp 2 −exp 1 la valeur 1. Certaines des op´rations arithm´tiques d´finies sur les nombres gardent un sens lorsque e e e l’un des op´randes ou les deux sont des adresses : e – l’addition et la soustraction d’un nombre entier ` une adresse . la structure vide. nous avons : e exp 1 + n ≡ (T *) ((unsigned long) exp 1 + n×sizeof(T )) et exp2 − exp1 ≡ (unsigned long)exp2 − (unsigned long)exp1 sizeof(T) L’indexation. Pour e ın´ r´aliser ces structures il est n´cessaire de poss´der une valeur de pointeur. ´ventuellement fictif. e e e repr´sentant la fin de liste. On e ee e peut alors interpr´ter p + i comme l’adresse de t[i0 + i] et q − p comme l’´cart entre les indices correspondant e e a ` ces deux ´l´ments. On peut e e e aussi dire cela de la mani`re suivante : exp 1 et exp 2 ´tant deux expressions de type  adresse d’un objet de type e e T  et n une expression enti`re.2 Les pointeurs et les tableaux 6 POINTEURS Le pointeur NULL. Il d´coule des explications pr´c´dentes que l’arithm´tique des adresses ne se justifie que si e e e e on peut consid´rer un pointeur p comme l’adresse d’un ´l´ment t[i0 ] d’un tableau. Exemple.2 6. L’id´e de base de ces extensions est la suivante : si exp exprime l’adresse d’un certain objet O1 alors exp+1 e exprime l’adresse de l’objet de mˆme type que O1 qui se trouverait dans la m´moire imm´diatement apr`s O1 e e e e et exp−1 l’adresse de celui qui se trouverait imm´diatement avant. distincte de toute adresse valide.h> : e #define NULL ((void *) 0) 6. En r´alit´ il y a bien plus qu’un simple lien de voisinage entre ces deux concepts. Garreta. car l’un e e e est la d´finition de l’autre : e Soit exp une expression quelconque de type adresse et ind une expression enti`re. 2004 . Une des principales applications des pointeurs est la mise en œuvre de structures r´cursives de taille variable : listes chaˆ ees de longueur inconnue. En pratique on utilise plutˆt la constante NULL qui est e o d´finie dans le fichier <stddef. ou type *t. a Cette valeur peut donc ˆtre utilis´e comme la valeur conventionnelle du  pointeur qui ne pointe vers rien  . Les pointeurs avec leur arithm´tique et l’acc`s aux ´l´ments des tableaux sont donc des ee e e ee concepts tr`s voisins. etc. un programme est plus facile ` lire si on montre une certaine constance dans le choix des a notations. alors dans un cas comme dans l’autre on peut ´crire indiff´remment e e t[0] ou *t ou t[i] ou *(t + i) Bien entendu.2. Le programme suivant parcourt un tableau en utilisant un pointeur : 66 c H. e a L’arithm´tique des adresses d´coule de ces remarques. e e c’est l’´quivalent en C de la constante nil de Pascal. Par d´finition. e C garantit qu’aucun objet valide ne se trouve ` l’adresse 0 et l’entier 0 est compatible avec tout type pointeur.6. ajouter 1 ` un pointeur e e a c’est le consid´rer momentan´ment comme un entier et lui ajouter une fois la taille de l’objet point´. l’expression : e e *(exp + ind ) se note : exp[ind ] On en d´duit que si on a l’une ou l’autre des d´clarations e e type t[100]. arbres de profondeur quelconque. D’un point de vue technique. indirection et indexation e ´ Arithmetique.

c’est-`-dire ex´cut´es de mani`re tr`s fr´quente. q[i]) e D´veloppons un exemple classique : la fonction strcpy(dest. s[i] et *(s + i) en sont.2 Les pointeurs et les tableaux char s[10] = "Hello". s est (l’adresse d’) un tableau de caract`res (figure 8) e s B o n s o i r\ 0 Fig. Dans ce mˆme esprit. srce) copie la chaˆ de caract`res srce e ıne e dans l’espace point´ par dest. Autrement dit. les expressions p = t et p++ sont l´gitimes.1. a 44 Bien sˆ r. t[100]. il s’agit de petites ´conomies dont le b´n´fice ne se fait sentir que lorsqu’elles s’appliquent ` des parties des programmes u e e e a qui sont vraiment critiques. stdout). tandis que t est une variable mais *t. main() { char *p = s. Il ne faut pas oublier que les e e d´clarations e int *p.3.6 POINTEURS 6. Garreta. t[i] ou *(t + i). 2004 67 . La similitude entre un tableau et un pointeur est grande. rappelons la diff´rence qu’il peut y avoir entre les deux d´clarations e e e suivantes : char s[] = "Bonsoir". car elles tentent de modifier une  non variable  (un nom e e de tableau n’est pas une lvalue). Mais il subsiste des diff´rences. } Remarque 1. ee e e dans le premier cas le compilateur a allou´ un vrai tableau de 8 caract`res. s n’est pas une variable e mais *s. while (*p != ’\0’) fputc(*p++. L’arithm´tique des adresses permet d’am´liorer l’efficacit´ de certains traitements de tableaux44 . notamment dans la partie ex´cutable e des fonctions. Remarque 2. et y a copi´ la chaˆ donn´e . 8 – Chaˆ de caract`res  variable  ıne e tandis que t est l’adresse d’un (tableau de) caract`re(s) (figure 9) e t o n s o i r \0 B o n s o i r \ A u 0 revo Fig. et char *t = "Bonsoir". mais t = p et t++ sont ill´gales. ce qui est la mˆme chose. *(s + i). surtout dans les d´clarations. introduisent p comme une variable pointeur et t comme une constante pointeur. Premi`re forme : e e 43 C’est uniquement d’un point de vue technique que ces expressions ne sont pas des variables car du point de vue de la syntaxe elles sont des lvalue tout ` fait l´gitimes et le compilateur ne nous interdira pas de les faire apparaˆ a e ıtre ` gauche d’une affectation. l’acc`s ` une donn´e ` travers e a e a une expression de la forme *p est r´put´e (l´g`rement) plus rapide que l’acc`s ` la mˆme donn´e ` travers l’expression e e e e e a e e a *(q + i) (ou. a e e e e e c H. Par suite. t[i] et *(t + i) n’en sont pas43 . en mettant e e e a ` profit le fait que l’indirection est plus rapide que l’indexation. 9 – Constante chaˆ de caract`res ıne e On pourra r´f´rencer ces caract`res indiff´remment par s[i]. la chaˆ a ´t´ rang´e avec les autres constantes litt´rales et le compilateur n’a allou´ qu’une ıne e e e e e variable de type pointeur dans laquelle il a rang´ l’adresse de cette constante. Ainsi. dans e e e ıne e le second cas. A ce propos voir aussi la section 5. Cependant.

il suffit de e e consid´rer que la boucle qui figure dans la premi`re forme ´quivaut ` : e e e a while ((*(dest + i) = *(srce + i)) != ’\0’) i++. return dest. avant toute utilisation du tableau. n <= N. a e – dans le cas d’un tableau local. pas besoin de constante connue ` la compilation */ a tab = malloc((N + 1) * sizeof(int)). 6. e – allouer l’espace ` l’ex´cution. p. p. if (tab == NULL) return. tab[p] += + /* N_MAX est une constante connue ` la compilation */ a n++) { = 1. par exemple. 45 Rappelons que les coefficients de la neme ligne du triangle de Pascal sont d´finis r´cursivement ` partir de ceux de la n − 1eme e e a p p−1 p ligne par : Cn = Cn−1 + Cn−1 . printf("\n"). p > 0. char srce[]) { register int i = 0. Exemple : la fonction ci-dessous calcule dans un e tableau la N eme ligne du triangle de Pascal45 et s’en sert dans des calculs auxquels nous ne nous int´ressons e pas ici. l’utilisation de ces tableaux dynamiques est identique ` celle des tableaux normaux (ce a qui. tab[0] = tab[n] for (p = n . e lorsque le tableau commence ` exister. } Deuxi`me forme : e char *strcpy(char *dest. } } Programmation avec un tableau dynamique : void Blaise(int N) { int n. int *tab. 68 c H.2 Tableaux dynamiques L’´quivalence entre les tableaux et les pointeurs permet de r´aliser des tableaux dynamiques. int tab[N_MAX + 1].2 Les pointeurs et les tableaux 6 POINTEURS char *strcpy(char dest[]. c’est-`-dire e e a des tableaux dont la taille n’est pas connue au moment de la compilation mais uniquement lors de l’ex´cution.1. while ((*d++ = *s++) != ’\0’) . i. par un appel de malloc . Pour cela il suffit de a – remplacer la d´claration du tableau par celle d’un pointeur . car on y a supprim´ l’indexation.1].6. } La deuxi`me forme est plus efficace. i. p--) tab[p . return dest. /* ici. /* exploitation de tab . Garreta. pour le programmeur. while ((dest[i] = srce[i]) != ’\0’) i++. Pour s’en convaincre. est une tr`s bonne nouvelle !). tab[i]). lib´rer l’espace ` la fin de l’utilisation. char *srce) { register char *d = dest. 2004 . e a Programmation avec un tableau ordinaire (statique) : void Blaise(int N) { int n. On notera qu’une seule ligne diff`re entre la version ` tableau statique et celle avec tableau dynamique. *s = srce.2. affichage : */ for (i = 0. e a Pour tout le reste. i <= n. for (n = 0. i++) printf("%5d".

i <= n.. p--) tab[p . 10 – Tableau bidimensionnel statique Etudions une autre mani`re d’acc´der aux ´l´ments de cette mˆme matrice. printf("\n"). Garreta.2.2 Les pointeurs et les tableaux for (n = 0. nous supposons ici que la taille d’un int. et donc celle d’un unsigned. e e a ee Cette expression traduit la disposition  par lignes  des tableaux rectangulaires. e e a c H. mais que. . que signifie m1[i] ? Il est int´ressant de constater e e e e que cette expression n’est pas une lvalue.3 Tableaux multidimensionnels ´ Reflexions sur la double indexation..1]. La d´claration d’une telle matrice est facile ` construire (cf. il nous reste m1[i][j] = *(m1 + (i × NC + j) × sizeof(float)) Nous retrouvons l’expression classique i × NC + j caract´risant l’acc`s ` un ´l´ment mi. si on supposait avoir la d´claration e ee e typedef float LIGNE[NC]. tab[i]). ` part cela.4. sont ´gales ` celle d’un pointeur. D’apr`s ce que nous savons d´j`. 2004 69 . Supposons que nous ayons ` ´crire un programme qui op`re a e e sur des matrices ` N L lignes et N C colonnes. comme le montre la a e e section suivante.1. puisqu’elle est de type  tableau de. tab[p] += + n++) { = 1. tab[0] = tab[n] for (p = n . /* ` ne pas oublier ( tab est une variable locale) */ a } free(tab). } Remarque. on peut ee e e ea l’interpr´ter de la mani`re suivante46 : e e m1[i][j] = *(m1[i] + j) = *(float *)((unsigned int) m1[i] + j × sizeof(float)) Le probl`me est : avec la d´claration que nous avons donn´e. a e a section 5. Soient les d´clarations e e ee e e 46 Pour all´ger les formules. n <= N. /* LIGNE = tableau de NC float */ alors m1[i] serait une expression de type LIGNE valant : m1[i] = *(m1 + i) = *(LIGNE *)((unsigned) m1 + i × sizeof(LIGNE)) = *(LIGNE *)((unsigned) m1 + i × NC × sizeof(float)) En oubliant pour un instant les types des pointeurs. Autrement dit.j d’une matrice N L×N C. p > 0. i++) printf("%5d".1) : tableau de NL tableau de NC float tableau de NC float float m1 (m1)[NL] ou m1[NL] (m1[NL])[NC] ou m1[NL][NC] Un ´l´ment particulier de cette matrice sera not´ m1[i][j]. par exemple. m1 i x NC j m1[i][j] Fig. car il faut g´rer soi-mˆme les calculs d’indices.6 POINTEURS 6. L’emploi de tableaux dynamiques ` plusieurs indices est beaucoup plus compliqu´ que celui a e des tableaux ` un indice comme ci-dessus. comme le montre la figure 10. /* exploitation de tab . elle est a trait´e comme un ´l´ment d’un tableau ordinaire. 6. affichage : */ for (i = 0.

nous pouvons les ignorer. Garreta.2 Les pointeurs et les tableaux 6 POINTEURS float m1[NL][NC].2. e De plus. /* en esp´rant que nl ≤ NL et nc ≤ NC */ e int i. Or nous sommes maintenant parfaitement ´quip´s pour r´aliser une telle matrice. La deuxi`me se lit. Version statique : #define NL 20 #define NC 30 . int nc) { double mat[NL][NC]. *m2[NL]. 6. 70 c H. il nous e suffit d’initialiser correctement ces pointeurs : chacun sera l’adresse d’une ligne de la matrice pr´c´dente. cette expression se traduira (en continuant ` n´gliger les conversions des types pointeurs) par : a e m2[i][j] = *(m2[i] + j × sizeof(float)) = *(*(m2 + i × sizeof(float *)) + j × sizeof(float)) Les multiplications par sizeof(float) et sizeof(float *) sont r´alis´es de mani`re tr`s rapide par les e e e e calculateurs (ce sont des multiplications par des puissances de 2).4 Tableaux multidimensionnels dynamiques Les consid´rations pr´c´dentes montrent pourquoi dans la r´alisation d’une matrice dynamique on devra e e e e renoncer au m´canisme de double indexation ordinaire. Pour r´aliser la matrice dont nous avons besoin. La matrice m1 existant par ailleurs. 11 – Tableau bidimensionnel r´alis´ avec un tableau de pointeurs e e Il est remarquable qu’un ´l´ment de la nouvelle  matrice  ainsi d´clar´e se note encore ee e e m2[i][j] mais maintenant.. il nous faut initialiser le tableau de pointeurs m2. Supposons que la fonction e e e void unCalcul(int nl. En effet. i < NL. void unCalcul(int nl. cela se fait ici par une expression d’une ´tonnante simplicit´ : e e for (i = 0. 2004 .. Nous e e aurons la configuration de la figure 11. m2 i m2[i] j m2[i][j] Fig. NC doit ˆtre connu ` la compilation pour que e e a l’expression i × NC + j ait un sens.6. int nc) impl´mente un algorithme qui requiert une matrice locale ` nl lignes et nc e a colonnes. En contrepartie nous occupons un peu plus d’espace e e (les NL pointeurs suppl´mentaires). compte tenu des priorit´s : float *(m2[NL]). j. La variable m2 est donc d´clar´e comme e e e e un tableau de NL pointeurs vers des nombres flottants. Il reste que nous avons remplac´  m1 + i × NC + j  par  *(m2 + i) + j . i++) m2[i] = m1[i]. Nous avons donc bien ´limin´ une  vraie  e e e multiplication : notre deuxi`me mani`re est plus efficace.

i < nl. int i. /* lib´ration (indispensable dans le cas d’une variablelocale) */ e for (i = 0. La matrice est r´alis´e par des morceaux de m´moire ´parpill´e. /* ´chec */ e c H. /* utilisation de la matrice mat. etc. Par exemple : */ for (i = 0. Par exemple : */ for (i = 0.6 POINTEURS 6. *espace. i++) free(mat[i]). Garreta. } Version dynamique (par souci de clart´ nous y avons omis la d´tection des ´checs de malloc) : e e e void unCalcul(int nl. etc. e e e e e m2 Fig. i < nl. i++) mat[i] = malloc(nc * sizeof(double)). 12 – Matrice dynamique (lignes allou´es s´par´ment) e e e ¨ Variante : lignes contigues. La structure de la matrice ressemble alors ` celle de la figure 11 : e a int unCalcul(int nl. for (i = 0. int i. /* initialisation des pointeurs */ mat = malloc(nl * sizeof(double *)). int nc) { double **mat. j < nc. j++) mat[i][j] = 0. j < nc. int nc) { double **mat. i++) for (j = 0. i++) for (j = 0. free(mat). comme le montre la figure 12. les lignes de la matrice sont allou´es ` l’occasion de nl appels distincts de e e e a malloc. j++) mat[i][j] = 0. i < nl. j. /* initialisation des pointeurs */ mat = malloc(nl * sizeof(double *)). } Dans cette mani`re de proc´der.2 Les pointeurs et les tableaux /* utilisation de la matrice mat. Une l´g`re variante consiste ` allouer d’un seul coup toute la m´moire e e a e n´cessaire aux lignes de la matrice. i < nl. j. if (mat == NULL) return 0. 2004 71 .

les caract`res eux-mˆmes ne sont nullement d´plac´s.. B o n s o i r \0\0 C a v a ? \0 B y e \ C e 0 n ' e s . /* ´chec */ e } for (i = 0. cette technique entraˆ une ´conomie de place sube ıne e stantielle.6. puisqu’elle permet de remplacer un tableau rectangulaire dont la largeur est d´termin´e par la plus e e grande longueur possible d’une chaˆ (avec un espace inoccup´ tr`s important) comme celui de la figure 13. i++) for (j = 0. i++) mat[i] = espace + i * nc. ıne e e par un espace lin´aire dans lequel chaque chaˆ n’occupe que la place qui lui est n´cessaire. 13 – Matrice de caract`res e 6.2. return 1.2 Les pointeurs et les tableaux 6 POINTEURS espace = malloc(nl * nc * sizeof(double)). il ordonne les lignes lues . Notre programme e e a lit des lignes de caract`res au terminal et les range dans une table. La figure 14 illustre ces structures de donn´es. encombrement réel des caractères Fig. comme le montre e ıne e la figure 14. e e e e 72 c H. 2004 . e Les lignes lues sont rang´es les unes ` la suite des autres dans un tableau unique. Notez que le tri se fait en ordonnant la table des e pointeurs . if (espace == NULL) { free(mat). i < nl. /* utilisation de la matrice mat.. Ensuite. i < nl. etc. return 0.5 Tableaux de chaˆ ınes de caract`res e Le remplacement d’un tableau ` deux indices par un tableau de pointeurs se r´v`le encore plus utile dans a e e le traitement des tableaux de chaˆ ınes de caract`res. jusqu’` la rencontre de la fin de fichier ou e a la lecture d’une ligne vide. Par exemple : */ for (i = 0. } /* succ`s */ e longueur maximum d'une chaîne B o n s o i r \0 \0 C a v a ? \0 C e n ' e st B y e \0 p as un texte g é n i a l !\ 0 Fig. nomm´ espace. j < nc. not´e mots. free(mat). Ici. j++) mat[i][j] = 0. est une table de renvois e e dans espace. finalement il affiche les lignes ordonn´es. La table des mots. 14 – Tableau de chaˆ ınes de caract`res e Nous allons illustrer cette m´thodologie par un exemple tr`s simple mais tout ` fait typique. Garreta. /* lib´ration (indispensable dans le cas d’une variablelocale) */ e free(espace). dont le e a e pointeur libre indique la premi`re place disponible.

2004 73 .h> #define MAXMOTS 100 #define MAXCARS 3000 char char char int espace[MAXCARS]. espace immodifiable allou´ et garni une fois pour toutes e par le compilateur. } } void main(void) { int i.6 Tableaux multidimensionnels formels Nous avons vu que si un argument formel d’une fonction ou une variable externe est un tableau ` un seul a indice.6 POINTEURS 6. nbr_mots). for (i = 0. ee e Mais que se passe-t-il pour les tableaux ` plusieurs indices ? Si un tableau a ´t´ d´clar´ a ee e e T table[N1 ][N2 ] . 6.. *mots[MAXMOTS]. t[i .1]. ok = 0. i++) printf("%s\n". while(gets(libre) != NULL && *libre != ’\0’) { mots[nbr_mots++] = libre. } if (ok) return. i < n. tandis que messages est fait de e e pointeurs vers des cellules de la  zone de constantes .h> #include <string.2. c H.) { int i. t[i] = w.1]. [Nk−1 ][Nk ] . "Autre erreur" }. comme dans : e char *messages[] = { "Fichier inexistant". t[i]) > 0) { char *w = t[i . libre += strlen(libre) + 1. n--. } Remarque. puts (´criture de chaˆ e ıne).. On peut signaler qu’une structure tout ` fait analogue ` notre table mots est cr´´e par le a a ee compilateur lorsqu’on initialise un tableau de chaˆ ınes de caract`res. for (i = 1.2 Les pointeurs et les tableaux Les fonctions gets (lecture de chaˆ ıne).1] = t[i]. alors l’indication du nombre de ses ´l´ments est sans utilit´ puisqu’il n’y a pas d’allocation d’espace. } tri(mots. nbr_mots = 0. int n) { for (. "Erreur physique d’entree-sortie". "Volume ou repertoire inexistant". i++) if (strcmp(t[i . Garreta. i < nbr_mots. La seule diff´rence entre notre tableau mots et le tableau messages que cr´erait le compilateur par suite e e de la d´claration ci-dessus est la suivante : les ´l´ments de mots sont des pointeurs vers un espace modifiable e ee faisant partie de la m´moire attribu´e pour les variables de notre programme. a e e #include <stdio. strcmp (comparaison de chaˆ ınes) et strlen (longueur d’une chaˆ ıne) appartiennent ` la biblioth`que standard et sont expliqu´es dans les sections suivantes. ok = 1. *libre = espace. "Nom de fichier incorrect". void tri(char *t[]. mots[i])..

Par e e exemple. iTable. la fonction suivante effectue la multiplication par un scalaire k de la matrice m d´clar´e NL × NC (deux e e constantes connues dans ce fichier) : void k_fois(double k. Il existe cependant des situations dans lesquelles on souhaiterait pouvoir noter les ´l´ments d’un tableau ee T1 . Teukolsky. 48 D’obscures contraintes techniques. j++) *((double *) m + i * NC + j) *= k. dans lesquelles l’arithm´tique des adresses subit des limitations drastiques. T2 . Ta+1 . le compilateur connaisse la taille d´clar´e pour chaque ligne. 1992). iMax : double *dTable(int iMin. on ´crirait des fonctions analogues fTable. e e e e e e Pour un tableau ` deux indices. en compilant la fonction. Tb . . i < NL. ayant perdu tout int´rˆt de nos jours. mais il faut a absolument que. . W. res . 2004 . . etc. int iMax) { double *res = malloc((iMax . Vetterling et B. il faut que ce calcul puisse ˆtre effectu´. La variable globale erreurTable est rendue n´cessaire par le fait qu’un r´sultat NULL ne suffit pas ici ` e e a caract´riser l’´chec de l’allocation . return res . e Cambridge University Press. e e 74 c H. comme les PC ` base de processeurs Intel ant´rieurs au 80286 (un lointain a e ancˆtre du Pentium). a et b ´tant deux entiers quelconques v´rifiant a ≤ b. 10).)..7 Tableaux non n´cessairement index´s ` partir de z´ro e e a e En principe. Press. Garreta. ` Cas des tableaux a un indice. sont des constantes) alors un acc`s comme e table[i1 ][i2 ] . D’o` la r`gle g´n´rale : e e u e e e lorsqu’un argument formel d’une fonction est un tableau ` plusieurs indices. . for (i = 0.6.2. i++) for (j = 0.. . Ta . j. .iMin peut ˆtre nul alors que l’allocation a r´ussi et res = e e e e NULL. return NULL. une autre solution aurait consist´ ` faire soi-mˆme les calculs d’indice : ea e void k_fois(double k. dans la grande majorit´ e e ee e des applications. la connaissance de la premi`re a e dimension d´clar´e est sans utilit´ mais les autres dimensions d´clar´es doivent ˆtre connues du compilateur. Bien entendu. Nous e e e e allons voir que l’allocation dynamique fournit un moyen simple et pratique47 pour utiliser de tels tableaux. . j < NC. } Remarque. . if (res == NULL) { erreurTable = 1. [ik−1 ][ik ] se traduit par *( (T *)table + i1 × N2 × N3 × · · · × Nk + · · · + ik−1 × Nk + ik ) Qu’il y ait ou non allocation d’espace. } (Si on avait besoin de tableaux de float. } Tout ce qu’on y gagne est un programme bien plus difficile ` lire ! a 6. j. un e a e e e tableau T d´clar´ de taille N est fait des ´l´ments T0 . etc. . T1 . de int.iMin. plus g´n´ralement. i++) for (j = 0. } erreurTable = 0. Flannery. en effet. iMin+1. void *m) { int i. cette convention est commode et fiable. TN −1 .2 Les pointeurs et les tableaux 6 POINTEURS (N1 . font que cette technique peut ne pas fonctionner dans e e les implantations de C sur d’anciennes plate-formes. La figure 15 illustre le r´sultat rendu par la fonction pr´c´dente lors d’un appel comme dTable(5. e etc. j < NC. les tableaux de C sont index´s ` partir de z´ro : sans qu’il soit n´cessaire de le sp´cifier.iMin + 1) * sizeof(double)). i < NL. La fonction dTable48 cr´e un tableau d’´l´ments de type double dont e ee les indices sont les entiers iMin. double m[][NC]) { int i. e e e 47 Nous expliquons ici une technique massivement employ´e dans la biblioth`que de calcul scientifique Numerical Recipes ( c e e Numerical Recipes Software) bas´e sur le livre Numerical Recipes in C (W. Nous avons vu que. cela donne : il est inutile d’indiquer combien il y a de lignes. N2 . TN ou. S. for (i = 0. j++) m[i][j] *= k.

Il aurait fallu d´finir une structure plus complexe qu’un simple e e a e e tableau. return NULL. } erreurTable = 0. un appel comme free(t)..1f\n".cMin.2 Les pointeurs et les tableaux adresse rendue comme résultat par dTable cellules effectivement allouées Fig. for (i = 5.. lMax e e et les colonnes sont index´es par les entiers cMin. i <= 10. free(espace). soit celle de res (actuellement des variables locales de la fonction dTable) pour g´rer correctement la lib´ration ult´rieure d’un tel tableau.4. cependant sa ee e lib´ration est un peu plus complexe que pour les tableaux dynamiques ordinaires. double *espace. int cMax) { int nbLin. i++) t[i] = 1000 + i. int lMax.cMin + 1. return NULL. sans aucune perte d’efficacit´. espace = malloc(nbLin * nbCol * sizeof(double)).. double *t = dTable(5.2. i++) printf("%. Un tableau cr´´ par un appel de la fonction dTable est dynamiquement allou´. . . e e a sugg`rent une mani`re de mettre en œuvre des matrices dont les lignes et les colonnes ne sont pas index´es ` e e e a partir de z´ro. nbCol = cMax . cMin+1 . . cMax : e double **dMatrice(int lMin. . Exemple d’utilisation : e a e .. if (lignes == NULL) { erreurTable = 1. nbCol. int cMin. . ` part e ee a e a cela. } lignes = malloc(nbLin * sizeof(double *)). En particulier.. i++) { lignes[i] = p. 2004 75 . } p = espace . lMin+1 . 10). 15 – Adresse rendue par la fonction dTable 0n acc`de aux ´l´ments d’un tel tableau ` l’aide d’un indice compris entre les valeurs indiqu´es mais. ` la suite du e a programme ci-dessus.lMin. if (espace == NULL) { erreurTable = 1. Les remarques pr´c´dentes. i < nbLin. i <= 10. de mani`re tout ` fait habituelle .. . incluant soit la valeur de iMin. **lignes. for (i = 5. en particulier. *p.6 POINTEURS 6. t[i]). } c H. Attention. p += nbCol.. i. return lignes . g´n´rera certainement une erreur ` l’ex´cution. nbLin = lMax .. for (i = 0. .lMin + 1. e La fonction dMatrice cr´e une matrice dont les lignes sont index´es par les entiers lMin. avec celles faites ` la section 6. Garreta. e e e ` Cas des tableaux a plusieurs indices.

j.. nla. 3).... voici la fonction qui effectue le produit de deux matrices A et B en d´posant le r´sultat dans e e une troisi`me matrice C.. Voici un e e e exemple d’utilisation : . . /*1*/ /*1*/ On notera que la fonction produitMatrices peut ˆtre optimis´e en supprimant des des multiplications qui e e sont faites dans la boucle la plus profonde (la seule qui doit nous int´resser. B[NLB][NCB]. . } . k < nca. tandis que nla. 10. NCA.8 Matrices non dynamiques de taille inconnue Un dernier cas qui se pr´sente parfois est celui o` on doit ´crire une fonction op´rant sur des matrices e u e e ordinaires (c. } } Exemple d’appel (NLA. C[NLC][NCC]. i++) for (j = -3.. sont des constantes introduites par des #define. e e Pour cela..1f ". l’emploi de ces matrices ne s’accompagne d’aucune perte d’efficacit´. sont des variables) : double A[NLA][NCA]. NCC). etc.. k. nca. NCB. etc. dNcb et dNcc repr´sentent les e nombres de colonnes avec lesquels on a d´clar´ les matrices A. j++) m[i][j] = 1000 * i + j. &C[0][0]. non dynamiques) alors que leurs dimensions ne sont pas connues lors de la compilation.. ncb. i <= 10. j <= 3. Garreta.. B et C respectivement. double **m = dMatrice(5. k++) s += A[i * dNca + k] * B[k * dNcb + j].. tandis que ncb est le nombre effectif de colonnes de B. obtention des valeurs des variables nla. i <= 10.2 Les pointeurs et les tableaux 6 POINTEURS Comme pr´c´demment.. -3. j < ncb. i++) { for (j = -3. double *B.-`-d. double s. int dNcb. printf("\n"). . Les arguments dNca.. etc. a Par exemple. C[i * dNcc + j] = s. du point de vue de l’efficacit´). for (i = 5. for (i = 0. j++) { s = 0. 2004 . m[i][j]). . produitMatrices(&A[0][0]. double *C.. nca. int ncb.2. for (i = 5. et des matrices A et B . Les arguments nla et nca repr´sentent le nombre effectif de lignes et de colonnes de e e A. e e void produitMatrices(double *A. j <= 3. i++) for (j = 0. int nla. for (k = 0. 6. j++) printf("%10.. int dNcc) { int i. i < nla. nca.6. NCA. int nca. int dNca.. &B[0][0]. il suffit de remplacer les deux lignes marqu´es /*1*/ par ceci (ia et ib sont des variables locales e nouvelles) : 76 c H.

e En revanche. Ainsi. etc. Pour une fonction il ne d´pend pas de la e ıt´ e e d´claration (qui ne pr´cise que le type du r´sultat et le nombre et les types des arguments) mais du nombre d’instructions qui e e e composent la fonction. Exemple : e double (*uneFonction)(int n. par exemple.. double val) { etc.1 Les adresses des fonctions Les fonctions et leurs adresses En ´tudiant les d´clarateurs complexes (section 5. 2004 77 . pour une raison facile ` comprendre : la taille d’une fonction ne peut pas a se d´duire de son type49 . e e e  adresse d’un [objet de type] T  et  tableau de [objets de type] T . v). Il n’y a plus de probl`me de taille (la taille d’une adresse e – ou pointeur – est constante). ib = j. par la suite. si c’est une variable globale. ia += 1. ne laisse rien deviner sur les caract´ristiques des fonctions dont uneFonction contiendra les e e adresses. de la mˆme mani`re que e e le nom d’un tableau repr´sente son adresse de base.4). Comment le programmeur obtient-il des adresses de fonctions ? Il faut savoir que. e e si on a la d´finition de fonction e double maFonction(int nbr. ib += dNcb. uneFonction re¸oit l’adresse d’une fonction φ pour valeur. } . il n’est pas d´lirant de rapprocher les fonctions et les donn´es. C’est un inconv´nient r´dhibitoire. double x). ia = i * dNca. Une fois compil´e (traduite e e e e en langage machine). ce nombre d´coule sans ambigu¨ e de la d´claration. ce qui demande de connaˆ combien d’octets sont e e ıtre n´cessaires. a c H. le nom d’une fonction repr´sente l’adresse de celle-ci. et rendant une valeur double. le langage C permet d’utiliser un concept ` peine plus complexe que le pr´c´dent : celui de a e e variable dont la valeur est l’adresse d’une fonction. un int et un double. c’est-`-dire de ce qu’elle fait et comment elle le fait. une fonction est une suite d’octets qui occupe une portion de m´moire exactement comme. Garreta. par exemple. alors l’expression suivante est c correcte et repr´sente un appel de φ (en supposant k de type int et u et v de type double) : e u = (*uneFonction)(k.3. Tout cela sugg`re qu’en C on aura le e droit de travailler avec des variables qui repr´sentent des fonctions. Si. des tableaux de fonctions. } alors l’expression maFonction : – poss`de le type  adresse d’une fonction ayant pour arguments un int et un double et rendant un e double  . peu utile ici) et.3 6.. Il va falloir y renoncer. Cette d´claration n’initialise pas e uneFonction (sauf.4) nous avons appris que la syntaxe de C permettait e e de manipuler le type  fonction rendant un [objet de type] T  (T repr´sente un autre type). et la syntaxe pour d´clarer une telle variable a d´j` ´t´ expliqu´e dans la section e eae e e sur les d´clarateurs complexes (cf. ` part le type des arguments a et du r´sultat. 49 Notez que. cette p´riphrase e e constituant elle-mˆme un d´clarateur que le programmeur peut composer librement avec les autres d´clarateurs. avec la valeur 0. Cet ´nonc´ d´clare uneFonction comme une variable (un pointeur) destin´e ` contenir des adresses de fonctions e e e e a ayant deux arguments. k++) { s += A[ia] * B[ib]. car toute d´finition de variable doit entraˆ e e e e ıner la r´servation d’un espace m´moire pour en contenir la valeur. Qu’en est-il exactement ? e Le concept le plus simple qu’on peut souhaiter dans ce domaine est celui de variable dont la valeur est une fonction. des fonctions e renvoyant comme r´sultat d’autres fonctions. u – a pour valeur l’adresse o` commence la fonction maFonction .. un e tableau de nombres.6 POINTEURS 6.3 Les adresses des fonctions . k < nca. – n’est pas une lvalue. /*2*/ /*2*/ /*2*/ /*2*/ /*2*/ /*2*/ /*2*/ 6.. for (k = 0. e e e e e Pour un tableau. section 5. malgr´ les apparences. La principale diff´rence est dans la possibilit´ ou l’impossibilit´ de pr´dire le nombre d’octets occup´s.

b ] e telle qu’il existe x0 ∈ [ a . la fonction dicho s’´crirait e double dicho(a. (*f)(). } L’argument f est d´clar´ comme un pointeur vers une fonction . 3. e e Les arguments de la fonction dicho sont : les bornes a et b de l’intervalle de recherche.6. repr´sent´ par une fonction. .0. est r´solu en C e a e par l’utilisation d’arguments de type  pointeur vers une fonction .3 Les adresses des fonctions 6 POINTEURS Par cons´quent. Si une fonction est le nom d’une fonction pr´c´demment d´finie. /* hypoth`se : f(a) * f(b) <= 0 */ e if ((*f)(a) > 0) { x = a. la pr´cision ε voulue e et surtout la fonction f dont on cherche un z´ro. Examinons deux applications de cela parmi les plus importantes. . l’affectation suivante est tout ` fait l´gitime : e a e uneFonction = maFonction. else b = x. changeant de signe e e e sur l’intervalle [0.5 (tri d’un tableau de chaˆ ınes de caract`res) en rempla¸ant la fonction de tri na¨ par la fonction qsort de la biblioth`que standard. b. double eps. double (*f)(double)) { double x.2. *libre = espace. . b ] telle que f (a) et f (b) ne sont pas de mˆme signe. eps. e c ıf e Cette fonction est expliqu´e ` la section 8. } while (fabs(b . elle trie un tableau par rapport ` un crit`re quelconque. } return x. 78 c H. ou  sans proptotypes . une_fonction).0. Autre exemple du mˆme tonneau e y = dicho(0. int nbr_mots = 0. eps. 1E-8.h> #define MAXMOTS 100 #define MAXCARS 3000 char espace[MAXCARS]. la fonction dicho recherche xε ∈ [ a .4. En clair : dicho d´termine xε . d´finie sur un e intervalle [ a . . Remarque. f) double a. 2004 . b ] v´rifiant |xε − x0 | ≤ ε et f (x0 ) = 0. if ((*f)(x) < 0) a = x. Si f est une fonction continue. 1]. ´ Exemple 1 : resolution de f (x) = 0 par dichotomie. . double b.3. 1. 1E-10. b = x. a = b.h> #include <string. Reprenons notre programme de la section 6. Si elle e a a e nous int´resse ici c’est parce qu’elle prend ce crit`re. pour argument : e e e e #include <stdio. 6. Garreta. A partir de cette affectation (et tant qu’on aura pas chang´ la valeur de uneFonction) les appels de e (*uneFonction) seront en fait des appels de maFonction.2. En syntaxe originale. Voici cette fonction : e double dicho(double a.0. . un appel correct de dicho serait x = dicho(0. cos). char *mots[MAXMOTS].a) > eps) { x = (a + b) / 2.2 Fonctions formelles Le probl`me des fonctions formelles. { etc. (la suite est la mˆme) e } Exemple 2 : utilisation de qsort.0. b. par cons´quent *f est une fonction et (*f)(x) e e e un appel de cette fonction. c’est-`-dire des fonctions arguments d’autres fonctions. qui est une e e solution de l’´quation f (x) = 0 si on tol`re une erreur d’au plus ε.

La frappe e e e e e d’une ligne r´duite au texte fin arrˆte le programme. nous savons que comparer sera appel´e avec deux adresses de chaˆ e e ınes pour arguments effectifs . ce ıne e ıne qui explique la fa¸on dont a et b apparaissent dans l’appel de strcmp. log }. a c H. on ne peut pas ignorer le fait que comparer sera appel´e (par e e qsort) avec pour arguments effectifs les adresses des chaˆ ınes ` comparer.h> double sin(double). comparer). double (*fon)(double). } table[] = { "sin". "log". c’est-`-dire des */ ı a /* adresses d’adresses de char */ return strcmp(*(char **)a. for (i = 0. Pour la comprendre.3 Les adresses des fonctions /* La d´claration suivante se trouve aussi dans <stdlib. ´tant de type void *. il faut consid´rer e ıtre e e les diverses contraintes auxquelles elle doit satisfaire : – comparer figurant comme argument dans l’appel de qsort. libre += strlen(libre) + 1. le type  adresse de chaˆ  est char **. Mais attention : quelle que soit la syntaxe employ´e. on ne peut acc´der aux objets qu’ils e e pointent sans leur donner auparavant (` l’aide de l’op´rateur de conversion de type) un type pointeur a e  utilisable  . const void *b) { /* Les arguments de cette fonction doivent ^tre d´clar´s "void *" (voyez la */ e e e /* d´claration de qsort). mots[i]). *(char **)b). } main() { int i. et toute cette question est bien o plus simple. – d’apr`s la documentation de qsort. Garreta. exp. sin. log(double). int comparer(const void *a. suivis d’un e nombre r´el . cos. En r´alit´. e – a et b. } qsort(mots. dans les appels de cette fonction que */ e e e /* nous faisons ici. son prototype doit correspondre exactement50 a ` celui indiqu´ dans le prototype de qsort . c 6. int (*comp)(const void *. il ´value et affiche alors la valeur de la fonction mentionn´e appliqu´e au nombre donn´.3. "cos". #define NBF (sizeof table / sizeof table[0]) 50 En syntaxe  sans prototype . struct { char *nom. suivi d’un ou plusieurs blancs. } Remarque.h> : */ e void qsort(void *base. e e #include <stdio. sizeof(char *). aucun contrˆle n’est fait sur le type des arguments de comparer. les arguments formels de comparer. ıne e – l’adresse de la fonction correspondante.6 POINTEURS 6. while(gets(libre) != NULL && *libre != ’\0’) { mots[nbr_mots++] = libre. 2004 79 .3 Tableaux de fonctions Le programme suivant montre l’utilisation d’un tableau d’adresses de fonctions : chaque ´l´ment du tableau ee table est form´ de deux champs : e – le nom d’une fonction standard. sous forme de chaˆ de caract`res . const void *)). size_t size. a et b sont des adresses de "cha^nes". exp(double). Notre programme lit des lignes constitu´es d’un nom de fonction. La d´finition de comparer peut paraˆ bien compliqu´e. i++) printf("%s\n". cos(double). "exp". nbr_mots. size_t nmemb. i < nbr_mots. une chaˆ ´tant de type char *.

4 Flou artistique A ce point de notre expos´ vous ˆtes en droit de trouver que les rˆles respectifs des objets de type  fonce e o tion. double x.. Cela a conduit ` d´finir e a e a e l’op´rateur d’appel de fonction aussi bien pour une expression de type  fonction. Examinons une fonction bien ordinaire.540302 atn 1 atn ??? fin Remarque. z = sin(x).6. cos(double). . soit explicitement : a e double sin(double).. les trois lignes ci-dessus ne sont c e pas coh´rentes. nom) != 0.. soit en incluant un fichier dans lequel sont ´crites ces d´clarations : e e #include <math. les noms des fonctions sin.fon)(x)). La d´claration du tableau table pr´sente une caract´ristique peu fr´quente : des fonctions e e e e sont r´f´renc´es mais elles ne sont pas en mˆme temps appel´es. } } Voici une ex´cution de ce programme : e sin 1 0. En effet. if (i < NBF) printf("%f\n". tandis que sin n’a pas besoin de l’ˆtre. int i. (*table[i].. exp(double).. Apr`s avoir constat´ l’impossibilit´ de d´clarer des e e e e e variables de type  fonction. Pourtant. log(double).h>.. nom). if (strcmp(nom. Voici l’initialisation de f par l’adresse de la fonction sin et deux appels ´quivalents de cette fonction (x.3.  . la d´finition d’une variable de type  adresse d’une fonction double ` un argument double  est : e a double (*f)(double). "fin") == 0) break. La premi`re montre que f et sin sont de mˆme type (` savoir  adresse d’une fonction. il n’y a aucun e e e e signe indiquant au compilateur qu’il s’agit de fonctions. Quelle que soit la valeur de x. &x). Garreta. or e e e a f doit ˆtre d´r´f´renc´ pour appeler la fonction. La d´claration de cette fonction.  que pour une expression e 80 c H.  sont confus.h> 6. etc. apparaissent dans cette d´claration sans ˆtre accompagn´s d’une paire de parenth`ses . on a voulu faire cohabiter la rigueur (un pointeur doit ˆtre d´r´f´renc´ pour e eee e acc´der ` l’objet qu’il pointe) et la tradition (le sinus de x s’est toujours ´crit sin(x)). scanf("%lf". y = (*f)(x). et donc l’obligation de passer par des e variables  adresse de fonction... D’autre part.. sinus.. 2004 . est : e double sin(double). e eee e e On peut comprendre la raison de cette incoh´rence. car une fonction n’a pas de taille d´finie. cos.  et des objets de type  adresse d’une fonction. y e et z sont des variables double) : f = sin.3 Les adresses des fonctions 6 POINTEURS main() { char nom[80]. ee e e e exp. for(i = 0. nom).nom. ) . C’est pourquoi ce programme sera trouv´ erron´ par le e e compilateur ` moins qu’on lui donne les d´clarations externes des fonctions. else printf("%s ???\n".) { scanf("%s". i++) . extraite du fichier <math. y et z re¸oivent la mˆme valeur. for(.. i < NBF && strcmp(table[i].841471 cos 1 0...

R.2 Exemple Le programme suivant effectue le mˆme travail que celui du 6. struct noeud *fils_gauche. e e e #include <stdlib. comme les arbres binaires. *fils_droit. sont accept´es elles aussi (elles ont la mˆme valeur que les pr´c´dentes). Garreta.4.1 Structures r´cursives e D´claration e Les structures r´cursives font r´f´rence ` elles-mˆmes.. Sedgewick.6 POINTEURS 6. Un arbre binaire est repr´sent´ par une adresse.4 Structures r´cursives e de type  adresse d’une fonction. qui est : e e e – soit l’adresse nulle . fils d ) constitu´ d’une information dont la nature d´pend du probl`me e e e ´tudi´ et de deux descendants qui sont des arbres binaires. struct noeud *fils_gauche. POINTEUR g. Notez que le nom de la structure (noeud) est ici indispensable. pour indiquer le type des objets point´s par e les champs fils gauche et fils droit. Il n’en d´coule pas moins que les deux expressions e e e e u = f(x). 6. Elles sont principalement utilis´es pour implanter des e ee a e e structures de donn´es dont la d´finition formelle est un ´nonc´ r´cursif. la d´claration d’une telle structure sera donc calqu´e sur la suivante : e e struct noeud { type valeur.4. POINTEUR alloc(char *m. Ainsi. les deux expressions (*f)(x) et sin(x) sont correctes . POINTEUR d) { POINTEUR p = malloc(sizeof(NOEUD)). chapitre 14) pour ranger les mots. du point de vue technique il n’est pas possible de d´clarer un champ d’une structure comme e ayant le mˆme type que la structure elle-mˆme (une telle structure serait ipso facto de taille infinie !). – soit l’adresse d’une structure constitu´e de trois champs : une information et deux descendants qui sont e des adresses d’arbres binaires. Algorithmes en langage C. typedef struct noeud { char *mot. Ainsi. } NOEUD. . *libre = espace.h> #include <stdio. e e e e 6.5. – soit un triplet ( valeur .h> #define MAXCARS 3000 char espace[MAXCARS]. mais ` la place d’une table on utilise un e a arbre binaire de recherche (voir. return p. on peut donner la d´finition r´cursive suivante : un arbre binaire est e e – soit un symbole conventionnel signifiant  la structure vide  .. Typiquement. par exemple. p->fils_gauche = g. }. par exemple. On e e contourne cette difficult´ en utilisant les pointeurs.2. La structure de donn´es mise en œuvre est repr´sent´e sur la figure 16. v = (*sin)(x). Parmi les plus fr´quemment rencontr´es : e e e e e e e les listes et les arbres. l’exp´rience e montre que cette tol´rance n’a pas de cons´quences n´fastes.4 6. 2004 81 . *fils_droit. p->mot = m. *POINTEUR. e e Bien entendu. p->fils_droit = d. fils g . } c H.

6.4 Structures r´cursives e

6

POINTEURS

POINTEUR insertion(POINTEUR r, char *mot) { if (r == NULL) r = alloc(mot, NULL, NULL); else if (strcmp(mot, r->mot) <= 0) r->fils_gauche = insertion(r->fils_gauche, mot); else r->fils_droit = insertion(r->fils_droit, mot); return r; }

libre

j e a n \0 a n d r é \0 p a u l \ 0
racine mot fils gauche fils droit

...

...

...

...

Fig. 16 – Arbre binaire de mots void parcours(POINTEUR r) { if (r != NULL) { parcours(r->fils_gauche); printf("%s\n", r->mot); parcours(r->fils_droit); } } main() { POINTEUR racine = NULL; char *p; p = gets(libre); while(*p != ’\0’) { libre += strlen(p) + 1; racine = insertion(racine, p); p = gets(libre); } parcours(racine); } 6.4.3 Structures mutuellement r´cursives e

Donnons-nous le probl`me suivant : repr´senter une  liste de familles  ; chaque famille est une  liste e e d’individus , avec la particularit´ que chaque individu fait r´f´rence ` sa famille. Les structures famille et e ee a individu se pointent mutuellement, chacune intervenant dans la d´finition de l’autre. Comment les d´clarer ? e e Pour r´soudre ce probl`me le compilateur C tol`re qu’on d´clare un pointeur vers une structure non encore e e e e d´finie. Plus pr´cis´ment, si la structure ayant le nom indiqu´ n’a pas encore ´t´ d´clar´e, l’expression  struct e e e e ee e e 82
c H. Garreta, 2004

...

6 POINTEURS

6.4

Structures r´cursives e

...

...
Liste de familles Liste d'individus

...

...

Fig. 17 – Structures mutuellement r´cursives e

nom  est l´gitime. Elle identifie un type incomplet et on peut l’utiliser ` tout endroit o` sa taille n’est pas e a u requise, notamment dans la d´claration d’un pointeur vers une telle structure. Cela nous permet de d´clarer nos e e structures mutuellement r´cursives sous la forme : e typedef struct famille { char *nom; struct individu *membres; struct famille *famille_suivante; } FAMILLE; typedef struct individu { char *prenom; struct individu *membre_suivant; struct famille *famille; } INDIVIDU; Une autre solution aurait ´t´ : ee typedef struct famille FAMILLE; typedef struct individu INDIVIDU; struct famille { char *nom; INDIVIDU *membres; FAMILLE *famille_suivante; }; struct individu { char *prenom; INDIVIDU *membre_suivant; FAMILLE *famille; };

...

c H. Garreta, 2004

83

´ 7 ENTREES-SORTIES

7

Entr´es-sorties e

Strictement parlant, les op´rations d’entr´e-sortie ne font pas partie du langage C ; elles sont effectu´es par e e e des fonctions de la biblioth`que que chaque syst`me offre avec le compilateur. Or, C est n´ et a v´cu longtemps e e e e en milieu UNIX avant d’ˆtre transport´ ailleurs ; la biblioth`que UNIX a donc souvent ´t´ prise pour r´f´rence, e e e ee ee semant quelque confusion car la r´alisation de certaines fonctions d’UNIX est inutile, voire impossible sur e certains syst`mes. La norme ANSI y a mis bon ordre ; en principe la liste des fonctions standard est maintenant e clairement ´tablie et un programme qui n’utilise que ces fonctions doit pouvoir ˆtre transport´ d’un syst`me e e e e a ` un autre sans modification. Rien n’empˆche que telle ou telle r´alisation particuli`re du langage propose des e e e fonctions additionnelles ; il faudra alors vous r´f´rer ` la documentation sp´cifique. ee a e

7.1

Flots

Quelles que soient les particularit´s du syst`me d’exploitation sous-jacent, les fonctions de la biblioth`que e e e standard des entr´es-sorties font en sorte que les fichiers soient vus par le programmeur comme des flots, c’este a `-dire des suites d’octets qui repr´sentent des donn´es selon une des deux modalit´s suivantes : e e e – soit le fichier contient les caract`res qui constituent l’expression ´crite des donn´es en question d’apr`s une e e e e certaine syntaxe. Ces caract`res sont eux-mˆmes repr´sent´s selon une convention largement r´pandue, e e e e e presque toujours le code ASCII. De tels fichiers sont appel´s fichiers de texte. Leur principale qualit´ est e e d’ˆtre exploitables par un logiciel, un syst`me ou un ´quipement diff´rents de celui qui les a produits ; en e e e e particulier, ils peuvent ˆtre affich´s, ´dit´s, imprim´s, etc. ; e e e e e – soit le fichier contient des donn´es enregistr´es sous une forme qui est la copie exacte de leur codage dans e e la m´moire de l’ordinateur. On l’appelle alors fichier binaire. Les op´rations de lecture ou d’´criture sur de e e e tels fichiers sont tr`s rapides, car elles ne requi`rent pas un travail d’analyse ou de synth`se de l’expression e e e ´crite des donn´es. En revanche, les fichiers binaires ne sont pas ´ditables ou imprimables ; ils ne sont pas e e e cens´s ˆtre transportables d’un logiciel ` un autre, encore moins d’un syst`me ` un autre. e e a e a Du point de vue du langage C (il n’en est peut ˆtre pas de mˆme pour le syst`me sous-jacent) ˆtre de texte e e e e ou binaire n’est pas une propri´t´ d’un fichier, mais de la mani`re dont il est trait´ par un programme. Ainsi ee e e la distinction entre ces deux types de fichiers ne se fait pas lors de la d´claration ou de l’ouverture51 du fichier, e mais par le choix des fonctions de lecture ou d’´criture qui sont utilis´es : certaines fonctions (fgets et fputs, e e lecture et ´criture d’une ligne, et fscanf et fprintf, lecture et ´criture de donn´es format´es) n’ont de sens que e e e e sur un flot de texte. D’autres fonctions (fread, fwrite) effectuent la lecture et l’´criture de paquets d’octets e sans interpr´tation aucune, et correspondent donc plutˆt aux flots binaires52 . e o Les flots binaires sont de simples suites d’octets. Les flots de texte ont une structure l´g`rement plus riche, e e puisqu’ils sont cens´s ˆtre organis´s comme des suites de lignes. Chaque ligne est faite d’un nombre quelconque e e e de caract`res distincts de ’\n’ et se termine par le caract`re ’\n’. Mˆme si sur un syst`me particulier les fichiers e e e e de texte ne sont pas ainsi organis´s, ou si le caract`re qui indique la fin d’une ligne est diff´rent, la biblioth`que e e e e standard fera en sorte qu’ils puissent ˆtre vus de cette mani`re par le programmeur53 . e e Tampons. Un fichier est dit tamponn´ 54 lorsque les transferts physiques d’octets entre le fichier et un e programme qui le lit ou l’´crit se font par paquets de taille fixe, la taille du tampon, mˆme si les ´changes e e e logiques sont faits par paquets de taille variable ou mˆme octet par octet. Par d´faut un flot est toujours associ´ e e e a ` un tampon de taille pr´d´termin´e, mais le programmeur peut, ` l’aide de la fonction setvbuf (cf. section e e e a 7.1.1), modifier la taille de ce dernier ou mˆme le supprimer compl`tement. e e Ce mode de gestion des fichiers r´duit le nombre d’op´rations physiques d’entr´e - sortie (beaucoup plus e e e lentes, quelles que soient les performances des disques durs actuels, que les transferts d’information purement internes) mais introduit un d´calage dans leur chronologie. Ce que le programmeur croit ˆtre une op´ration e e e d’´criture dans un fichier n’est en r´alit´ qu’une  op´ration logique , c’est-`-dire une recopie dans un tampon e e e e a situ´ en m´moire, dont le contenu sera transf´r´ ult´rieurement vers le fichier. Par cons´quent, il est difficile ou e e ee e e impossible de savoir dans quel ´tat se trouve le fichier ` un moment donn´ ; de plus, si le programme vient ` e a e a subir une terminaison anormale, une partie des informations logiquement ´crites dans le fichier seront perdues, e
y a quand-mˆme un point de d´tail qu’il fait fixer lors de l’ouverture (voyez la fonction fopen) : y a-t-il lieu de guetter les e e marques de fins-de-ligne pour les transformer dans le caract`re ’\n’ et r´ciproquement ? e e 52 Cons´quence : sauf si le syst`me sous-jacent l’interdit, un fichier ´crit comme un flot de texte peut ˆtre lu comme un flot binaire. e e e e Cela revient ` ignorer le fait que les octets dont il se compose repr´sentent des donn´es d’un ordre sup´rieur. La d´marche inverse, a e e e e lire comme un flot de texte un fichier binaire, n’a pas de signification et peut provoquer des erreurs fatales (les fins de ligne seront absentes ou incomprises, le tampon d´bordera, etc.). e 53 En particulier, le couple (CR,LF ) utilis´ par certains syst`mes est traduit en lecture par l’unique caract`re ’\n’. Inversement, e e e sur de tels syst`mes, l’´criture  logique  du caract`re ’\n’ se traduit par l’´criture effective du couple (CR,LF ). e e e e 54 En bon franglais, on dit plutˆt  bufferis´ . o e
51 Il

84

c H. Garreta, 2004

Le nom doit ˆtre correct pour le e e e e syst`me d’exploitation sous-jacent . chaque occurrence d’une marque de fin de ligne est d´tect´e et. Le e descripteur du flot cr´´ est positionn´ en ´criture et ` la fin du fichier.1. z´ro dans les autres cas. "ab". C’est notamment le cas d’UNIX. o` les fins de ligne sont indiqu´es par le caract`re ’\n’. "a+b" : si on envisage d’utiliser le fichier en mode binaire il faut ajouter la lettre b au mode ("r+b". const char *mode) Cette fonction ouvre le fichier dont le nom est indiqu´ par la chaˆ nom et rend un pointeur sur le flot e ıne correspondant. Sur l’int´rˆt qu’il peut y avoir ` effectuer des lectures et des ´critures sur le mˆme fichier. "wb". Le fichier peut exister ou non . La structure FILE est e e d´finie dans le fichier <stdio. ee a e e voir les remarques faites ` la section 7. mais les op´rations d’´criture sont permises aussi. e "rb". cette fonction provoque l’´criture  physique  imm´diate du tampon en e e cours de remplissage. Seules les op´rations d’´criture ee e e a e e sont permises. etc.4. Un fichier est donc repr´sent´ en C par une variable d’un type pointeur. Remarque 1. seules les op´rations de lecture ee e e e sont permises sur ce flot. La seule diff´rence que la lettre b dans le mode introduit dans le fonctionnement de toutes e les fonctions d’entr´e-sortie est la suivante : si le fichier est  de texte  (i. "a+" comme "a". section 7.3 ` propos des fichiers en acc`s relatif. ee ee 7.1. seules les op´rations d’´criture sont permises sur ce flot.h> puis une d´claration de la forme e FILE *fichier . Le fichier doit exister . son contenu est enti`rement e e effac´. son contenu n’est pas d´truit. si la lettre b ne figure pas e dans le mode) alors : – en lecture. l’appel de cette fonction sur un flot d’entr´e e e e 55 S’il existe une probabilit´ non n´gligeable pour qu’un programme ait une fin anormale (par exemple ` cause de conditions e e a d’exploitation difficiles) il est prudent de programmer des appels r´guliers de la fonction fflush. sa d´claration e e e e n’autorise aucune des op´rations propres aux fichiers. 2004 85 .´ 7 ENTREES-SORTIES 7. Parmi les principales fonctions qui effectuent les op´rations g´n´rales (ouverture. Mais pour la plupart des biblioth`ques actuelles. e e e e e e – toute demande d’´criture du caract`re ’\n’ produit en fait l’envoi effectif au fichier de la marque de fin de ligne requise par le syst`me sous-jacent. e Les valeurs permises pour mode sont : "r" (read ) ouverture d’un fichier.h>. son contenu n’est pas effac´. cf. e e "w" (write) cr´ation d’un fichier. Le fichier existe ou non . e D’apr`s la norme officielle du langage C. Le descripteur du flot cr´´ est positionn´ en ´criture et au d´but du fichier (qui est vide). fermeture. l’effet de fflush sur un flot qui n’est pas un flot de sortie est e ind´fini. mais les op´rations de lecture sont permises aussi. aussi longtemps qu’on n’aura pas fait le n´cessaire (voyez e e la fonction fopen ci-apr`s) pour qu’il pointe sur une structure l´gitimement allou´e et qui constitue la description e e e d’un fichier ouvert. e e "w+" comme "w". En principe. Pour utiliser un ou plusieurs flots il faut donc ´crire en tˆte de son programme e e e la directive #include <stdio. a a e int fflush(FILE *flot) Dans le cas d’un flot de sortie. "r+b". remplac´e par l’unique caract`re ’\n’. Elle rend EOF en cas d’erreur. s’il existe. Garreta.1 Flots parce qu’elles n’ont jamais ´t´ physiquement transf´r´es55 . "r+" comme "r".1. e Il n’est donc pas exclu que sur un syst`me d’exploitation donn´ l’ajout de la lettre b au mode soit sans e e effet. En e ee e e e principe. "wb+" et "ab+") . e "a" (append) allongement d’un fichier. Par cons´quent.1 Fonctions g´n´rales sur les flots e e Les flots sont repr´sent´s dans les programmes par des variables de type FILE *. cela ne regarde pas le langage C. "w+b" et "a+b" peuvent se noter aussi "rb+". mais les op´rations de lecture sont permises aussi.) sur les flots e e e nous avons : FILE *fopen(const char *nom. si cette marque n’est pas le e e caract`re ’\n’ lui-mˆme. u e e Remarque 2.). ou NULL si l’op´ration a ´chou´ (fichier absent. "w+b". s’il existe. e c H. Le descripteur du e flot cr´´ est positionn´ en lecture et au d´but du fichier. etc.e.

FILE *tmpfile(void) Cette fonction cr´e un fichier temporaire en mode "w+b". un fichier qui a ´t´ ouvert en lecture n’est en g´n´ral pas une ee e e erreur importante. Garreta.. sans nom externe. "rb"). Si le fichier physique qui correspond au flot indiqu´ est un organe interactif. . etc. ainsi. e int fclose(FILE *flot) Cette fonction ferme le flot indiqu´. par un appel de fclose. car e ee e eee – le contenu du tampon d’´criture en cours de formation lors de la terminaison du programme ne sera e probablement pas sauvegard´ dans le fichier . une lecture au clavier provoque la vidange du e e tampon d’´criture ` l’´cran. la restitution de l’espace e e allou´ pour le tampon et les descripteurs du flot. Cela se produit non pas lorsque le e fichier est positionn´ sur sa fin mais lorsqu’il est plac´ au-del` de sa fin. Par exemple. Elle rend z´ro en cas de succ`s. Elle renvoie le flot associ´ ou NULL en cas d’erreur.h>) contient alors un code e e e e e renseignant sur la nature de l’erreur. le rendre vrai. e Cette sp´cification de la fonction feof fonde le sch´ma suivant. e – dans certains syst`mes. Cela permet qu’une question soit effectivement affich´e avant que l’utilisae a e e teur ne doive taper la r´ponse correspondante. e e a Autrement dit. int ferror(FILE *flot) Cette fonction renvoie une valeur non nulle si et seulement si l’indicateur d’erreur du flot indiqu´ est e  vrai . char *tampon. par exemple e l’´cran d’un poste de travail. int mode. A la suite d’un appel comme fclose(flot) l’emploi de la variable flot est ill´gitime. fichier = fopen(nom. Un fichier qui n’a jamais ´t´ ferm´ a donc une existence tr`s pr´caire. l’appel fflush(stdin) fait disparaˆ tous les caract`res d´j` tap´s mais pas encore ıtre e ea e lus par le programme. Elle renvoie une valeur non nulle si ee e et seulement si l’indicateur de fin de fichier du flot indiqu´ est vrai. En particulier. Mais oublier de fermer un fichier qui a ´t´ ouvert en ´criture (c’est-`-dire un fichier ee e a qui vient d’ˆtre cr´´) peut ˆtre fatal pour l’information qui y a ´t´ ´crite.. feof n’est jamais vrai apr`s l’ouverture (en lecture) du fichier. ´ventuellement. dans le cas fr´quent o` l’entr´e standard e e u e correspond au clavier.. les fichiers nouvellement cr´´s ne subissent les op´rations qui les rendent connus e ee e du syst`me d’exploitation (comme l’inclusion de leur nom dans un r´pertoire) qu’au moment de leur e e fermeture.. La variable globale enti`re errno (d´clar´e dans <errno. v´rification du succ`s de l’ouverture de fichier e e .. e a e int setvbuf(FILE *flot. qui sera automatiquement e d´truit ` la terminaison du programme. une autre valeur en e e e cas d’erreur. size t taille) 86 c H. feof(fic) devient vrai imm´diatement apr`s qu’une lecture sur fic ait ´chou´ parce qu’il e e e e n’y avait plus [assez] d’informations dans fic pour la satisfaire.1 Flots ´ 7 ENTREES-SORTIES supprime les caract`res disponibles dans le tampon. – le d´but d’une op´ration de lecture sur l’unit´ d’entr´e associ´e (les organes d’entr´e-sortie interactifs e e e e e e forment g´n´ralement des couples) . ee e e e int feof(FILE *flot) Cette fonction n’a d’int´rˆt que si flot d´signe un fichier en lecture. Remarque. produisant l’´criture physique des tampons. Il faut au moins une [tentative de] lecture pour. alors la fonction fflush est implicitement appel´e dans deux circonstances e e tr`s fr´quentes : e e e e e – l’´criture du caract`re ’\n’ qui produit l’´mission d’une marque de fin de ligne et la vidange effective du tampon.. par exemple.7. e Oublier de fermer. 2004 . lecture de donn´es sur fichier e while ( ! feof(fichier)) { traitement des donn´es lues e lecture de donn´es sur fichier e } . mˆme lorsque ce dernier e e est vide. Elle doit ˆtre appel´e imm´diatement apr`s une entr´e-sortie sur ce flot pour savoir si l’op´ration e e e e e e en question a ´chou´. de lecture s´quentielle d’un fichier : e e e FILE *fichier.

supposons e e que prog2 soit un autre programme qui fait ses entr´es-sorties de la mˆme mani`re. L’´criture ou la lecture physique a lieu lors de la rencontre du caract`re e a e e ’\n’ qui d´termine la fin des lignes. Activ´ par la commande prog1. e e d´finies dans <stdio. la commande prog1 < fichier1 | prog2 > fichier2 active prog1 et prog2. Si tampon est e e a e NULL. Ils sont connect´s (on dit affect´s) aux e e e organes d’entr´e-sortie les plus  naturels  par rapport ` la mani`re dont on utilise l’ordinateur. il lira ses donn´es au clavier et affichera ses r´sultats ` l’´cran. 2004 87 . Elle doit ˆtre appel´e apr`s l’ouverture du e ea e e e flot mais avant toute lecture ou ´criture. l’ex´cution d’un programme coma e e mence avec trois flots de texte automatiquement ouverts par le syst`me. c H. Garreta. ou EOF si la fin du fichier est atteinte ou si une erreur e e survient. e a Bien entendu. L’´criture ou la lecture physique a lieu lorsque le tampon contient taille e caract`res. Mais e e e a e s’il est lanc´ par la commande e prog1 < fichier1 (resp. e IONBF Pas de tampon. Elle est habituellement affect´e au clavier du poste de travail. e a e L’argument taille indique la taille voulue pour le tampon.´ 7 ENTREES-SORTIES 7. Ces fichiers e a e sont d´clar´s dans <stdio. Les fonctions de lecture (resp.1 Lecture et ´criture textuelles e Lecture et ´criture de caract`res et de chaˆ e e ınes Les fonctions de lecture-´criture les plus simples effectuent la lecture ou l’´criture d’un caract`re isol´ ou e e e e bien la lecture ou l’´criture d’une ligne. tout cela se combine. ses r´sultats sont fournis ` titre de e e a donn´es ` prog2.2 Les unit´s standard d’entr´e-sortie e e Sans que le programmeur n’ait ` prendre aucune disposition particuli`re. En UNIX et MS-DOS cela fonctionne de la e mani`re suivante : supposons que prog1 soit un programme qui n’utilise que les unit´s standard stdin et e e stdout. *stderr. C’est une vraie fonction.2 Lecture et ´criture textuelles e Cette fonction red´finit le tampon associ´ ` un flot ouvert. L’argument tampon. e e Remarque. dont les r´sultats sont ´crits dans fichier2. e a e e 7. L’utilisation des unit´s standard apparaˆ encore plus int´ressante lorsque l’on sait que dans e ıt e UNIX et plusieurs autres syst`mes d’exploitation il est permis de r´affecter les unit´s standard lors d’une e e e ex´cution du programme. alors la fonction alloue un espace propre.2 7. Par exemple. e e a e e e a e stderr est l’unit´ standard d’affichage des erreurs. Les donn´es de prog1 sont lues dans fichier1. Une ´criture ou une lecture physique a lieu ` chaque lecture ou ´criture logique. L’argument mode doit ˆtre une des trois constantes suivantes.2. stdout).h>. Elle est habituellement affect´e ` l’´cran du poste de travail. est cens´ pointer un espace (de taille au moins ´gale ` taille) qui sera utilis´ comme tampon. fichier2) remplacera le clavier (resp. l’´cran). de la mani`re suivante : e e e FILE *stdin. 7. Elle est aussi affect´e ` l’´cran du poste de travail. stdin est l’unit´ standard d’entr´e. *stdout. Pour la lecture nous avons : e int fgetc(FILE *flot) Renvoie le caract`re suivant sur le flot indiqu´.1. e e e stdout est l’unit´ standard de sortie. d’´criture) qui ne sp´cifient pas un autre flot utilisent stdin (resp. De mani`re similaire. Alors la commande e e e prog1 | prog2 active en parall`le ces deux programmes avec la sortie standard de prog1 connect´e ` l’entr´e standard de prog2 e e a e (les sorties de prog1 sont lues et exploit´es par prog2 au fur et ` mesure qu’elles sont produites par prog1). s’il n’est pas NULL. sans qu’il faille recompiler ce dernier. : prog1 > fichier2) alors le fichier fichier1 (resp.h> : e IOFBF Tampon de taille fixe. e IOLBF Tampon ´gal ` une ligne.

e Exemples. tout appel de gets peut entraˆ e ıner un d´bordement de m´moire. } s[i] = ’\0’. Par exemple. e a char *gets(char *s) Lecture d’une ligne sur le flot stdin. e e C’est la raison pour laquelle on dit que tout programme comportant ne serait-ce qu’un appel de gets est bogu´. cette fonction lit des caract`res sur a e e e e le flot indiqu´ et les place dans l’espace point´ par s. Elle renvoie e e e le mˆme r´sultat que fgets mais.7. } D’autre part. Un e e e e caract`re ’\0’ est ajout´ ` la fin du tableau. Pour illustrer ces fonctions. autrement dit. tandis que e *s++ = getc(table_de_fichiers[i++]) est certainement erron´e. a e e e e e 57 Ainsi. car ayant un effet ind´fini (on ne peut pas dire combien de fois i sera e e incr´ment´) . Elle s’arrˆte lorsqu’elle a lu n−1 caract`res ou e e e e lorsqu’elle a rencontr´ un caract`re ’\n’ de fin de ligne (ce caract`re est copi´ dans le tableau). e e – lorsqu’il faut obtenir l’adresse de l’op´ration en question. int n. c = getchar(). dans lequel gets puisse ranger les caract`res qui seront lus. l’´criture d’une fonction e e analogue ` gets en utilisant getchar : a char *myGets(char *s) { int c. L’argument de gets doit ˆtre l’adresse d’un espace disponible (accessible en ´criture) et de e e taille suffisante. while (c != ’\n’) { if (c == EOF) return NULL. e – le caract`re de fin de ligne est remplac´ par ’\0’. return s. c = getchar(). e e Atention. Les caract`res lus sont rang´s dans l’espace point´ par s. Plus pr´cis´ment. FILE *flot) Lecture d’une ligne en veillant ` ne pas d´border. sans se pr´occuper de savoir si l’espace dans lequel ces caract`res e a e e sont rang´s est de taille suffisante pour les recevoir . e a char *fgets(char *s. pour la passer comme argument d’une autre e fonction. c’est-`-dire de simples transferts entre m´moires. ou NULL si une erreur ou la fin du fichier e ea a ´t´ rencontr´e. un appel comme appliquer(getc. l’´criture d’une fonction analogue ` getchar (version fonction) en utilisant gets : e a 56 Il s’agit d’entr´es-sorties tamponn´es. Garreta. le e e a e e e gain de temps obtenu en utilisant getc ` la place de fgetc aurait ´t´ n´gligeable devant le temps requis par l’entr´e-sortie elle-mˆme. D’une part. contrairement ` cette derni`re e e a e – il n’y a pas de test de d´bordement57 .2 Lecture et ´criture textuelles e 7 ´ ENTREES-SORTIES int getc(FILE *flot) Fait la mˆme chose que fgetc mais c’est peut ˆtre macro. voici deux exercices d’´cole. Il ne faut pas oublier cependant qu’il y a deux cas o` les macros ne peuvent pas ˆtre u e utilis´es : e – lorsque l’argument a un effet de bord. ee e Lisez aussi la remarque faite ci-apr`s ` propos de la fonction gets. e e L’int´rˆt d’utiliser une macro r´side dans l’efficacit´ sup´rieure des (petites) macros par rapport aux ee e e e (petites) fonctions56 . La fonction rend s. Une expression comme *s++ = fgetc(table_de_fichiers[i++]) est correcte (mais peu utilis´e). S’il s’´tait agi d’op´rations physiques. si getc est une macro. 2004 . gets lit des caract`res jusqu’` la rencontre de ’\n’. s[i++] = c. e 88 c H. fichier) sera certainement trouv´ incorrect par le compilateur. e int getchar(void) getchar() ´quivaut ` getc(stdin) . i = 0.

static char *pos = tampon. } return *pos++.. expr 1 . c est le caract`re suivant le nombre */ e ungetc(c. int putchar(int caractere) putchar(c) ´quivaut ` putc(c. flot). "\n"). FILE *flot)  D´lecture  du caract`re c.2.’0’. Contrairement e ıne e e a ` fputs un caract`re ’\n’ est ajout´ ` la suite de la chaˆ e ea ıne. expr n ) c H. c = getc(flot). stdout). FILE *flot) Fait la mˆme chose que fputc mais peut ˆtre une macro. e a e e e e o` la fin de certaines unit´s lexicales est indiqu´e par un caract`re qui les suit sans en faire partie.´ 7 ENTREES-SORTIES 7. x = 0. Cette fonction remet le caract`re c dans le flot indiqu´. } 7. int ungetc(int c. while (’0’ <= c && c <= ’9’) { x = 10 * x + c . Elle renvoie le mˆme r´sultat que fputs. return x. une valeur ≥ 0 dans les ıne e autres cas. if (*pos == 0) { if (gets(tampon) == NULL) return EOF. FILE *flot) ´ Ecrit le caractere indiqu´ sur le flot indiqu´. e a int fputs(const char *s. e e e e C’est une vraie fonction. Renvoie le caract`re ´crit ou EOF si une erreur survient. c = getc(flot).2 Lecture et ´criture textuelles e int myGetchar(void) { static char tampon[256] = "". pos = tampon. FILE *flot) ´ Ecrit la chaˆ s sur le flot indiqu´. Voir ` ce propos les remarques faites ` l’occasion e e a a de l’introduction de getc. Sur chaque flot. int puts(const char *s) Cette fonction ´crit la chaˆ s sur le flot stdout. expr 2 . 2004 89 . qui en est le fournisseur) d’un e e a e e caract`re lu ` tort. int putc(int caractere. } Voyons maintenant les fonctions d’´criture : e int fputc(int caractere. e e Cette op´ration incarne donc l’id´e de restitution (` l’unit´ d’entr´e. strcat(tampon. } /* ici..2 Ecriture avec format printf La fonction d’´criture sur l’unit´ de sortie standard (en principe l’´cran du poste de travail) avec conversion e e e et mise en forme des donn´es est : e printf( format . Exemple : u e e e la lecture d’un nombre entier non n´gatif : e int lirentier(FILE *flot) { /* lecture d’un entier (simpliste) */ int c. seule la place pour un caract`re  d´lu  est garantie. . Garreta. C’est une mani`re de r´soudre un probl`me que pose l’´criture d’analyseurs lexicaux. o` il sera trouv´ e e e e u e lors de la prochaine lecture. La fonction renvoie EOF en cas d’erreur. On ne e e peut pas  d´lire  le caract`re EOF.

x).2f<". printf("printf(">%-10s<". avec l’affichage produit par chacun : Appel printf("printf(">%5s<".2f<". avec un nombre de chiffres apr`s la virgule e e e a e ´gal ` la valeur de j.2f<".46< >123< >123. l.0f<".456001< > 123.456. printf(">%. s).2f<". e a 90 c H.7.2f<". printf(">%+-12. expr 2 . i.*s<". printf(">%-12. ıne o e e e m´lang´s ` des indications sur les types des expressions expr 1 . s).46 < > 123.0f<".46 < > 123.5s<".46< >123.2 Lecture et ´criture textuelles e 7 ´ ENTREES-SORTIES format est une chaˆ qui joue un rˆle particulier. Voici une liste d’appels de printf. x). s). Il en est de mˆme e e e pour la sp´cification de la pr´cision (cf. tableau 4. printf("printf(">%-10.2f<". int l = -10. j..5s<". i et j ´tant des variables enti`res et x e e ˚ e e une variable flottante. La liste suivante illustre l’utilisation de la largeur et de la pr´cision pour les chaˆ e ınes. printf(">%.*f". e Exemple 1. Supposons avoir donn´ les d´clarations : e e char *s = "ABCDEFG". tableau 4. La fonction printf recopie sur le flot de sortie tout caract`re qui ne fait pas partie d’une telle e sp´cification. e Cela fonctionne de la mani`re suivante : la fonction printf parcourt la chaˆ format et reconnaˆ certains e ıne ıt groupes de caract`res comme ´tant des sp´cifications de format. x). Affichage obtenu >ABCDEFG< > ABCDEFG< >ABCDEFG < > ABCDE< >ABCDE < >ABCDE< >ABCDE < La largeur du champ (cf. e e Notez que seuls les ´l´ments 1 (le caract`re %) et 6 (la lettre qui indique le type de la conversion) sont ee e obligatoires dans une sp´cification de format. x). qui doit ˆtre de type entier. C’est le caract`re % qui d´clenche cette ree e e e e connaissance. Par exemple. Voici une liste d’appels de printf. l’expression printf("%*. s). Supposons qu’on ait donn´ la d´claration-initialisation e e float x = 123. ´crit la valeur de x sur un nombre de caract`res ´gal ` la valeur de i. s). Elle ˚ e e e a est alors d´finie par la valeur de l’argument dont c’est le tour. n = 5. n 4)..46< >000000123. n. avec l’affichage produit par chacun : Appel printf(">%f<". s). printf("printf(">%. printf(">%#.46 < >+123. Affichage obtenu >123. La ieme sp´cification d´termine la mani`re dont la valeur de expr i sera ´crite. x). printf(">% -12. printf("printf(">%*. e e e e e Les sp´cifications de format reconnues par la fonction printf sont expliqu´es dans la table 4 (page 91). x).5s<".< Exemple 2. printf("printf(">%10. printf(">%12. printf("printf(">%10s<". . printf(">%012. x).2f<". printf(">%12. x). x). printf(">%12f<". Elle est form´e de caract`res qui seront ´crits normalement. expr n et sur l’aspect sous lequel il faut e e a ´crire les valeurs de ces expressions. x).46< >123. s). n 3) peut ˆtre indiqu´e par le caract`re * ` la place d’un nombre. x).456001< > 123. x). Garreta. 2004 .

ıne e f : argument : double . Cette indication est sans effet e a si un int et un long sont la mˆme chose . e e x : argument : int . e n : argument : int *. un point suivi d’un nombre qui indique la pr´cision : e – dans les conversions e. automatiquement augment´e si elle se r´v`le insuffisante. Facultativement.. lorsqu’il existe (voir ci-dessous). Le nombre de f est donn´ par la pr´cision. e e a # : utiliser le format alternatif. Obligatoirement. e espace : si le premier caract`re n’est pas un signe. a e a + : imprimer le signe du nombre mˆme lorsque celui-ci est positif . Cette indication est sans effet si un int et un short sont e a la mˆme chose . des z´ros seront e e e ajout´s ` gauche. dans l’ordre indiqu´. Facultativement. Il s’agit d’une largeur minimum. Pr´cision par d´faut : 6. e 2. e e o : argument : int . Si la pr´cision vaut z´ro. fprintf et sprintf e c H. a g : argument : double .. le cas ´ch´ant. des ´l´ments suivants : e e ee 1. g ou G) : l’argument est un long double. e e c : argument : unsigned char . e e Format alternatif : le nombre est pr´c´d´ de 0x. e l (associ´e ` d. E. e Format alternatif (voir # au point 2) : un 0 est imprim´ au d´but du nombre. parmi e d : argument : int . e s : argument : char * .: cadrer ` gauche du champ (par d´faut le cadrage se fait ` droite) . Pr´cision par d´faut : 6. e e e X : comme x mais avec ABCDEF et 0X ` la place de abcdef et 0x. impression : notation en  virgule fixe  [-]zzz. a p : argument : void * . impression : d´pend de l’implantation (par exemple %X). e e : argument : double.´ 7 ENTREES-SORTIES 7. e Tab. E ou f : le nombre de chiffres ` ´crire apr`s la virgule d´cimale .ffffff e±nn. Si la pr´cision vaut z´ro. 3. u. le caract`re %. a u : argument : int . e e e e e e e Format alternatif : le point est toujours imprim´. Aucune impression. impression : notation octale non sign´e.9abcdef. e e 0 : compl´ter les nombres par des z´ros (` la place de blancs) . 4 – Sp´cifications de format pour printf. impression : chaˆ de caract`res. e E : comme %e avec E ` la place de e. impression : comme %e si l’exposant est inf´rieur ` -4 ou sup´rieur ` la pr´cision. le point est supprim´.2 Lecture et ´criture textuelles e Chaque sp´cification de format se compose. impression : notation d´cimale sign´e. placer un espace au d´but d’un nombre . Garreta. x ou X) : l’argument est un long ou un unsigned long. e L (associ´e ` f. Facultativement. modifie e e e en cons´quence la largeur du champ : e h (associ´e ` d ou u) : l’argument est un short. une lettre qui compl`te l’information sur la nature de l’argument et. e. e a e a e comme %f sinon. un caract`re de conversion. Impression : notation en  virgule flottante  [-]z. impression : notation d´cimale non sign´e. e G : comme %g avec E ` la place de e. un nombre qui indique la largeur du champ. le point est supprim´. e a 6. 2004 91 . – pour une chaˆ : le nombre maximum de caract`res ` afficher . % : pas d’argument correspondant . ıne e a – pour un entier (conversions d et u) : le nombre minimum de chiffres. impression : notation hexad´cimale non sign´e avec les chiffres 0. ae e e – dans les conversions g ou G : le nombre de chiffres significatifs . qui recevra pour valeur le nombre de caract`res effectivement ´crits jusqu’` pr´sent e e e a e par l’appel en cours de la fonction printf. impression : le caract`re %. Effet : l’argument correspondant doit ˆtre l’adresse d’une e variable enti`re. e e e 4. Format alternatif : le point est toujours imprim´. dans un ordre quelconque. un ou plusieurs modifieurs. Obligatoirement. Facultativement. Le nombre de f est donn´ par e la pr´cision.fff. parmi : . e a 5. e e e e e e Format alternatif : le point est toujours imprim´. impression : un seul caract`re. Le cas ´ch´ant.

EOF. Chaque appel d’une telle fonction e commence par lire le premier caract`re que l’appel pr´c´dent n’a pas  consomm´ . Lecture de deux entiers s´par´s par une virgule et encadr´s par une paire de parenth`ses : e e e e scanf("(%d. e – S’il n’y a pas d’espacement. Jusque-l` : a – Tout caract`re ordinaire du format. adresse n ) L’argument format est une chaˆ qui indique la mani`re de convertir les caract`res qui seront lus . &calu). les espacements au d´but des nombres sont toujours saut´s. Donn´es incorrectes : e (trop de blancs). Lecture d’un entier : scanf("%d". Elle rend la main lorsque la fin du format est atteinte ou sur une erreur. l’activation de scanf se termine. 33) (des blancs.. le programme e e 58 Rappelons que fgetc. Voir le tableau 5.%d ) )". Exemple 2. . Garreta. &x). e e e – Cependant. mais uniquement devant les nombres). Exemple de donn´e incorrecte : e e e e Exemple 4. &y). par cons´quent il est essentiel que ces arguments soient des adresses de variables ` lire. e e e e Exemple 1. commencez par v´rifier e les valeurs lues par la fonction scanf (par exemple avec des appels de printf suivant imm´diatement les appels e de scanf). adresse 1 . notez bien qu’ici nous e sommes oblig´s de d´clarer la variable calu de type char . les donn´es lues ont de nombreuses occasions d’ˆtre e e incorrectes et on peut s’attendre ` ce que scanf soit d’un maniement difficile. mais leur pr´sence indique e e que les donn´es correspondantes peuvent ˆtre s´par´es dans le flot d’entr´e par un nombre quelconque de e e e e e caract`res d’espacement ou de fin de ligne. e e On peut m´langer des appels de scanf et d’autres fonctions de lecture. Elles indiquent la mani`re d’analyser les caract`res lus e e e sur le flot d’entr´e et de ranger les valeurs ainsi obtenues. c’est-`-dire qui n’est ni un caract`re d’espacement (blanc. Comme ci-dessus mais avec une syntaxe plus souple (les blancs sont tol´r´s partout) : ee scanf(" ( 22 . getc et getchar font ce travail bien plus simplement. . dans le format. ıne e e adresse 2 . les caract`res ordinaires et les sp´cifications peuvent ˆtre s´par´s entre eux par des e e e e e caract`res d’espacement. Cependant. e a La fonction scanf constituant un vrai analyseur lexical. tabulation) e a e ni un caract`re faisant partie d’une sp´cification de format (commen¸ant par %). Ce sont des  sorties  de la e fonction scanf.%d)". Les sp´cifications de format reconnues par la fonction scanf sont expliqu´es dans la table 5 (page 93). Lecture d’un caract`re58 : e char calu.3 Lecture avec format scanf La fonction de lecture avec format ` l’unit´ d’entr´e standard (en principe le clavier du poste de travail) est a e e scanf( format . 92 c H.7. Toutes les donn´es de l’exemple pr´c´dent sont correctes. ou 33 ) ( 22.. le caract`re courant est lu. (%d 33 . Si cette identification a lieu. adresse 1 .. &x.33) ( 22 . L’exp´rience ne d´coit pas cette a e e¸ attente ! Voici un bon conseil : si un programme ne fonctionne pas comme il le devrait. e – Dans le format. alors les e e donn´es correspondantes dans le flot d’entr´e doivent ˆtre adjacentes. scanf("%c". &x.2 Lecture et ´criture textuelles e 7 ´ ENTREES-SORTIES 7. Si l’identification n’a pas lieu. Donn´es correctes : e (22. et le parcours du format se poursuit. adresse 2 . sans ˆtre rang´ e e e e e dans aucune variable.. Remarque. 2004 . &y). doit s’identifier au cae e c ract`re courant du flot d’entr´e.. adresse n indiquent les variables devant recevoir les donn´es lues. Le nombre de ces espacements est sans importance. e e Fonctionnement : scanf parcourt le format. Exemple 3. entre les caract`res ordinaires ou les sp´cifications.2. – Les sp´cifications de format commencent par %.. . Nous avons indiqu´ par ailleurs qu’en C on range souvent les caract`res dans des variables de e e type int afin de permettre la m´morisation d’une valeur  intruse .

Donn´e : nombre exprimant un pointeur c e comme il serait imprim´ par printf avec le format %p. 5.. d’apr`s l’analyse du e e format. f. Les blancs et e e e tabulations ne sont pas pris pour des s´parateurs (ils sont copi´s comme les autres caract`res) et il e e e n’y a pas de ’\0’ automatiquement ajout´ ` la fin de la chaˆ ea ıne. Aucune lecture. a 3. pour la lecture de nombres coll´s les uns aux autres). Devant e. un caract`re de conversion parmi e d – Argument : int *. x : l’argument est l’adresse d’un short (et non pas l’adresse d’un int) .. point d´cimal et suite de chiffres facultatifs. le langage C ne pr´cisant rien ` ce sujet. l devant d. Donn´e : la plus longue suite faite de caract`res appartenant e e e e ` l’ensemblea indiqu´ entre crochets. x : l’argument est l’adresse d’un long (et non pas l’adresse d’un int).caract`re] – Argument : char *. Obligatoirement. Donn´e : entier en notation hexad´cimale (pr´c´d´ ou non de 0x ou 0X). e e x – Argument : int *. Donn´e : entier en octal (pr´c´d´ ou non de 0).. i. g – Comme e. celle-ci re¸oit pour valeur le nombre de caract`res effectivement lus jusqu’` ce point par le e c e a pr´sent appel de la fonction scanf. Donn´e : comme ci-dessus mais les caract`res permis sont e e e e maintenant ceux qui n’appartiennent pas ` l’ensemble indiqu´.  croit  recevoir l’adresse d’un char (un octet). &calu). Facultativement. e e – Argument : float *. u. un nombre donnant la largeur maximum du champ (utile. s – Argument : char *. e ıne e e e tabulation. o. le caract`re %. i.). Un caract`re au moins sera lu. aurait ´t´ incorrect (bien qu’il puisse fonctionner correctement sur certains syst`mes).. une lettre qui apporte des informations compl´mentaires sur la nature de l’argument core respondant : h devant d. e e e a Pour sp´cifier le caract`re ] l’´crire imm´diatement apr`s le [ initial. . Un ’\0’ sera automatiquement ajout´ apr`s les e e caract`res lus. e 4. scanf("%c". Garreta. u. p – Argument : void ** (ou void *. Donn´e : nombre entier en notation d´cimale. Donn´e : entier en notation d´cimale. par exemple. Or on ne doit pas faire d’hypoth`se sur la mani`re dont e e sont organis´s les octets qui composent un entier.´ 7 ENTREES-SORTIES 7. ıne [^caract`re. f – Comme e. signe facultatif et suite de e chiffres) facultatif. e [caract`re. e e i – Argument : int *. L devant e. Facultativement. Ces caract`res sont copi´s dans l’espace dont l’adresse est donn´e en argument. e e e e e c – Argument : char *. octale (pr´c´d´ de 0) ou hexad´cimale e e e e e e (pr´c´d´ de 0x ou 0X)..2 Lecture et ´criture textuelles e Chaque sp´cification de format se compose. le caract`re * qui indique une suppression d’affectation : lire une donn´e comme indiqu´ et e e e l’ oublier  (c’est-`-dire ne pas le ranger dans une variable). Obligatoirement. Un ’\0’ est ajout´ ` la fin de a e e ea la chaˆ lue.. 5 – Sp´cifications de format pour scanf. suite de e a chiffres. alors que scanf. fin de ligne) qui n’en fait pas partie.. Facultativement. Donn´e : entier en notation d´cimale. dans l’ordre indiqu´. e n – Argument : int *. car on y fournit ` scanf ee e a l’adresse d’un entier (fait de plusieurs octets) pour y ranger un caract`re.. des ´l´ments suivants : e e ee 1. Une autre solution e e a c H. e 2. exposant (e ou E. g : l’argument est l’adresse d’un long double (et non pas d’un float). Effet : l’argument correspondant doit ˆtre l’adresse d’une variable e enti`re .caract`re] – Argument : char *. e e e e u – Argument : unsigned int *. Donn´e : autant de caract`res que la largeur du champ l’indique (par d´faut : e e e 1). Donn´e : chaˆ de caract`res termin´e par un caract`re d’espacement (blanc. f. o. Donn´e : nombre flottant. fscanf et sscanf e int calu. Le caract`re % doit se pr´senter sur le flot d’entr´e. e e e o – Argument : int *. c’est-`-dire dans l’ordre : signe facultatif. pour ce que ¸a change. a e % Pas d’argument. e e e e e Tab. 2004 93 . g : l’argument est l’adresse d’un double (et non pas d’un float) .

printf("donne x : "). les blancs ne sont pas de la d´coration. printf("x = %d.. . calu = tmp. y = 33. les caract`res ` lire existant tous d`s le d´part.. Or la seule mani`re pour l’ordinateur de s’assurer qu’il a bien saut´ e e e e tous les caract`res blancs consiste ` lire (sans le consommer) le premier caract`re non blanc qui les suit. &x). Parce qu’on a ´crit. L’op´rateur constatera avec ´tonnement que e e pour que son premier nombre sont pris en compte il est oblig´ de composer le deuxi`me ! Exemple de dialogue e e (le signe ¶ repr´sentant la frappe de la touche  Entr´e ) : e e donne x : 11¶ ¶ ¶ la machine ne r´agit pas. ils jouent un rˆle bien c a e o pr´cis. y = %d\n". Exemple 6.4 A propos de la fonction scanf et des lectures interactives A propos de  sauter les blancs . ce caract`re n’´tant pas consomm´. e e a e e Mais cela peut cr´er des situations confuses dans le cas d’une lecture interactive mettant en œuvre le clavier et e un op´rateur vivant. &tmp).7. e ` A propos de la lecture d’un caractere.2 Lecture et ´criture textuelles e 7 ´ ENTREES-SORTIES correcte aurait ´t´ la suivante : ee int calu. y). il fait ´chouer la lecture du second nombre : e e e e donne x : 11¶ donne y : x = 11. scanf(" %d ". le programme na¨ ıf suivant lit des nombres les uns ` la suite des autres et affiche le carr´ de chacun : a e 94 c H. x. &nblu. Exemple 5.. les blancs dans le format se traduisent donc par l’ordre  ` pr´sent.. Par exemple. le premier appel de scanf e e e ci-dessus signifie  lisez un entier et tous les blancs qui le suivent . ! ? e 22¶ donne y : x = 11. scanf("%d".. . e e e Pour la fonction scanf.. &nblu. char calu. ce qui indique que les donn´es ` lire correspondantes e e e e a peuvent alors ˆtre s´par´es par un nombre quelconque de blancs. &y). scanf("%c". char calu. char tmp. . sautez tous a e les caract`res blancs qui se pr´sentent .. quel qu’il soit : e e int nblu.. Lecture d’un nombre entier et du caract`re le suivant imm´diatement. scanf("%d %c".. &calu). cela ne pose pas de probl`me. Exemple : e int x. Une autre situation g´n´ratrice de confusion est celle o` des e e u lectures de nombres sont suivies par des lectures de caract`res ou de chaˆ e ınes. /* a¨e. y = 22 Notez que faire suivre le premier nombre d’un caract`re non blanc conventionnel n’est pas une bonne solution e car. &calu). 7. scanf("%d%c". Garreta.. 2004 . des blancs apr`s le format %d.. Lecture d’un nombre entier et du premier caract`re non blanc le suivant : e int nblu. Nous avons dit que les sp´cifications qui constituent le format e peuvent ˆtre s´par´es entre elles par un ou plusieurs blancs. .. Si la e a e lecture se fait dans un fichier. peut-ˆtre sans faire attention. */ ı printf("donne y : "). y = 33 Le¸on ` retenir : dans le format de scanf.2.

.").. printf("un autre calcul (O/N) ? "). Dans beaucoup d’environnements on peut faire cela avec la fonction e a e fflush : int x..´ 7 ENTREES-SORTIES 7.. La machine n’a pas attendu la r´ponse (O ou N) de l’op´rateur. bien sˆr. do { printf("donne un nombre : "). c. printf("%d ^ 2 = %d\n". x. ici. scanf("%d". . le tampon de stdin contient (au moins) un ’\n’ while (getchar() != ’\n’) . &x). x. ! Que s’est-il pass´ ? L’op´rateur a compos´ e e e e e un nombre suivi imm´diatement d’un caract`re fin-de-ligne (traduction de la frappe de la touche  Entr´e ). Si la fonction fflush est inop´rante sur le flot stdin (un tel comportement est permis par la norme du e langage C) alors on peut r´soudre le mˆme probl`me en faisant des appels r´p´t´s de la fonction getchar. printf("au revoir. aurait dˆ provoquer une nouvelle lecture e u physique) trouve cette fin-de-ligne et l’affecte ` c comme r´ponse. Ainsi. &x). pour a e que ce programme se comporte comme son programmeur attend il faudrait qu’` la suite de l’invite  donne un a nombre : . x * x). L’exemple ci-dessus devient e . l’utilisateur saisisse un nombre imm´diatement suivi d’un caract`re O ou N qui est la r´ponse ` une e e e a question qui ne lui a pas encore ´t´ pos´e ! ee e Cela n’est pas raisonnable.. c. x. c H.... . maintenant le tampon est vide c = getchar(). do { printf("donne un nombre : "). dans l’esprit du programmeur. printf("un autre calcul (O/N) ? "). ce qui fait quitter la boucle. ← lecture d’un nombre printf("%d ^ 2 = %d\n". x * x). . e e e Cette fin de ligne est in´vitable mais n’a pas ´t´ consomm´e par scanf et est rest´e disponible pour des lectures e ee e e ult´rieures. scanf("%d".."). La bonne mani`re de faire consiste ` vider le tampon d’entr´e avant u e a e de proc´der ` une lecture de caract`re. Garreta.\end{verbatim} Voici un dialogue (la r´plique de l’op´rateur est soulign´e. le signe ¶ repr´sente la frappe de la touche  Entr´e ) : e e e e e donne un nombre : 14¶ 14 ^ 2 = 196 un autre calcul (O/N) ? au revoir. } while (c == ’O’).. scanf("%d".... char s[BUFSIZ]. printf("un autre calcul (O/N) ? "). ce caract`re devant ˆtre e a e a e e consomm´ lui aussi. 2004 95 . printf("au revoir.. &x). ← lecture d’un caract`re e } while (c == ’O’). maintenant le tampon est vide c = getchar(). le tampon de stdin contient (au moins) un ’\n’ fflush(stdin).2 Lecture et ´criture textuelles e int x.. printf("%d ^ 2 = %d\n". ici. c = getchar(). x * x). L’appel de getchar (qui. de e e e e ee mani`re ` consommer tous les caract`res restant dans le tampon jusqu’` la fin-de-ligne... ..

FILE *flot) Cette fonction ´crit les nombre objets. dans les programmes simples... exp k ) ´quivaut ` e a fprintf(stdout. Ajoutons seulement qu’elle renvoie le nombre de caract`res effectivement e e ´crits (on utilise rarement cette information) ou un nombre n´gatif en cas d’erreur.. . e e int scanf(const char *format. Elle e a e e ıne permet donc de dissocier les deux fonctionnalit´s de printf.. autrement elle rend le nombre de variables lues avec succ`s. int fscanf(FILE *flot. Elle renvoie le nombre d’objets a e effectivement lus. .. e e int fprintf(FILE *flot.) { printf("donne un nombre : "). exp k ) int sprintf(char *destination. Elle renvoie le nombre d’objets ´crits. ) Nous avons d´crit cette fonction.. . ` cause de la rencontre de la fin du fichier59 .. ) Cette fonction effectue les mˆmes mises en forme que ses deux sœurs. ıne 7... size t fwrite(const void *source. exploitation du nombre lu } printf("au revoir. . const char *format.. &x) < 1) break.3 Op´rations en mode binaire e 7 ´ ENTREES-SORTIES 7. . e 59 Lorsqu’un fichier est lu par des appels de la fonction fread. format .").. exp 1 . const char *format.3 7. const char *format. . size t taille. const char *format. et les e e copie les uns ` la suite des autres dans l’espace point´ par destination. size t nombre.. size t nombre.. . mais sont concat´n´s ensemble dans la chaˆ destination.1 Op´rations en mode binaire e Lecture-´criture e size t fread(void *destination. exp 1 .. ) Comme scanf mais sur le flot indiqu´ (` la place de stdin). qui peut ˆtre inf´rieur au nombre demand´. transcodage et ´criture des donn´es. e e 96 c H.. exp k ) int sscanf(const char *source.. .5 Les variantes de printf et scanf Voici la famille au complet.h> : e e int printf(const char *format..7.. chacun ayant la taille indiqu´e. avec la diff´rence que les caract`res e e e produits ne sont pas ajout´s ` un flot. . if (scanf("%d". Exemple : e e e e for(. ) Comme printf mais sur le flot indiqu´ (` la place de stdout). . ) Nous avons d´crit cette fonction. mais dans la chaˆ source. Ainsi e a printf( format . FILE *flot) Cette fonction essaye de lire sur le flot indiqu´ nombre objets. 2004 . . ) Cette fonction effectue la mˆme analyse lexicale que ses sœurs. comparer avec z´ro le nombre d’objets effectivement lus est la e mani`re la plus pratique de d´tecter la fin du fichier.2. . Ajoutons qu’elle renvoie EOF si une fin de fichier ou une erreur a empˆch´ e e e toute lecture . e e e a d’une erreur. comme elle est d´clar´e (en syntaxe ANSI) dans le fichier <stdio. .. avec la particularit´ que le texte analys´ e e e n’est pas pris dans un flot. exp k ) ´quivaut ` e a fscanf(stdin. la fin d’une s´rie de donn´es. Cette indication fournit une e mani`re bien pratique pour d´tecter..3. Garreta. afin e e e d’utiliser la premi`re sans la deuxi`me.. qui se trouvent les uns ` la suite e e a des autres ` l’adresse indiqu´e par source. . exp 1 ... size t taille. Ainsi e a scanf( format . La boucle pr´c´dente sera arrˆt´e par n’importe quel caract`re non blanc ne pouvant pas faire partie d’un e e ee e nombre. format . chacun ayant la taille indiqu´e. etc. qui peut ˆtre inf´rieur a e e e e au nombre demand´ (en cas d’erreur). exp 1 .

Celle-ci est obtenue en ajoutant la valeur de d´placement e a e a ` une valeur de base qui peut ˆtre la position courante.. const fpos t *ptr) La valeur de ptr provenant d’un appel de getpos. la fin ou le d´but du fichier. fichier = fopen(nom . e e e c H.3 Op´rations en mode binaire e 7.. ` partir de la e a e a donn´e de son rang dans le fichier sans ˆtre oblig´ d’acc´der au pr´alable aux enregistrements qui le pr´c`dent.. 0L. l’op´ration  lecture de l’enregistrement de rang n  se e e a e e programme alors selon le sch´ma suivant : e ARTICLE article. dans lequel une composante ne peut ˆtre lue ou ´crite autrement qu’` la suite de e a e e e e a la lecture ou de l’´criture de la composante qui la pr´c`de. . int origine) Cette fonction positionne le pointeur du fichier associ´ au flot indiqu´.. 2004 97 .h>) a une taille suffisante pour contenir une telle information.h>) : SEEK SET : base = le d´but du fichier e SEEK CUR : base = la position courante SEEK END : base = la fin du fichier La fonction renvoie z´ro en cas de succ`s. e void fsetpos(FILE *flot. e e e e e e e Pour donner une signification utilisable ` la notion de  neme article . . champs dont se composent tous les articles . Sur un fichier de texte. Un fichier destin´ ` ˆtre utilis´ en acc`s relatif sera d´clar´ normalement. il e e vaut mieux utiliser getpos et setpos. SEEK_SET). il faut que le fichier puisse ˆtre vu a e comme une suite d’articles de mˆme taille (mais la biblioth`que C ignorera ce fait . fread( & article.. Un appel de cette fonction e e e ´quivaut ` fseek(flot. La premi`re lecture ou ´criture e e e e ult´rieure se fera ` partir de la nouvelle position... selon le mod`le : e e e e e typedef struct { . n * sizeof(ARTICLE). Le type e fpos t (d´fini dans <stdio. cette fonction positionne le flot indiqu´ ` l’emplacement ea correspondant. une valeur non nulle en cas d’erreur. Un appel de cette fonction sur un fichier en ´criture produit la vidange du tampon (fseek implique e fflush). fpos t *ptr) Place dans ptr la position courante dans le flot indiqu´ en vue de son utilisation par setpos. exploitation des valeurs des champs de article .. ` ´ ´ L’acces relatif aux elements des fichiers. Garreta. Cela s’obtient g´n´ralement en d´clarant une structure qui repr´sente un article. fichier).. 1. C’est l’argument e e origine qui indique de quelle base il s’agit . long deplacement.. sizeof(ARTICLE). le fichier restera une suite e e d’octets). Lire ci-apr`s les explications sur l’acc`s relatif aux fichiers.´ 7 ENTREES-SORTIES 7. et ouvert de fa¸on ` autoriser les eae e e e e c a lectures et les ´critures : e FILE *fichier. fseek(fichier.2 Positionnement dans les fichiers int fseek(FILE *flot. . e e void rewind(FILE *flot) Positionnement au d´but du fichier du pointeur de fichier associ´ au flot indiqu´. e a a e void fgetpos(FILE *flot. la valeur de cet argument doit ˆtre une des constantes (d´finies e e dans <stdio.. Si les articles sont num´rot´s ` partir de z´ro... "rb+"). SEEK SET) suivi de la mise ` z´ro de tous les indicateurs d’erreur.3. La principale application des fonctions de positionnement est la r´alisation de ce qu’on appelle l’acc`s relatif 60 ou parfois acc`s direct aux composantes d’un fichier : e e e l’acc`s ` une composante (on dit un enregistrement) que ce soit pour le lire ou pour l’´crire. L’op´ration  ´criture de la composante de rang n  ob´it au sch´ma e e e e 60 L’acc`s relatif s’oppose ` l’acc`s s´quentiel... } ARTICLE.

puisqu’il y a e e e toujours un appel de fseek entre deux acc`s au fichier. if (srce == NULL) return ERREUR_OUVERTURE.4. ils se proposent d’illustrer les fonctions de traitement de fichiers. int nombre. soit par un appel d’une fonction de positionnement comme fseek. dest = fopen(tampon.. srce)) > 0) fwrite(tampon. fseek(fichier. . 2004 0 1 2 /* codes conventionnels */ /* ` l’usage du syst`me */ a e /* d’exploitation */ . while ((nombre = fread(tampon.7.h> #define PAS_D_ERREUR #define ERREUR_OUVERTURE #define ERREUR_CREATION FILE *srce. g´n´ralement e e une fonction du syst`me d’exploitation fait ce travail) : e #include <stdio. 7. "wb"). gets(tampon). ıfs aussi bien pour les fichiers de texte que pour les fichiers binaires. fclose(srce). Supposons que le e 98 c H. Notez que cette contrainte est satisfaite si on pratique des acc`s relatifs selon les sch´mas indiqu´s ci-dessus.4 Exemples Les programmes suivants sont assez na¨ . fwrite( & article. sizeof(ARTICLE).1 Fichiers “en vrac” Le programme suivant fait une copie identique d’un fichier (ce programme n’est pas bien utile. printf("source : "). soit par un appel e e e explicite de la fonction fflush. e e e e e ou r´ciproquement.. 1. nombre. gets(tampon). La possibilit´ d’effectuer des lectures et des ´critures sur le mˆme fichier pose des probl`mes e e e e dans la gestion des tampons associ´s au fichier que les fonctions de la biblioth`que standard ne r´solvent pas. 1.4 Exemples 7 ´ ENTREES-SORTIES ARTICLE article.. n * sizeof(ARTICLE). Elle se compose d’un appel de fread demandant la lecture de 512 octets. affectation des champs de article .. quel que soit leur mode.. } L’essentiel de ce programme est sa boucle while. return PAS_D_ERREUR. e 7. if (dest == NULL) return ERREUR_CREATION. "rb"). e e e Ainsi chaque fois qu’une s´rie de lectures sur un fichier va ˆtre suivie d’une s´rie d’´critures sur le mˆme fichier. SEEK_SET). . 1. dest). printf("destination : "). Attention. il appartient au programmeur de pr´voir l’´criture effective du tampon. Garreta. suivi d’un appel de fwrite pour ´crire les nombre octets effectivement lus. 512. fichier).. srce = fopen(tampon. fclose(dest). main() { char tampon[512]. *dest.

gets(nom). /* constitution d’un fichier d’articles */ /* ` partir d’un fichier de texte */ a #include <stdio. 2004 99 . a e Exemple TOMBAL Pierre Rue de la Gaiete de Vivre 10 MENSOIF Gerard Allee ’Les verts’ 20 etc.2 Fichiers binaires et fichiers de texte L’objet du programme suivant est la constitution d’un fichier d’articles ` partir d’un fichier de texte qui se a pr´sente comme une r´p´tition du groupe suivant : e e e – un nom et un pr´nom sur la mˆme ligne . } art. printf("destination : "). c H. if ((srce = fopen(nom. /* fichier de texte */ /* fichier binaire */ printf("source : "). main() { char nom[256].h> #include <string. FILE *srce. Garreta. Le programme est ind´pendant de la nature du fichier de texte.h> #define #define #define #define #define PAS_D_ERREUR ERREUR_OUVERTURE ERREUR_CREATION ERREUR_ECRITURE ERREUR_FERMETURE 0 1 2 3 4 /* un article du fichier */ struct { char nom[32]. if ((dest = fopen(nom. char adresse[80].4 Exemples N fichier ` copier comporte N octets .h> #include <stdlib.´ 7 ENTREES-SORTIES 7. ces deux op´rations seront ex´cut´es 512 fois avec nombre ´gal ` 512 puis a e e e e a (sauf si N est multiple de 512) une derni`re fois avec nombre ´gal au reste de la division de N par 512. e e 7. prenom[32]. exprimant un certain  nombre de passages  (` un p´age autoroutier). qui peut ˆtre aussi bien le clavier d’un e e terminal qu’un fichier existant par ailleurs. "wb")) == NULL) exit(ERREUR_CREATION). e e – une adresse sur la ligne suivante . int nombre_de_passages. "r")) == NULL) exit(ERREUR_OUVERTURE). FILE *dest.4. – un entier seul sur une ligne. gets(nom).

if ((passages = fopen(gets(nom). dest) != 1) exit(ERREUR_ECRITURE). 2004 . e e 7. Imaginons que le fichier cr´´ ` l’exemple pr´c´dent soit le fichier des abonn´s ` un certain p´age. /* enlevons le ’\n’ : */ art. Le nombre de passages est lu par scanf.prenom) != 2) break. FILE *passages. main() { char nom[256]. printf("fichier des abonnes : "). Voyons un exemple o` ce mˆme fichier est trait´ en acc`s e e a e e u e e e relatif. *abonnes. art. exit(PAS_D_ERREUR). art. if ((abonnes = fopen(gets(nom). printf("fichier des passages : ").nom. Mais l’adresse est lue par gets. e e #include <stdio.) { if (fscanf(srce. prenom[32]. car e e elle peut contenir des blancs qui ne doivent pas ˆtre supprim´s. 80. if (fwrite(&art. car cela e nous arrange bien que les blancs soient pris pour des s´parateurs et enlev´s. 1. "rb+")) == NULL) exit(ERREUR_OUVERTURE_ABONNES). sizeof art. fgets(art. } if (fclose(dest) != 0) exit(ERREUR_ECRITURE).adresse) . Le nom et le pr´nom sont lus par scanf aussi. " %s %s ".7.adresse. char adresse[80].adresse[strlen(art.3 Fichiers en acc`s relatif e Dans le programme pr´c´dent nous avons vu un fichier binaire organis´ en articles et trait´ s´quentiellement e e e e e (chaque article ´tant ´crit ` la suite du pr´c´dent).1] = ’\0’. } Notez les diverses fonctions qui ont ´t´ utilis´es pour lire du texte. srce). " %d ".h> #define PAS_D_ERREUR 0 #define ERREUR_OUVERTURE_ABONNES 1 #define ERREUR_OUVERTURE_PASSAGES 2 struct { char nom[32]. 100 c H. int nombre_de_passages. long n. fscanf(srce. Garreta..4 Exemples 7 ´ ENTREES-SORTIES for (. et que ee a e e e a e nous disposions par ailleurs d’un fichier binaire contenant les num´ros des abonn´s qui ont emprunt´ ce p´age e e e e durant une p´riode donn´e.nombre_de_passages).4. } art. ee e car cette fonction est la seule qui convertit les nombres. On nous demande d’´crire un programme pour incr´menter la valeur du champ e e e e nbr passages de chaque abonn´ concern´. "rb")) == NULL) exit(ERREUR_OUVERTURE_PASSAGES). &art.

sizeof art. Renvoie le nombre e a e d’octets effectivement lus. int nombre) Ecrit les nombre octets qui se trouvent ` l’adresse indiqu´e dans le fichier indiqu´. fseek(abonnes. fwrite(&art. L’argument mode doit avoir pour valeur une des e constantes symboliques (d´finies dans <ioctl. etc.´ 7 ENTREES-SORTIES 7. stdout et stderr sont respectivement repr´sent´es e e e en tant que fichiers par les descripteurs d’indices 0. 2004 101 .5 Les fichiers de bas niveau d’UNIX Les fichiers de bas niveau du syst`me UNIX ne sont pas fondamentalement distincts des flots que l’on vient e de d´crire. sizeof art. ils sont repr´sent´s tout simplement par un indice dans ee e e cette table. } 7.5 Les fichiers de bas niveau d’UNIX for (.). soit un nombre qui s’´crit particuli`rement bien en octal. etc. Garreta. } fclose(abonnes). int creat(char *nom. qui peut ˆtre inf´rieur ` nombre (fin de fichier. exit(PAS_D_ERREUR). sizeof n. Les fonctions disponibles sont : int open(char *nom. ou un nombre n´gatif en cas d’erreur. En fait. e e e l’argument permissions indique que le droit de lecture. n * sizeof art. 1.nombre_de_passages++. fread(&art. Comme pour la fonction open. art. les trois unit´s standard stdin. void *adresse. Rend le nombre a e e d’octets effectivement ´crits.h>) : e O RDONLY : ouverture en lecture seulement O WRONLY : ouverture en ´criture seulement e O RDWR : ouverture en lecture / ´criture e Cette fonction renvoie le num´ro du descripteur ouvert pour le fichier. e e e a e int close(int descfic) Ferme le fichier indiqu´. 0751). e c H. abonnes). abonnes). int permissions) Ouvre un fichier existant ayant le nom externe indiqu´. Les fichiers de bas niveau sont la mani`re habituelle de r´aliser les fichiers e e e e binaires lorsqu’on ne poss`de pas un C ANSI.) { if (fread(&n.. aux membres de son groupe de travail ou ` tous les utilisateurs e e a du syst`me. 1. d’une table de descripteurs de e e fichiers. e e e Par exemple : fic = creat("tmp/monfic". le droit d’´criture ou le droit d’ex´cution est e e accord´ ou non au propri´taire du fichier. char *adresse. e e a int write(int fichier. pour g´rer ses fichiers. fseek(abonnes. passages) != 1) break. les flots et les fichiers de bas niveau sont deux mani`res de voir depuis un programme les e e mˆmes entit´s de base (les fichiers). int mode. 1. SEEK_SET). et l’ouvre en ´criture. Lorsque les fichiers sont g´r´s au plus bas niveau. 1 et 2. e Dans le syst`me UNIX chaque processus dispose. e e Pour l’argument permissions voir ci-dessous. SEEK_SET). n * sizeof art. int permissions) Cr´e un fichier nouveau. ayant le nom indiqu´. qui peut ˆtre inf´rieur ` nombre (erreur d’´criture.). Par exemple. Cela fait trois groupes de trois bits. int nombre) Lit nombre octets depuis le fichier indiqu´ et les range ` partir de l’adresse indiqu´e. erreur de lecture.h> ou dans <sys/file. cr´e un fichier nouveau et lui accorde les permissions : e propri´taire e groupe autres lecture 1 1 0 ´criture e 1 0 0 ex´cution e 1 1 1 en octal 7 5 1 int read(int fichier.

5 Les fichiers de bas niveau d’UNIX 7 ´ ENTREES-SORTIES long lseek(int fichier. dest. close(srce). de plus. while ((n = read(srce. int origine) Positionne le fichier indiqu´ sur l’octet ayant le rang indiqu´. 512)) > 0) write(dest. e e . char tampon[512]..relative au d´but du fichier (si origine = 0) e .1. long rang. if (argc < 3 || (srce = open(argv[1].. char *argv[]) { int srce. tampon.7.. n. Au sujet de l’utilisation qui est faite des arguments de main voir la section 8.. Voici la version UNIX (et. tampon. 2004 .3... } 102 c H. donn´ plus haut. main(int argc. Ce rang exprime une position. close(dest). qui effectue une e copie identique d’un fichier quelconque. Garreta. non ANSI) du programme.. return 0.relative ` la fin du fichier (si origine = 2) a Exemple. 0777)) < 0) return 1. 0. n).relative ` la position courante (si origine = 1) a . 0777)) < 0 || (dest = creat(argv[2]..

ses actions semblent en faire partie. Ainsi ces d´finitions sont ´crites dans un seul fichier. des suppressions et des remplacements de morceaux de texte nullement astreints ` a correspondre aux entit´s syntaxiques du langage. e G´n´ralement associ´es au processus de la compilation. En particulier le e e e e pr´processeur ne connaˆ pas la syntaxe du langage C. else et endif qui identifie la directive. e e La deuxi`me forme est : e #include <nom-de-fichier > Ici. sauf quelques exceptions.h (pour header. un certain nombre de fichiers en-tˆte sont livr´s avec les biblioth`ques. ni le corps e des fonctions. etc. faisant partie de la biblioth`que standard : le pr´processeur  sait  dans quels e e e r´pertoires ces fichiers sont rang´s. define.1 Inclusion de fichiers La possibilit´ d’inclure des fichiers sources dans d’autres fichiers sources s’av`re tr`s utile. e ee on peut s’en passer aussi longtemps qu’on limite la pratique de C ` des exercices de petite taille. mais il faut sae e e voir que le pr´processeur est un programme s´par´. Ce sont des fichiers dont le nom se termine par . doit occuper la premi`re position de la ligne) .). en syntaxe originale. largement ind´pendant du compilateur. union et typedef qui d´finissent e e les constantes et les types n´cessaires pour utiliser la biblioth`que en question . Ce sont des ´l´ments importants du langage mais. e e – parfois. Les transformations qu’il effectue sur un programme e ıt sont simplement des ajouts. Pour cette raison on ne trouve jamais dans les fichiers en-tˆte ni des variables non externes. e Cette directive poss`de deux formes : e #include "nom-de-fichier " C’est la forme g´n´rale. comprenant e e e – principalement.h" 61 Notez bien qu’il ne s’agit pas de fournir les fonctions de la biblioth`que (cela est fait par l’apport du fichier objet au moment de e l’´dition de liens) mais uniquement de donner l’information requise pour que les appels de ces fonctions puissent ˆtre correctement e e traduits par le compilateur. par exemple e e e pour ins´rer dans chacun des fichiers s´par´s qui constituent un programme l’ensemble de leurs d´clarations e e e e communes (types.1. etc. la biblioth`que math´matique. e e e – souvent. ce qui en facilite la e e maintenance. les prototypes des fonctions qui forment la biblioth`que ou qui incarnent une fonctionnalit´ e e particuli`re (les entr´es .sorties. ifndef.´ ´ 8 AUTRES ELEMENTS DU LANGAGE C 8 Autres ´l´ments du langage C ee Cette section regroupe un ensemble de notions ayant en commun que leur int´rˆt apparaˆ surtout ` l’occasion ee ıt a de l’´criture de gros programmes. Le fichier sp´cifi´ est ins´r´ ` l’endroit o` la directive figure avant que la compilation e e e e eea u ne commence . La chaˆ de caract`res "nom-de-fichier " doit ˆtre un e e ıne e e nom complet et correct pour le syst`me d’exploitation utilis´. structures. e Exemples (avec des noms de fichier en syntaxe UNIX) : #include <stdio. – le corps de la directive. un ensemble de directives #define et de d´clarations struct. e Les directives destin´es au pr´processeur comportent. il ne mentionne pas le chemin d’acc`s au fichier. quelques d´clarations de variables externes. car il est convenu qu’il s’agit d’un e fichier appartenant au syst`me. 2004 103 . – un mot-cl´ parmi include. ifdef. ce doit ˆtre un fichier source ´crit en C. dans l’ordre : e e e – un signe # (qui.1 Le pr´processeur e Le pr´processeur transforme le texte source d’un programme avant que la compilation ne commence. c H.) . a 8. la gestion des chaˆ e e ınes de caract`res. e e e – plus rarement.h> #include "/users/henri/projet_grandiose/gadgets. Pour que les appels de ces fonctions puissent ˆtre correctement compil´s61 dans des programmes qui e e les utilisent. if. e 8. quelques directives #include concernant d’autres fichiers en-tˆte lorsque les ´l´ments d´finis dans e ee e le fichier en-tˆte en question ne peuvent eux-mˆmes ˆtre compris sans ces autres fichiers . dont la structure d´pend de la nature de celle-ci. Nous e passerons sous silence quelques autres directives mineures comme pragma. Un cas particulier tr`s important de ce partage des d´clarations est celui des biblioth`ques livr´es avec le e e e e compilateur. Elles sont essentiellement compos´es de fonctions utilitaires d´j` compil´es dont seul le fichier objet e ea e est fourni. line ou error . Garreta. en-tˆte) et e e e qui contiennent les d´clarations n´cessaires pour qu’un programme puisse utiliser correctement les fonctions de e e la biblioth`que standard. le nom est incomplet .

Il ne faut pas confondre inclusion de fichiers et compilation s´par´e. e 63 Mais il d´coule de la section 1. Les utilisations (on dit les appels) de la macro se font sous la forme nom(texte 1 . certains compilateurs C poss`dent une option (sous UNIX l’option -E) qui produit e l’affichage du programme source uniquement transform´ par le pr´processeur. Voici une erreur que l’on peut faire : #define NBLIGNES 15. b = w_. ils apparaissent aussi dans le corps de celle-ci. Les macros avec arguments sont appel´es aussi pseudo-fonctions.). Les fichiers que l’on inclut e e au moyen de la directive #include contiennent du texte source C qui sera compil´ chaque fois que l’inclusion e sera faite (c’est pourquoi ces fichiers ne contiennent jamais de fonctions). b.8... Le service rendu par la directive #include est de permettre d’avoir un texte en un seul exemplaire. se termine par le caract`re \. #define NBCOLONNES (2 * NBLIGNES). 2004 . . etc.1 Le pr´processeur e 8 ´ ´ AUTRES ELEMENTS DU LANGAGE C Attention. t[j] = w_. Par exemple. Au moins d’un point de vue logique. les u ıt e chaˆ ınes de caract`res et les occurrences o` le nom de la macro est coll´ ` un autre identificateur).. 8. Partout e a dans le texte o` le nom de la macro apparaˆ en qualit´ d’identificateur (ce qui exclut les commentaires. Les identificateurs entre parenth`ses sont les arguments e a e de la macro .1 que le corps d’une macro peut occuper en fait plusieurs lignes : il suffit que chacune d’elles. type) { type w_. w_ = t[i]. (il remplacera imm´diatement 2 * 15 par 30). t[j]. Ainsi. e e 65 Dans cet exemple. dans lequel ident 1 aura ´t´ remplac´ par texte 1 . e e 104 c H. e e 64 Pour permettre de trouver ce genre d’erreurs. short *) R´sultat de la substitution (on dit aussi d´veloppement) de la macro : e e { short * w_. chaque occurrence de l’identificateur NBLIGNES sera remplac´e par le u e texte 15 et chaque occurrence de l’identificateur NBCOLONNES par le texte (2 * 15). les accolades { et } donnent au corps de la macro une structure de bloc qui sera comprise et exploit´e par e le compilateur. ces remplacements se feront avant que la compilation ne commence. a = b. . e sauf la derni`re.1.].2. Macros avec arguments. ident k ) corps Il ne doit pas y avoir de blanc entre le nom de la macro et la parenth`se ouvrante. C’est une erreur difficile ` d´celer.. cette expression qui paraˆ sans d´faut aura ´t´ transform´e par le pr´processeur en64 ıt e ee e e double matrice[15. } Exemple d’appel : permuter(t[i]. entre a e a l’endroit o` figurent les deux directives suivantes : u #define NBLIGNES 15 #define NBCOLONNES (2 * NBLIGNES) et la fin du fichier o` elles apparaissent. Garreta.2 D´finition et appel des “macros” e Les macros sans argument62 se d´finissent par la directive : e #define nom corps Le nom de la macro est un identificateur. w_ = a.][(2 * 15. e ee e ident 2 par texte 2 . Elles se d´finissent par des expressions de la forme : e #define nom(ident 1 . e e e le corps de la macro s’´tend jusqu’` la fin de la ligne. non de permettre de le compiler une seule fois. lequel est un texte quelconque n’ayant ` ob´ir ` aucune syntaxe. mais cette structure est parfaitement indiff´rente au pr´processeur. Mais plus loin il donnera a e pour erron´e une d´claration comme : e e double matrice[NBLIGNES][NBCOLONNES]. il sera remplac´ e u ea e par le corps de la macro. texte k ) Cette expression sera remplac´e par le corps de la macro. le compilateur trouvera double matrice[15][(2 * 15)]. Comme pr´c´demment. car le compilateur ne signalera ici rien de particulier. En effet. } 62 Le mot macro est une abr´viation de l’expression  macro-instruction  d´signant un m´canisme qui existe depuis longtemps e e e dans beaucoup d’assembleurs. t[i] = t[j]. ` la place de a double matrice[NBLIGNES][NBCOLONNES]. Exemple65 : #define permuter(a. Le corps de la macro s’´tend jusqu’` la fin de la ligne63 . e Attention.

1 Le pr´processeur e Les appels de macros ressemblent beaucoup aux appels de fonctions.186 * (p)) 2. Il eˆt ´t´ bien peu efficace d’appeler une fonction pour ne faire que la u u ee permutation de deux variables ! Autre int´rˆt des macros. etc. e e pour ´viter des probl`mes li´s aux priorit´s des op´rateurs.’a’) : (c)) On voit que l’´valuation de toupper(c) comporte au moins deux ´valuations de c. Il faut donc que le corps d’une macro soit r´duit. La mˆme facilit´. Premi`re correction : e #define TVA(p) (0. Voici un e e e e a exemple ´l´mentaire : en C original (dans lequel on ne dispose pas du type void *) la fonction malloc doit ˆtre ee e g´n´ralement utilis´e en association avec un changement de type : e e e p = (machin *) malloc(sizeof(machin)). comme ci-dessus. a e e En faveur des macros : leur efficacit´. au moins deux caract`res sont lus.3 Compilation conditionnelle Les directives de conditionnelle (compilation) se pr´sentent sous les formes suivantes : e c H. On notera e u e que si toupper avait ´t´ une fonction. Si nous d´finissons la macro a e #define NOUVEAU(type) ((type *) malloc(sizeof(type))) alors les allocations d’espace m´moire s’´criront de mani`re bien plus simple et expressive e e e p = NOUVEAU(machin). D’o` la forme habituelle : e u #define TVA(p) (0. Le texte de la macro ´tant recopi´ ` la place de l’appel. Mais il faut comprendre que ce n’est e pas du tout la mˆme chose.1. 2004 105 .186 * p) Le probl`me pr´c´dent est corrig´. Garreta. proches du mat´riel ou sujettes ` modification. un ´ventuel remplacement ult´rieur de malloc par un autre proc´d´ d’obtention de m´moire sera e e e e e facile et fiable. elles peuvent avoir un type pour argument. Des telles expressions peuvent figurer ` de nombreux endroits d’un programme.186 * p1 + p2). Par exemple. Il ne faut jamais appeler une macro inconnue avec des arguments qui ont un effet de bord. } 8.196 * x. 1. le corps d’une macro est recopi´ puis compil´ ` chaque endroit o` figure l’appel e e ea u de la macro. et ne fait rien aux autres caract`res. on gagne ` e e ea a chaque fois le coˆt d’un appel de fonction. car ceux-ci peuvent ˆtre ´valu´s plus d’une fois. De plus. Par exemple : { calu = getchar(). ce qui n’est sˆrement pas l’effet recherch´. Voici un tr`s mauvais appel e e e de cette macro : calu = toupper(getchar()) En effet. calu = toupper(calu).). ce qui vaut e e e toujours 0. cette expression se d´veloppe en e calu = (’a’ <= (getchar()) && (getchar()) <= ’z’ ? (getchar()) + (’A’ . la principale utilit´ des macros est d’am´liorer l’expressivit´ et la portabilit´ des programmes e e e e e en centralisant la d´finition d’op´rations compliqu´es.´ ´ 8 AUTRES ELEMENTS DU LANGAGE C 8. Mais ee ee a puisque ce n’en est pas une. Exemple classique : on dispose d’une macro nomm´e toupper qui transforme e e e e toute lettre minuscule en la majuscule correspondante. il faut l’appeler avec un argument sans effet de bord.’a’) : (getchar())). A chaque appel. car une expression comme (int) TVA(x) sera d´velopp´e en (int) 0. Alors que le corps d’une fonction figure en un seul exemplaire dans le programme e ex´cutable dont elle fait partie. e En d´finitive. ce qui est probablement erron´. puisqu’il n’y aura qu’un point du programme ` modifier. Un autre inconv´nient des macros est que leur d´finition  hors syntaxe  les rend e e e tr`s difficiles ` maˆ e a ıtriser au-del` d’un certain degr´ de complexit´ (pas de variables locales. sans quoi son utilisation peut finir par ˆtre tr`s on´reuse e e e e en termes d’espace occup´. Elle pourrait ˆtre e e ainsi d´finie (mais on n’est pas cens´ le savoir) : e e #define toupper(c) (’a’ <= (c) && (c) <= ’z’ ? (c) + (’A’ .196 * p est tr`s imprudente. l’expression toupper(getchar()) aurait ´t´ tout ` fait correcte. elles sont ind´pendantes des types de leurs arguments ee e ou mˆme. la macro e e e e e #define TVA(p) 0. Il est quasiment obligatoire d’´crire les arguments et le corps des macros entre parenth`ses. mais il y en a un autre : l’expression TVA(p1 + p2) est d´velopp´e en e e e e e e (0. a Attention. ne pas r´crire un e e e certain code source. peut ˆtre obtenue avec une macro et avec une fonction.

0). Dans la forme sans #else. 106 c H. 2004 .. texte compil´ si la condition est vraie. #else . #ifdef BINAIRES_ANSI FILE *fic. #ifdef BINAIRES_ANSI fic = fopen(nom.. #endif La partie else est facultative ... expression doit pouvoir ˆtre ´valu´e ee e e e par le pr´processeur.. c’est le premier texte qui sera ignor´ tandis que le second sera lu par a e le compilateur. ok = (fic >= 0). Une mani`re d’´crire un programme ind´pendant de ce fait consiste ` enfermer les appels de ces e e e a fonctions et les d´clarations qui s’y rapportent dans des s´quences conditionnelles. c’est-`-dire nulle. Ce doit donc ˆtre une expression constante ne contenant pas l’op´rateur de changement e e e de type ni les op´rateurs sizeof ou &. D´claration : e e e . on a donc droit aussi aux formes :     #if expression #ifdef identificateur   #ifndef identificateur . ok = (fic != NULL).. "r"). Garreta... #else fic = open(nom. #else int fic.8. #endif Lorsque le premier ´l´ment de cette construction est #if expression.. Elle sera ´valu´e et e e e – si elle est vraie.1 Le pr´processeur e 8 ´ ´ AUTRES ELEMENTS DU LANGAGE C    #if expression  #ifdef identificateur   #ifndef identificateur . On obtient de la sorte des textes sources qui restent e e portables malgr´ le fait qu’ils contiennent des ´l´ments non portables. texte compil´ si la condition est fausse. si la condition est fausse aucun texte ne sera compil´. ignor´ si elle est fausse e e .... – si elle est fausse. e Les directives #ifdef identificateur #ifndef identificateur ´quivalent respectivement ` e a #if  identificateur est le nom d’une macro actuellement d´finie  e #if  identificateur n’est pas le nom d’une macro actuellement d´finie  e La compilation conditionnelle se r´v`le ˆtre un outil pr´cieux pour contrˆler les parties des programmes e e e e o qui d´pendent de la machine ou du syst`me sous-jacent. Voici un exemple : les fonctions de lecturee ee ´criture sur des fichiers binaires ne sont pas les mˆmes dans la biblioth`que ANSI et dans la biblioth`que UNIX e e e e classique.. tandis que celui qui figure entre #else et #endif sera purement et simplement tenu pour inexistant ... #endif .. ignor´ si elle est vraie e e . alors le texte qui se trouve entre #if et #else sera trait´ par le a e compilateur. c’est-`-dire non nulle. ignor´ si elle est fausse e e ... #endif . Ouverture : ... texte compil´ si la condition est vraie.

sizeof(art).. L’emploi de la compilation conditionnelle associ´e ` la d´finition de noms de macros est rendu encore plus e a e commode par le fait qu’on peut utiliser – des noms de macros normalement d´finis (#define. 66 Suivant l’analyse de Bertrand Meyer (Conception et programmation par objets. mais dans certains cas il faut une telle dose de ruse et d’autodiscipline. e Th´oriquement.. L’exp´rience montre que les crit`res e e e pr´c´dents induisent sur le langage en question quelques contraintes assez pr´cises. #endif #endif peut en C ANSI s’abr´ger en e #if expression1 . elles pourront ˆtre faites par des proc´dures de commandes (scripts) donc e e automatis´es . #else #if expression2 . e e – favoriser la production de composants logiciels qui peuvent se combiner librement pour produire de nouveaux syst`mes (crit`re de composabilit´ ) . Une m´thode de conception doit e a e e – aider ` diviser chaque nouveau probl`me en sous-probl`mes qu’on peut r´soudre s´par´ment (crit`re de a e e e e e e d´composabilit´ ) . Elle n’est pas facile ` e e e e a d´finir .2 La modularit´ de C e Lecture : .. Le pr´processeur du C ANSI offre ´galement l’op´rateur defined qui permet d’´crire les die e e e rectives #ifdef ident (resp.. en faire l’outil d’une m´thode de conception modulaire.. Garreta... #ifndef ident) sous la forme #if defined ident (resp. sizeof(art)) == sizeof(art)).2 La modularit´ de C e A ce point de notre expos´ nous pouvons nous poser une question importante : le langage C est-il modulaire ? e La modularit´ est une qualit´ que les bonnes m´thodes de conception doivent poss´der. c H.. ce qui permet de changer le texte e qui sera compil´ sans toucher au fichier source. #if !defined ident). comme celles-ci : e e e a e – les modules doivent correspondre ` des entit´s syntaxiques du langage . &art. la construction #if expression1 . De plus. #elif expression2 . pour un r´sultat si peu fiable. e – des noms de macros d´finis dans la commande qui lance la compilation. e que le jeu n’en vaut pas la chandelle. 2004 107 . on peut toutefois la cerner ` travers quelques crit`res66 . e e Remarque. e e e e e e – permettre au concepteur d’´crire des modules dont chacun peut ˆtre compris isol´ment par un lecteur humain (crit`re de compr´hensibilit´ ) . En pratique un langage est dit modulaire lorsqu’on peut. e e ea – assurer que l’effet d’une condition anormale se produisant dans un module restera localis´ ` ce module ou. sans douleur et sans artifice. #endif ... #endif 8. tout langage de programmation peut servir ` mettre en œuvre une m´thode de conception e a e modulaire... e – un ensemble de noms de macros pr´d´finis dans chaque syst`me. fic) == 1)..´ ´ 8 AUTRES ELEMENTS DU LANGAGE C 8. #ifndef BINAIRES_ANSI ok = (fread(&art. InterEditions.) . e e e – permettre qu’une petite modification des sp´cifications du probl`me entraˆ uniquement la modification e e ıne d’un petit nombre des modules de la solution (crit`re de continuit´ ) . Par exemple dans e e e e le compilateur des syst`mes UNIX la macro UNIX est d´finie et vaut 1.. puisque ces d´finitions se font au niveau de la e e commande de compilation. 1990) ` laquelle il semble difficile a d’ajouter ou de retrancher quelque chose. #else ok = (read(fic. pour le caract´riser. D’autre part. 1. au pire. n’atteindra qu’un petit nombre de modules  voisins  ( crit`re de protection).

etc. le type int) que oe e a l’objet d´sign´ par x ne poss`de pas forc´ment . aucune v´rification cependant ne sera faite. soit sous forme de directives #define soit sous forme de type ´num´r´. afin de e e c e e pouvoir ˆtre utilis´es dans un programme pr´sent et un nombre quelconque de programmes futurs. Mais le langage offre peu d’outils pour rendre fiable le partage des noms qui e e doivent ˆtre publics. car il y a toujours une hi´rarchie e e parmi les modules.c) et dans chacun des modules qui les utilisent. on peut presque toujours voir la modularit´ en termes de serveurs et clients. Garreta. les mˆmes d´clarations de variables e e e e e et les mˆmes prototypes de fonctions . qui e e ee sont des informations symboliques ´chang´es entre le serveur et ses clients.h) contenant toutes les d´clarations publiques. e e 8. on constate qu’aucune structure syntaxique ne signale les modules ni n’en d´limite la port´e. un module ne devrait offrir que des fonce e tions67 . Les d´finitions correspondantes figureront dans le module serveur. – Des d´finitions de structures (struct. ces d´clarations sont ´crites en un seul endroit. la compilation s´par´e est une des id´es-cl´s du langage. Bien entendu. segment. si dans un module B on doit r´f´rencer une variable ou une fonction d´finie dans un module A. e e e – Les d´clarations extern des variables publiques du serveur. ´crivez une fonction qui ne fait que cela (mais e en contrˆlant la validit´ de la consultation ou de la modification).2 La modularit´ de C e 8 ´ ´ AUTRES ELEMENTS DU LANGAGE C – chaque module doit partager des informations avec aussi peu d’autres modules que possible. et quand un tel partage existe il doit concerner aussi peu d’´l´ments que possible . e e – Les d´clarations des fonctions publiques du serveur.8.2. Typiquement. union) et de types (typedef) qui d´finissent la nature des objets e e manipul´s par la biblioth`que. e ea Par exemple. C’est bien plus sˆ r que de donner libre acc`s ` la variable.. Le propre des biblioth`ques est d’ˆtre con¸ues de mani`re ind´pendante des clients. Ce fichier doit ˆtre inclus par la directive #include e e e dans le module qui implante les objets publics (fichier A. De cette mani`re tous ces fichiers  voient  les mˆmes d´finitions de types. e e Bien sˆr. Exemple : dans une biblioth`que e e e graphique. sauf sp´cification contraire. d’autant plus que le caract`re non syntaxique de la e e e directive #include brouille l’organisation du programme en fichiers distincts. cela constitue un germe de documentation. En fait. Chaque module communique e avec tous les autres et. 2004 . En syntaxe originale seules les d´clarations des fonctions qui ne rendent pas un entier e sont n´cessaires. L’int´rˆt de e e e ee leur associer le meilleur dispositif pour minimiser le risque de mauvaise utilisation est ´vident. e e A la lumi`re de ces principes la modularit´ de C apparaˆ fort rudimentaire. Mais il donne ` x des attributs (la classe variable. o e u e a 108 c H. Ainsi la compilation e e e e e de B se fait dans la plus totale ins´curit´. un fichier en-tˆte comportera les ´l´ments suivants : e ee – Des directives #include concernant les autres fichiers en-tˆte n´cessaires pour la compr´hension (par le e e e compilateur) des ´l´ments qui apparaissent dans le fichier en-tˆte en question. Remarque. e e ıt Si l’on prend pour modules les fichiers sources qui composent un programme. ee – quand deux modules partagent des informations. et ces outils restent d’un emploi facultatif. ces types permettent aux clients de d´clarer les objets qui e e e sont les arguments et les r´sultats des fonctions de la biblioth`que. e e e ıt e e c’est-`-dire des modules que leurs fonctionnalit´s placent en position de prestataires de services vis-`-vis des a e a autres modules qui composent un programme . e e e 67 S’il est utile qu’un client puisse consulter ou modifier une variable du serveur. subordonn´ ` l’autodiscipline du programmeur. cela doit ˆtre clairement indiqu´ dans leurs deux textes. et il est possible de rendre inaccessibles u e e e e les noms qui peuvent ˆtre priv´s. ee e – Des d´finitions de constantes. et toute modification de e e e l’une d’entre elles se r´percute sur tous les fichiers qui en d´pendent. pour ne pas dire inexistante. tous les noms de variables et fonctions du module serveur qui ne figurent pas dans le fichier en-tˆte doivent ˆtre rendus priv´s (en les qualifiant static). la d´finition de types point.1 Fichiers en-tˆte e Le seul moyen dont dispose l’auteur d’un module A pour s’assurer que les autres modules qui forment un programme utilisent correctement les variables et fonctions qu’il rend publiques consiste ` ´crire un fichier enae tˆte (fichier A. les noms conventionnels des couleurs. ce qui sera contrˆl´ par l’´diteur de liens. Aucune d´claration particuli`re e e n’est requise pour indiquer que des objets sont partag´s entre plusieurs modules. on parle alors de module serveur et de modules clients. mais mˆme dans ce cas c’est une bonne habitude que d’y mettre les d´clarations de toutes e e e les fonctions. Cet ´nonc´ postule l’existence d’un objet nomm´ e e e e e x. tous les objets de chaque module sont partag´s. L’emploi de variables publiques est d´conseill´ . e e La n´cessit´ de ces fichiers en-tˆte apparaˆ encore plus grande quand on consid`re le cas des biblioth`ques. e Typiquement. il ee e suffit d’´crire dans B une d´claration comme extern int x . Les d´finitions correspondantes (sans le qualifieur extern) figureront dans le module serveur. Exemple : dans une biblioth`que e e e graphique.

1985-1988 Copyright American Telephone & Telegraph Used with permission.2 La modularit´ de C e 8. int _filbuf (FILE *). Garreta.h auquel nous avons enlev´ ce qui ne sert pas notre propos : e /* * * * * * * */ stdIO. int setvbuf (FILE *. α #ifndef __STDIO__ #define __STDIO__ #include <stddef. char *. unsigned char *_ptr. unsigned char *_base. int fflush (FILE *).2.h> /* * Miscellaneous constants */ #define EOF (-1) #define BUFSIZ 1024 #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 /* * The basic data structure for a stream is the FILE. unsigned char *_end. Inc.h -. unsigned short _size.h A titre d’exemple visitons rapidement le plus utilis´ des fichiers en-tˆte de la biblioth`que standard : stdio. mais d’illustrer les indications du paragraphe e e pr´c´dent sur l’´criture des fichiers en-tˆte. unsigned short _flag. size_t). unsigned short _file. /* * Things used internally: */ extern FILE _iob[]. 2004 δ 109 . } FILE.h> #include <stdarg. c H. FILE *fopen (const char *. e e e Notre but n’est pas de prolonger ici l’´tude des entr´es-sorties.´ ´ 8 AUTRES ELEMENTS DU LANGAGE C 8.2 Exemple : stdio. /* * The */ #define #define #define standard predefined streams stdin stdout stderr (&_iob[0]) (&_iob[1]) (&_iob[2]) γ /* * File access functions */ int fclose (FILE *). Le texte ci-apr`s est fait de morceaux de la version MPW (Macintosh e e e e e Programmer Workshop) du fichier stdio. int _flsbuf (int. Apple Computer Inc. FILE *). */ typedef struct { β int _cnt. int. (1985) All rights reserved.Standard C I/O Package Modified for use with Macintosh C Apple Computer.h. const char *).

#define getc(p) (--(p)->_cnt >= 0 \ ? (int) *(p)->_ptr++ : _filbuf(p)) #define getchar() getc(stdin) #define putc(x. size_t. Notez d’autre part que la connaissance e 110 c H..). int puts (const char *). e e e e e β On peut noter ici que la vue du type FILE qu’ont les modules clients est tr`s diff´rente de celle qu’ils e e auraient en utilisant un langage tr`s modulaire comme Modula II ou Ada. . ainsi que la structure elle-mˆme .). size_t fwrite (const void *. const char *. . const char *. size_t.2 La modularit´ de C e 8 ´ ´ AUTRES ELEMENTS DU LANGAGE C /* * */ int int int int int int Formatted input/output functions printf (const char *. FILE *)..8..h. e En C nous n’avons pas d’outils pour proc´der de la sorte : ou bien un type est priv´.). dans le a e e style de typedef void *FILE_ADDRESS. int ungetc (int.h> #include "MonBazar.. ce qui entraˆ un travail inutile et parfois des erreurs li´es ` des red´finitions. size_t.. imaıne e a e ginons que MonBazar. int. .). Par exemple. stdout) /* * Direct input/output functions */ size_t fread (void *. Tous e les fichiers en-tˆte peuvent ˆtre prot´g´s de cette mani`re contre les inclusions multiples.. FILE *). Durant la pr´sente compilation. #endif __STDIO__ Renvois : α Lorsque les fichiers en-tˆte sont d’un int´rˆt g´n´ral ils finissent par ˆtre inclus dans de nombreux autres e ee e e e fichiers. . Cela s’appelle un type opaque et augmente la fiabilit´ des programmes. connu uniquement e e du module serveur. char *fgets (char *.. p) (--(p)->_cnt >= 0 \ ? ((int) (*(p)->_ptr++ = (unsigned char) (x))) \ : _flsbuf((unsigned char) (x). FILE *). Dans ces langages les noms e et les types des champs d’une structure comme FILE resteraient priv´s. .).h soit un fichier comportant la directive #include <stdio. char *gets (char *). L’utilisation d’un pointeur g´n´rique.h) devient d´fini.. const char *. eux-mˆmes inclus les uns dans les autres.h>. ε /* * Character input/output functions and macros */ int fgetc (FILE *).. Les deux premi`res directives de ce e fichier r´solvent ce probl`me : lorsque ce fichier est pris en compte une premi`re fois.h" le compilateur risquerait de compiler deux fois le fichier stdio. le nom __STDIO__ e e e (nom improbable arbitrairement associ´ ` stdio..). const char *. Dans la compilation d’un fichier commen¸ant par les deux directives c #include <stdio. sprintf (char *. FILE *). FILE *).. Le compilateur risque alors de lire plusieurs fois un mˆme e e fichier. mais aurait la cons´quence de rendre impossibles ` e a d´celer les mauvaises utilisations du type en question par le client. FILE *). scanf (const char *. . (p))) #define putchar(x) putc((x). size_t. int fputs (const char *. dissimulerait bien l’information sur la structure FILE. ou bien il faut  tout dire  ` son sujet. fscanf (FILE *. int fputc (int.. la e a e e directive #ifndef __STDIO__ fera que l’int´rieur du fichier ne sera plus jamais lu par le compilateur. fprintf (FILE *. 2004 . e e seul le type adresse d’une telle structure serait rendu public. Garreta. sscanf (const char *.

les prototypes e e e FILE *fopen(const char *. FILE *stream). pour ce e e e e e qui concerne les variables et les fonctions. argument k . Les arguments du programme principal Cette section ne concerne que les environnements. size_t. 2004 111 . Le premier client de ce dispositif doit ˆtre le fournisseur lui-mˆme. se nomme echo . e L’ex´cution d’un programme commence par la fonction main. Voici comment a on pourrait ´crire ce programme : e c H. pour ´viter les collisions de noms. dans les modules clients des noms des champs de la structure FILE est indispensable pour l’utilisation des macros comme getc ou putc. par exemple parce qu’ils apparaissent dans le corps e e d’une macro.3 8.3. const char *mode). le rˆle de liste officielle des seuls noms publics. en syntaxe ANSI : e int main(int argc. ces noms apportent une information suppl´mentaire qui e augmente la s´curit´ d’utilisation de la biblioth`que. la  privacit´  n’est assur´e que par une convention entre le syst`me et les utilisateurs : e a e e e tout nom commen¸ant par un blanc soulign´   appartient au syst`me et l’utilisateur doit feindre d’en c e e ignorer l’existence. qui sont les arguments du programme. Voici l’en-tˆte complet de main. Par exemple. e e e a e δ Autres remarques. Tout se passe comme si le syst`me d’exploitae e tion avait appel´ cette fonction comme une fonction ordinaire. la syntaxe n’exige pas les noms des arguments mais uniquement e e leurs types. supposons que ce programme e e soit lanc´ par la commande : e echo Pierre Paul alors. Ceci n’est pas la meilleure mani`re de donner les prototypes des fonctions dans un fichier en-tˆte. Le fichier en-tˆte joue ainsi.3 Deux ou trois choses bien pratiques. e Imaginons avoir ´crit un programme qui. ε Les appels de fonctions avec des arguments variables ne b´n´ficient pas des v´rifications syntaxiques que e e e le C ANSI effectue lorsque la fonction a fait l’objet d’une d´finition de prototype. ne seraient d’aucune utilit´ ` un programmeur qui h´siterait ` propos du rˆle ou de l’ordre des arguments ea e a o de ces fonctions. une fois compil´. Par convention.h> De cette mani`re on garantit que l’auteur et l’utilisateur de chaque variable ou fonction sont bien d’accord e sur la d´finition de l’entit´ en question. size_t size. main re¸oit les arguments que montre la figure 18. mˆme ceux qu’on e e aurait aim´ garder priv´s alors qu’on ne le peut pas. L’information concernant la nature de ces arguments est e a o port´e par le format . Il faut savoir que lors de cet appel. elle ne pourra ˆtre exploit´e qu’` l’ex´cution. Sauf pour les arguments e nomm´s (le premier ou les deux premiers). Garreta. e e Une autre r`gle ` respecter par l’auteur du ou des modules serveurs : qualifier static tous les noms qui ne e a sont pas d´clar´s dans le fichier en-tˆte... const char *). Cependant. char *argv[]) avec : argc : nombre d’arguments du programme argv : tableau de chaˆ ınes de caract`res. o 8. dans lesquels les programmes sont activ´s en composant une commande de la forme nom-du-programme argument 1 . s’ils sont bien choisis.. comme UNIX ou MS-DOS.1 Deux ou trois choses bien pratiques. size_t fread(void *. size_t. FILE *).´ ´ 8 AUTRES ELEMENTS DU LANGAGE C 8. size_t fread(void *buffer. size_t count. comme ici le nom de la table des descripteurs de fichiers iob... il faut que chacun des fichiers qui r´alisent l’implantation des e e variables et fonctions  promises  dans stdio. Dans une e e d´claration qui n’est pas une d´finition. γ Tous les noms qui apparaissent dans le fichier en-tˆte deviennent de ce fait publics. des arguments e sont fournis au programme.. Pour que la e e protection contre l’erreur recherch´e soit effective. Le langage C n’ayant rien pr´vu ` ce effet. le premier e argument est le nom du programme lui-mˆme. les arguments que l’on passe ` l’une de ces six fonctions e a ´chappent donc ` tout contrˆle du compilateur. c Supposons que tout ce que l’on demande ` echo soit de recopier la liste de ses arguments. contrairement ` leurs versions  ´quivalentes  : a e FILE *fopen(const char *filename.h comporte la directive #include <stdio.

etc. srce)) > 0) fwrite(tampon. i < argc. "wb")) == NULL) return ERREUR_CREATION.3 Deux ou trois choses bien pratiques. 1. } La frappe de la commande  echo Pierre Paul  produit l’affichage de echo Pierre Paul La principale application de ce m´canisme est la fourniture ` un programme des param`tres dont il peut e a e d´pendre. 512. comme des noms de fichiers. des tailles de tableaux. 18 – Arguments de main main(int argc.. while ((nombre = fread(tampon. char *argv[]) { int i. int nombre. Garreta. i++) printf("%s\n". if ((dest = fopen(argv[2]. return PAS_D_ERREUR. if ((srce = fopen(argv[1]. qui prend les noms des fichiers source et destination ea comme arguments : #include <stdio. voici une nouvelle version du e programme de copie de fichiers donn´ ` la section 7. nombre.1. "rb")) == NULL) return ERREUR_OUVERTURE. fclose(dest). *dest. } Si nous appelons copier le fichier ex´cutable produit en compilant le texte ci-dessus. Par exemple. char *argv[]) { char tampon[512]. 8 ´ ´ AUTRES ELEMENTS DU LANGAGE C e c h o \0 argc 3 argv P i e r r e\ 0 P a u l \0 NULL Fig.h> #define #define #define #define PAS_D_ERREUR ERREUR_OUVERTURE ERREUR_CREATION PAS_ASSEZ_D_ARGUMENTS 0 1 2 3 FILE *srce. dest). alors nous pourrons e l’ex´cuter en composant la commande e copier fichier-source fichier-destination 112 c H. argv[i]).8. main(int argc. for (i = 0.. return 0. 1.4. if (argc < 3) return PAS_ASSEZ_D_ARGUMENTS. 2004 .

) . 2004 113 . valeur). etc.h> . e main analyse() analyse setjmp setjmp(c) expression expression() longjmp(c. quel que soit le nombre de ces fonctions. void longjmp(jmp_buf contexte. Garreta. enregistre dans contexte certaines informations traduisant l’´tat du syst`me.3. l’appel e e e longjmp(contexte... et en maˆ ıtrisant le  point de chute .. remet le syst`me dans l’´tat qui a ´t´ enregistr´ dans la variable contexte. e e Le principal service rendu par ce dispositif est de permettre de programmer simplement. peut ` tout moment rencontrer une erreur dans a ses donn´es.´ ´ 8 AUTRES ELEMENTS DU LANGAGE C 8. – chacune des fonctions expression. 8..h Le m´canisme des longs branchements permet d’obtenir la terminaison imm´diate de toutes les fonctions e e qui ont ´t´ appel´es (et ne sont pas encore termin´es) depuis que le contrˆle est pass´ par un certain point du ee e e o e programme. int code). terme. Il est r´alis´ ` l’aide des deux fonctions : e ea int setjmp(jmp_buf contexte).2 Branchements hors fonction : setjmp. g´n´ralement globale. etc. Ensuite. qui rend la poursuite du traitement inutile ou impossible. Examinons un e exemple classique : supposons qu’une certaine fonction expression soit un analyseur syntaxique pr´sentant e deux caract´ristiques fr´quentes : e e – la fonction expression est ` l’origine d’une imbrication dynamique fort complexe (expression appelle a une fonction terme. jmp_buf contexte. en rendant cette fois non pas z´ro mais la valeur indiqu´e dans l’appel de longjmp.. L’appel setjmp(contexte). 19 – Branchement hors fonction Voici comment l’appel d’expression pourrait ˆtre emball´ dans une fonction  enveloppe  nomm´e analyse e e e (voir la figure 19) : c H. Cela fonctionne de la mani`re suivante : tout d’abord il faut d´clarer une variable. l’abandon d’une famille de fonctions qui se sont mutuellement appel´es. le syst`me se e e ee e e e e trouve comme si l’appel de setjmp(contexte) venait tout juste de se terminer.v) terme terme() Fig. de e e e e type jmp buf (type d´fini dans le fichier en-tˆte setjmp. qui appelle une fonction facteur qui ` son tour appelle une fonction primaire laquelle a rappelle expression. Plus pr´cis´ment.3 Deux ou trois choses bien pratiques. puis renvoie z´ro.h) : e e #include <setjmp. facteur.

Garreta. facteur. la d´claration pr´c´dente se r´crit plus simplement : e e e e PROCEDURE *signal(int numero. et elle nous apprend que la fonction signal prend deux arguments.h de la fa¸on suivante : e e c void (*signal(int numero. mais e e e e e il est commode de les consid´rer comme tels et de les r´cup´rer de la mˆme mani`re. Avec cela. Cela est vrai. En g´n´ral on traite de la mˆme mani`re le vrai et le faux asynchronisme. La fonction signal rend l’adresse de la PROCEDURE qui ´tait jusqu’alors enregistr´e pour ce mˆme e e e e e ´v´nement. jmp_buf contexte. il est possible d’effectuer l’appel e longjmp(contexte.3 Interruptions : signal.3 Deux ou trois choses bien pratiques. e e e e e La fonction signal est d´clar´e dans le fichier signal. mˆme si cette fonction a ´t´ rappel´e depuis. Une e e e e r´f´rence ` travers un pointeur invalide ou une division par z´ro ne sont pas r´ellement asynchrones (si on avait ee a e e connu parfaitement le programme et les donn´es on aurait pu pr´dire tr`s exactement de tels ´v´nements). sauf pour ce qui concerne la valeur des variables. d´pendant de chaque syst`me. On ne peut effectuer un appel de longjmp que pour ramener le syst`me dans l’une des fonctions appel´es e e et non encore termin´es. D´finissons le type PROCEDURE comme celui d’une fonction sans e a e r´sultat d´fini ayant un argument de type int : e e typedef void PROCEDURE(int). Lors d’un appel de signal. alors elle sera automatiquement appel´e par le syst`me avec pour argument le num´ro de e e e l’´v´nement. 1).). 8 ´ ´ AUTRES ELEMENTS DU LANGAGE C #include <setjmp.h L’objet de la fonction signal est la d´tection d’´v´nements asynchrones qui peuvent se produire pendant e e e l’ex´cution d’un programme. e e e e e L’effet de signal est d’enregistrer la PROCEDURE donn´e en argument.v) qui serait plac´ e e ee e e dans la fonction main apr`s l’appel analyse() (voir la figure 19) serait erron´.. elle est donc e e imm´diatement suivie par l’appel de expression. de telle mani`re que si l’´v´nement en e e e e question se produit. ` savoir un int et l’adresse d’une PROCEDURE. car il utiliserait le contexte d’une e e activation de fonction (la fonction analyse) qui n’existe plus. } else return ERREUR. notamment les variables globales : e elles ne reprennent pas la valeur qu’elles avaient lorsque longjmp a ´t´ appel´e. Remarques. Ce prototype n’est pas tr`s facile ` lire. Appeler longjmp avec un e e contexte qui a ´t´ m´moris´ dans une fonction dont l’activation est termin´e est une erreur aux cons´quences ee e e e e ind´finies. e e 114 c H. a et renvoie l’adresse d’une PROCEDURE. ee e 2. setjmp est appel´e et rend 0 . Si le travail de expression se termine normalement. 2004 . alors e analyse rendra SUCCES et elle aura ´t´ transparente. } Fonctionnement : lorsque la fonction analyse est activ´e.. exactement sur la partie condition de l’instruction e o if (setjmp(contexte) == 0) mais cette fois la valeur rendue par setjmp sera 1 et analyse rendra donc la valeur ERREUR. 1.h> . int analyse(void) { if (setjmp(contexte) == 0) { expression(). l’argument numero doit d´signer un des ´v´nements qu’il est possible de r´cup´rer. L’appel de longjmp se pr´sente comme une remise du syst`me dans l’un des ´tats par lesquels e e e il est pass´.8. return SUCCES. est r´cup´rable ` travers le e e e e e e a m´canisme d´crit ici. un appel longjmp(c. D’autre part. etc. e e u car ils ne r´sultent pas d’instructions normalement ins´r´es dans la s´quence qui forme le programme..  Asynchrones  signifie qu’on ne peut pas pr´voir le moment o` ils se produiront. 8. pour laquelle l’espace local est encore allou´ dans la pile. Par exemple... e ee e Un certain ensemble de types d’´v´nements. Une coupure e e e e e e de courant ou une interruption provoqu´e par l’utilisateur sont des ´v´nements vraiment impr´visibles.3. void (*manip)(int)))(int). dans la fonction expression et toutes celles ee appel´es  au-dessus  d’elle (terme.. PROCEDURE *manip). qui ram`ne le contrˆle dans la fonction analyse. .

void voirSkissPass(int numero) { int c. plusieurs syst`mes.4 La biblioth`que standard e La biblioth`que standard ANSI se compose de deux sortes d’´l´ments : e ee e e – un ensemble de fichiers objets 69 contenant le code compil´ des fonctions de la biblioth`que et participant.. Garreta. Supposons qu’une section d’un certain programme effectue des calculs tr`s complexes dont la dur´e e e risque d’inqui´ter l’utilisateur final. comme UNIX et VMS.).4 La biblioth`que standard e Six ´v´nements sont pr´vus par le standard ANSI (ils ne sont pas tous implant´s dans tous les syst`mes . etc. mais de tels fichiers ne sont que des fichiers e e e objets un peu arrang´s pour en faciliter l’emploi par les ´diteurs de liens..dll...... il s’agit de la combinaison des deux touches  Ctrl  et  C . chaque syst`me peut ajouter ses propres particularit´s) : e e SIGABRT : fin anormale du programme (appel de la fonction abort) SIGINT : interruption provoqu´e par l’utilisateur (touche Ctrl-C.lib.. . etc. . printf("J’en suis a l’iteration: %d\nOn arrete? ". autres op´rations e ..h> int iteration_numero. d´bordement de e e e e a e e tableau) SIGILL : instruction ill´gale (souvent : mauvais pointeur dans l’appel d’une fonction) e La biblioth`que fournit en outre deux m´canismes de r´cup´ration d’une interruption pr´d´finis : e e e e e e SIG DFL : le m´canisme par d´faut utilis´ par le syst`me pour l’´v´nement en question lorsque la fonction e e e e e e signal n’a pas ´t´ appel´e ee e SIG IGN : le m´canisme trivial qui consiste ` ignorer l’´v´nement e a e e Exemple.. en appuyant sur une touche particuli`re68 . e e 68 Sur 69 Il c H. . calculs terriblement complexes . maniprec). e s’agit en r´alit´ de fichiers  biblioth`ques  (extensions . while ((c = toupper(c)) != ’O’ && c != ’N’). etc. if (c == ’O’) exit(1).. /* sortie de la section on´reuse */ e signal(SIGINT. autres op´rations e .a.) e e SIGSEGV : acc`s m´moire ill´gal (souvent : mauvais pointeur dans l’acc`s ` une donn´e. voirSkissPass). L’utilisateur e apprendra alors l’´tat d’avancement de son calcul (traduit par la valeur de la variable iteration numero) et e aura ` choisir entre la continuation et la terminaison du programme : a #include <signal. /* abandonner le programme */ else return. /* reprendre le travail interrompu */ } main() { PROCEDURE *maniprec.´ ´ 8 AUTRES ELEMENTS DU LANGAGE C 8.. en e e e e e outre. typedef void PROCEDURE(int). 2004 115 . On souhaite donc offrir ` ce dernier la possibilit´ d’interrompre le programme e a e lorsqu’il estime que celui-ci devient excessivement long. /* entr´e dans la section on´reuse */ e e maniprec = signal(SIGINT.) e SIGTERM : demande d’arrˆt de l’utilisateur (interruption  forte ) e SIGFPE : erreur arithm´tique (division par z´ro. iteration_numero). . do c = getchar(). } 8. .

. simplement en d´finissant en tˆte du programme71 e e e l’identificateur NDEBUG : #define NDEBUG peu importe la valeur 70 Notez que. table[i] = 0.h) ` la section 4. } l’ex´cution de ce programme donnera (puisqu’une faute de frappe a rendu infinie la boucle for ) : e Assertion failed: 0 <= i && i < TAILLE.. Si l’expression est fausse (c. Au besoin. a e 8.. il arrive seuvent e qu’une situation anormale produite en un point d’un programme ne se manifeste qu’en un autre point sans rapport avec avec le premier . ` la section 7 . e e a – la r´cup´ration des interruptions (avec le fichier signal. il y a un moyen simple de les e indiquer explicitement..h).. il ne se passe rien. ee e Des parties importantes de la biblioth`que standard ont d´j` ´t´ expliqu´es . notamment : e eaee e – la biblioth`que des entr´es .sorties (associ´e au fichier stdio. e e e a a – les listes variables d’arguments (introduites dans le fichier stdarg. j < TAILLE. cependant. line numero ensuite l’ex´cution du programme est avort´e. 2004 . il est alors tr`s difficile de remonter depuis la manifestation du d´faut jusqu’` e e a sa cause.2 . ce dernier n’a pas ` connaˆ des d´tails du programme source (en principe il ne connaˆ a ıtre e ıt mˆme pas le langage de programmation employ´).3.c.. for (i = 0.h> .c etc. Les appels de assert ne doivent pas figurer dans l’ex´cutable livr´ ` l’utilisateur final. e e a Nous passerons sous silence certains modules mineurs (comme la gestion de la date et l’heure). file fichier . un message est e e a e imprim´ sur stderr. – un ensemble de fichiers en-tˆte contenant les d´clarations n´cessaires pour que les appels de ces fonctions e e e puissent ˆtre correctement compil´s. de la forme e Assertion failed: expression. e 71 Sous UNIX il n’est mˆme pas n´cessaire d’ajouter une ligne au programme : il suffit de composer la commande gcc en sp´cifiant e e e une option  -D .4.-`-d. – les branchements hors-fonction (d´clar´s dans le fichier setjmp.. lorsque des fichiers de la biblioth`que standard ne sont pas implicites. Garreta. line 241 La macro assert est un outil pr´cieux pour la mise au point des programmes. Elle se compose d’une macro unique : e void assert(int expression) qui fonctionne de la mani`re suivante : si l’expression indiqu´e est vraie (c’est-`-dire non nulle) au moment o` e e a u la macro est ´valu´e. nous reprenons cette organisation pour expliquer les princie e e paux ´l´ments de la biblioth`que. 116 c H.3. sous UNIX. l’option -lm dans la commande gcc sp´cifie l’apport de la biblioth`que /lib/libm.1 Aide ` la mise au point : assert. #define TAILLE 100 int table[TAILLE].h) ` la section 8. Beaucoup de temps peut ˆtre gagn´ en postant de telles assertions aux endroits  d´licats .. on doit donc neutraliser tous les appels de assert e qui y figurent. . que cet outil s’adresse au programmeur et n’est utile que pendant le d´veloppement e du programme. Par exemple.h) ` la section 8.4 . Sans elle. d’autre part parce que les appels de assert ralentissent les e e programmes.8. comme ceci : gcc -DNEBUG -o monprog monprog.70 ` l’´dition de liens de votre e a e programme. e e Ces fichiers en-tˆte sont organis´s par th`mes . pour deux e ea raisons : d’une part. Cela peut s’obtenir en une seule op´ration. si elle vaut z´ro). Il est clair. o` les e e e u variables du programme doivent satisfaire des contraintes cruciales. . file ex.a (ce fichier e e contient le code des fonctions math´matiques). reportez-vous ` la documentation de votre syst`me. Lorsque la mise au point d’un programme est termin´e. i++) { .h a Cette  biblioth`que  ne contient aucune fonction.3.4 La biblioth`que standard e 8 ´ ´ AUTRES ELEMENTS DU LANGAGE C pour la plupart sans qu’il y soit n´cessaire de l’indiquer explicitement. assert(0 <= i && i < TAILLE).3. Exemple na¨ : e e ıf #include <assert.

sprintf(s. la suite obtenue e ee e est celle qui correspond ` srand(1)... i). /* NON ! */ La v´rification que 0 <= x && x <= N porte sur une valeur lue ` l’ex´cution et garde tout son int´rˆt dans e a e ee le programme final.. C’est pourquoi elle doit plutˆt ˆtre programm´e avec du e e o e e  vrai  code : . long atol(const char *s). 8. Garreta. char *s. qui vaut au moins 32767. } .4. Cette suite ne d´pend que de la valeur de e la semence donn´e lors de l’appel de srand. maintenant i a la valeur num´rique dont la chaˆ e ıne s est l’expression textuelle Note.. Pour faire le travail inverse de atoi ou une autre des fonctions pr´c´dentes on peut employer e e sprintf selon le sch´ma suivant : e int i. voir ci-dessous. if( ! (0 <= x && x <= N)) { fprintf(stderr... "Erreur.... N). comme les erreurs dans les donn´es saisies. Le fait qu’il soit possible et utile de faire disparaˆ les appels de assert du programme ex´cutable ıtre e final montre bien que assert n’est pas la bonne mani`re de d´tecter des erreurs que le programmeur ne peut pas e e ´viter. &x). e exit(-1). voir rand ci-dessus. du moins ` partir e e a a e a du deuxi`me terme. scanf("%d". le long. Celle-ci se fera alors comme si tous les appels de assert avaient ´t´ gomm´s. Pour que la suite al´atoire fournie par les appels de rand que fait un programme soit e diff´rente ` chaque ex´cution de ce dernier il fait donc commencer par faire un appel de srand en veillant e a e c H. a Voyez srand ci apr`s. Ainsi – sauf pour un programme ´ph´m`re ` usage strictement e e e e e a personnel – le code suivant n’est pas acceptable : . lorsque les assert sont d´sarm´s. "%d". peu importe – donnent lieu ` des suites tout ` fait diff´rentes. La valeur de x doit ^tre comprise entre 0 et %d\n". e Application. ıne e Exemple int i... . le double) dont la chaˆ s est l’expression ´crite. Deux valeurs diff´rentes de la semence e e – ´loign´es ou proches.´ ´ 8 AUTRES ELEMENTS DU LANGAGE C 8. &x). affectation de s . ee e N. affectation de i . e void srand(unsigned int semence) Initialise une nouvelle suite pseudo-al´atoire.. 2004 117 . i = atoi(s).2 Fonctions utilitaires : stdlib.. Si srand n’a pas ´t´ appel´e. double atof(const char *s) Ces fonctions calculent et rendent l’int (resp.4 La biblioth`que standard e juste avant d’effectuer la compilation finale du programme.. assert(0 <= x && x <= N). .B. char s[80]. .h int atoi(const char *s).. maintenant la chaˆ ıne s est l’expression textuelle de la valeur de i int rand(void) Le ieme appel de cette fonction rend le ieme terme d’une suite d’entiers pseudo-al´atoires compris entre 0 e et la valeur de la constante RAND MAX.. scanf("%d".

par exemple ` travers la fonction time72 . elle obtient e e un nouvel espace ayant la taille voulue. 2004 . Sinon. Cette e e e adresse doit n´cessairement provenir d’un appel de malloc. qu’on peut r´gler en faisant intervenir d’autres fonctions de mesure du temps. Si cela peut se faire sur place alors la fonction renvoie la mˆme adresse adr. par exemple des millisecondes. e e e e void free(void *adresse) Indique au syst`me que l’espace m´moire ayant l’adresse indiqu´e n’est plus utile au programme. on peut e e e utiliser les constantes : – EXIT SUCCESS : la valeur conventionnelle qui indique. i++) { x = rand() / (float) RAND_MAX. cette valeur est utilis´e lorsque le programme a ´t´ appel´ dans le e e ee e cadre d’un proc´dure de commandes (ou script). que le e programme a r´ussi ` accomplir sa mission . e void *realloc(void *adr. /* le premier tirage est tr`s li´ ` la semence */ e e a for(i = 0. Cette e fonction essaie d’agrandir ou de r´tr´cir (usage rare) l’espace point´ par adr afin qu’il ait la la taille e e e demand´e. lib`re cet espace. demande ` l’interpr`te des commandes du e e a e syst`me d’exploitation d’ex´cuter la commande indiqu´e et rend le code donn´ par cette commande (cette e e e e 72 La fonction time renvoie le nombre de secondes ´coul´es depuis le premier janvier 1970. calloc ou realloc.h> #include <time. e void *calloc(size t nombre. float x. La signification de ce code d´pend du syst`me . Une mani`re d’obtenir cela consiste ` utiliser l’heure e e a courante (elle change tout le temps !). a Par exemple. Garreta. ou NULL en cas d’´chec. etc. le programme suivant initialise et affiche une suite pseudo-al´atoire de dix nombres flottants e xi v´rifiant 0 ≤ xi ≤ 1 : e #include <stdio.8. calloc ou realloc. srand(time(NULL)). y copie le contenu de l’espace point´ par adr. printf("%f\n". Cet espace e e e n’est pas initialis´. size t taille) R´-allocation d’espace. Si on doit ´crire un programme devant initialiser plusieurs suites al´atoires distinctes en moins d’une seconde – ce qui est e e quand mˆme rarissime – on aura un probl`me. Elle renvoie donc un nombre entier qui e e change toutes les secondes. x). chacun ayant la taille indiqu´e. void abort(void) Provoque un arrˆt anormal du programme (c’est un ´v´nement d´tect´ par la fonction signal). pour le syst`me sous-jacent. ou e NULL en cas d’´chec. L’espace allou´ est initialis´ par des z´ros.h> main() { int i. e a e – EXIT FAILURE : le programme n’a pas r´ussi. e void exit(int code) Produit l’arrˆt normal (fermeture des fichiers ouverts. /* version C de randomize */ rand(). 118 c H.h> #include <stdlib. i < 10. La valeur du code indiqu´ est e e transmise au syst`me d’exploitation. La valeur de adr doit provenir d’un appel de malloc. } } void *malloc(size t taille) Alloue un espace pouvant m´moriser un objet ayant la taille indiqu´e. e e e e e int system(const char *commande) Suspend momentan´ment l’ex´cution du programme en cours.) du programme.4 La biblioth`que standard e 8 ´ ´ AUTRES ELEMENTS DU LANGAGE C a ` lui passer un argument diff´rent chaque fois. size t taille) Alloue un espace suffisant pour loger un tableau de nombre objets. Par e e e exemple clock compte le temps (mais c’est un temps relatif au lancement du programme) en fractions de seconde. e e enfin renvoie l’adresse de l’espace nouvellement allou´.

ou NULL si une ıne e telle variable n’est pas d´finie. Fonctionne correctement mˆme si ces deux zones se rencontrent ou se chevauchent. ıne a char *strcat(char *destin. Ainsi.4 La biblioth`que standard e valeur d´pend du syst`me). size t taille.4. strlen("ABC") vaut 3. avec un argument int (resp. Cette fonction trie sur place (c’est-`-dire sans utiliser un tableaux auxiliaire). const void *)) Quick sort. nulle ou positive selon que le premier objet est respectivement inf´rieur. long labs(long x) Valeur absolue. elle utilise la fonction de comparaison donn´e par comp. size t nbr. ayant e e l’adresse de base donn´e par tab. e e ıne e e d’une commande l´gale pour le syst`me utilis´. char *getenv(char *nom) Rend la chaˆ qui est la valeur de la variable d’environnement ayant le nom indiqu´. voir ci-dessus. Il s’agit du nombre de caract`res utiles : le caract`re ’\0’ e ıne e e qui se trouve ` la fin de toutes les chaˆ a ınes n’est pas compt´. const char *source. form´ de nbr objets chacun ayant la e taille indiqu´e. e e e Toutes les versions de C n’offrent pas ce service. size t nombre) Copie la zone m´moire d’adresse source de de taille nombre dans la zone de mˆme taille et d’adresse e e destin . a e int bsearch(const void *ptr. La notion de variable d’environnement est d´finie au niveau du syst`me e e e sous-jacent.4. qsort utilise la fonction qui est la valeur de l’argument comp. ıne a ıne int strcmp(const char *a. const char *b) Compare les chaˆ ınes a et b pour l’ordre lexicographique (c’est-`-dire l’ordre par lequel on range les mots a dans un dictionnaire) et rend une valeur n´gative. e void *memcpy(char *destin. const void *tab. Rend destin. size t nombre) Copie la zone m´moire d’adresse source de de taille nombre dans la zone de mˆme taille et d’adresse e e destin . Rend destin. et rendre une valeur n´gative. const char *source) Copie la chaˆ source ` la suite de la chaˆ destin. size t nbr. int (*comp)(const void *. 8. const char *source. z´ro sinon.3. Voir ` la section 6. ou tri rapide. Garreta. void *memmove(char *destin. un tableau dont tab donne l’adresse de base. const char *source) Copie la chaˆ source ` l’adresse destin. qui est e d´finie comme pour qsort. a par ordre croissant.h char *strcpy(char *destin. int (*comp)(const void *. e Exemple (bien connu des utilisateurs de Dev-C++ . avec options et arguments. e inf´rieur. size t taille. e c H. ´gal ou sup´rieur ` b. ee Cette fonction doit prendre les adresses de deux objets comme ceux dont le tableau est fait. un mod`le d’utilisation de cette fonction. void qsort(void *tab. e 73 La fonction correspondante pour les r´els s’appelle fabs (cf. mais on peut savoir ce qu’il en est : l’appel system(NULL) rend une valeur non nulle si l’interpr`te de commandes peut effectivement ˆtre appel´ depuis un proe e e gramme. section 8. e e e a size t strlen(const char *s) Rend le nombre de caract`res de la chaˆ s.5). e int abs(int x). form´ de nbr objets chacun ayant la taille indiqu´e. Pour cela. e Pour comparer les ´l´ments du tableau. pause est une commande MS-DOS/Windows) : system("pause"). un objet ´gal ` e e e e a celui ayant ptr pour adresse.´ ´ 8 AUTRES ELEMENTS DU LANGAGE C 8.2. long)73 . const void *)) Binary search.3 Traitement de chaˆ ınes : string. respectivement. ´gal ou sup´rieur e e e e au second. 2004 119 . exemple 2. nulle ou positive selon que a est. Ces deux zones ne doivent pas se rencontrer. La chaˆ commande doit ˆtre l’expression compl`te. recherche dichotomique. Cette fonction recherche dans un tableau qui doit ˆtre tri´.

e double pow(double x. e e 8.4. e CHAR MAX. c’est-`-dire qu’il n’est pas un caract`re de contrˆle. int isspace(int c) ⇔ c est un caract`re d’espacement : ’ ’. ch x = double tanh(double x) rend la valeur de la tangente hyperbolique de x. Garreta.4.4 La biblioth`que standard e 8 ´ ´ AUTRES ELEMENTS DU LANGAGE C 8.4 Classification des caract`res : ctype. On doit avoir x ∈ [−1. int isalpha(int c) ⇔ c est une lettre. double log10(double x) rend la valeur du logarithme d´cimal de x. double y) rend la valeur de xy . dans − π . double atan(double x) rend la valeur de arctan x. lorsqu’ils sont vrais. π . soit par des macros. 1].6 Limites propres ` l’impl´mentation : limits. CHAR MIN valeurs extrˆmes d’un char. minuscule) rend la lettre minuscule (resp. π .h a e Ces fichiers en-tˆte d´finissent les tailles et les domaines de variation pour les types num´riques. On doit avoir x ∈ [−1. float. ex −e−x 2 ex +e−x 2 x = chx shx e e double log(double x) rend la valeur du logarithme n´p´rien de x. e a e o int isgraph(int c) ⇔ c est un caract`re imprimable autre qu’un caract`re d’espacement. ’\r’ ou ’\f’. transform´ en double. 120 c H. double tan(double x) rend la valeur de tan x. ’\n’. e SCHAR MAX. e e int ispunct(int c) ⇔ c est une ponctuation.8. dans [0. soit sh x = double cosh(double x) rend la valeur du cosinus hyperbolique de x. e int iscntrl(int c) ⇔ c est un caract`re de contrˆle (c’est-`-dire un caract`re dont le code ASCII est e o a e compris entre 0 et 31). e e a e double fabs(double x) la valeur absolue de x. 2004 . Il se produit une erreur si x = 0 et y = 0 ou si x < 0 et y n’est pas entier. 1]. π]. 2 2 v double atan2(double y. double asin(double x) rend la valeur de arcsin x. double x) rend la limite de la valeur de arctan u dans − π . 8. Les ee e e e pr´dicats rendent. int isprint(int c) ⇔ c est un caract`re imprimable. 2 2 double acos(double x) rend la valeur de arccos x. int isupper(int c) ⇔ c est une lettre majuscule. e int isalnum(int c) ⇔ c est une lettre ou un chiffre. log10 x. ni une lettre ni un chiffre. On y trouve : e e e CHAR BIT nombre de bits par caract`re. e UCHAR MAX valeur maximum d’un unsigned char. double ceil(double x) rend la valeur du plus petit entier sup´rieur ou ´gal ` x. majuscule) correspondante. sinon rend le caract`re c lui-mˆme. une valeur non nulle qui n’est pas forc´ment ´gale ` 1 : e e e a int islower(int c) ⇔ c est une lettre minuscule. e e a e double floor(double x) rend la valeur du plus grand entier inf´rieur ou ´gal ` x. ex .h e Les ´l´ments de cette biblioth`que peuvent ˆtre implant´s soit par des fonctions. dans − π . int tolower(int c). int isdigit(int c) ⇔ c est un chiffre d´cimal. √ double sqrt(double x) rend la valeur de x. ’\t’. π lorsque u → x+ 2 2 y et v → y. On doit avoir x > 0. Pour x = 0 c’est la mˆme chose que arctan x e double sinh(double x) rend la valeur du sinus hyperbolique de x. double cos(double x) rend la valeur de cos x. int toupper(int c) si c est une lettre majuscule (resp. transform´ en double.5 Fonctions math´matiques : math.4.h. th double exp(double x) rend la valeur de l’exponentielle des. c’est-`-dire un caract`re imprimable qui n’est ni un caract`re a e e d’espacement. log x. On doit avoir x > 0. SCHAR MIN valeurs extrˆmes d’un signed char.h e double sin(double x) rend la valeur de sin x. On doit avoir x ≥ 0.

). e e FLT RADIX base de la repr´sentation exponentielle. Ces fichiers d´finissent ´galement les constantes analogues pour les double (noms en  DBL . Le fichier float. INT MAX. Garreta.4 La biblioth`que standard e SHRT MAX. FLT MAX plus grand nombre repr´sentable. LONG MAX.´ ´ 8 AUTRES ELEMENTS DU LANGAGE C 8.0. e FLT MAX EXP plus grand exposant n tel que FLT RADIXn − 1 soit repr´sentable. e FLT MIN plus petit nombre positif repr´sentable (sous forme normalis´e).  ` la place e e a de  FLT . e UINT MAX valeur maximum d’un unsigned int.. ) et les long double (noms en  LDBL . e FLT ROUNDS type de l’arrondi pour l’addition.0 + e = 1... LONG MIN valeurs extrˆmes d’un long. e FLT MAX 10 EXP plus grand n tel que 10n soit repr´sentable. SHRT MIN valeurs extrˆmes d’un short.. FLT MANT DIG nombre de chiffres de la mantisse. e USHRT MAX valeur maximum d’un unsigned short. e ULONG MAX valeur maximum d’un unsigned long.. INT MIN valeurs extrˆmes d’un int. c H.. e e FLT MIN EXP plus petit exposant n tel que FLT RADIXn soit repr´sentable (sous forme normalis´e).h d´finit : e FLT DIG pr´cision (nombre de chiffres d´cimaux de la mantisse). e e FLT MIN 10 EXP plus petit exposant n tel que 10n soit repr´sentable (sous forme normalis´e). 2004 121 . e e FLT EPSILON plus petit nombre e tel que 1.

60 continue. 6 comparaison (op´rateur). 22 e .h. 38. 58 e default. 62 e ceil. 120 close. 23 e *= (op´rateur). 13 bits (champs de bits). 97 e acos. 47 argc. 120 expression conditionnelle (op´rateur). (op´rateur). 28 e ? : (op´rateur). 120 creat. 120 chaˆ de caract`res. 21 e a e acc`s relatif. 10 CHAR BIT. 29 e ^= (op´rateur). 106 enum´ration. 36. 118 exp. 19. 106 en-tˆte (fichier). 25. 32 e ->(op´rateur). 118 abs. 20 e %= (op´rateur). 32 e ~(op´rateur). (op´rateur). 50 arguments par adresse. 32 e abort. 45. 120 CHAR MAX.(op´rateur). 42 bsearch. 64 adresse d’une fonction. 118 EXIT FAILURE. 24 e adresse d’un objet. 36. 28 e >>(op´rateur). 120 CHAR MIN. 24. 111 arithm´tique des adresses. 120 atan2. 108 e endif. 116 atan. 48 arguments en nombre variable. 32 e & (op´rateur). 56 char. 32 e && (op´rateur). 36. 32 e ++ (op´rateur). 21 e . 116 assert. 118 EXIT SUCCESS.Index () (op´rateur). 28 e <<(op´rateur). 24. 29 a e do. 117 atoi. 36. 105 conjonction (op´rateur).. 31 e == (op´rateur). 28 e e d´clarateur complexe. 31 e appel de fonction. 120 adresse (op´rateur). 39 dur´e de vie (d’une variable).h. 101 ctype. 22 e a e conditionnelle (compilation). 117 automatique (variable). 23 e -= (op´rateur). 31 e extern. 22 e += (op´rateur). 31 e [ ] (op´rateur). 28 e <<= (op´rateur). 25. 120 atof. 13 e effet de bord. 37 break. 30 e const. 21 e /= (op´rateur).(op´rateur). 77 affectation (op´rateur). 32 e . 54 ıne e champs de bits. 15. 29 a e connecteurs logiques (op´rateurs). 19 e * (op´rateur). 34 e cos. 30 e conjonction bit-`-bit (op´rateur). 62 e conversions arithm´tiques usuelles. 28 e compl´ment ` 1 (op´rateur). 29 e &= (op´rateur). 42 conversion de type (op´rateur). 22 e |(op´rateur). 7 e case. 101 commentaire. 32 e = (op´rateur). 119 acc`s ` un champ (op´rateur). 27 e e asin. 120 d´calage de bits (op´rateur). 32 e ||(op´rateur). 12 arguments des fonctions (passage). 30 e disjonction bit-`-bit (op´rateur). 57 e etiquette. 117 atol. 38 exit. 7. 66 e arithm´tiques (op´rateurs). 120 cosh. 28 e >= (op´rateur). 119 calloc. 33 e . 16 . 36. 56 bloc (instruction). 18 else. 29 e |= (op´rateur). 41 cast (op´rateur). 28 e <= (op´rateur). 36. 53 ıne e chaˆ de caract`res (initialisation). 49 argv. 32 e <(op´rateur). 28. 41 define. 120 assert. 118 caract`re. 30 e >(op´rateur). 30 e ^(op´rateur). 104 disjonction (op´rateur). 111 argument formel.

6 e open. 54 ıne e initialisation d’une structure. 120 isupper. 120 iscntrl. 85 float. 120 locale (variable). 6 if. 7. 121 LONG MIN. 119 limits. 84 FILE. 116 nombre entier. 9 nombre flottant. 101 ordre d’´valuation des expressions. 101 O RDWR. 96 priorit´ des op´rateurs. 64 post-decr´mentation (op´rateur). 88 getchar.h. 30 e LONG MAX. 88 fichier binaire. 121 FLT ROUNDS. 121 FLT MIN EXP. 121 FLT MIN. 48 pointeur. 85 fgetc. 121 FLT MAX. 102 lvalue. 120 isprint. 119 memmove. 104 main. 7. 121 FLT MIN 10 EXP. 97 fsetpos. 120 isgraph. 103 indexation (op´rateur). 2004 INT MAX. 106 ifndef. 88 globale (variable). 22 e e e pr´processeur. 121 INT MIN. 97 fwrite. 36.h. 89 fputs. 118 math. 113 L. 40 fprintf. 120 log10. 101 O WRONLY. 120 flots. 113 lseek. 120 memcpy. 84 flottante (constante). 120 islower. 55 initialisation d’une variable. 111 malloc. 88 getenv. 120 ispunct. 7 labs. 29 a e passage des arguments des fonctions. 121 FLT EPSILON. 120 isalpha. 96 fseek. 120 pr´-decr´mentation (op´rateur). f (suffixe d’une constante). 36. 38. 121 FLT RADIX. 86 ferror. 119 moins unaire (op´rateur). 86 feof. 13 c H. 38 hexad´cimale (´criture d’un nombre). 22 e e pow. 19 e e 123 . 11 NULL. 66 e indirection (op´rateur). 7 FLT DIG. 103 e printf. 121 FLT MAX EXP. 7 e op´rateur. 121 FLT MAX 10 EXP. 12 goto. l (suffixe d’une constante). 106 include. 119 gets. 97 fgets. 23 e mot-cl´. 120. 120 logiques (op´rateurs). 16 F. 6 e n´gation (op´rateur). 120 isdigit. 12 log. 22 e e e pr´-incr´mentation (op´rateur). 118 fscanf. 20. 101 octale (´criture d’un nombre). 121 longjmp.h. 106 ifdef. 120 fclose. 120 isspace. 23 e initialisation d’un tableau. 120 jmp buf. 22 e e NDEBUG. 66 O RDONLY. 89. 121 fonction formelle. 87 fgetpos. 121 isalnum. 86 fflush. 85 for. 89 fread.INDEX INDEX externe (identificateur). 7 e e identificateur. 36. 18 macros. 121 floor. 34 e ou exclusif bit-`-bit (op´rateur). 121 FLT MANT DIG. 96 getc. 22 e e post-incr´mentation (op´rateur). 96 free. 52 initialisation d’une chaˆ de caract`res. 78 fopen. 84 fichier de texte. 96 fputc. 7 fabs. Garreta.

11 virgule (op´rateur). 121 ULONG MAX. 33 e void. 85 stdlib.h. 97 setjmp. 119 string. 14 statique (variable).h. 15 putc. 96 SCHAR MAX. 114 sin. 24 sprintf. 113 setjmp. Garreta. 97 SEEK END. 113 setvbuf. 120 typ´ ´num´r´. 86 SHRT MAX. 89 puts. 96 sqrt. 121 ungetc. 120 sinh. 101 realloc. 56 USHRT MAX. 15 e prototype d’une fonction. 120 srand. 117 read. 55 structure (signification d’une variable structure). 52 e (initialisation). 120 tmpfile. 89 union. 120 SCHAR MIN. 121 variable. 89 qsort. 79 tableau dynamique. 55 structures r´cursives. 84 e tan. 118 tableau tableau tableau tableau 124 (d´claration). 57 ee e e type ´num´r´. 97 rvalue. 119 rand. 82 e switch. 118 register.INDEX INDEX priv´ (dentificateur). 60 while. 68 taille d’un objet (op´rateur). 45 void *. 89 putchar. 119 strlen. 41 system. 36. 52 [dynamique] multidimensionnel. 121 SHRT MIN. 121 signal. 81. 61 types. 120 tanh. 43 rewind. 87 stdio. 65 volatile. 2004 . 87 stdin. 119 strcpy. 117 sscanf. 24 e tampon (d’entr´e ou de sortie). 96 static. 120 UINT MAX. 119 strcmp. 69 tableau de chaˆ ınes de caract`res. 36.h. 119 struct. 11 e e e typedef. 120 sizeof. 117 stdout. 87 strcat. u (suffixe d’une constante). 18 s´quence d’´chappement. 7 e e scanf. 9 types d´sincarn´s. 14 return. 14 stderr. 120 SEEK CUR. 44 public (dentificateur).h. 54 e structure (initialisation). 114 signal. 92. 101 c H. 52 (signification d’une variable tableau). 54 structure (d´claration). 72 e tableau de fonctions.h. 97 SEEK SET. 62 e e U. 39 write. 86 tolower. 78. 15. 36. 7 UCHAR MAX.

Sign up to vote on this title
UsefulNot useful