P. 1
PolyC

PolyC

|Views: 15|Likes:
Published by Olmac Othman

More info:

Published by: Olmac Othman on Oct 15, 2011
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

10/15/2011

pdf

text

original

Sections

  • 1 ´El´ements de base
  • 1.1 Structure g´en´erale d’un programme
  • 1.2 Consid´erations lexicales
  • 1.2.1 Pr´esentation du texte du programme
  • 1.2.2 Mots-cl´es
  • 1.2.3 Identificateurs
  • 1.2.4 Op´erateurs
  • 1.3.4 Expressions constantes
  • 1.4.2 Types ´enum´er´es
  • 1.4.3 Nombres flottants
  • 1.5 Variables
  • 1.5.1 Syntaxe des d´eclarations
  • 1.5.2 Visibilit´e des variables
  • 1.5.3 Allocation et dur´ee de vie des variables
  • 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´epar´ee
  • 1.6.1 Identificateurs publics et priv´es
  • 1.6.2 D´eclaration d’objets externes
  • 2 Op´erateurs et expressions
  • 2.1 G´en´eralit´es
  • 2.1.1 Lvalue et rvalue
  • 2.1.2 Priorit´e des op´erateurs
  • 2.2 Pr´esentation d´etaill´ee des op´erateurs
  • 2.2.1 Appel de fonction ()
  • 2.2.2 Indexation []
  • 2.2.3 S´election
  • 2.2.4 S´election dans un objet point´e ->
  • 2.2.5 N´egation!
  • 2.2.6 Compl´ement `a 1 ~
  • 2.2.7 Les c´el`ebres ++ et --
  • 2.2.8 Moins unaire -
  • 2.2.9 Indirection *
  • 2.2.10 Obtention de l’adresse &
  • 2.2.11 Op´erateur sizeof
  • 2.2.12 Conversion de type (“cast” operator)
  • 2.2.13 Op´erateurs arithm´etiques
  • 2.2.14 D´ecalages << >>
  • 2.2.15 Comparaisons == != < <= > >=
  • 2.2.16 Op´erateurs de bits & | ^
  • 2.2.17 Connecteurs logiques && et ||
  • 2.2.18 Expression conditionnelle ? :
  • 2.2.19 Affectation =
  • 2.2.20 Autres op´erateurs d’affectation += *= etc
  • 2.2.21 L’op´erateur virgule ,
  • 2.3 Autres remarques
  • 2.3.1 Les conversions usuelles
  • 2.3.2 L’ordre d’´evaluation des expressions
  • 2.3.3 Les op´erations non abstraites
  • 3 Instructions
  • 3.1 Syntaxe
  • 3.2 Pr´esentation d´etaill´ee des instructions
  • 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
  • 4 Fonctions
  • 4.1 Syntaxe ANSI ou “avec prototype”
  • 4.1.1 D´efinition
  • 4.1.2 Type de la fonction et des arguments
  • 4.1.3 Appel des fonctions
  • 4.2 Syntaxe originale ou “sans prototype” 4 FONCTIONS
  • 4.1.4 D´eclaration “externe” d’une fonction
  • 4.2 Syntaxe originale ou “sans prototype”
  • 4.2.1 D´eclaration et d´efinition
  • 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´es
  • 5.1 Tableaux
  • 5.1.1 Cas g´en´eral
  • 5.1.2 Initialisation des tableaux
  • 5.1.3 Chaˆınes de caract`eres
  • 5.2 Structures et unions
  • 5.2.1 Structures
  • 5.2.2 Unions
  • 5.2.3 Champs de bits
  • ES 5.3 Enum´erations
  • 5.3 Enum´erations
  • 5.4 D´eclarateurs complexes
  • 5.4.1 Cas des d´eclarations
  • 5.4.2 Pointeurs et tableaux constants et volatils
  • 5.4.3 La d´eclaration typedef
  • 5.4.4 Cas des types d´esincarn´es
  • 6 Pointeurs
  • 6.1 G´en´eralit´es
  • 6.1.1 D´eclaration et initialisation des pointeurs
  • 6.1.2 Les pointeurs g´en´eriques et le pointeur NULL
  • 6.2 Les pointeurs et les tableaux
  • 6.2.1 Arithm´etique des adresses, indirection et indexation
  • 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`eres
  • 6.2.6 Tableaux multidimensionnels formels
  • 6.2.7 Tableaux non n´ecessairement index´es `a partir de z´ero
  • 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
  • 6.3.3 Tableaux de fonctions
  • 6.3.4 Flou artistique
  • 6.4 Structures r´ecursives
  • 6.4.1 D´eclaration
  • 6.4.2 Exemple
  • 6.4.3 Structures mutuellement r´ecursives
  • 7 Entr´ees-sorties
  • 7.1 Flots
  • 7.1.1 Fonctions g´en´erales sur les flots
  • 7.1.2 Les unit´es standard d’entr´ee-sortie
  • 7.2 Lecture et ´ecriture textuelles
  • 7.2.1 Lecture et ´ecriture de caract`eres et de chaˆınes
  • 7.2.2 Ecriture avec format printf
  • 7.2.3 Lecture avec format scanf
  • 7.2.4 A propos de la fonction scanf et des lectures interactives
  • 7.2.5 Les variantes de printf et scanf
  • 7.3 Op´erations en mode binaire
  • 7.3.1 Lecture-´ecriture
  • 7.3.2 Positionnement dans les fichiers
  • 7.4 Exemples
  • 7.4.1 Fichiers “en vrac”
  • 7.4.2 Fichiers binaires et fichiers de texte
  • 7.4.3 Fichiers en acc`es relatif
  • 7.5 Les fichiers de bas niveau d’UNIX
  • 8 Autres ´el´ements du langage C
  • 8.1 Le pr´eprocesseur
  • 8.1.1 Inclusion de fichiers
  • 8.1.2 D´efinition et appel des “macros”
  • 8.1.3 Compilation conditionnelle
  • 8.2 La modularit´e de C
  • 8.2.1 Fichiers en-tˆete
  • 8.2.2 Exemple : stdio.h
  • 8.3 Deux ou trois choses bien pratiques
  • 8.3.1 Les arguments du programme principal
  • 8.3.2 Branchements hors fonction : setjmp.h
  • 8.3.3 Interruptions : signal.h
  • 8.4 La biblioth`eque standard
  • 8.4.1 Aide `a la mise au point : assert.h
  • 8.4.2 Fonctions utilitaires : stdlib.h
  • 8.4.3 Traitement de chaˆınes : string.h
  • 8.4.4 Classification des caract`eres : ctype.h
  • 8.4.5 Fonctions math´ematiques : math.h
  • 8.4.6 Limites propres `a l’impl´ementation : limits.h, float.h

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, 1988-2011

` 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 74 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, 1988-2011

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

. . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . . . . . . . . . . . . .1 Lecture-´criture . . . . e e 7. . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . .3. . . . . . . . 8 Autres ´l´ments du langage C ee 8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2 Fonctions utilitaires : stdlib. . . . 7. . . . . . . . . 7. . . . . . . . .4 A propos de la fonction scanf et des lectures interactives 7. . . e 8. . . e 7. . . . . . . . . . . . . . . . . . . . . . . . . . . . .2 La modularit´ de C . . . . . . . . . . . . . .. . . .2. . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . .h . . . . . . . . . . . . . . . . .2 Fichiers binaires et fichiers de texte .h . . . . . . . . . . . . . . . . . . . . . . .2 Exemple : stdio. . . . . . . 8.3 Fichiers en acc`s relatif . . . . 8.4 La biblioth`que standard . . . .1 Fichiers en-tˆte . .2 D´finition et appel des “macros” . . . . . .2. . . . . . .h . . . . . . . . . . . . . . . . . .1 Aide ` la mise au point : assert. . . . 8. . . . . Imprim´ le 20 mars 2011 e henri. . . . .3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6. . .5 Les variantes de printf et scanf . . . . 8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Garreta. . . . . . . . . .4. . . . . . . . . . . . . . . . .4 Classification des caract`res : ctype. . . .4. . . . . . . . . . . . . . . . . . .2 Ecriture avec format printf . . . . . . . . 6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . e 8. . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . 8. e 7. . . . . . . . .4. . . .4. . . . . . . . . . . . . . . . . . . . .3 Compilation conditionnelle . .3 Deux ou trois choses bien pratiques. .4. . . . . . . . . Structures r´cursives . . . . . . . . . . . . a e . . . .1. . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3 Structures mutuellement . . . . . . . .3 Interruptions : signal. . . . . . .3 Traitement de chaˆ ınes : string. . . . . . . . . . . . . . . . . . . . . . .1 Flots . . . . . . . . . . . . . . . . . .3. . e 8. e 8. . . . . . . . . . . . .1 Fonctions g´n´rales sur les flots . . . . . . . .3. . . . . . . . . . . . e 6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5 Fonctions math´matiques : math. . . . . . 7. . . . . . . . . . . . . . . 7. . . . . . . . . . . . .2 Les unit´s standard d’entr´e-sortie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1988-2011 . . . .1 Fichiers “en vrac” . . . . . . .2 Positionnement dans les fichiers . . . . . . .2. . . . . .h . . . . . . . . . . . . .3. .h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7. . e 8. . . . . . . . . .3. . . 7. . . . . . . . . .h . . . . . . .1.1 Inclusion de fichiers . . . .1 Lecture et ´criture de caract`res et de chaˆ e e ınes . . . . . . . . .1. . . . . . . . . . . . . . e 7. . . . . . . . . . . . . . . . . . .4 Exemples . .4 Flou artistique . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . .2 Lecture et ´criture textuelles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .garreta@univmed. . .2 Branchements hors fonction : setjmp. . . . . 8. . . . e e 7. . . . .h . . . . . . . . . . . .3 Op´rations en mode binaire . . . . . . . . . . . . . . . . . . . . .2 Exemple .h . . . . . . . . . . . . . . . . . . . . . . .1 Les arguments du programme principal . . . .4. . . . . . . 79 80 81 81 81 83 85 85 86 88 88 88 90 93 95 97 97 97 98 99 99 100 101 102 104 104 104 105 106 108 109 110 112 112 114 115 116 117 118 120 121 121 121 7 Entr´es-sorties e 7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6 Limites propres ` l’impl´mentation : limits. . .1 Le pr´processeur . . . .4. . . . . . . . . . .h. . . . . . . . . . . . . .3 Tableaux de fonctions .4. . . . . . . . .` TABLE DES MATIERES ` TABLE DES MATIERES 6. . . . . . . . . . . . . . . . . . e 8. . . . . . . . e 8. . .h . . . . . . . float. e 6. . .4. . . . . . . . .3 Lecture avec format scanf . . . .5 Les fichiers de bas niveau d’UNIX . . . . . . . . . . . . . . .4 6. 8. . . . . . . . . . . . . . .1. . . . . .4. . . . . . . . . . . . . . . . . . 8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7. . . . . . . . . . . . . . . .fr 4 c ⃝ H. . . . . .1 D´claration . . . . .2. . . . . . . . . . . . . . . . . 8. . . . . . . . . . . a 8. . . . . . . . . . . . . r´cursives e . . .4. . 7. . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . e 7. . . . . . 7. . . . . . . . . . . . . . . . . . . . . . . . .

comme la construction ≪ Program . a Pour finir cette entr´e en mati`re.2 1. } 1. en a ee e particulier. sans aucune autre propri´t´ sp´cifique . 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. Le programme montr´ ici est ´crit selon des r`gles strictes. En C on n’a donc pas une structure syntaxique englobant tout. e e 2. D’o` la question : par o` l’ex´cution doit-elle commencer ? La r`gle g´n´ralement suivie par l’´diteur de liens est u u e e e e e la suivante : parmi les fonctions donn´es il doit en exister une dont le nom est main. appel´ fichier source.h> int main() { printf("Bonjour\n").. L’´diteur de liens (ou linker ) prend en entr´e plusieurs fichiers objets et biblioth`ques (une e e e e vari´t´ particuli`re de fichiers objets) et produit un unique fichier ex´cutable. ´ventuellement garni de valeurs initiales.2. des tabulations et des sauts ` la ligne peuvent ˆtre plac´s ` tout endroit o` cela ne coupe pas un identificateur. sans lequel on e e ee ne saurait commencer un cours de programmation 1 : #include <stdio. tandis que les d´finitions de variables se traduisent par des e r´servations d’espace. e Chaque fichier objet est incomplet. e 1.1 ´ ´ ELEMENTS DE BASE 1 1. Des blancs. Le compilateur e e e e lit toujours un fichier. et produit un fichier. union. tout comme celles des autres fonctions.1 Consid´rations lexicales e Pr´sentation du texte du programme e Le programmeur est maˆ ıtre de la disposition du texte du programme. 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.. Par exemple. insuffisant pour ˆtre ex´cut´. d´clarations de variables et de fonctions externes. En fait. le lancement du programme ´quivaut ` l’appel de cette fonction par le syst`me d’exploitation. end. et que l’instruction ≪ return 0. ` part cela. 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 d´finitions de variables et e d´finitions de fonctions. qui sont : ee e – – – – – des des des des des directives pour le pr´processeur (lignes commen¸ant par #). voici la version C du c´l`bre programme-qui-dit-bonjour. typedef). les directives pour le pr´processeur (cf. L’´diteur de liens est largement ee e e e ind´pendant du langage de programmation utilis´ pour ´crire les fichiers sources. 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. un programme n’est qu’une collection de fonctions assortie d’un ensemble de variables globales.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. enum. les variables internes ` main sont locales. main est une fonction comme les autres. e a e Notez bien que. dit fichier objet. 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. 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. Garreta. return 0.1) doivent comporter un # dans la premi`re position de la ligne. e c constructions de types (struct. car le pr´processeur n’est pas le compilateur C et ne travaille pas sur la a e e e syntaxe du langage. ≫ du langage Pascal . 1988-2011 5 . qui peuvent mˆme avoir ´t´ e e e e ee ´crits dans des langages diff´rents. c ⃝ H. e e e Cela ne constitue pas une exception ` la r`gle donn´e ici. la plupart des compilateurs acceptent que main soit e e e d´clar´e void au lieu de int. ≫ n’apparaisse pas explicitement. section 8. C’est par elle que l’ex´cution e e commencera . un nombre ou un a e e a u symbole compos´ 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. N´anmoins. ou que ce type ne figure pas.

e – dans les identificateurs. 1988-2011 . e Attention.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. Cependant.h ≫) pour les besoins des biblioth`ques. le texte ≪ /* voici un grand e e e /* et un petit */ commentaire */ ≫ est erron´.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. En outre. par convention un programmeur ne doit pas utiliser des identificateurs qui commencent par ce caract`re. Cela assure qu’il n’y aura jamais de conflit avec les noms introduits e (` travers les fichiers ≪ . ♡ e 1.4 Op´rateurs e Symboles simples : ( = ) .2 Mots-cl´s e constitutionnellement".1. Le caract`re (appel´ ≪ blanc soulign´ ≫) est consid´r´ comme une lettre .2. lorsqu’il s’agit d’identificateurs externes. car ces noms commencent par un tel blanc a e soulign´. le nombre de caract`res discriminants est au moins de 31. 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. dont le premier est une lettre.2. il est conseill´ d’encadrer par des blancs toute e a e e e utilisation d’un op´rateur. Par exemple. 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 . Lorsque seul le compilateur est concern´. ≫ Les mots suivants sont r´serv´s. est compris comme ceci : ≪ message = "anti 1. e e 6 c ⃝ H. il peut donc figurer ` n’importe e e e ee a quelle place dans un identificateur. y compris ee e e e a ` l’int´rieur d’une chaˆ de caract`res. Cela est vrai en toute circonstance. car sa non-observance e e cr´e une expression ambigu¨. c’est-`-dire partag´s par plusieurs fichiers sources. * ! / ~ % < | > & ? ^ : Symboles compos´s : e -> ++ -<= >= == != && || << >> += -= *= /= %= <<= >>= |= &= ^= Tous ces symboles sont reconnus par le compilateur comme des op´rateurs. Les langages C et C++ cohabitant dans la plupart des compilateurs actuels. Garreta. [ + ] . Dans certaines circonstances cette r`gle est plus qu’un conseil. le texte a e ıne e message = "anti\ constitutionnellement". 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. Il est interdit d’ins´rer des e e caract`res blancs ` l’int´rieur d’un symbole compos´. 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++. 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. car seul ≪ /* voici un grand /* et un petit */ ≫ sera e vu comme un commentaire par le compilateur.2.

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

e e 8 c ⃝ H. Exemples : sizeof(int). on peut aussi le noter \d2 d1 ou \d1 e ıt´ Par exemple. la chaˆ suivante d´finit la suite des 9 caract`res 3 A. Une constante de type chaˆ de caract`res repr´sente une suite finie de caract`res. ı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.4 Expressions constantes Une expression constante est une expression de l’un des types suivants : e – toute constante litt´rale . Notez que. – un caract`re nul est ajout´ imm´diatement apr`s le dernier caract`re de la chaˆ pour en indiquer la fin . il est utilis´ tr`s fr´quemment en C. ’A’ . qui ne sera pas ´valu´e . escape (de code ASCII 27).1. ıne e e e e e ıne. 1. e &table[50] . D. e e a – l’expression constitu´e par l’application de l’op´rateur sizeof ` un descripteur de type.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 . e e a – l’expression constitu´e par l’application de l’op´rateur sizeof ` une expression quelconque. etc. S’il commence par un ou deux z´ros e e et si cela ne cr´e pas une ambigu¨ e. 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. e e Les expressions constantes peuvent ˆtre ´valu´es pendant la compilation. Nous verrons qu’en fait cette chaˆ comporte un caract`re de plus qui en marque la fin. \ et E : "A\033B\"C\fD\\E" Une constante de type caract`re appartient au type char. C. &fiche. de nos jours il e e e e s’agit presque toujours du code ASCII 4 .nom. 1.’a’. sizeof(2 * x + 3). dans une expression.10) ` une variable statique. ’A’. "HELLO". "HELLO" + 6 . e e e ’\0’ est toujours interchangeable avec 0. Garreta. "Bonjour" B o n j o u r \0 Figure 1 – Repr´sentation de la chaˆ "Bonjour" e ıne Par cons´quent. logique. on peut le noter indiff´remment 0. voyez la section 2. de longueur quelconque. c’est-`-dire entier repr´sent´ sur un octet. de mani`re contigu¨. ”. ` 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 .2. exemples : -1. Cela est fait ` titre facultatif par e e e a les compilateurs de certains langages. 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. ` l’endroit o` elle est ´crite.3. sizeof(char *) . – la constante chaˆ repr´sente alors. B. exemples : &x.14159265. ’\000’ e e e ou ’\0’ (mais certainement pas ’0’) . La e a e e valeur d’une constante caract`re est le nombre qui repr´sente le caract`re de mani`re interne . 1988-2011 . 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. e e e – l’expression constitu´e par l’application de l’op´rateur & (op´rateur de calcul de l’adresse. e ≪ Par caract`re nul on entend le caract`re dont le code interne est 0 . dans l’ordre o` ils e ıne e e e e u figurent dans la chaˆ .) ` une e e e a ou deux expressions constantes . exemples : 1. ıne e 4. En standard le langage C ne pr´voit pas le codage Unicode des caract`res. exemples : sizeof x.5e-2 . saut ıne e e de page. 2 * 3. – une expression correcte form´e par l’application d’un op´rateur courant (arithm´tique.

c’est tout) mais de l’ensemble de e e e e nombres qu’on a choisi de consid´rer et. de toute variable cens´e pouvoir repr´senter n’importe quelle valeur de cet e e e ensemble.1 ´ ´ ELEMENTS DE BASE 1. On dit parfois qu’une donn´e ≪ est un entier sign´ ≫ ou ≪ est un entier non sign´ ≫. laissant e e a e au programmeur le soin de r´aliser lui-mˆme. etc. ` l’aide des seuls types num´riques. mais ce fait est officiellement ignor´ par le programmeur. En C on a fait le choix oppos´. Par exemple. e e e e e e – Le deuxi`me crit`re de classification des donn´es num´riques est la taille requise par leur repr´sentation.1 Nombres entiers et caract`res e La classification des types num´riques ob´it ` deux crit`res : e e a e – Si on cherche ` repr´senter un ensemble de nombres tous positifs on pourra adopter un type non sign´ . parfois mˆme e e e e sur la rigueur. les caract`res. Dans d’autres langages. Comme pr´c´demment. par extension. L’organisation g´n´rale de cet ensemble e e e est ´vidente : on dispose de deux sortes de types de base. En termes de temps d’ex´cution.4. le nombre 123 consid´r´ comme un ee e ee 5. 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. Garreta. 1988-2011 9 . e 1.4 Types fondamentaux l’ex´cution ne commence. non d’une valeur particuli`re. et donc d’une variable devant repr´senter tout e e e ´l´ment de l’ensemble. c’est un attribut d’un ensemble. e 1. les bool´ens. l’implantation des types de e e a e niveau sup´rieur. qui reste oblig´ e e e de consid´rer ces donn´es comme appartenant ` des ensembles disjoints. sont cod´s e e e de mani`re interne par des nombres. a e e 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 . C’est un abus de langage : le caract`re e e e e sign´ ou non sign´ n’est pas un attribut d’un nombre (un nombre donn´ est positif ou n´gatif. les constantes symboliques. l’´valuation des expressions constantes est donc e e e enti`rement ≪ gratuite ≫.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 Table 1 – Les types du langage C Le tableau 1 pr´sente l’ensemble des types connus du compilateur C. les nombres entiers et les nombres flottants. c ⃝ H.. 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.

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

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

extern float a. Par cons´quent.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. Un argument formel est accessible de l’int´rieur de la fonction. 1988-2011 . Arguments formels. Le corps d’une fonction est lui-mˆme un bloc. z. int une_fonction(int j) { short k. Dans le bloc o` il e ee e a u est d´clar´. } Sans prototype : long i = 1. Avec prototype : long i = 1. 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 . 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 . e Exemple. y = 0. Garreta. k une variable locale et j un argument formel de une fonction.. } Ci-dessus. en aucun cas on ne peut y faire r´f´rence depuis un point ext´rieur ` ce bloc. Toutes les d´clarations de variables locales ` un bloc doivent ˆtre ´crites au d´but du bloc. { = expression rien } Les d´clarations de variables peuvent se trouver : e – en dehors de toute fonction. .. 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 9. ee e Variables globales. . 1. partout o` une variable locale plus e e u profonde ne le masque pas. il s’agit alors d’arguments formels. e a Variables locales. a e – dans l’en-tˆte d’une fonction. static unsigned short cpt = 1000. avec une simplification : les fonctions ne peuvent pas ˆtre imbriqu´es les unes dans les e e autres. on ne doit pas d´clarer un argument formel et une variable locale du niveau le plus haut avec le mˆme nom. e e e – soit entre le nom de la fonction et le { initial (fonction d´finie en syntaxe originale ou sans prototype). b...1.5 Variables 1 ´ ´ ELEMENTS DE BASE et chaque var-init est de la forme : identificateur Exemples : int x. En aucun cas on ne peut y faire r´f´rence depuis l’ext´rieur de la fonction. les arguments formels des fonctions sont consid´r´s e ee comme des variables locales du niveau le plus haut. 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. 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. avant la premi`re e a e e e e instruction. i est une variable globale. Pour ce qui concerne leur visibilit´. il s’agit alors de variables globales . { short k. et une complication : tout bloc peut comporter ses propres d´finitions de variables locales. c’est-`-dire des variables d´clar´es au d´but du bloc le plus a e e e ext´rieur 9 .5. Tout bloc peut comporter un ensemble de d´clarations de variables. – ` l’int´rieur d’un bloc. qui sont alors e dites locales au bloc en question. mais d’autres blocs peuvent ˆtre e e imbriqu´s dans celui-l`. e e e 12 c ⃝ H. int une_fonction(j) int j.

e e e A l’oppos´. il ne ≪ voit ≫ pas a les autres. Exemple : double x = 0. En toute circonstance la d´claration d’une variable statique peut indiquer une valeur e initiale ` ranger dans la variable. int t[5] = { 11.. 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. une telle initialisation n’a rien en commun avec une affectation comme celles qui sont faites durant l’ex´cution du programme. Par cons´quent : e e e ea e e – la valeur initiale doit ˆtre d´finie par une expression constante (calculable durant la compilation) . Il s’agit ici uniquement de pr´ciser la valeur qui doit e e ˆtre d´pos´e dans l’espace allou´ ` la variable. e La d´claration d’une variable locale peut elle aussi comporter une initialisation. i = exp .5 et o 1. elle n’a aucune incidence ni sur la taille ni sur la dur´e du e e programme ex´cutable produit. e Remarque. 33. e – une telle initialisation ≪ coˆte ≫ le mˆme prix que l’affectation correspondante. Cela est la d´finition e e mˆme des arguments des fonctions. 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. Garreta. ´ventuellement garni de valeurs initiales. L’ine e terpr´tation de ces z´ros d´pend du type de la variable. et qu’elle concerne l’´dition de liens. . 44. 1.5. 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. avant que l’ex´cution ne commence.5. de e e e e les allouer dans un espace m´moire de taille ad´quate. Le syst`me d’exploitation se charge. e c ⃝ H. Ainsi. ´quivaut au couple e int i. 1. Certains qualifieurs (static. e Les variables statiques pour lesquelles aucune valeur initiale n’est indiqu´e sont remplies de z´ros.5. Cela est vrai y compris pour des variables de types complexes (tableaux ou a structures).5. 22. a e e e a e chaque fois que la fonction ou le bloc est activ´ . register. c’est-`-dire le temps u e a d’´valuation de l’expression qui d´finit la valeur initiale. 1988-2011 13 .4 Initialisation des variables Variables statiques. Bien que la syntaxe soit analogue. pendant la traduction d’un fichier. 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.6.1 ´ ´ ELEMENTS DE BASE 1. 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. plac´e ` l’int´rieur d’un bloc.5 Variables du fichier o` la d´claration figure. la construction e a e int i = exp. imm´diatement avant l’activation du programme..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. non la compilation. 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. c’est-`-dire permanentes : elles existent pendant toute la a dur´e de l’ex´cution. e car le compilateur ne traduit qu’un fichier source ` la fois et. e e – une telle initialisation est enti`rement gratuite.5e3. e La question de la visibilit´ inter-fichiers est examin´e ` la section 1. voir les sections 1. puisqu’elle est ´valu´e ` l’ex´cution. 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. 55 }. /* d´claration */ e /* affectation */ /* d´claration + initialisation */ e Par cons´quent : e – l’expression qui donne la valeur initiale n’a pas ` ˆtre constante. e e e Variables automatiques.

Cons´quence ` retenir : les variables locales statiques se marient mal avec e a la r´cursivit´.. cpt++. statique (c’est-`-dire permanente). Malgr´ tout le bien qu’on vient d’en dire. e – pour sa dur´e de vie. Cette limitation ne fait pas partie du C ANSI..5 Variables locales statiques Le qualifieur static.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.1. cpt). cpt).. Par exemple. Dans le C original. e e 1. 1002. printf("%d ". Exemple : e void bizarre1(void) { static int cpt = 1000. Ainsi. dans certains calculateurs de telles variables ee e 14 c ⃝ H. plac´ devant la d´claration d’une variable locale. cpt++. ou entrer e e ee en conflit avec un autre objet de mˆme nom. dans l’exemple suivant : e e void fonction_suspecte(void) { static int i.5.5 Variables 1 ´ ´ ELEMENTS DE BASE Remarque. 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. car la deuxi`me activation de fonction suspecte acc`de aussi ` i. . e Attention. a 1. } Lorsque la d´claration d’une telle variable comporte une initialisation.5. 1988-2011 . cpt++. Garreta. produit une variable qui est e e – pour sa visibilit´. 1001. une variable e locale statique conserve sa valeur entre deux activations cons´cutives de la fonction. α fonction_suspecte(). 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. il s’agit de l’initialisation d’une vae riable statique : elle est effectu´e une seule fois avant l’activation du programme. 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. cpt). On notera e a pour finir que la version suivante est erron´e : e void bizarre3(void) { int cpt = 1000. } 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. } En effet. D’autre part. On aurait pu obtenir un effet analogue avec le programme int cpt = 1000. etc.. des appels successifs e de la fonction ci-dessus produisent l’affichage des valeurs 1000. } mais ici la variable cpt est globale et peut donc ˆtre modifi´e inconsid´r´ment par une autre fonction. printf("%d ". β . void bizarre2(void) { printf("%d ". tous les appels de bizarre3 afficheront la mˆme valeur 1000. locale . e a Elle n’est accessible que depuis l’int´rieur du bloc o` elle est d´clar´e. une variable automatique ne peut ˆtre initialis´e que si elle est simple e e (c’est-`-dire autre que tableau ou structure). Cela est tout ` fait e e e e a a inhabituel pour une variable locale.

g`ne le travail du compilateur eee e e et obtient un programme moins efficace que s’il n’avait jamais utilis´ ce qualifieur. elle n’aurait jamais de valeur d´finie. etc. Par exemple toute variable e dont la valeur peut ˆtre modifi´e de mani`re asynchrone (dans une fonction de d´tection d’interruption. sur un syst`me particulier ces deux qualifieurs peuvent n’avoir aucun autre effet. et e e e donc d’optimisations. La question ne e e e concerne que les noms de variables et de fonctions. peu ≪ optimisateur ≫. La d´claration d’une variable const doit n´cessairement comporter une initialisation car sinon. de types.4.1 Variables.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. C’est regrettable mais. Il n’y a pas de e e probl`me pour les variables locales. une variable qualifi´e const n’est pas tout e ` fait une expression constante au sens de la section 1. Il ne sera donc question que des noms des variables globales et des noms des fonctions.3. 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. parmi lesquels la d´termination des variables critiques et leur allocation e e dans les registres de la CPU. Il convient donc e e d’appliquer ce qualifieur aux variables les plus critiques d’abord. Ils n’appartiennent a e pas au C original.) doit ˆtre qualifi´e volatile. Elles sont automae e e tiquement initialis´es ` z´ro chaque fois qu’elles sont cr´´es. Il est conseill´ de toujours d´clarer const les variables et les arguments formels qui peuvent l’ˆtre. 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). Le compilateur accorde ce traitement sp´cial aux e a e ee e variables dans l’ordre o` elles figurent dans les d´clarations. e 10. car les autres identificateurs (noms de structures. 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. Il diminue le nombre d’hypoth`ses. e e e c ⃝ H. Garreta. une telle variable ne pouvant e e pas ˆtre affect´e par la suite.4. a mˆme qualifi´e const. sur les syst`mes o` cela a un sens. pointeur). dont la visibilit´ se r´duit ` l’´tendue de la fonction ou du bloc contenant e e e a e leur d´finition. Exemple : char *strcpy(char *dest.) n’existent que pendant la compilation et ne peuvent pas ˆtre partag´s par deux fichiers. de e e o e cette mani`re l’acc`s ` leur valeur ne met pas en œuvre le bus de la machine. e 1.6 Variables. Mis e ` part cela. ee a Les compilateurs sont tenus de signaler toute tentative d´celable de modification d’une variable const. la nature exacte d’une telle optimisation n’´tant pas sp´cifi´e. Ce renseignement permet au compilateur d’optimiser la gestion de la variable. return dest. L’utilisation du qualifieur register est int´ressante lorsque l’on doit utiliser un compilateur e rustique. 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. En particulier. Le sens du qualifieur volatile d´pend lui aussi de l’impl´mentation. etc. while ((*d++ = *s++) != 0) . que le compilateur peut faire sur une variable ainsi qualifi´e.5. pour la plupart des compilateurs. e e a Les variables ainsi d´clar´es doivent ˆtre locales et d’un type simple (nombre. ou par e e e e un canal d’entr´e-sortie. parce u e que tous les registres de la CPU sont pris) les d´clarations register restantes sont ignor´es. e e e e Note. Il s’av`re alors que le programmeur. fonctions et compilation s´par´e e e sont log´es dans un registre de l’unit´ centrale de traitement (CPU) plutˆt que dans la m´moire centrale . 1.6 1. } Attention.2. *s = srce. y compris dans une section du programme qui ne e comporte aucune r´f´rence ` cette variable. expliqu´es ` la e a section 5. Lorsque cela n’est plus possible (par exemple. char *srce) { register char *d = dest. Cela pr´vient e e e e u e le compilateur que sa valeur peut changer myst´rieusement. 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 initiale 10 indiqu´e lors de la e e d´claration. Le C ANSI introduit aussi les notions de pointeur constant et de pointeur sur constante. 1988-2011 15 . pour ces compilateurs une variable.1 ´ ´ ELEMENTS DE BASE 1.6.

e e – dans une d´claration externe de tableau.2 D´claration d’objets externes e Nous ne consid´rons donc d´sormais que les noms publics.1. Sauf pour les e e e a e deux points suivants : – une d´claration externe ne doit pas comporter d’initialisateur (puisque la d´claration externe n’alloue pas e e la variable). o` ces variables sont uniquement r´f´renc´es. sans toucher ` la visibilit´) et quand il s’applique e a e ` un identificateur global (static change la visibilit´. ` sa d´finition. – ` r´sultat entier (int). 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. int table[100]. on ´crira : u e e unsigned long n = 1000. En g´n´ral. extern int table[].2) . il est inutile d’indiquer la taille de celui-ci (puisque la d´claration externe n’alloue pas le tableau). pr´c´dant la d´claration d’un identificateur global. 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. section 4. e a 1. Identificateurs publics et priv´s. – le qualifieur static. d’utiliser le qualifieur static pour rendre priv´s tous les identificateurs qui peuvent l’ˆtre. e Regle 1. au mot extern pr`s. 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. sans modifier la dur´e de vie). valeur initiale. elle est alors suppos´e ˆtre e e – externe. 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. D´finition et d´claration d’une variable ou d’une fonction. e Jargon. a e e e Lorsqu’un programme est d´compos´ en plusieurs fichiers sources il est fortement conseill´. les propri´t´s de l’objet externe doivent ˆtre e a ee e indiqu´es pour que la compilation puisse avoir lieu correctement. e e – une d´claration se limite ` indiquer que l’objet en question a dˆ ˆtre cr´´ dans un autre fichier qui sera e a ue ee fourni lors de l’´dition de liens. – 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 . Exemple. rend celui-ci priv´.6 Variables. e (≪ Cr´er ≫ une variable ou une fonction c’est r´server l’espace correspondant. 1988-2011 . mˆme si elle est ult´rieurement d´finie dans le fichier o` figure l’appel. En plus de cela : – une d´finition produit la cr´ation de l’objet dont l’identificateur est le nom . 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. – Sauf indication contraire. tout identificateur global est public . etc. 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. Dans un autre fichier cette variable aura ´t´ d´finie : ee e unsigned long n. 16 c ⃝ H. – par cons´quent. de publique en priv´e. Garreta. – 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 . les noms externes doivent faire l’objet d’une e e e e d´claration : le compilateur ne traitant qu’un fichier ` la fois. pour ne pas dire e e e obligatoire. rempli par l’´ventuelle valeur e e e initiale de la variable ou par le code de la fonction). Un identificateur qui n’est pas public est appel´ e e e priv´. on ´crira : u ee e e extern unsigned long n. d’automatique en statique.) de l’identificateur en question. 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. fonctions et compilation s´par´e e e 1 ´ ´ ELEMENTS DE BASE Jargon.6. Exemple : extern unsigned long n. uniquement parce qu’ils partagent ` tort des identificateurs publics. Dans un autre fichier. Regle 2. a e – sans prototype (cf. Dans le fichier o` sont d´finies les variables n et table. La d´claration externe d’une variable doit ˆtre identique.

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

la valeur de l’expression est alors d´finie comme le e e e contenu de cet emplacement. e 18 c ⃝ H. une expression ` effet de bord modifie le contenu d’une ou plusieurs variables. e table[i]. fiche. a ıtre a Cela justifie les appellations lvalue (≪ left value ≫) et rvalue (≪ right value ≫). Les op´rateurs servent ` construire des expressions complexes. 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. 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´. pointeurs et fonctions) comme dans la d´claration complexe : e e e char (*t[20])(). car elle modifie la valeur de la variable x. l’expression y + 1 est pure. valeur). 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 []. l’expression 2 * i + 3 poss`de le type entier et la valeur 23. Certaines expressions sont repr´sent´es par une formule e e qui d´termine un emplacement dans la m´moire . Par exemple. Toute lvalue peut ˆtre vue comme une rvalue . 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 [].2 ´ OPERATEURS ET EXPRESSIONS 2 2. e e Dans la partie ex´cutable des programmes on trouve deux sortes d’expressions : e – Lvalue (expressions signifiant ≪ le contenu de. les constantes symboliques (lundi.) et les noms de e variables (x.1 Lvalue et rvalue Toute expression poss`de au moins deux attributs : un type et une valeur. Les op´rateurs dont il sera question ici peuvent aussi apparaˆ dans les d´clarations. C’est le cas des noms des variables. alors qu’en C de telles expressions sont habituelles. s’il s’agit ou non d’une lvalue. e Pour les expressions simples.) comme des expressions construites avec des op´rateurs ee e et ob´issant ` la loi commune. 0. dans e a u 2 + (t[i]) les parenth`ses sont superflues. car la priorit´ de l’op´rateur [] est sup´rieure ` celle de +. 1988-2011 . valeur. Dans tous les cas une expression repr´sente une valeur. e a Remarque 2. C’est le cas des constantes et e des expressions d´finies comme le r´sultat d’une op´ration arithm´tique. mais pas d’adresse. Exemples : x. en C un grand nombre d’expressions ont de tels effets.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.numero.) n’ont pas statut d’op´rateur.). ’A’. pour chaque sorte d’expression complexe. parfois dangereux. etc. 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. Une lvalue poss`de trois attributs : une adresse. Les expressions simples sont e e les constantes litt´rales (0. pour e ıtre e la construction des types d´riv´s (tableaux. etc. etc. c’est-`-dire les constantes litt´rales et les identificateurs.1. il suffit d’ignorer le contenant (adresse) pour ne voir que le e contenu (type. – Rvalue (expressions signifiant ≪ la valeur de. nombre. etc. Par e a exemple. qui est une expression ´galement l´gitime.. false. Remarque 1. ≫). des composantes des enregistrements et des tableaux. ≫)... la situation est la suivante : a e – sont des lvalue : – les noms des variables simples (nombres et pointeurs). Exemples : 12. e e e e les ´l´ments des tableaux.4. La raison principale de la distinction entre lvalue et rvalue est la suivante : seule une lvalue peut figurer ` gauche du signe = dans une affectation . e e 2. ^ et . comme 2 * x + 3 ou e a sin(0. Garreta. si i est une variable e enti`re valant 10. notamment pour ce qui est des priorit´s. 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.). 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. mais ce e e e e a n’est pas le cas dans (2 + t)[i].31416e1). – les noms des variables d’un type struct ou union 11. etc. n’importe quelle rvalue peut apparaˆ ` droite. Bien sˆr. C’est une originalit´ de C que de consid´rer les ≪ d´signateurs ≫ complexes (les objets point´s.. Une rvalue ne poss`de que deux e e e e e attributs : type.31416e1. Au contraire. les champs des structures. 2 * i + 3. tandis que l’affectation x = y + 1 (qui en C est une expression) est ` effet a de bord. On peut distinguer les expressions pures des expressions avec effet de bord 11 . Comme nous le verrons. Les sections suivantes pr´ciseront. D’autres expressions ne sont pas associ´es ` un emplae a cement de la m´moire : elles ont un type et une valeur. e e e e e La question des d´clarations complexes sera vue ` la section 5. notamment pour ce qui concerne la priorit´ des op´rateurs et l’usage des parenth`ses. un type et une valeur.

exp n ) exp 1 . Format : e a exp 0 ( exp 1 . Exemple : y = carre(2 * x) + 3..2 Priorit´ des op´rateurs e e C comporte de tr`s nombreux op´rateurs. .z signifie (x e a y) . Un coup d’œil au corps de la fonction (cf. e e L’expression exp 0 ( exp 1 .1. 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 ^ | && || ? : = *= . l’expression x .. section 1.2 ´ OPERATEURS ET EXPRESSIONS 2. exp n sont appel´s les arguments effectifs de l’appel de la fonction. Le signe → indique l’associativit´ ≪ de gauche ` droite ≫ . l’autre binaire (` deux arguments). ++ % -> -- -un *un &un > >= /= %= += -= <<= Table 2 – Op´rateurs du langage C e Remarque. exp n ) n’est pas une lvalue. exp 0 doit ˆtre de type fonction e e rendant une valeur de type T ou bien 12 adresse d’une fonction rendant une valeur de type T. l’expression carre(2 * x) a le type double. 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.2 2. sens de l’associativit´ e → sizeof (type) ← → → → → → → → → → → ← >>= &= ^= |= ← → . l’un unaire (` un argue e e a ment). Le signe ← indique l’associativit´ de ≪ droite ` gauche ≫ . 1988-2011 19 .. . par exemple.2 Pr´sentation d´taill´e des op´rateurs e e e e – ne sont pas des lvalue : – les constantes.3. e 2. Dans le tableau 2. soit ici la valeur 4x2 .4. mais en aucun e e e cas la chronologie de l’´valuation des sous-expressions. exp n ) poss`de le type T.z.. par exemple.. les suffixes un et bin pr´cisent de quel op´rateur a e e il s’agit.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. e 12. Garreta. comme -. sachant que carre est le nom d’une fonction rendant un double. Dans certains cas un mˆme signe. Notez que le sens de l’associativit´ des op´rateurs pr´cise la signification d’une expression. . La table 2 montre l’ensemble de tous les op´rateurs. Alors exp 0 ( exp 1 . – les noms des variables de type tableau.2. Cette double possibilit´ est comment´e ` la section 6. notamment pour ce qui concerne le jeu des priorit´s et la possibilit´ de e e e e parenth´sage.2) pourrait nous apprendre que la valeur de cette expression n’est autre que le carr´ de son argument.. 2. d´signe deux op´rateurs. .y . 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... – les noms des fonctions. l’expression x = y = z signifie e a x = (y = z). ≫).6. cette valeur est pr´cis´e par une ou plusieurs instructions ≪ return exp. e e a c ⃝ H. class´s par ordre de priorit´.

.4) les arguments correspondant ` la partie variable sont trait´s comme les arguments a e des fonctions sans prototype... e 2.4) e e 20 c ⃝ H.N − 1 d´termin´ par le nombre N d’´l´ments allou´s par la d´claration du tableau. Alors exp 0 [exp 1 ] est de e e type T . mˆme lorsque la liste des arguments est e vide. a – chaque argument effectif doit ˆtre compatible. avec la fonction carre d´finie ` la section 1. quand le compilateur rencontre l’instruction e printf(expr 0 . les deux fonctions les plus e utilis´es : printf et scanf. excusez du peu. 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]. e ee En C. – il n’est jamais v´rifi´ que la valeur de l’indice dans une r´f´rence ` un tableau appartient ` l’intervalle e e ee a a 0.3. En C ANSI. section 4. Cela constitue e un pi`ge assez vicieux tendu aux programmeurs dont la langue maternelle est Pascal.2. Il existe n´anmoins un moyen pour ´crire des fonctions avec un nombre variable d’arguments (cf. mais change compl`tement le sens de l’expression. Il faut savoir que lorsqu’une fonction a ´t´ d´clar´e comme ayant des arguments en nombre ee e e variable (cf. . exp 1 doit ˆtre d’un type entier. a e Cette remarque est loin d’ˆtre marginale car elle concerne. avec l’argument formel correspone dant . par exemple. C’est le cas. expr k sont trait´s e e e selon les r`gles de la section B. e ee e Deux d´tails auxquels on tient beaucoup en C : e ee – le premier ´l´ment d’un tableau a toujours l’indice 0 . . – 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. l’appel e a y = carre(2).3. Bien noter que les parenth`ses doivent apparaˆ e ıtre. e Remarque 2. 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..6. mais les autres param`tres effectifs expr 1 . e – tout argument effectif de type char ou short est converti en int . t[1] le second. au sens de l’affectation. Format : ee e e ee exp 0 [ exp 1 ] exp 0 doit ˆtre de type ≪ tableau d’objets de type T ≫. Ainsi. il e e ee e e n’y a jamais de ≪ test de d´bordement ≫. 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).2 Indexation [] ´ Definition restreinte (´l´ment d’un tableau). 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 . e Exemple. – les autres arguments effectifs ne subissent aucune conversion. – tout argument effectif de type float est converti en double . expr 1 . cette expression d´signe l’´l´ment du tableau dont l’indice est donn´ par la valeur de exp 1 . 1988-2011 . Op´ration : acc`s au ieme ´l´ment d’un tableau. Par exemple. mais leurs composantes peuvent ˆtre ` leur tour des a e a tableaux. c’est-`-dire selon les r`gles de la section B ci-dessus. si la fonction a ´t´ d´finie ou d´clar´e avec prototype (cf.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. il v´rifie que expr 0 est bien de type char *. section 4. Toutes ces questions sont reprises plus en d´tail ` la section 4.2.1) : ee e e e – le nombre des arguments effectifs doit correspondre ` celui des arguments formels 13 . etc. les tableaux sont toujours ` un seul indice . En C original. section 4. Leur absence ne provoque pas d’erreur.2.. Autrement dit. expr k ).. e a Remarque 1. L’expression t[0] d´signe le premier ´l´ment du tableau t. 13. Garreta.

1988-2011 21 .adresse fiche. si exp 0 est de type tableau. exp. exp->identif est strictement synonyme de (*exp).num fiche. e Op´ration : acc`s ` un champ d’une structure ou d’une union.2 Pr´sentation d´taill´e des op´rateurs e e e e ´ ` Definition complete (indexation au sens large). Format : e exp 0 [ exp 1 ] exp 0 doit ˆtre de type ≪ adresse d’un objet de type T ≫. 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. sauf si elle est de type tableau. e e 2. Exemple.identif (remarquez que les parenth`ses sont indise pensables). struct { char rue[32]. exp doit ˆtre une lvalue. l’expression exp 0 [exp 1 ] est une lvalue. 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. identif exp doit poss´der un type struct ou union. sauf si elle est de type tableau. Avec la d´claration e struct personne { long int num.adresse.2.identif d´signe le champ identif de l’objet e e d´signe par exp. Format : e e a exp . et identif doit ˆtre le nom d’un des champs de la structure ou de e e l’union en question.4 S´lection dans un objet point´ -> e e Op´ration : acc`s au champ d’une structure ou d’une union point´e. les expressions fiche. e Cette expression est une lvalue. En outre.3 S´lection . } adresse. sauf si elle est de type tableau. Ainsi. char *ville. d´signent les divers champs de la variable fiche. 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 e e champs de la structure ou de l’union en question.rue etc.2 ´ OPERATEURS ET EXPRESSIONS 2.2. Par exemple. avec la d´claration : e c ⃝ H. Alors. exp->identif d´signe le champ identif e de la structure ou de l’union dont l’adresse est indiqu´e par la valeur de exp. Garreta. } fiche. les deux d´finitions de l’indexation donn´es co¨ e e ıncident. Seules les deux premi`res sont des lvalue. Dans ces conditions. exp 1 doit ˆtre de type entier. 2. e Cette expression est une lvalue. 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 Figure 2 – L’indexation Il est clair que.

Bien que cela ne soit pas exig´ par le langage C. l’´galit´ ` z´ro d’un flottant n’est souvent que le fruit du hasard. Les expressions exp++ et ++exp ne sont pas des lvalue. La portabilit´ d’un e a e e programme o` cet op´rateur figure n’est donc pas assur´e. e e e e – un effet de bord : le mˆme que celui de l’affectation exp = exp + 1. struct noeud *fils. Cette expression est e e e caract´ris´e par : e e – un type : celui de exp . Le compl´ment ` un n’est pas une op´ration abstraite (cf. *frere.3. Cette expression d´signe une valeur de l’ensemble {0.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). e 1. e 2. } *ptr. A cause de l’impr´cision inh´rente ` la a e e e a plupart des calculs avec de tels nombres. Ce doit ˆtre une lvalue.2. 0 devient 1. 1988-2011 . Op´ration : post-incr´mentation.6 Compl´ment ` 1 ~ e a Op´ration : n´gation bit ` bit. Garreta. si exp = 0 0. 22 c ⃝ H. u e e 2. e e – une valeur : la mˆme que exp avant l’´valuation de exp++ . Format : e e a ~exp exp doit ˆtre d’un type entier. – un effet de bord : le mˆme que celui de l’affectation exp = exp + 1. Cette expression est e e e caract´ris´e par : e e – un type : celui de exp . 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. Remarque.3). les expressions suivantes sont correctes : ptr->info ptr->fils->frere ptr->fils->frere->frere 2.2. si exp ̸= 0 Cette expression n’est pas une lvalue. e ea e 2. double). l’autre pr´fix´ e e e e e e e e (´crit devant). Format : e e !exp Aucune contrainte. Format : e e e ++exp exp doit ˆtre de type num´rique (entier ou flottant) ou pointeur.2 Pr´sentation d´taill´e des op´rateurs e e e e 2 ´ OPERATEURS ET EXPRESSIONS struct noeud { int info. Op´ration : pr´-incr´mentation. – une valeur : la mˆme que exp apr`s l’´valuation de exp++ . Remarque.2. d´finie par : e e { !exp ≡ 1. Format : e e exp++ exp doit ˆtre de type num´rique (entier ou flottant) ou pointeur. Ce doit ˆtre une lvalue.5 N´gation ! e Op´ration : n´gation logique. on ´vitera de ≪ nier ≫ (et plus g´n´ralement e e e e de comparer ` z´ro) des expressions d’un type flottant (float.2. section 2. 1}. Cette expression n’est pas une lvalue.

L’expression *exp est une lvalue. MAXPILE est une constante repr´sentant un majorant du nombre d’´l´ments dans la pile) : ee e ee OBJET espace[MAXPILE]. Il est tr`s agr´able de constater que les op´rateurs ++ et -. Format : ee *exp ≪ ≫ Op´ration : acc`s ` un objet point´.8 Moins unaire - Op´ration : changement de signe. 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. espace[nombreElements++] = x. L’op´rateur ++ b´n´ficie de l’arithm´tique des adresses au mˆme titre que +. en C. Si c’est le pas on dit que la valeur de p est une adresse valide . a a ≪ c ⃝ H. si exp est de type e e e e e pointeur vers un objet de type T ≫. L’explication e e a est la mˆme. Ainsi. selon le sch´ma suivant : e – d´claration et initialisation d’une pile (OBJET est un type pr´d´fini. e 2. e c Application : r´alisation d’une pile. les pires erreurs sont ` craindre ` plus ou moins court terme.et --exp.9 Indirection * d´r´ference ≫. On dit aussi e e a e exp doit ˆtre une expression de type e ayant pour adresse la valeur de exp. L’expression *p signifie acc`s ` la m´moire dont p contient l’adresse. Format : e -exp exp doit ˆtre une expression num´rique (enti`re ou r´elle).2 ´ OPERATEURS ET EXPRESSIONS 2.2 Pr´sentation d´taill´e des op´rateurs e e e e Exemple. ≪ adresse d’un objet de type T . en rempla¸ant +1 par −1. la quantit´ effectivement ajout´e ` exp par l’expression exp++ d´pend de e e a e la taille de T. la machine pourra toujours le consid´rer comme une adresse e et acc´der ` la m´moire correspondante. Ainsi par exemple les expressions Pascal e↑[i] et a a e e[i]↑ doivent respectivement s’´crire.2. L’affectation y = x++. e ee e 2. d´pendant du probl`me particulier e e e e e consid´r´ . int nombreElements = 0.donnant lieu ` des expressions exp-. Ce n’est pas une lvalue. (*e)[i] et *(e[i]). ce qui oblige souvent ` utiliser des parenth`ses. Quel que soit le contenu de p. ´quivaut ` e a y = x. e e e Remarque 2. Garreta. 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. *exp repr´sente alors l’objet de type T e Remarque 1. C’est une op´ration e a e e sans filet ≫. 1988-2011 23 . x = espace[--nombreElements]. y = ++x. x = x + 1. e dans le cas contraire. y = x. e – op´ration ≪ d´piler une valeur et la ranger dans x ≫ : e if (nombreElements <= 0) erreur("tentative de depilement d’une pile vide"). On notera que. si on proc`de comme indiqu´ ci-dessus. ≪ Il existe de mˆme deux op´rateurs unaires -. Il appartient au programmeur de prouver que cete m´moire a ´t´ e a e e ee effectivement allou´e au programme en question. – op´ration e ≪ empiler la valeur de x ≫ : if (nombreElements >= MAXPILE) erreur("tentative d’empilement dans une pile pleine").2. On prendra garde au fait qu’il existe un bon nombre d’op´rateurs ayant une priorit´ sup´rieure e e e ` celle de *.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. la deuxi`me pouvant aussi s’´crire *e[i]. x = x + 1.

1988-2011 . Une autre utilisation ´l´gante de cet op´rateur est la cr´ation de composantes ≪ fixes ≫ dans ee e e les structures chaˆ ees. e 24 c ⃝ H. Un tel emploi de & devrait ˆtre consid´r´ comme erron´. Remarque. ce n’est pas une lvalue.11 Op´rateur sizeof e Op´ration : calcul de la taille correspondant ` un type. &t[j]. struct en_tete *suivant. mais beaucoup de compilateurs se e ee e contentent de l’ignorer. e e e e Dans un cas comme dans l’autre sizeof. Plus exactement. Format : e e &exp exp doit ˆtre une expression d’un type quelconque T. 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". Ce doit ˆtre une lvalue. &p->nom). Garreta. e e pointeur vers un int ≫. 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.. Exemple 2. ≫ et pour valeur l’adresse de l’objet repr´sent´ e e ≪ Ainsi. i et *p d´signent le mˆme objet.10 Obtention de l’adresse & Op´ration : obtention de l’adresse d’un objet occupant un emplacement de la m´moire. En particulier. 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) . 2. alors ` la suite a Exemple 1. Une expression r´duite ` un nom de tableau. si i est une variable de type int et p une variable de type de l’instruction p = &i. d’autres e a maillons seront cr´´s dynamiquement durant l’ex´cution : ee e entree en_tete_fixe Figure 3 – Maillon fixe en tˆte d’une liste chaˆ ee e ın´ struct en_tete { long taille. 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). L’expression &exp n’est pas une lvalue. est une expression constante.2 Pr´sentation d´taill´e des op´rateurs e e e e 2 ´ OPERATEURS ET EXPRESSIONS 2. 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. } en_tete_fixe = { 0. e e L’expression &exp a pour type ≪ adresse d’un objet de type T par exp.4). 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). l’op´rateur & appliqu´ ` de telles expressions e ee e ea est donc sans objet. La taille des objets est exprim´e en nombre d’octets. struct en_tete *entree = &en_tete_fixe.2.2. &en_tete_fixe }.2. ` 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 .. &i.

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

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 .c: In function ‘main’ monProgramme.. x = i / (float) j.2 Pr´sentation d´taill´e des op´rateurs e e e e 2 ´ OPERATEURS ET EXPRESSIONS Cependant. C’est donc une mani`re de cacher des erreurs sans les r´soudre. Exemple 1.. A ce sujet.. mais parfois n´cessaire. e e Exemple typique : si on a oubli´ de mettre en tˆte du programme la directive #include <stdlib. mais le probl`me n’est pas r´solu. .2.h>. si expr est d’un type poina e teur et type 2 est un autre type pointeur. et il n’y a aucun moyen de rendre cette affectation juste aussi longtemps que le compilateur 15. 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. a e ´ N. En effet. mais ailleurs la e u e valeur rendue par malloc sera endommag´e lors de son affectation ` p. L’op´rateur de changee ment de type est parfois n´cessaire. struct en_tete *suivant. p = malloc(sizeof(MACHIN)). 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. certains compilateurs acceptent cette expression. – 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`). /* ERREUR */ Exemple 2. e e e e ≪ g´n´rique ≫ : e e 26 c ⃝ H. Une utilisation pointue et dangereuse. voyez la remarque de la page e a 65. l’expression (type 2 )expr est toujours accept´e par le compilateur sans e le moindre avertissement. il est seulement e e cach´. /* CECI NE REGLE RIEN! */ . e e pour une raison facile ` comprendre : cet op´rateur fait taire le compilateur. A la compilation on a des avertissements.. e e l’utilisation de la fonction malloc de la biblioth`que standard soul`ve des critiques : e e . Il suffit pourtant de bien lire l’avertissement affich´ par le compilateur pour trouver la solution. MACHIN *p.. Ce qui est anormal n’est pas l’absence de cast. Voici deux mani`res de ranger dans x (une variable de type e e float) leur quotient d´cimal : e x = (float) i / j.. . mais le fait que le r´sultat de malloc soit tenu e e pour un entier 15 . et il est vrai que la compilation a lieu maintenant en silence.. }. seul capable de e garantir qu’` tel moment de l’ex´cution du programme ptr pointe bien un objet de type struct en tete. 1988-2011 . /* ERREUR */ x = (float)(i / j). Voici un exemple de manipulation cet objet : a ((struct en_tete *) ptr)->taille = n...) lui fait dire qu’on fabrique un pointeur (p) ` partir d’un entier (le r´sultat de malloc) sans a e op´rateur cast. L’appel d’une fonction bien ecrite ne requiert jamais un ≪ cast ≫. Par exemple (avec gcc) : monProgramme.. p = (MACHIN *) malloc(sizeof(MACHIN)). Rappelez-vous que toute fonction appel´e et non d´clar´e est suppos´e de type int. ou encore leur quotient par d´faut). L’affectation e p = malloc(.B. une telle conversion de type est faite sous la responsabilit´ du programmeur. Garreta. mais son utilisation diminue toujours la qualit´ du programme qui l’emploie. Si i et j sont deux variables enti`res.. 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). – d´claration d’un pointeur e void *ptr. l’expression i / j repr´sente leur division enti`re (ou e e e euclidienne. Bien entendu..

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). comme dans e e e ee e moyenne = somme / nombre.) et donne un r´sultat entier.3. 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). e – si au moins une des expressions expr 1 ou expr 2 n’est pas enti`re. 16.1). float moyenne. 1988-2011 27 .2 ´ OPERATEURS ET EXPRESSIONS 2.. double). la solution est simple : e moyenne = somme / 100.5 mais 0. e ≪ usuelles ≫ (cf.. En C on note par le mˆme signe la division enti`re. nombre. e Ici. Par exemple. 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).3. Avant l’´valuation de l’expression.2 Pr´sentation d´taill´e des op´rateurs e e e e fera une telle hypoth`se fausse. etc. la valeur de moyenne n’est pas ce que ce nom sugg`re. int. e expr2 Il r´sulte de cette r`gle un pi`ge auquel il faut faire attention : 1/2 ne vaut pas 0. soit. e o c ⃝ H.2. 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 .h> . De mˆme. Garreta. c’est mieux. les op´randes subissent les conversions e e langage C supporte l’arithm´tique des adresses (cf. multiplication.1).0. 2. Format : e    +      .. et la division flottante dans laquelle le r´sultat est e e flottant (float. si q est le quotient de a par b alors |q| est le quotient de |a| par |b|. 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). Le A propos de la division. et notamment la r`gle dite ≪ du plus fort ≫. La solution est donc d’informer ce dernier ` propos du type de malloc. qui prend deux op´randes e e e entiers (short. . dans e e e e int somme. p = malloc(sizeof(MACHIN)). 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... long. moyenne = somme / 100.13 Op´rateurs arithm´tiques e e Ce sont les op´rations arithm´tiques classiques : addition. e e e et le r´sultat est entier. soustraction. Dans ce cas particulier.2.    * exp1 exp2   /         % Aucune de ces expressions n’est une lvalue. division et modulo e e (reste de la division 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.1). car somme et 100 sont tous deux entiers et l’expression e somme / 100 est enti`re. section 2. D’autre part. . ici la solution est un peu plus compliqu´e : e moyenne = somme / (double) nombre. d’une directive ad hoc : #include <stdlib.. section 2. les r`gles qui commandent les types des op´randes et du r´sultat des e e e expressions arithm´tiques (cf. Mˆme probl`me si le d´nominateur avait ´t´ une variable enti`re.

section 2. inf´rieur.3. section 1. 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).. e a Les bits sortants sont perdus. e A propos de ≪ ˆ etre vrai ≫ et ≪ ˆ etre non nul ≫. << 1 Remarque analogue pour le d´calage ` droite >>. long.3. 1988-2011 . aussi bien si exp 1 est sign´e que si elle est non sign´e a e 28 c ⃝ H.. e u e 2. e e Ces expressions ne sont pas des lvalue. en C.). diff´rent. short. int.1). les op´randes subissent les conversions usuelles (cf.1). Format : e e { exp1 << >> } exp2 exp 1 et exp 2 doivent ˆtre d’un type entier (char.14 D´calages << >> e Op´ration : d´calages de bits. des z´ros . De cette mani`re le d´calage ` gauche correspond (sauf d´bordement) ` la multiplication par 2exp2 . Ainsi.3.2.1).2. ce qui ´vite beaucoup de parenth`ses disgracieuses. e a e e e des copies du bit de signe 18 . Comme on a dit (cf. sup´rieur. 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.. La e e e portabilit´ d’un programme o` ils figurent n’est donc pas assur´e. ces op´rateurs ont une priorit´ sup´rieure ` celle des a e e e a connecteurs logiques. Les bits entrants sont : – dans le cas du d´calage ` gauche. Remarque. Notez que. 18. vers la droite) d’un nombre de positions ´gal ` la e e a valeur de exp 2 . si on suppose que la valeur de exp 1 est cod´e sur huit bits. inf´rieur ou ´gal. Par exemple. Autrement dit. le type bool´en e 17. section 2.3). l’expression e e 0 <= x && x < 10 est correctement ´crite. exp 1 << exp 2 est la mˆme chose (si << 1 apparaˆ exp 2 fois) que e ıt ((exp 1 << 1) << 1) .. 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 exp 1 est d’un type sign´. tandis que le d´calage ` e e a e a e a e e droite correspond ` la division enti`re par 2exp2 . L’expression exp 1 << exp 2 (resp. des z´ros .2 Pr´sentation d´taill´e des op´rateurs e e e e 2 ´ OPERATEURS ET EXPRESSIONS 2.15 Comparaisons == != < <= > >= Il s’agit des comparaisons usuelles : ´gal. contrairement ` ce qui se passe en Pascal. not´s e e b 7b 6b5b4b3b2b1b0 (chaque bi vaut 0 ou 1). 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 . Ces exe e e pressions ne sont pas des lvalue.4. sup´rieur ou ´gal. e a e – dans le cas du d´calage ` droite : si exp 1 est d’un type non sign´. les op´randes subissent les conversions usuelles (cf. 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 gauche 17 (resp. Garreta. e e e Avant la prise en compte de l’op´rateur. Les op´rateurs de d´calage de bits ne sont pas des op´rations abstraites (cf. section 2.2.

2 Pr´sentation d´taill´e des op´rateurs e e e e n’existe pas en C. Ces programmes sortent du cadre de ce cours. 1988-2011 29 . etc.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. soulign´. 2. Bien sˆr. c’est-`-dire des programmes qui re¸oivent les informations transmises par les composants mat´riels ou ee a c e qui commandent le fonctionnement de ces derniers. la mise ` z´ro de a). selon la table a suivante. pour traduire le bout de Pascal if a = 0 then etc. ´crivent e if (a = 0) etc. on peut ´crire respectivement e if (i) etc. 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. 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 . a Par exemple. On admet g´n´ralement qu’` cause de propri´t´s techniques des processeurs. unsigned long attributs). section 2. N − 1 ].. On ne peut plus aujourd’hui conseiller cette pratique. Cela ressemble ` ceci : e e e a void afficher(char *texte. while (*ptchar != ’\0’) etc. La relation d’´galit´ se note ==. il fallait ´crire a e u e if (a == 0) etc. comme effet de bord.. p = p->suiv) etc. capitalis´. pour vraie dans tous les autres cas. N valant 8. elle sera tenue pour fausse si elle est nulle. italique. for (p = liste. e c ⃝ H. p. while (*ptchar) etc. 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. Une cons´quence de ce fait est la suivante : ` la place de e a if (i != 0) etc. non =. for (p = liste. 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. En effet.). Exemple.3. Ces exe e e pressions ne sont pas des lvalue.2. Attention. 16 ou 32. Voici une faute possible chez les nouveaux venus ` C e e a en provenance de Pascal qui. c’est-`-dire des sous-ensembles de l’intervalle d’entiers [ 0 . ont pris l’habitude de les utiliser largement. N’importe quelle expression peut occuper la place d’une condition .1). les biblioth`ques graphiques comportent souvent une fonction qui effectue l’affichage d’un texte e affect´ d’un ensemble d’attributs (gras. Pour faire en sorte que l’argument attributs puisse repr´senter un ensemble d’attributs quelconque. C’est pourquoi les premiers programmeurs C. En tant e qu’expression. ces expressions raccourcies e e a ee repr´sentent une certaine optimisation des programmes. 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. Du point de vue syntaxique cette construction est correcte. Format : e   & | exp1  ^ exp 1 et exp 2 doivent ˆtre d’un type entier. Dans de telles applications on est souvent aux pries avec des nombres dont chaque bit a une signification propre.2 ´ OPERATEURS ET EXPRESSIONS 2. ind´pendante des autres. mais elle est loin d’avoir l’effet escompt´. En effet. p = p->suiv) etc. a = 0 vaut 0 (avec. qui ne e disposaient que de compilateurs simples. Garreta. p != NULL. ou e e e des valeurs des op´randes. Il n’y a pas beaucoup d’exemples ≪ de haut niveau ≫ 19 d’utilisation de ces op´rateurs. ces op´rateurs sont principalement destin´s ` l’´criture de logiciel de bas niveau. les op´randes subissent les conversions usuelles (cf..

comportent un seul 1 plac´ diff´remment de l’un ` l’autre.. e a e Une autre cons´quence de cette mani`re de concevoir && et || est stylistique.. En effet.001000 */ etc.. alors le second op´rande ne sera mˆme pas ´valu´.. ch) != 0) i++. while (i < nombre && strcmp(table[i].2. /* affichage en gras et italique */ (avec les constantes de notre exemple. e e e a L’utilisateur d’une telle fonction emploie l’op´rateur | pour composer. 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 .000010 */ #define SOULIGNE 4 /* en binaire: 00.2 Pr´sentation d´taill´e des op´rateurs e e e e 2 ´ OPERATEURS ET EXPRESSIONS #define GRAS 1 /* en binaire: 00.. cela est une bonne nouvelle. ´crits en binaire. . le concepteur de la fonction utilise l’op´rateur & pour savoir si un attribut appartient ou non ` oe e a l’ensemble donn´ : e .17 Connecteurs logiques && et || Op´rations : conjonction et disjonction. s’´crit 00. 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. en binaire.2.. s’il suffit ` d´terminer e e e a e le r´sultat de la conjonction ou de la disjonction. 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.000001 */ #define ITALIQUE 2 /* en binaire: 00. En C la conjonction et la e e disjonction s’´valuent dans l’esprit des instructions plus que dans celui des op´rations.000100 */ #define CAPITALISE 8 /* en binaire: 00. 2. Cette expression repr´sente un ´l´ment de type int e ee parmi { 0. 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 . la valeur de l’expression GRAS | SOULIGNE est un nombre qui. si cette protection ne fait pas partie e e de la s´mantique de l’op´rateur pour le langage utilis´.. Ce programme est juste parce que la condition strcmp(table[i]... 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 – 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. e e Ces expressions ne sont pas des lvalue. e e 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... soit ch une variable e chaˆ L’op´ration ≪ recherche de ch dans table ≫ peut s’´crire : ıne.. c’est-`-dire des nombres e e a qui. Applications. 1988-2011 . GRAS | SOULIGNE). e e . l’ensemble d’attributs e qu’il souhaite : afficher("Bonjour". Garreta. Les valeurs utilis´es pour repr´senter les attributs sont des puissances de 2 distinctes.000101).. Ainsi. Format : e { } && exp1 exp2 || exp 1 et exp 2 sont deux expressions de n’importe quel type. ch) avec un premier argument table[i] invalide peut avoir des cons´quences tout ` fait d´sastreuses).. C garantit que le premier op´rande sera ´valu´ d’abord et que. exp 2 n’est pas ´valu´e et exp 1 && exp 2 vaut 0 . Pour le e e e e e programmeur. e De son cˆt´. i = 0.. 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 . lors de l’appel. le programme r´sultant peut ˆtre faux. 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.... exp 2 n’est pas ´valu´e et exp 1 || exp 2 vaut 1 .

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

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 ). Garreta.* / % e e e e >> << & ^ |. lecture et traitement d’une suite de caract`res dont la fin est indiqu´e par un point : e e . 1988-2011 . e e e L’expression exp 1 = exp 2 n’est pas une lvalue.2. L’expression r´sultante n’est pas une lvalue. Format : e e                 exp1                += -= *= /= %= >>= <<= &= ^= |=                                exp2 exp 1 doit ˆtre une lvalue. certains compilateurs (dont ceux conformes ` la norme 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 . 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. Autre exemple.1) et de celle d’une variable de type structure (cf. une affectation est donc consid´r´e en C comme a ee une expression : elle ≪ fait ≫ quelque chose. a – dans les autres cas. b = 0.. Contrairement ` ce qui se passe dans d’autres langages.. de la signification du nom d’une variable de type tableau (cf. 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 . e a – si exp 1 et exp 2 sont de types adresses distincts. ee e e e 2.. ` ce titre. On en d´duit la possibilit´ des affectations multiples. ` la condition qu’elles a a aient ´t´ explicitement d´clar´es comme ayant exactement le mˆme type. Elle aura donc le mˆme effet que les trois affectations a = 0. while ((c = getchar()) != ’. comprise comme a = (b = (c = 0)). Essentiellement : – deux types num´riques sont toujours compatibles pour l’affectation. comme e e e dans l’expression : a = b = c = 0. section 2. Des contraintes p`sent sur les types des deux op´randes d’une affectation exp 1 = exp 2 .’) exploitation de c . mais aussi elle ≪ vaut ≫ une certaine valeur et. La valeur de exp 2 subit ´ventuellement e e une conversion avant d’ˆtre rang´e dans exp 1 . c e = 0. 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. avec pour effet de bord le rangement de cette valeur dans l’emplacement de la m´moire d´termin´ par exp 1 . exp 1 et exp 2 sont compatibles pour l’affectation si et seulement si elles sont de mˆme e type.2.12) . 5. Cela fonctionne de la mani`re suivante : si • repr´sente l’un des op´rateurs + . e Op´ration binaire vue comme une modification du premier op´rande.. D’autre part. 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) .1.2. – on peut affecter le contenu d’une variable de type structure ou union ` une autre. elle peut a figurer comme op´rande dans une sur-expression.1) on d´duit que : e – on ne peut affecter un tableau ` un autre. e e ´ Exemple. 5. Lorsqu’elles sont e e satisfaites on dit que exp 1 et exp 2 sont compatibles pour l’affectation.20 Autres op´rateurs d’affectation += *= etc.

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, 1988-2011

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 float 21 . – 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 int 22 : 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 ; – sinon, convertir les deux op´randes en unsigned long. Le type de l’expression sera unsigned long. e – 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 e e e e exp 1 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, 1988-2011

2

´ OPERATEURS ET EXPRESSIONS

2.3

Autres remarques

configurations de bits, mais seule leur interpr´tation comme des entit´s de niveau sup´rieur (nombres entiers, e e e flottants...) est utile pour d´finir l’effet de l’op´ration en question ou les contraintes qu’elle subit. e e A l’oppos´, un petit nombre d’op´rateurs, le compl´ment ` un (~), les d´calages (<< et >>) et les op´rations e e e a e e bit-`-bit (&, ^ et |), n’ont pas forc´ment de signification abstraite. Les transformations qu’ils effectuent sont a e d´finies au niveau des bits constituant le codage des op´randes, non au niveau des nombres que ces op´randes e e e repr´sentent. De telles op´rations sont r´serv´es aux programmes qui remplissent des fonctions de tr`s bas e e e e e niveau, 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. 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. Ce d´faut, 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), 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, et le pousser ` : e e e a – isoler les op´rations non abstraites dans des fonctions de petite taille bien rep´r´es ; e ee – documenter soigneusement ces fonctions ; – constituer des jeux de tests validant chacune de ces fonctions.

c ⃝ H. Garreta, 1988-2011

35

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

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

non nulle) instruction 1 est ex´cut´e .4 Instruction if.) imbriqu´es les unes dans les autres.3 Etiquettes et instruction goto Format : ´tiquette : instruction e Une ´tiquette est un identificateur . 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. if (.. Exemple : o e for (i = 0. e transf`re le contrˆle ` l’instruction pr´fix´e par l’´tiquette en question. j <= N2.. e o a e e e Th´oriquement.. Dans la deuxi`me forme. tout algorithme peut ˆtre programm´ sans utiliser l’instruction goto. Il est donc rare que l’on ait besoin de l’instruction goto en C. l’´nonc´ e e lirecaractere. break et continue...else. k++) { . e e Imaginons que lirecaractere soit le nom d’une fonction sans argument. grande_boucle: /* ici on a quitt´ les deux boucles internes (sur j et k) */ e . rien n’est ex´cut´.) goto grande_boucle. k <= N2. i < N1. elle doit ˆtre plac´e devant une fonction.. . e e e e e si elle est fausse (nulle) instruction 2 est ex´cut´e... Garreta. /* mais on est toujours dans la boucle la plus externe (sur i) */ } 3.2. 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. Un tel emploi de goto est avantageusement remplac´ en C par l’utilisation des e e instructions return. L’instruction goto ´tiquette . e 3. j++) for (k = 0. } .2.. sera trouv´ l´gal par le compilateur. 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. Dans certains langages e e e comme Pascal..3. expression est ´valu´e : si elle est e e e e e vraie instruction 1 est ex´cut´e . Formats : if (expression) instruction 1 else instruction 2 et if (expression) instruction 1 Dans la premi`re forme.. sinon. for. 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. expression est ´valu´e : si elle est vraie (i. Une cons´quence regrettable de tout cela est un pi`ge assez vicieux tendu aux pascaliens. Elle ne se r´v`le utile que lorsqu’il faut e e abandonner plusieurs structures de contrˆle (if. 1988-2011 ... il suffit de l’´crire devant une e a e e instruction pour qu’elle soit automatiquement connue comme un nom ` port´e locale. Elle n’a pas ` faire l’objet d’une d´claration explicite . e e e e 38 c ⃝ H.2 Pr´sentation d´taill´e des instructions e e e 3 INSTRUCTIONS Remarque.e. i++) { for (j = 0. while... Cependant. s´par´e de celle-ci par un e e e e e caract`re deux points.. L’appel correct de cette fonction s’´crit : e lirecaractere().

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

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 Figure 4 – Instruction while (` gauche) et do.. C’est en cela qu’elle est l’analogue de l’instruction repeat... Les expressions expr 1 et expr 3 peuvent ˆtre absentes (les points-virgules doivent cependant apparaˆ e ıtre). expr 2 .. } Ainsi..until du e Pascal. while (expr 2 ) { instruction expr 3 .. e e e – expr 3 est une expression (par exemple une incr´mentation) ´valu´e ` la fin du corps de la boucle. l’instruction Pascal : for i:=0 to 9 do t[i]:=0 se traduit tout naturellement en C for (i = 0.... 1988-2011 . ex´cute donc au moins une fois l’instruction qui constitue son corps avant e d’´valuer la condition de continuation.2.until ´quivalente (≪ r´p´ter jusqu’` ce que.. Exemple : e 40 c ⃝ H.6 Instruction for Format : for ( expr 1 . 3.. . dans la construction for(expr 1 . il est ´valu´ avant l’ex´cution du corps de la boucle . ) instruction Bien entendu. cette construction ´quivaut strictement ` celle-ci : e e a expr 1 .. e e – expr 2 est le test de continuation de la boucle .. 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 !). Garreta.while (≪ faire tant que. Mais on notera que la condition figurant dans une instruction do. e e e a Par exemple.. ≫) sont inverses e e e a l’une de l’autre. On consid`re alors qu’elle est toujours vraie.. expr 2 .while (` droite) a a L’instruction do. expr 2 . ≫) et celle qui figurerait dans une instruction repeat.. expr 3 ) : – expr 1 est l’expression qui effectue les initialisations n´cessaires avant l’entr´e dans la boucle . ) instruction ´quivaut ` e a while (expr 2 ) instruction La condition de continuation expr 2 peut elle aussi ˆtre absente.3. Par exemple for ( . expr 3 ) instruction Par d´finition. la boucle ≪ ind´finie ≫ peut se programmer : e for ( . i++) t[i] = 0.while. i < 10. e e Ainsi.

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 (. comme dans l’exemple suivant. le contrˆle est transf´r´ ` l’instruction qui suit cet ´nonc´ .of. jusqu’` la fin du bloc ou jusqu’` une instruction de rupture (break).. &n)..) if (*ch++ == 0) return ch. case 2: j++. e e – si la valeur de expression ne correspond ` aucun case et s’il n’y a pas d’´nonc´ default. exploitation de la donn´e n e .) { printf("donne un nombre (0 pour sortir): "). if (n == 0) break. 1988-2011 41 .... switch (i) { case 3: j++. case 1: j++... } Si on suppose que i ne peut prendre que les valeurs 0 .. alors le contrˆle est transf´r´ ` l’instruction qui e e o eea suit l’´nonc´ default .7 Instruction switch Format : switch ( expression ) corps Le corps de l’instruction switch prend la forme d’un bloc {. 3. et si l’´nonc´ default existe.2.. Exemple (idiot) : j = 0. e e Attention... } 3. Autrement dit..of.. e e e e e o eea – s’il existe un ´nonc´ case avec une constante qui ´gale la valeur de expression. } Lorsque l’abandon de la boucle correspond aussi ` l’abandon de la proc´dure courante. scanf("%d". dans lequel on compte la fr´quence de chaque chiffre et des caract`res e e blancs dans un texte : c ⃝ H.. Garreta. alors l’instruction ci-dessus a le mˆme effet que e l’affectation j = i.} 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 .2 Pr´sentation d´taill´e des instructions e e e for (. on doit utiliser l’instruction a break.. Pour obtenir un comportement similaire ` celui du case. e e – si un tel case n’existe pas. toutes les instructions qui le suivent sont e a e e ex´cut´es.. .3 INSTRUCTIONS 3. de Pascal. Lorsqu’il y a branchement r´ussi ` un ´nonc´ case. alors aucune a e e instruction n’est ex´cut´e. de Pascal. 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...

. default: nb_autres++. while. l’instruction break e o provoque l’abandon de la structure et le passage ` l’instruction ´crite imm´diatement derri`re. Dans la port´e d’une structure de contrˆle (instructions for.. e e Lorsque plusieurs boucles sont imbriqu´es. elle produit l’abandon de l’it´ration courante et. ee e e e e e e dans le cas de for. } 3. break. le d´marrage de l’it´ration e e e e e suivante.’0’]++. par l’ex´cution de l’expression d’incr´mentation). i < 10. continue. case ’ ’: case ’\n’: case ’\t’: nb_blancs++.. Garreta. 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´.2.. for (i = 0. } ´quivaut ` e a { for (expr 1 . e 42 c ⃝ H. } L’instruction continue est moins souvent utilis´e. break.. goto sortie. ) nb_chiffre[i++] = 0. expr 2 . do ou for). 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).3. expr 3 ) { . . a e Par exemple la construction for (expr 1 . le cas ´ch´ant. do et switch)..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. expr 2 . break. 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 . expr 3 ) { .8 Instructions break et continue Formats : break. Dans la port´e d’une structure de contrˆle de type boucle e e o (while. } sortie: .. 1988-2011 . Dans ce cas.. c’est la plus profonde qui est concern´e par les instructions break e e et continue. .

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

La syntaxe indiqu´e ici est incompl`te . 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. e e e par un identificateur. Autrement dit.4 FONCTIONS 4 Fonctions Beaucoup de langages distinguent deux sortes de sous-programmes 24 : les fonctions et les proc´dures. 1988-2011 . . e e En C on retrouve ces deux mani`res d’appeler les sous-programmes. Garreta.4. car la d´finition serait prise pour une d´claration (cf. e ee l’appel d’une fonction renvoie un r´sultat.1 4. 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. Ainsi. 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. int n). mais du point de la syntaxe le langage e ne connaˆ que les fonctions. – tout type struct ou union. return compteur. le compilateur suppose qu’il s’agit d’une fonction ` r´sultat e a e entier.. e e 44 c ⃝ H. Ou. l’en-tˆte de la a e fonction extract donn´e en exemple ne peut pas s’´crire sous la forme char *extract(char *dest. Nous expliquons principalement le C ANSI. *srce. for (compteur = 0. Restriction : on n’a pas le droit de ≪ mettre en facteur ≫ un type commun ` plusieurs arguments. *dest = ’\0’. char *srce. e e e e Exemple. 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. La notion de sous-programme (comme les procedures de Pascal.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. 25. C’est pourquoi on ne parlera ici que de fonctions. e La premi`re ligne de la d´finition d’une fonction s’appelle l’en-tˆte. section 4. tandis que l’appel d’une proc´dure est une instruction. rassemblant dans une e section sp´cifique (cf. int extract(char *dest. c’est-`-dire globales.1.. – tout type pointeur . 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. compteur++) *dest++ = *srce++. On verra ult´rieurement (cf. qui est une fonction e a comme les autres ayant pour seule particularit´ un nom convenu. L’appel e d’une fonction est une expression. si on pr´f`re. ou parfois le prototype. alors que l’appel d’une proc´dure ne renvoie rien. un sous-programme est toujours suppos´ renvoyer une valeur.1.2) la mani`re de faire du C original.4). Si le type de la fonction n’est pas indiqu´. Ce peut ˆtre : e e e e – tout type num´rique . e 4.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 . } Contrairement ` d’autres langages. les subroutines de Fortran. a e e section 4. mˆme ıt e e lorsque celle-ci n’a pas de sens ou n’a pas ´t´ sp´cifi´e. section 5. compteur < combien && *srce != ’\0’. etc. Ainsi.2 Type de la fonction et des arguments L’en-tˆte de la fonction d´finit le type des objets qu’elle renvoie.1. de la fonction. La pr´sence d’un point-virgule e e e ` cet endroit provoquerait des erreurs bien bizarres. e e 4.) est suppos´e ici connue du e lecteur. e e e Chaque formule d´claration-un-ident poss`de la mˆme syntaxe qu’une d´claration de variable 25 . C’est le cas notamment de main.

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

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

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

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

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

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

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

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

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

54 c ⃝ H. Elles se d´clarent sous la syntaxe e e e suivante : { }{ } ’{’ declaration . Cette erreur est impossible ` d´celer ` la a e a compilation. Selon le syst`me sous-jacent. les variables-chaˆ ınes de caract`res peuvent ˆtre initialis´es lors e e e de leur d´claration. soit aucune erreur ne sera signal´e mais la validit´ de la suite du traitement sera e e e compromise. Garreta. ’l’. e e Ici. ’a’..’ . le nom que l’on souhaite donner ` la structure. bien que l’expression *p soit une lvalue (ce qui est une propri´t´ syntaxique). b. 5. pour l’essentiel. 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". ’t’. u e e e e – facultativement. ’u’.’ rien rien A la suite du mot-cl´ struct on indique : e a – facultativement. Exemple. ’a’.2. struct fiche { int numero. l’expression ≪ struct nom ≫ pourra ˆtre employ´e comme une sorte de nom de type . ’u’. c. (la deuxi`me formule d´finit un tableau de taille 6). voir aussi la remarque 2 de la section 6. e e e – facultativement. la liste des variables que l’on d´finit ou d´clare comme poss´dant la structure ici d´finie.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. } 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. prenom[32]. Heureusement. A ce propos. Initialisation.5. ’\0’ }. la liste des d´clarations des champs de la structure. while (s[i++] != ’\0’) .. Dans ce cas. soit une erreur sera signal´e ` l’ex´cution. constituerait une tentative de modification d’un objet constant.1 Structures et unions Structures Les structures sont des variables compos´es de champs de types diff´rents. le type ≪ adresse d’un caract`re ≫. ’t’. il ne s’agit que d’un moyen commode pour indiquer une collection de valeurs initiales ` ranger dans un a tableau. e ee e e e et toute occurrence de *p comme op´rande gauche d’une affectation : e *p = expression.."). bien sˆr) . Comme tous les tableaux.. ee elle ne doit pas ˆtre consid´r´e comme telle car *p repr´sente un objet m´moris´ dans la zone des constantes. 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’.2 5. Chaque champ peut avoir un type quelconque (y compris un autre type structure. nom struct nomopt ’.1. Comment allez-vous?". Par exemple. 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’. declaration ’}’ nom ’. char message2[] = { ’S’. return i . } a. entre accolades.2.1. si p a ´t´ d´clar´ e a e ee e e char *p.. Par la suite.. provoquant l’abandon e e a e imm´diat du programme. 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. char message2[] = "Salut". l’affectation suivante est tout ` fait l´gitime : a e p = "Bonjour. 1988-2011 . ’\0’ }. char nom[32].

Plus loin. x.a. x. – les structures sont pass´es par valeur lors de l’appel des fonctions . dans le sens des adresses croissantes : x. e. b. 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) . comme une expression d’un type primitif. y. b´n´ficie e e a e e de la s´mantique des valeurs. x. char nom[32]. x. nom et e e prenom . e a a e 35. x. prenom[32].c. c. } a. L’expression correcte de cette propri´t´ consisterait ` dire qu’une structure.c et enfin x. tous les autres champs devant commencer ` une adresse paire . par opposition ` un tableaux qui. x. e ın´ Comme dans beaucoup de langages. par exemple les maillons des listes chaˆ ees). struct fiche a.). x.f.e et enfin x. a 34. ou encore struct { int numero. sur e e ce point les compilateurs plus anciens pr´sentent des diff´rences de comportement. e e La possibilit´ pour une fonction de rendre une structure comme r´sultat est officielle dans le C ANSI .b. etc. Garreta.d.e. 1988-2011 55 . b et c en ´crivant simplement a e struct fiche x.a. qui engendrent des trous anonymes entre les champs. Alors que. ou de cr´er un pointeur sur une telle structure (ce qui est indispensable dans le cas des structures e e r´cursives. y. est assujetti ` la s´mantique des valeurs. Contigu¨ e. type 2 c.numero = 1234. Deux cas relativement fr´quents sont les suivants : e e a a – les champs de type char sont ` n’importe quelle adresse. on pourra d´clarer de nouvelles e a e variables analogues ` 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. c ⃝ H. x.b. les long ` une adresse multiple de quatre. char nom[32]. e e Voici quelques informations sur la disposition des champs des structures. Exemple : e a. } on trouve. e – le r´sultat d’une fonction peut ˆtre une structure. b. avec la d´claration correspondante en Pascal (facile ` imaginer).d. Ainsi. x. ≫. c. prenom[32]. D’autre part il est facile de e e voir que -sauf dans le cas de structures vraiment tr`s simples. x. a a – les champs d’un type simple doivent commencer ` une adresse multiple de leur taille (les short ` une adresse paire. type 3 f.2 Structures et unions Cette d´claration introduit trois variables a. beaucoup de compilateurs alloueraient les champs e a dans l’ordre : x. Il s’agit souvent des mˆmes r`gles e e e que celles qui p`sent sur les variables simples.f 35 . d. Position. on acc`de aux champs d’une variable de type structure au moyen de e l’op´rateur ≪ .les transmettre comme r´sultats des fonctions est e e en g´n´ral peu efficace. chacune constitu´e des trois champs numero. Les champs des structures subissent en g´n´ral des contraintes d’alignement d´pendant du ıt´ e e e syst`me sous-jacent.5 ´ OBJETS STRUCTURES 5. y. Nous aurions pu aussi bien faire les struct fiche { int numero. 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. Elles sont sans importance lorsque les structures sont exploit´es par le programme qui les cr´e. avec la d´finition e e struct modele { type 1 a. en mˆme temps elle donne ` cette structure le nom fiche. C garantit que les champs d’une structure sont allou´s dans l’ordre o` ils apparaissent dans la e u d´claration. (remarquez qu’il faut ´crire e d´clarations : e ≪ struct fiche ≫ et non pas ≪ fiche ≫). b. lui. b et c. }.

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

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

e 58 c ⃝ H.3) : e u e e e apr`s la d´claration e e typedef enum jourouvrable { lundi. etc. (fevrier vaut 2.4.1. 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. mardi vaut 1. – comme argument de sizeof (rare) : ≪ sizeof(int [100]) ≫ . Il nous reste ` voir comment d´clarer des ≪ tableaux a e de pointeurs ≫ . novembre. char *). 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. Appelons cela des types d´sincarn´s. Bien sˆr. des ≪ tableaux d’adresses de fonctions ≫. – dans les prototypes des fonctions : ≪ strcpy(char *. 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 . octobre. avril. septembre = 9.5. les e e e d´clarations des variables externes et les d´clarations des champs des structures et des unions . c. y. qui apparaissent dans trois sortes d’´nonc´s : e e – les d´finitions de variables (globales et locales). des fonctions ou des adresses d’objets simples. 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 ≫ . e Les nombres entiers et les valeurs de tous les types ´num´r´s sont totalement compatibles entre eux. octobre vaut 10. a. mardi.4. section 5. facile ` concevoir. Garreta. les expressions ≪ enum jourouvrable ≫ et ≪ JOUROUVRABLE ≫ sont ´quivalentes. – l’identificateur qu’il s’agit de d´clarer. Par e ee cons´quent. vendredi } JOUROUVRABLE. 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. jeudi. jeudi. fabrique la d´claration C souhait´e. Par d´faut. ` partir d’une sorte de description ≪ ` e e e a a l’endroit ≫. Autrement dit. mars vaut 3. d’un type enum.4 D´clarateurs complexes e Jusqu’ici nous avons utilis´ des formes simplifi´es des d´clarations. 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. La question concerne au premier chef les descripteurs de types. nous devons expliquer la syntaxe des formules qui permettent de d´crire des types de complexit´ e e quelconque. etc. 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. fevrier. mercredi. mercredi. e Ces formules ont mauvaise r´putation. vendredi } x. les ´num´rations peuvent se combiner avec la d´claration typedef (cf. 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.) 5. e e – les d´finitions des fonctions et les d´clarations des fonctions externes . [] et * qui repr´sentent des proc´d´s r´cursifs de e e e e e construction de types complexes . soit e e e e des tableaux. elles s’av`rent e a a e difficiles ` composer et ` lire par le programmeur. Tous les objets ´taient soit simples. mars. d’une struct ou union ou le nom d’un type baptis´ par typedef . voir section 8. 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. ≫ 5. Si elles sont faciles ` lire et ` traiter par le compilateur. 1988-2011 . e a e Pour cette raison nous allons donner un proc´d´ m´canique qui. decembre }. e e – un certain nombre d’occurrences des op´rateurs (). Ainsi. mardi. les d´clarations des param`tres formels des fonctions. struct ou union ou bien un type auquel on a donn´ un nom par une d´claration typedef .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). en C.2). b. enum.4 D´clarateurs complexes e 5 ´ OBJETS STRUCTURES ou encore enum { lundi. des ≪ fonctions rendant des adresses de tableaux ≫. etc.).

nous pouvons ´noncer la e recette de cuisine ≫ du tableau 3. u ≪ Dans ces conditions. En r´alit´ ces indications s’ajoutent aux r`gles donn´es ici. u ≪ fonction rendant un T ≫. En pratique on n’en a presque jamais besoin. les d´clarations de tabPtr et ptrTab peuvent ˆtre ´crites ensemble : e e e e int *tabPtr[]. un a e e ae e pointeur sur un tableau 37 d’entiers : pointeur sur un tableau de int tableau de int int ptrTab *ptrTab (*ptrTab)[] Les parenth`ses ci-dessus ne sont pas facultatives. a e e e Appliquant les r`gles du tableau 3. 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. Soit ` d´clarer tabPtr comme un tableau (de dimension ind´termin´e) de pointeurs d’entiers. e e Exemple 2. cherchons ` ´crire maintenant la d´claration de ptrTab. cette ligne constitue la d´claration C e a e cherch´e. presque sans modifier le processus que ces r`gles e e e e e ´tablissent : tout simplement. dans les r`gles (a) et (b). 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. alors les parenth`ses e e autour de exp d peuvent ˆtre omises. (*ptrTab)[]. e e e et la description naturelle d’un type tableau comporte l’indication du nombre de composantes de celui-ci. Tant que l’expression ` gauche n’est pas un type de base. car [] a une priorit´ sup´rieure ` *). la description naturelle du type. 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). struct ou union. e Table 3 – Construction d’un d´clarateur complexe e Exemple 1. En toute rigueur les r`gles a et b de ce tableau devraient ˆtre plus complexes. Pour la comparer ` la pr´c´dente. car elles encadrent une expression qui commence par * e (dit autrement. e Exemple 3. 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).5 ´ OBJETS STRUCTURES 5. enum. a – ` droite. 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. c ⃝ H. o` T est la description naturelle d’un type . Ainsi. o` T est la description naturelle d’un type . 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] 37. l’identificateur ` d´clarer (un seul). elles sont n´cessaires. si exp d n’est pas de la forme *exp. Garreta. La notion de pointeur sur un tableau est assez acad´mique. comme il faudra l’´crire dans un programme : ≪ int *tabPtr[]. 1988-2011 59 . Pour obtenir la d´claration d’une variable ou d’une fonction : e 1. Ecrivez : – ` gauche. o` T est la description naturelle d’un type. u adresse d’un T ≫ (ou ≪ pointeur sur un T ≫).4 D´clarateurs complexes e – la formule – la formule – la formule ≪ ≪ tableau de T ≫. des informations annexes sont ´crites ` l’int´rieur des crochets des tableaux et e e a e des parenth`ses des fonctions. ≫. a a e 2. Soit ` d´clarer une matrice de 10 lignes et 20 colonnes.

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. Les cas les plus utiles sont les suivants : 1. 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. contentons-nous de montrer des exemples ea significatifs. signal rend un pointeur vers une telle fonction. ` la ee e e a vue de l’expression float matrice[10][20]. ne doit e e e pas ˆtre modifi´e). int (*fon)(int)))(int) Finalement. e e 2.4. int (*fon)(int)) void (*signal(int sig. Cet exemple abominable est extrait de la biblioth`que UNIX.4 D´clarateurs complexes e 5 ´ OBJETS STRUCTURES Exemple 4. int (*fon)(int)) fonction (avec argument int s) rendant void *signal(int sig. 1988-2011 . les expressions finales obtenues sont assez lourdes ` dig´rer pour un humain. Plutˆt que de ıtre e o rendre encore plus lourdes des explications qui le sont d´j` beaucoup. int (*fon)(int)) pointeur sur fonction (avec argument int) rendant void signal(int sig. 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. l’´nonc´ e e e e void (*signal(sig. La formule 60 c ⃝ H. Voici comment ´crire un tel type e adresse de fonction (avec argument double) rendant double fonction (avec argument double) rendant double double La d´claration cherch´e est donc e e ≪ adrFon *adrFon (*adrFon)(double) double (*adrFon)(double) ≫. Ainsi. De mˆme. En outre. c’est-`-dire sans sp´cifier les arguments de la fonction signal (cette d´claration est fournie a e e dans le fichier <signal. Voici comment il faudrait la d´clarer en e a e syntaxe originale. Nous savons d´j` ´crire le type ≪ pointeur sur une fonction eae 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).2 Pointeurs et tableaux constants et volatils Les qualifieurs const et volatile peuvent aussi apparaˆ dans les d´clarateurs complexes. La formule : const type *nom d´clare nom comme un pointeur vers un objet constant ayant le type indiqu´ (la variable point´e. func))(). c’est-`-dire d’une fonction rendant void. nous fera reconnaˆ imm´diatement l’expression (*signal(2. Exemple 5. La fonction signal renvoie e l’adresse d’une ≪ proc´dure ≫. 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.f))(n) comme un appel correct de la ≪ proc´dure ≫ renıtre e e due par l’appel de signal.5. 5. Garreta. 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. *nom. 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 ≫. int (*fon)(int)))(int). la d´claration cherch´e est e e void (*signal(int sig. Comme annonc´.

5. b. Remarquez l’utilisation diff´rente d’un nom de structure (pr´c´d´ du mot struct) et d’un nom de type. tec[100]. *fils_droit. on fait suivre le mot r´serv´ typedef d’une expression identique ` une e e e a d´claration de variable. . volatils ou non. que par NOEUD a. Par e e e e tradition.7). *pec. b. c section 1. struct noeud *fils_gauche. e a e e L’ensemble des r`gles de syntaxe qui s’appliquent aux d´clarations de variables s’appliquent ´galement ici. On a donc la possibilit´ de d´clarer des tableaux d’objets volatils. c. *const pce = &e. c de ce type pourront par la suite ˆtre d´clar´es aussi bien par l’expression e e e struct noeud a. des pointeurs volatils vers des e e objets. Voici un autre exemple : typedef struct noeud { int info. Garreta. 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. } NOEUD. *const pcec = &ec. 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. 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 ≫. c. const int ec = 0. 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. etc.5 ´ OBJETS STRUCTURES 5. Des variables e a. e e 3. les noms des types d´finis ` l’aide de la d´claration typedef sont ´crits en majuscules.4. pcec. ec. Cette expression d´clare l’identificateur NOEUD comme le nom du type ≪ la structure noeud ≫ . a c ⃝ H. Plus simplement. b. te[100]. 5. on peut introduire plusieurs noms de types dans un seul ´nonc´ typedef. e e e Ainsi par exemple les d´clarations e int e. Notez qu’il n’y a pas besoin d’une notation sp´ciale pour d´clarer un tableau constant d’objets (variables) puisque la signifie e cation ordinaire des tableaux est exactement celle-l`. 1988-2011 61 . Elle permet de donner un nom e ` un type dans le but d’all´ger par la suite les expressions o` il figure.. e En r´sum´. *pec. MATRICE mat. Ainsi. Syntaxe : a e u typedef d´claration e Pour d´clarer un nom de type. *pcec ou tec[i] est ill´gale. *pe.3 La d´claration typedef e Cette construction est l’homologue de la d´claration type du langage Pascal. ` la suite de e e a 38.4 D´clarateurs complexes e type *const nom 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). la formule const type nom[expr opt ] d´clare nom comme un tableau d’objets constants 38 (nom[i] ne doit pas ˆtre modifi´). sont ´quivalentes ` e a float mat[10][20]. 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´. e e e Par exemple. Exemple : les d´clarations e e typedef float MATRICE[10][20]..

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

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

Apr`s cette instruction. e 6. Il existe trois mani`res e sˆres de donner une telle valeur ` un pointeur : u a 1. de telles op´rations ´tant e e e e obligatoirement confin´es dans les programmes ´crits en assembleur. Prendre l’adresse d’une variable existant par ailleurs. a Exemple. on fait r´f´rence ` ce dernier par a ee a l’expression : *variable Par exemple. 1988-2011 . d´finit la variable p comme ≪ pointeur vers un char ≫. e e Un pointeur n’est pas un concept unique. 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. 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. Provoquer l’allocation d’un nouvel espace. 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. Initialisation : p = &x. l’expression char *p.1. p contient maintenant une adresse valide. c’est-`-dire allouer dynamiquement une variable anonyme. e a a e e Comme dans l’exemple pr´c´dent. e e 2. elle contiendra e ee e l’adresse d’un caract`re. d´claration : e type *p. 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).6 POINTEURS 6 6. auquel on pourra faire r´f´rence par l’expression e ee *p Initialisation des pointeurs.1 D´claration et initialisation des pointeurs e Nous avons ´tudi´ ` la section pr´c´dente la syntaxe des d´clarations des pointeurs. en C les pointeurs sont le moyen d’am´liorer e e l’acc`s aux ´l´ments des tableaux ordinaires. e a Dans tous les langages o` ils existent. en incorporant au langage des techniques d’adressage indirect e ee couramment utilis´es en assembleur. La diff´rence est qu’ici elle n’a pas de nom propre : e e e e 64 c ⃝ H. e Les premiers langages ´volu´s ne permettaient pas les manipulations de pointeurs. En plus de cet aspect. Initialisation : p = malloc(sizeof(type)). Un pointeur correctement initialis´ est toujours le renvoi ` une deuxi`me variable. 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. Exemple. Par la suite. dont l’utilisateur n’est cens´ connaˆ ni la structure ni le comportement.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. d´claration : e type x. l’adresse de la variable x. Lorsqu’elle aura ´t´ correctement initialis´e. cette syntaxe est type *variable Si par la suite on affecte ` une telle variable l’adresse d’un certain objet. Cela n’en e e e ıtre facilite pas le maniement aux programmeurs d´butants. accessible ` partir du contenu de p. Un pointeur contient en principe une adresse valide. une variable tout ` fait analogue ` la variable x de l’exemple pr´c´dent existe. les expressions x et *p d´signeront le mˆme objet (le contenu de x). Dans le cas le plus e e a e e e simple. *p en d´signe le contenu. sans ressemblance avec les autres types de donn´es. *p. Mais ils y sont pr´sent´s comme des e e e e e entit´s fort myst´rieuses. la variable e a e point´e. Garreta. Le langage Pascal a introduit les pointeurs e e parmi les types de donn´es disponibles dans des langages g´n´ralistes.

6

POINTEURS

6.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), la variable ae ee ee e point´e deviendrait inaccessible et serait d´finitivement perdue. e e 3. Obtenir une valeur par des calculs l´gitimes sur des pointeurs. Par exemple, comme on le verra bientˆt, e o avec les d´clarations e type t[10], *p, *q; suivies de l’initialisation p = &t[0]; l’expression q = p + 3; affecte ` q l’adresse du quatri`me ´l´ment du tableau t, c’est-`-dire &t[3]. a e ee a Il existe aussi une mani`re risqu´e, en tout cas non portable, de donner une valeur ` un pointeur : lui affecter e e a une constante ou une expression num´rique, dans le style de : e p = (struct machin *) 0x2A3E; De telles expressions, indispensables dans certaines fonctions de bas niveau (contrˆle d’organes mat´riels, etc.), o e rendent non portables les programmes o` elles figurent. Elles doivent donc ˆtre ´vit´es chaque fois que cela est u e e e possible. A propos de la taille des pointeurs et celle des entiers. 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 ; il est donc parfois erron´ et toujours a e dangereux de supposer qu’un entier puisse contenir un pointeur. Cette question peut sembler tr`s secondaire, e l’utilisation de variables enti`res pour conserver ou transmettre des pointeurs paraissant bien marginale. Or e c’est ce qu’on fait, peut-ˆtre sans s’en rendre compte, lorsqu’on appelle une fonction qui renvoie un pointeur e sans avoir pr´alablement d´clar´ la fonction. Par exemple, supposons que sans avoir d´clar´ la fonction malloc e e e e e (ni inclus le fichier <stdlib.h>) on ´crive l’affectation e p = (struct machin *) malloc(sizeof(struct machin)); Le compilateur rencontrant malloc pour la premi`re fois, il postule que cette fonction renvoie un int et ins`re e e ` cet endroit le code qui convertit un entier en un pointeur. Cette conversion peut fournir un r´sultat tout ` fait a e a erron´. Notamment, sur un syst`me o` les entiers occupent deux octets et les pointeurs quatre, les deux octets e e u hauts du r´sultat de malloc seront purement et simplement remplac´s par z´ro. 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 6.1.2 Les pointeurs g´n´riques et le pointeur NULL e e (ou tout simplement p = t; )

Certaines fonctions de la biblioth`que, comme malloc, rendent comme r´sultat une ≪ adresse quelconque ≫, e e ` charge pour l’utilisateur de la convertir en l’adresse sp´cifique qui lui convient. La question est : comment a 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, la tradition s’´tait cr´´e de prendre le type char * pour ≪ pointeur e a e ee vers n’importe quoi ≫. En effet, sur beaucoup de syst`mes les adresses des objets de taille non unitaire supportent e des contraintes d’alignement (les short aux adresse paires, les long aux adresses multiples de quatre, etc.), alors que les adresses des caract`res sont les seules ` ne subir aucune restriction. 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´ciproquement 42 . Par exemple, le fichier standard <stdlib.h> contient la d´claration suivante e e de la fonction malloc : void *malloc(size_t size); Appel : p = malloc(sizeof(struct machin)); Note. A la section 2.2.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´, en C ANSI. e e
42. Avec une protection suppl´mentaire : le type void ´tant incompatible avec tous les autres types, 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. e e e e e e e

c ⃝ H. Garreta, 1988-2011

65

6.2

Les pointeurs et les tableaux

6

POINTEURS

Le pointeur NULL. Une des principales applications des pointeurs est la mise en œuvre de structures r´cursives de taille variable : listes chaˆ ees de longueur inconnue, arbres de profondeur quelconque, etc. Pour e ın´ r´aliser ces structures il est n´cessaire de poss´der une valeur de pointeur, distincte de toute adresse valide, e e e repr´sentant la fin de liste, la structure vide, etc. e C garantit qu’aucun objet valide ne se trouve ` l’adresse 0 et l’entier 0 est compatible avec tout type pointeur. a Cette valeur peut donc ˆtre utilis´e comme la valeur conventionnelle du ≪ pointeur qui ne pointe vers rien ≫ ; e e c’est l’´quivalent en C de la constante nil de Pascal. En pratique on utilise plutˆt la constante NULL qui est e o d´finie dans le fichier <stddef.h> : e #define NULL ((void *) 0)

6.2
6.2.1

Les pointeurs et les tableaux
Arithm´tique des adresses, indirection et indexation e

´ Arithmetique. 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 ; a – la soustraction de deux adresses (de types rigoureusement identiques). 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. e De mani`re analogue, 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 ), il semble naturel de donner ` exp 2 −exp 1 la valeur 1. e a L’arithm´tique des adresses d´coule de ces remarques. D’un point de vue technique, 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´. 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, 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. 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, ´ventuellement fictif. 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 ` ces deux ´l´ments. Les pointeurs avec leur arithm´tique et l’acc`s aux ´l´ments des tableaux sont donc des a ee e e ee concepts tr`s voisins. En r´alit´ il y a bien plus qu’un simple lien de voisinage entre ces deux concepts, 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. Par d´finition, 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]; ou type *t; 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, un programme est plus facile ` lire si on montre une certaine constance dans le choix des a notations. Exemple. Le programme suivant parcourt un tableau en utilisant un pointeur : 66
c ⃝ H. Garreta, 1988-2011

6

POINTEURS

6.2 Les pointeurs et les tableaux

char s[10] = "Hello"; main() { char *p = s; while (*p != ’\0’) fputc(*p++, stdout); } Remarque 1. La similitude entre un tableau et un pointeur est grande, notamment dans la partie ex´cutable e des fonctions. Mais il subsiste des diff´rences, surtout dans les d´clarations. Il ne faut pas oublier que les e e d´clarations e int *p, t[100]; introduisent p comme une variable pointeur et t comme une constante pointeur. Ainsi, les expressions p = t et p++ sont l´gitimes, mais t = p et t++ sont ill´gales, car elles tentent de modifier une ≪ non variable ≫ (un nom e e de tableau n’est pas une lvalue). Remarque 2. Dans ce mˆme esprit, rappelons la diff´rence qu’il peut y avoir entre les deux d´clarations e e e suivantes : char s[] = "Bonsoir"; et char *t = "Bonsoir"; s est (l’adresse d’) un tableau de caract`res (figure 8) e

s B o n s o i r\ 0
Figure 8 – Chaˆ de caract`res ıne e

variable ≫

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

Figure 9 – Constante chaˆ de caract`res ıne e

On pourra r´f´rencer ces caract`res indiff´remment par s[i], *(s + i), t[i] ou *(t + i). Cependant, ee e e dans le premier cas le compilateur a allou´ un vrai tableau de 8 caract`res, et y a copi´ la chaˆ donn´e ; dans e e e ıne e le second cas, 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. Par suite, s n’est pas une variable e mais *s, s[i] et *(s + i) en sont, tandis que t est une variable mais *t, t[i] et *(t + i) n’en sont pas 43 . A ce propos voir aussi la section 5.1.3. L’arithm´tique des adresses permet d’am´liorer l’efficacit´ de certains traitements de tableaux 44 , en mettant e e e ` profit le fait que l’indirection est plus rapide que l’indexation. Autrement dit, l’acc`s ` une donn´e ` travers a 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, ce qui est la mˆme chose, q[i]) e D´veloppons un exemple classique : la fonction strcpy(dest, srce) copie la chaˆ de caract`res srce e ıne e dans l’espace point´ par dest. 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. a 44. Bien sˆ r, il s’agit de petites ´conomies dont le b´n´fice ne se fait sentir que lorsqu’elles s’appliquent ` des parties des u e e e a programmes qui sont vraiment critiques, c’est-`-dire ex´cut´es de mani`re tr`s fr´quente. a e e e e e

c ⃝ H. Garreta, 1988-2011

67

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

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

int nc) impl´mente un algorithme qui requiert une matrice locale ` nl lignes et nc e a colonnes.2. e De plus. Version statique : #define NL 20 #define NC 30 . Nous avons donc bien ´limin´ une ≪ vraie ≫ mule e e tiplication : notre deuxi`me mani`re est plus efficace. *m2[NL]. compte tenu des priorit´s : float *(m2[NL]). 70 c ⃝ H. En effet. Supposons que la fonction e e e void unCalcul(int nl. Nous e e aurons la configuration de la figure 11.6.. Or nous sommes maintenant parfaitement ´quip´s pour r´aliser une telle matrice. Il reste que nous avons remplac´ ≪ m1 + i × NC + j ≫ par ≪ *(m2 + i) + j ≫. j. 1988-2011 . i++) m2[i] = m1[i]. matrice ≫ ainsi d´clar´e se note encore e e ≪ 6. int nc) { double mat[NL][NC]. La variable m2 est donc d´clar´e comme e e e e un tableau de NL pointeurs vers des nombres flottants.2 Les pointeurs et les tableaux 6 POINTEURS float m1[NL][NC]. cela se fait ici par une expression d’une ´tonnante simplicit´ : e e for (i = 0. NC doit ˆtre connu ` la compilation pour que e e a l’expression i × NC + j ait un sens. m2 i m2[i] j m2[i][j] Figure 11 – Tableau bidimensionnel r´alis´ avec un tableau de pointeurs e e Il est remarquable qu’un ´l´ment de la nouvelle ee m2[i][j] mais maintenant. Garreta. i < NL. 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). Pour r´aliser la matrice dont nous avons besoin. /* en esp´rant que nl ≤ NL et nc ≤ NC */ e int i. void unCalcul(int nl.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. La matrice m1 existant par ailleurs.. La deuxi`me se lit. En contrepartie nous occupons un peu plus d’espace (les e e NL pointeurs suppl´mentaires). il nous e suffit d’initialiser correctement ces pointeurs : chacun sera l’adresse d’une ligne de la matrice pr´c´dente. il nous faut initialiser le tableau de pointeurs m2. nous pouvons les ignorer.

j < i. j++) free(mat[j]). j < nl. j < nc. if (mat[i] == NULL) { for (j = 0. Elles ont l’avantage d’ˆtre ind´pendantes du type effectif de mat. i < nl. /* initialisation des pointeurs */ mat = malloc(nl * sizeof(double *)). j < nc. i++) for (j = 0. j++) mat[i][j] = 0.6 POINTEURS 6. } Version dynamique : void unCalcul(int nl. free(mat). e e e e e m2 Figure 12 – Matrice dynamique (lignes allou´es s´par´ment) e e e ¨ Variante : lignes contigues. i < nl. j++) free(mat[j]). Garreta. j. La matrice est r´alis´e par des morceaux de m´moire ´parpill´e. e e ≪ sizeof(double *) ≫ et ≪ c ⃝ H. Notez que les expressions ≪ sizeof *mat ≫ et ≪ sizeof *mat[0] ≫ ´quivalent respectivement ` e a sizeof(double) ≫. La structure de la matrice ressemble alors ` celle de la figure 11 : e a 47. 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. } } /* utilisation de la matrice mat. i < nl. j++) mat[i][j] = 0. free(mat). /* lib´ration (indispensable dans le cas d’une variablelocale) */ e for (j = 0. 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 47 . etc. 1988-2011 71 . for (i = 0. i++) for (j = 0. Par exemple : */ for (i = 0. if (mat == NULL) return 0. Par exemple : */ for (i = 0. int i.2 Les pointeurs et les tableaux /* utilisation de la matrice mat. } Dans cette mani`re de proc´der. int nc) { double **mat. etc. i++) { mat[i] = malloc(nc * sizeof(double)).

2 Les pointeurs et les tableaux 6 POINTEURS int unCalcul(int nl. j. } /* succ`s */ e longueur maximum d'une chaîne B o n s o i r \0 \0 C a v a ? \0 Ce n' e st B y e \0 pa s un texte g é n i a l !\ 0 Figure 13 – Matrice de caract`res e 6. /* utilisation de la matrice mat. cette technique entraˆ une ´conomie de place sube ıne e stantielle. ıne e e par un espace lin´aire dans lequel chaque chaˆ n’occupe que la place qui lui est n´cessaire.2. B o n s o i r \ \0 C a 0 v a ?\ B y e\ C e 0 0 n ' e s . free(mat). Notre programme e e a 72 c ⃝ H. i < nl. Ici. j < nc... if (mat[0] == NULL) { free(mat). if (mat == NULL) return 0. i < nl. /* lib´ration (indispensable dans le cas d’une variablelocale) */ e free(mat[0]). comme le montre e ıne e la figure 14. int i. 1988-2011 . i++) for (j = 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.1] + nc. return 1. Par exemple : */ for (i = 0. j++) mat[i][j] = 0. etc. int nc) { double **mat. /* initialisation des pointeurs */ mat = malloc(nl * sizeof *mat). encombrement réel des caractères Figure 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. /* ´chec */ e } for (i = 1. 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++) mat[i] = mat[i .6. /* ´chec */ e mat[0] = malloc(nl * nc * sizeof *mat[0]). return 0.

puts (´criture de chaˆ e ıne). i++) printf("%s\n". nbr_mots). finalement il affiche les lignes ordonn´es. "Volume ou repertoire inexistant". t[i . nomm´ espace. comme dans : e char *messages[] = { "Fichier inexistant". "Erreur physique d’entree-sortie". dont le e a e pointeur libre indique la premi`re place disponible. } if (ok) return.h> #include <string. 1988-2011 73 . ok = 1. t[i]) > 0) { char *w = t[i .h> #define MAXMOTS 100 #define MAXCARS 3000 char char char int espace[MAXCARS]. 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. ok = 0. 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. e e e e Les fonctions gets (lecture de chaˆ ıne).) { int i. while(gets(libre) != NULL && *libre != ’\0’) { mots[nbr_mots++] = libre. a e e #include <stdio.1]. tandis que messages est fait de e e pointeurs vers des cellules de la ≪ zone de constantes ≫. c ⃝ H.6 POINTEURS 6. est une table de renvois e e dans espace. libre += strlen(libre) + 1.2 Les pointeurs et les tableaux lit des lignes de caract`res au terminal et les range dans une table. jusqu’` la rencontre de la fin de fichier ou e a la lecture d’une ligne vide. int n) { for (. Garreta. "Nom de fichier incorrect". i < n. 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. mots[i]). } Remarque. Ensuite. *mots[MAXMOTS]. void tri(char *t[].1] = t[i]. il ordonne les lignes lues . t[i] = w. e Les lignes lues sont rang´es les unes ` la suite des autres dans un tableau unique. "Autre erreur" }. nbr_mots = 0. not´e mots.. Notez que le tri se fait en ordonnant la table des e pointeurs . i < nbr_mots.1]. espace immodifiable allou´ et garni une fois pour toutes e par le compilateur. for (i = 1. for (i = 0. La figure 14 illustre ces structures de donn´es. n--. } } void main(void) { int i. *libre = espace. La table des mots. } tri(mots. les caract`res eux-mˆmes ne sont nullement d´plac´s. i++) if (strcmp(t[i .

. sont des constantes) alors un acc`s comme e table[i1 ][i2 ] . i < NL. 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. cela donne : il est inutile d’indiquer combien il y a de lignes. Ta ..2. le compilateur connaisse la taille d´clar´e pour chaque ligne. 1992). j < NC. 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. T2 . 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 ] . void *m) { int i. . un e a e e e tableau T d´clar´ de taille N est fait des ´l´ments T0 . une autre solution aurait consist´ ` faire soi-mˆme les calculs d’indice : ea e void k_fois(double k. e e e e e e Pour un tableau ` deux indices. i < NL. (N1 . Flannery. } Remarque. T1 . Garreta. D’obscures contraintes techniques font que sur certains mat´riels ou syst`mes l’arithm´tique des adresses subit des e e e e limitations drastiques et. Tb . alors l’indication du nombre de ses ´l´ments est sans utilit´ puisqu’il n’y a pas d’allocation d’espace. for (i = 0. TN ou. [Nk−1 ][Nk ]. Teukolsky. } Tout ce qu’on y gagne est un programme bien plus difficile ` lire ! a 6. i++) for (j = 0. W. a et b ´tant deux entiers quelconques v´rifiant a ≤ b. for (i = 0. cette convention est commode et fiable. Ta+1 . Strictement parlant. . j++) *((double *) m + i * NC + j) *= k. notamment. les tableaux de C sont index´s ` partir de z´ro : sans qu’il soit n´cessaire de le sp´cifier. dans la grande majorit´ e e ee e des applications. Il existe cependant des situations dans lesquelles on souhaiterait pouvoir noter les ´l´ments d’un tableau ee T1 . j. la norme du langage C ne garantit pas que la technique expos´e ici fonctionne correctement sur tous e les syst`mes. i++) for (j = 0. La fonction dTable 49 cr´e un tableau d’´l´ments de type double dont e ee les indices sont les entiers iMin. Press. . . j++) m[i][j] *= k. etc. 49. 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. Nous e e e e allons voir que l’allocation dynamique fournit un moyen simple et pratique 48 pour utiliser de tels tableaux. S. Bien entendu. Nous expliquons ici une technique massivement employ´e dans la biblioth`que de calcul scientifique Numerical Recipes (⃝ e e Numerical Recipes Software) bas´e sur le livre Numerical Recipes in C (W. Vetterling et B. TN −1 . ` Cas des tableaux a un indice. N2 . e Cambridge University Press. plus g´n´ralement. . mais il faut a absolument que.. .6. . Nous avons vu que. [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. iMax : c 48. rendent le r´sultat de la soustraction d’un entier ` un pointeur peut ˆtre incorrect ou e a e inutilisable.. j < NC. il faut que ce calcul puisse ˆtre effectu´. en compilant la fonction. double m[][NC]) { int i. 1988-2011 .7 Tableaux non n´cessairement index´s ` partir de z´ro e e a e En principe..2.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. j. Par e e exemple. iMin+1. 74 c ⃝ H. .2 Les pointeurs et les tableaux 6 POINTEURS 6.

e La fonction dMatrice cr´e une matrice dont les lignes sont index´es par les entiers lMin. en effet. nbLin = lMax . e etc. return res .iMin peut ˆtre nul alors que l’allocation a r´ussi et res ̸= e e e e NULL. . Attention. 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. 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. 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 . int iMax) { double *res = malloc((iMax . nbCol. . en particulier. Il aurait fallu d´finir une structure plus complexe qu’un simple e e a e e tableau. **lignes.iMin + 1) * sizeof(double)).. .4. cependant sa ee e lib´ration est un peu plus complexe que pour les tableaux dynamiques ordinaires. La figure 15 illustre le r´sultat rendu par la fonction pr´c´dente lors d’un appel comme dTable(5. incluant soit la valeur de iMin. *p. .. 10). . etc. lMax e e et les colonnes sont index´es par les entiers cMin. 10). } c ⃝ H. return NULL. . g´n´rera certainement une erreur ` l’ex´cution. sans aucune perte d’efficacit´.. double *t = dTable(5.6 POINTEURS 6. En particulier. t[i]). de int. e e e ` Cas des tableaux a plusieurs indices. } (Si on avait besoin de tableaux de float. espace = malloc(nbLin * nbCol * sizeof(double)). on ´crirait des fonctions analogues fTable. cMax : e double **dMatrice(int lMin. 1988-2011 75 . if (res == NULL) { erreurTable = 1. avec celles faites ` la section 6. } erreurTable = 0.2 Les pointeurs et les tableaux double *dTable(int iMin. un appel comme free(t). res . i <= 10. Un tableau cr´´ par un appel de la fonction dTable est dynamiquement allou´. return NULL.1f\n". int lMax. int cMax) { int nbLin. for (i = 5. Garreta. i. cMin+1 . nbCol = cMax .lMin + 1. for (i = 5. e e e adresse rendue comme résultat par dTable cellules effectivement allouées Figure 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. ` part e ee a e a cela. ` la suite du e a programme ci-dessus. iTable. int cMin.cMin + 1. . i++) printf("%.... i++) t[i] = 1000 + i..2.. Exemple d’utilisation : e a e . Les remarques pr´c´dentes. i <= 10. lMin+1 .iMin.). if (espace == NULL) { erreurTable = 1. double *espace. de mani`re tout ` fait habituelle .

m[i][j]). 1988-2011 /*1*/ /*1*/ . i <= 10. Les arguments nla et nca repr´sentent le nombre effectif de lignes et de colonnes de e e A. j++) printf("%10. . double s. for (i = 5. double **m = dMatrice(5. k < nca. p += nbCol.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. 6.. etc. Les arguments dNca.. int ncb. double *C. j <= 3. j <= 3. 3). } erreurTable = 0. . double *B. B et C ont ´t´ respectivement d´clar´s .6.. i++) for (j = 0.lMin. a Par exemple. int nca.-`-d. return lignes . tandis que ncb est le nombre effectif de colonnes de B. 10. NCA.. return NULL. } . Garreta. tandis que nla. for (i = 5. i++) { lignes[i] = p. printf("\n"). int dNcb. i <= 10. for (k = 0. j. i < nbLin. nca. free(espace).2 Les pointeurs et les tableaux 6 POINTEURS lignes = malloc(nbLin * sizeof(double *)). Voici un e e e exemple d’utilisation : .. ee e e void produitMatrices(double *A. for (i = 0. j++) { s = 0. int dNca. i++) for (j = -3.. int nla. non dynamiques) alors que leurs dimensions ne sont pas connues lors de la compilation.. } Comme pr´c´demment. i++) { for (j = -3.cMin. j < ncb. i < nla. k. dNcb et dNcc repr´sentent les e nombres de colonnes avec lesquels les matrices A. -3. } } Exemple d’appel (NLA. C[i * dNcc + j] = s. } p = espace . for (i = 0.1f ". etc. sont des constantes introduites par des #define. k++) s += A[i * dNca + k] * B[k * dNcb + j]. int dNcc) { int i. if (lignes == NULL) { erreurTable = 1. 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.. j++) m[i][j] = 1000 * i + j.. l’emploi de ces matrices ne s’accompagne d’aucune perte d’efficacit´.2. sont des variables) : 76 c ⃝ H.

produitMatrices(&A[0][0]. ` part le type des arguments a et du r´sultat. ce nombre d´coule sans ambigu¨ e de la d´claration. un int et un double. a c ⃝ H. par la suite. Tout cela sugg`re qu’en C on aura e le droit de travailler avec des variables qui repr´sentent des fonctions. ce qui demande de connaˆ combien d’octets sont e e ıtre n´cessaires. . } . et rendant une valeur double. /*2*/ /*2*/ /*2*/ /*2*/ /*2*/ /*2*/ /*2*/ 6.. il n’est pas d´lirant de rapprocher les fonctions et les donn´es. C’est un inconv´nient r´dhibitoire.. Garreta. du point de vue de l’efficacit´). Pour cela.. par exemple. Il n’y a plus de probl`me de taille (la taille d’une adresse e – ou pointeur – est constante).. des tableaux de fonctions. e En revanche. 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. nla. k < nca. La principale diff´rence est dans la possibilit´ ou l’impossibilit´ de pr´dire le nombre d’octets occup´s. double x). etc.. Il va falloir y renoncer. NCA. ne laisse rien deviner sur les caract´ristiques des fonctions dont uneFonction contiendra les e e adresses. &B[0][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. Une fois compil´e (traduite e e e e en langage machine). uneFonction re¸oit l’adresse d’une fonction ϕ pour valeur.. e e e e e Pour un tableau. nca. par exemple. des fonctions e renvoyant comme r´sultat d’autres fonctions. etc. 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. 50. ib += dNcb.4). for (k = 0. si c’est une variable globale. ncb. e e il suffit de remplacer les deux lignes marqu´es /*1*/ par ceci (ia et ib sont des variables locales nouvelles) : e ...6 POINTEURS 6. 1988-2011 77 . malgr´ les apparences. 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 Les adresses des fonctions double A[NLA][NCA].3.. c’est-`-dire de ce qu’elle fait et comment elle le fait. k++) { s += A[ia] * B[ib]. Si. 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. section 5. peu utile ici) et. une fonction est une suite d’octets qui occupe une portion de m´moire exactement comme. . e e e ≪ adresse d’un [objet de type] T ≫ et ≪ tableau de [objets de type] T ≫. Cette d´claration n’initialise pas e uneFonction (sauf. 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. obtention des valeurs des variables nla. On notera que la fonction produitMatrices peut ˆtre optimis´e en supprimant des multiplications qui sont e e faites dans la boucle la plus profonde (la seule qui doit nous int´resser. v). B[NLB][NCB]. et des matrices A et B . un e tableau de nombres.1 Les adresses des fonctions Les fonctions et leurs adresses En ´tudiant les d´clarateurs complexes (section 5. ia = i * dNca. 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.. cette p´riphrase e e constituant elle-mˆme un d´clarateur que le programmeur peut composer librement avec les autres d´clarateurs.3 6. C[NLC][NCC]. nca. NCB. &C[0][0]. ib = j. ia += 1. Notez que.. pour une raison facile ` comprendre : la taille d’une fonction ne peut pas a se d´duire de son type 50 .4) nous avons appris que la syntaxe de C permettait de e e manipuler le type ≪ fonction rendant un [objet de type] T ≫ (T repr´sente un autre type). Exemple : e double (*uneFonction)(int n. NCC). avec la valeur 0.

0. b. a = b. e e Les arguments de la fonction dicho sont : les bornes a et b de l’intervalle de recherche. . Examinons deux applications de cela parmi les plus importantes. changeant de signe sur l’intervalle [0.3 Les adresses des fonctions 6 POINTEURS Comment le programmeur obtient-il des adresses de fonctions ? Il faut savoir que. eps. (*f)(). ´ Exemple 1 : resolution de f (x) = 0 par dichotomie. . (la suite est la mˆme) e } Exemple 2 : utilisation de qsort. double val) { etc. Reprenons notre programme de la section 6. l’affectation suivante est tout ` fait l´gitime : e a e uneFonction = maFonction. e e si on a la d´finition de fonction e double maFonction(int nbr. double b. par cons´quent *f est une fonction et (*f)(x) e e e e e e un appel de cette fonction. u – n’est pas une lvalue. 1988-2011 . un appel correct de dicho serait x = dicho(0. /* hypoth`se : f(a) * f(b) <= 0 */ e if ((*f)(a) > 0) { x = a. .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. de la mˆme mani`re que e e le nom d’un tableau repr´sente son adresse de base. b ] v´rifiant |xε − x0 | ≤ ε et f (x0 ) = 0.3.0. la fonction dicho recherche xε ∈ [ a . la fonction dicho s’´crirait e double dicho(a.2. – a pour valeur l’adresse o` commence la fonction maFonction . b. Remarque. 1. double (*f)(double)) { double x. Si une fonction est le nom d’une fonction pr´c´demment d´finie. est r´solu en C e a e par l’utilisation d’arguments de type ≪ pointeur vers une fonction ≫. 6. Voici cette fonction : e double dicho(double a. Autre exemple du mˆme tonneau e y = dicho(0. Par cons´quent. Ainsi.0. b ] e telle qu’il existe x0 ∈ [ a . . qui est une e e solution de l’´quation f (x) = 0 si on tol`re une erreur d’au plus ε. la pr´cision ε voulue e et surtout la fonction f dont on cherche un z´ro. } L’argument f est d´clar´ comme un pointeur vers une fonction . ou ≪ sans proptotypes ≫. . 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. double eps. 1E-10. En syntaxe originale. cos). . b ] telle que f (a) et f (b) ne sont pas de mˆme signe. eps. 1]. 3. { etc. Si f est une fonction continue.a) > eps) { x = (a + b) / 2. b = x. f) double a. 1E-8. En clair : dicho d´termine xε . } 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 ≫ .6. e c ıf e 78 c ⃝ H. else b = x. } return x. le nom d’une fonction repr´sente l’adresse de celle-ci.2 Fonctions formelles Le probl`me des fonctions formelles. c’est-`-dire des fonctions arguments d’autres fonctions. if ((*f)(x) < 0) a = x. Garreta. } while (fabs(b . d´finie sur un e intervalle [ a .0. une_fonction).

mots[i]). *libre = espace. La frappe e e e e e d’une ligne r´duite au texte fin arrˆte le programme. et toute cette question est o bien plus simple. Pour la comprendre. – d’apr`s la documentation de qsort. La d´finition de comparer peut paraˆ bien compliqu´e. Si elle e a a e nous int´resse ici c’est parce qu’elle prend ce crit`re. En syntaxe ≪ sans prototype ≫. *(char **)b). } Remarque. elle trie un tableau par rapport ` un crit`re quelconque. sizeof(char *). 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 ≫ .4.h> #define MAXMOTS 100 #define MAXCARS 3000 char espace[MAXCARS]. a c ⃝ H. 1988-2011 79 . /* La d´claration suivante se trouve aussi dans <stdlib. nbr_mots. – l’adresse de la fonction correspondante. int nbr_mots = 0. Garreta. exp(double). for (i = 0. Notre programme lit des lignes constitu´es d’un nom de fonction.3 Les adresses des fonctions Cette fonction est expliqu´e ` la section 8. log(double). } main() { int i. sous forme de chaˆ de caract`res . comparer). const void *)).h> #include <string. En r´alit´.h> double sin(double). c’est-`-dire des */ ı a /* adresses d’adresses de char */ return strcmp(*(char **)a. une chaˆ ´tant de type char *. c 6. i++) printf("%s\n". int comparer(const void *a. suivi d’un ou plusieurs blancs.2. libre += strlen(libre) + 1. size_t nmemb. il faut consid´rer e ıtre e e les diverses contraintes auxquelles elle doit satisfaire : – comparer figurant comme argument dans l’appel de qsort. suivis d’un e nombre r´el .3. repr´sent´ par une fonction. char *mots[MAXMOTS]. ´tant de type void *. dans les appels de cette fonction que */ e e e /* nous faisons ici. nous savons que comparer sera appel´e avec deux adresses de chaˆ e e ınes pour arguments effectifs . e – a et b. son prototype doit correspondre exactement 51 a ` celui indiqu´ dans le prototype de qsort . a et b sont des adresses de "cha^nes". while(gets(libre) != NULL && *libre != ’\0’) { mots[nbr_mots++] = libre. 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. cos(double). e e #include <stdio. il ´value et affiche alors la valeur de la fonction mentionn´e appliqu´e au nombre donn´. aucun contrˆle n’est fait sur le type des arguments de comparer. const void *b) { /* Les arguments de cette fonction doivent ^tre d´clar´s "void *" (voyez la */ e e e /* d´claration de qsort). les arguments formels de comparer. size_t size. } qsort(mots. ce ıne e ıne qui explique la fa¸on dont a et b apparaissent dans l’appel de strcmp. 51.h> : */ e void qsort(void *base. int (*comp)(const void *.6 POINTEURS 6.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 ıne e – le nom d’une fonction standard. le type ≪ adresse de chaˆ ≫ est char **. i < nbr_mots. pour argument : e e e e #include <stdio. Mais attention : quelle que soit la syntaxe employ´e.

≫ sont confus. apparaissent dans cette d´claration sans ˆtre accompagn´s d’une paire de parenth`ses . exp(double). les noms des fonctions sin. } } Voici une ex´cution de ce programme : e sin 1 0. if (i < NBF) printf("%f\n".) { scanf("%s". "exp". } table[] = { "sin".h> 6.540302 atn 1 atn ??? fin Remarque.. "cos". D’autre part. log(double). double x. 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. Voici l’initialisation de f par l’adresse de la fonction sin et deux appels ´quivalents de cette fonction (x.3 Les adresses des fonctions 6 POINTEURS struct { char *nom. soit explicitement : a e double sin(double).6. En effet. 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. ee e e e exp. Garreta. ≫ et des objets de type ≪ adresse d’une fonction.. y e et z sont des variables double) : 80 c ⃝ H. else printf("%s ???\n". La d´claration de cette fonction. scanf("%lf".3. i++) . il n’y a aucun e e e e signe indiquant au compilateur qu’il s’agit de fonctions..fon)(x)). soit en incluant un fichier dans lequel sont ´crites ces d´clarations : e e #include <math. nom) != 0. i < NBF && strcmp(table[i]. "log". etc. log }. #define NBF (sizeof table / sizeof table[0]) main() { char nom[80]. cos(double).. int i. extraite du fichier <math. cos. "fin") == 0) break. sinus.841471 cos 1 0. exp.nom. la d´finition d’une variable de type ≪ adresse d’une fonction double ` un argument double ≫ est : e a double (*f)(double). nom). 1988-2011 .h>. double (*fon)(double). &x). est : e double sin(double). cos. for(. (*table[i]. if (strcmp(nom.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. sin.. Examinons une fonction bien ordinaire. for(i = 0.

car une fonction n’a pas de taille d´finie. Typiquement. ≫) . ≫. les trois lignes ci-dessus ne sont c e pas coh´rentes. 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.. Garreta. Un arbre binaire est repr´sent´ par une adresse. 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.4 6. ≫ que pour une expression e de type ≪ adresse d’une fonction. par exemple. Algorithmes en langage C. comme les arbres binaires. e e e e 6. z = sin(x).h> #define MAXCARS 3000 char espace[MAXCARS]. *libre = espace. La premi`re montre que f et sin sont de mˆme type (` savoir ≪ adresse d’une fonction. l’exp´rience e montre que cette tol´rance n’a pas de cons´quences n´fastes. fils g .. Ainsi.2 Exemple Le programme suivant effectue le mˆme travail que celui du 6. R. par exemple.h> #include <stdio. Apr`s avoir constat´ l’impossibilit´ de d´clarer des e e e e e variables de type ≪ fonction. 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. 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 !). Parmi les plus fr´quemment rencontr´es : e e e e e e e les listes et les arbres.5. e e e a or f doit ˆtre d´r´f´renc´ pour appeler la fonction. Notez que le nom de la structure (noeud) est ici indispensable.2.. c ⃝ H.. Ainsi. Pourtant. struct noeud *fils_gauche...1 Structures r´cursives e D´claration e Les structures r´cursives font r´f´rence ` elles-mˆmes. Quelle que soit la valeur de x. Sedgewick. mais ` la place d’une table on utilise un e a arbre binaire de recherche (voir.4. 1988-2011 81 . la d´claration d’une telle structure sera donc calqu´e sur la suivante : e e struct noeud { type valeur. 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). On e e contourne cette difficult´ en utilisant les pointeurs. La structure de donn´es mise en œuvre est repr´sent´e sur la figure 16.. e e Bien entendu. tandis que sin n’a pas besoin de l’ˆtre. v = (*sin)(x). y = (*f)(x).4 Structures r´cursives e f = sin. y et z re¸oivent la mˆme valeur. – soit un triplet ( valeur .6 POINTEURS 6. 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)). e e e #include <stdlib.4. et donc l’obligation de passer par des e variables ≪ adresse de fonction.. sont accept´es elles aussi (elles ont la mˆme valeur que les pr´c´dentes). on peut donner la d´finition r´cursive suivante : un arbre binaire est e e – soit un symbole conventionnel signifiant ≪ la structure vide ≫ . ≫. *fils_droit. ≫ . 6. – soit l’adresse d’une structure constitu´e de trois champs : une information et deux descendants qui sont e des adresses d’arbres binaires.. }. chapitre 14) pour ranger les mots. e eee e e On peut comprendre la raison de cette incoh´rence.. les deux expressions (*f)(x) et sin(x) sont correctes . qui est : e e e – soit l’adresse nulle .

Figure 16 – Arbre binaire de mots void parcours(POINTEUR r) { if (r != NULL) { parcours(r->fils_gauche). POINTEUR d) { POINTEUR p = malloc(sizeof(NOEUD)). 1988-2011 . POINTEUR g. *fils_droit. parcours(r->fils_droit). else r->fils_droit = insertion(r->fils_droit.. mot). p->fils_gauche = g... printf("%s\n".. NULL). char *mot) { if (r == NULL) r = alloc(mot. return p. r->mot) <= 0) r->fils_gauche = insertion(r->fils_gauche. } NOEUD..6. .. r->mot). Garreta.. } libre j e a n \0 a n d r é \0 p a u l \ 0 racine mot fils gauche fils droit . . NULL. p->mot = m. } POINTEUR insertion(POINTEUR r. POINTEUR alloc(char *m.. } } 82 c ⃝ H. .. mot). .. p->fils_droit = d. return r.4 Structures r´cursives e 6 POINTEURS typedef struct noeud { char *mot. struct noeud *fils_gauche. *POINTEUR. else if (strcmp(mot.

} parcours(racine). char *p. Garreta. avec la particularit´ que chaque individu fait r´f´rence ` sa famille. p = gets(libre). chaque famille est une ≪ liste e e d’individus ≫. racine = insertion(racine. p). while(*p != ’\0’) { libre += strlen(p) + 1. . struct famille *famille_suivante.6 POINTEURS 6. struct individu *membres. Une autre solution aurait ´t´ : ee c ⃝ H. Cela nous permet de d´clarer nos e e structures mutuellement r´cursives sous la forme : e typedef struct famille { char *nom.... si la structure ayant le nom indiqu´ n’a pas encore ´t´ d´clar´e.. 83 . chacune intervenant dans la d´finition de l’autre.. Comment les d´clarer ? e e .3 Structures mutuellement r´cursives e Donnons-nous le probl`me suivant : repr´senter une ≪ liste de familles ≫ .. . Elle identifie un type incomplet et on peut l’utiliser ` tout endroit o` sa taille n’est pas e a u requise. typedef struct individu { char *prenom. Les structures famille et e ee a individu se pointent mutuellement. Liste de familles Liste d'individus . struct individu *membre_suivant. 1988-2011 .4 Structures r´cursives e main() { POINTEUR racine = NULL. Plus pr´cis´ment. notamment dans la d´claration d’un pointeur vers une telle structure. struct famille *famille. } INDIVIDU. p = gets(libre).4. Figure 17 – Structures mutuellement r´cursives 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... l’expression ≪ struct e e e e ee e e nom ≫ est l´gitime.. } FAMILLE. } 6..

1988-2011 . 84 c ⃝ H.4 Structures r´cursives e 6 POINTEURS typedef struct famille FAMILLE. }. INDIVIDU *membres. typedef struct individu INDIVIDU. }. FAMILLE *famille.6. Garreta. struct individu { char *prenom. INDIVIDU *membre_suivant. FAMILLE *famille_suivante. struct famille { char *nom.

). o e c ⃝ H. quelles que soient les performances des disques durs actuels. en e e e e particulier. 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 ≫. Les flots de texte ont une structure l´g`rement plus riche. De tels fichiers sont appel´s fichiers de texte. modifier la taille de ce dernier ou mˆme le supprimer compl`tement. Cela revient ` ignorer le fait que les octets dont il se compose repr´sentent des donn´es d’un ordre sup´rieur. e e Ce mode de gestion des fichiers r´duit le nombre d’op´rations physiques d’entr´e . voire impossible sur e certains syst`mes. les fichiers binaires ne sont pas ´ditables ou imprimables . de plus. e 52. ils peuvent ˆtre affich´s. 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.7 ´ ENTREES-SORTIES 7 Entr´es-sorties e Strictement parlant. lecture et ´criture de donn´es format´es) n’ont de sens que e e e e sur un flot de texte. un fichier ´crit comme un flot de texte peut ˆtre lu comme un flot e e e e binaire. c’este `-dire des suites d’octets qui repr´sentent des donn´es selon une des deux modalit´s suivantes : a 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.sortie (beaucoup plus e e e lentes. le tampon d´bordera. ´dit´s. e 54. e e e ee ee semant quelque confusion car la r´alisation de certaines fonctions d’UNIX est inutile. e e 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 la m´moire de l’ordinateur. Garreta. Un fichier est dit tamponn´ 55 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. 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. car elles ne requi`rent pas un travail d’analyse ou de synth`se de l’expression e e e ´crite des donn´es. 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’.LF ) utilis´ par certains syst`mes est traduit en lecture par l’unique caract`re ’\n’. n’a pas de signification et peut provoquer des erreurs fatales (les fins de ligne seront absentes ou incomprises. ou si le caract`re qui indique la fin d’une ligne est diff´rent. 1988-2011 85 . e e puisqu’ils sont cens´s ˆtre organis´s comme des suites de lignes. lire comme un flot de texte un fichier binaire. le couple (CR. l’´criture ≪ logique ≫ du caract`re ’\n’ se traduit par l’´criture effective du couple (CR. mˆme si les ´changes e e e logiques sont faits par paquets de taille variable ou mˆme octet par octet. Cons´quence : sauf si le syst`me sous-jacent l’interdit. e e e e e presque toujours le code ASCII. Ainsi ee e e la distinction entre ces deux types de fichiers ne se fait pas lors de la d´claration ou de l’ouverture 52 du fichier. e e Tampons. En bon franglais. une partie des informations logiquement ´crites dans le fichier seront perdues. Leur principale qualit´ est e e d’ˆtre exploitables par un logiciel. et correspondent donc plutˆt aux flots binaires 53 . Rien n’empˆche que telle ou telle r´alisation particuli`re du langage propose des a e e e fonctions additionnelles .1). les op´rations d’entr´e-sortie ne font pas partie du langage C . Mˆme si sur un syst`me particulier les fichiers e e e e de texte ne sont pas ainsi organis´s. Inversement.1 Flots Quelles que soient les particularit´s du syst`me d’exploitation sous-jacent. C est n´ et a v´cu longtemps e e e e en milieu UNIX avant d’ˆtre transport´ ailleurs . Par cons´quent. la biblioth`que e e e e standard fera en sorte qu’ils puissent ˆtre vus de cette mani`re par le programmeur 54 . c’est-`-dire une recopie dans un tampon e e e e a situ´ en m´moire. e mais par le choix des fonctions de lecture ou d’´criture qui sont utilis´es : certaines fonctions (fgets et fputs. encore moins d’un syst`me ` un autre. e o Les flots binaires sont de simples suites d’octets. e e e sur de tels syst`mes. En particulier. etc. Les op´rations de lecture ou d’´criture sur de e e e tels fichiers sont tr`s rapides. Il 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 53. Or. et fscanf et fprintf. il faudra alors vous r´f´rer ` la documentation sp´cifique. mais le programmeur peut. La d´marche a e e e e inverse. ee a e 7. La norme ANSI y a mis bon ordre . D’autres fonctions (fread. On l’appelle alors fichier binaire. dont le contenu sera transf´r´ ult´rieurement vers le fichier. ` l’aide de la fonction setvbuf (cf. . 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 ` un autre sans modification. la taille du tampon. ils ne sont pas e e e cens´s ˆtre transportables d’un logiciel ` un autre. imprim´s. fwrite) effectuent la lecture et l’´criture de paquets d’octets e sans interpr´tation aucune. En revanche.1. etc. elles sont effectu´es par e e e des fonctions de la biblioth`que que chaque syst`me offre avec le compilateur. un syst`me ou un ´quipement diff´rents de celui qui les a produits . e e e e 55. mais de la mani`re dont il est trait´ par un programme. section a e e e a 7. Par d´faut un flot est toujours associ´ e e e ` un tampon de taille pr´d´termin´e. que les transferts d’information purement internes) mais introduit un d´calage dans leur chronologie. la biblioth`que UNIX a donc souvent ´t´ prise pour r´f´rence. il est difficile ou e e ee e e impossible de savoir dans quel ´tat se trouve le fichier ` un moment donn´ .LF ). on dit plutˆt ≪ bufferis´ ≫. e e lecture et ´criture d’une ligne. Ces caract`res sont eux-mˆmes repr´sent´s selon une convention largement r´pandue. si le programme vient ` e a e a subir une terminaison anormale.

Un fichier est donc repr´sent´ en C par une variable d’un type pointeur.h> puis une d´claration de la forme e FILE *fichier . seules les op´rations de lecture ee e e e sont permises sur ce flot.h>. seules les op´rations d’´criture sont permises sur ce flot. son contenu n’est pas effac´.4. e "rb". mais les op´rations d’´criture sont permises aussi. s’il existe.1. 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.). u e e Remarque 2. C’est notamment le cas d’UNIX. "wb". etc. a a e int fflush(FILE *flot) Dans le cas d’un flot de sortie. En e ee e e e principe. l’appel de cette fonction sur un flot d’entr´e e e e 56. Le nom doit ˆtre correct pour le e e e e syst`me d’exploitation sous-jacent . e e "w" (write) cr´ation d’un fichier. chaque occurrence d’une marque de fin de ligne est d´tect´e et. Le fichier existe ou non .) sur les flots e e e nous avons : FILE *fopen(const char *nom. remplac´e par l’unique caract`re ’\n’. "ab".1 Flots ´ 7 ENTREES-SORTIES parce qu’elles n’ont jamais ´t´ physiquement transf´r´es 56 . Remarque 1. En principe. Le fichier peut exister ou non . e Les valeurs permises pour mode sont : e "r" (read ) ouverture d’un fichier. e e "w+" comme "w". ou NULL si l’op´ration a ´chou´ (fichier absent. 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. Par cons´quent. "a+" comme "a". si cette marque n’est pas le e e caract`re ’\n’ lui-mˆme. mais les op´rations de lecture sont permises aussi. Le descripteur du flot cr´´ est positionn´ en lecture et au d´but du fichier. fermeture.1. Le fichier doit exister . sa d´claration e e e e n’autorise aucune des op´rations propres aux fichiers. 1988-2011 . z´ro dans les autres cas. mais les op´rations de lecture sont permises aussi. "r+b". e "a" (append) allongement d’un fichier. son contenu n’est pas d´truit.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 *. cf.3 ` propos des fichiers en acc`s relatif. cela ne regarde pas le langage C. La structure FILE est e e d´finie dans le fichier <stdio. Pour utiliser un ou plusieurs flots il faut donc ´crire en tˆte de son programme e e e la directive #include <stdio. 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 e e de ligne requise par le syst`me sous-jacent. si la lettre b ne figure pas e dans le mode) alors : – en lecture. Mais pour la plupart des biblioth`ques actuelles. Le descripteur du flot cr´´ est positionn´ en ´criture et au d´but du fichier (qui est vide). o` les fins de ligne sont indiqu´es par le caract`re ’\n’.1. ee ee 7. Garreta. Sur l’int´rˆt qu’il peut y avoir ` effectuer des lectures et des ´critures sur le mˆme fichier. e D’apr`s la norme officielle du langage C. "w+b". Parmi les principales fonctions qui effectuent les op´rations g´n´rales (ouverture. Seules les op´rations d’´criture ee e e a e e sont permises. "wb+" et "ab+") . ee a e e voir les remarques faites ` la section 7. "r+" comme "r". 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. section 7. l’effet de fflush sur un flot qui n’est pas un flot de sortie est e ind´fini. "w+b" et "a+b" peuvent se noter aussi "rb+". s’il existe. "a+b" : si on envisage d’utiliser le fichier en mode binaire il faut ajouter la lettre b au mode ("r+b". e 86 c ⃝ H. cette fonction provoque l’´criture ≪ physique ≫ imm´diate du tampon en e e cours de remplissage. Le e descripteur du flot cr´´ est positionn´ en ´criture et ` la fin du fichier. Elle rend EOF en cas d’erreur. etc. son contenu est enti`rement e e effac´. 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.e. 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.7.

Elle rend z´ro en cas de succ`s. e Oublier de fermer. feof n’est jamais vrai apr`s l’ouverture (en lecture) du fichier. 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. 1988-2011 87 . ´ventuellement..7 ´ ENTREES-SORTIES 7. e a e int setvbuf(FILE *flot.. ainsi. 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. size t taille) c ⃝ H. e Cette sp´cification de la fonction feof fonde le sch´ma suivant. v´rification du succ`s de l’ouverture de fichier e e . dans le cas fr´quent o` l’entr´e standard e e u e correspond au clavier.. . Remarque.. Elle renvoie le flot associ´ ou NULL en cas d’erreur. feof(fic) devient vrai imm´diatement apr`s qu’une lecture sur fic a ´chou´ parce qu’il e e e e n’y avait plus [assez] d’informations dans fic pour la satisfaire. FILE *tmpfile(void) Cette fonction cr´e un fichier temporaire en mode "w+b".. char *tampon. e e a Autrement dit. par un appel de fclose. e – dans certains syst`mes. Garreta. etc. 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. Il faut au moins une [tentative de] lecture pour. qui sera automatiquement e d´truit ` la terminaison du programme. En particulier. une lecture au clavier provoque la vidange du e e tampon d’´criture ` l’´cran. Par exemple.1 Flots supprime les caract`res disponibles dans le tampon. e int fclose(FILE *flot) Cette fonction ferme le flot indiqu´. Un fichier qui n’a jamais ´t´ ferm´ a donc une existence tr`s pr´caire. par exemple. une autre valeur en e e e cas d’erreur. fichier = fopen(nom. Elle renvoie une valeur non nulle si ee e et seulement si l’indicateur de fin de fichier du flot indiqu´ est vrai. 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. mˆme lorsque ce dernier e e est vide. 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. "rb"). sans nom externe.. La variable globale enti`re errno (d´clar´e dans <errno. Cela permet qu’une question soit effectivement affich´e avant que l’utilisae a e e teur ne doive taper la r´ponse correspondante. 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´. Si le fichier physique qui correspond au flot indiqu´ est un organe interactif. A la suite d’un appel comme fclose(flot) l’emploi de la variable flot est ill´gitime. produisant l’´criture physique des tampons. le rendre vrai. la restitution de l’espace e e allou´ pour le tampon et les descripteurs du flot. de lecture s´quentielle d’un fichier : e e e FILE *fichier. 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 ≫. Cela se produit non pas lorsque le e fichier est positionn´ sur sa fin mais lorsqu’il est plac´ au-del` de sa fin. 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 . lecture de donn´es sur fichier e while ( ! feof(fichier)) { traitement des donn´es lues e lecture de donn´es sur fichier e } . int mode. – 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) .h>) contient alors un code e e e e e renseignant sur la nature de l’erreur. par exemple e l’´cran d’un poste de travail. un fichier qui a ´t´ ouvert en lecture n’est en g´n´ral pas une ee e e erreur importante.

Garreta. Par exemple. 88 c ⃝ H. De mani`re similaire. tout cela se combine. Si tampon est e e a e NULL. e a e e 7. e e a e Les fonctions de lecture (resp. e e e stdin est l’unit´ standard d’entr´e. stdout). 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. supposons e e que prog2 soit un autre programme qui fait ses entr´es-sorties de la mˆme mani`re.2 Lecture et ´criture textuelles e ´ 7 ENTREES-SORTIES Cette fonction red´finit le tampon associ´ ` un flot ouvert. e e a e stderr est l’unit´ standard d’affichage des erreurs.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. 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. Pour la lecture nous avons : e int fgetc(FILE *flot) Renvoie le caract`re suivant sur le flot indiqu´. Activ´ par la commande prog1. ses r´sultats sont fournis ` titre de e e a donn´es ` prog2.7. il lira ses donn´es au clavier et affichera ses r´sultats ` l’´cran. Elle doit ˆtre appel´e apr`s l’ouverture du e ea e e e flot mais avant toute lecture ou ´criture. fichier2) remplacera le clavier (resp. d’´criture) qui ne sp´cifient pas un autre flot utilisent stdin (resp. s’il n’est pas NULL. C’est une vraie fonction. e e d´finies dans <stdio. e a e L’argument taille indique la taille voulue pour le tampon. Mais e e e a e s’il est lanc´ par la commande e prog1 < fichier1 (resp. : prog1 > fichier2) alors le fichier fichier1 (resp.2 Les unit´s standard d’entr´e-sortie e e Sans que le programmeur n’ait ` prendre aucune disposition particuli`re. Elle est aussi affect´e ` l’´cran du poste de travail. e IOLBF Tampon ´gal ` une ligne. l’ex´cution d’un programme coma e e mence avec trois flots de texte automatiquement ouverts par le syst`me. ou EOF si la fin du fichier est atteinte ou si une erreur e e survient. stdout est l’unit´ standard de sortie. L’argument tampon. sans qu’il faille recompiler ce dernier. 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). Les donn´es de prog1 sont lues dans fichier1. L’´criture ou la lecture physique a lieu lorsque le tampon contient taille e caract`res. e a Bien entendu. de la mani`re suivante : e e e FILE *stdin. 7. 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.1.2 7.h>. e IONBF Pas de tampon. 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. 1988-2011 . la commande prog1 < fichier1 | prog2 > fichier2 active prog1 et prog2. Elle est habituellement affect´e ` l’´cran du poste de travail. Une ´criture ou une lecture physique a lieu ` chaque lecture ou ´criture logique. L’argument mode doit ˆtre une des trois constantes suivantes.h> : e IOFBF Tampon de taille fixe. l’´cran). alors la fonction alloue un espace propre. dont les r´sultats sont ´crits dans fichier2. *stderr.2. *stdout. e e Remarque. Ces fichiers e a e sont d´clar´s dans <stdio. Elle est habituellement affect´e au clavier du poste de travail. est cens´ pointer un espace (de taille au moins ´gale ` taille) qui sera utilis´ comme tampon.

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

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

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

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

. &x. adresse 1 . e e e e Exemple 1.. par cons´quent il est essentiel que ces arguments soient des adresses de variables ` lire. Elle rend la main lorsque la fin du format est atteinte ou sur une erreur.. e e e – Cependant. e a La fonction scanf constituant un vrai analyseur lexical. ou 33 ) ( 22. 1988-2011 93 . Exemple 3. c’est-`-dire qui n’est ni un caract`re d’espacement (blanc. ıne e e adresse 2 . adresse n ) L’argument format est une chaˆ qui indique la mani`re de convertir les caract`res qui seront lus .%d ) )". &calu). e e On peut m´langer des appels de scanf et d’autres fonctions de lecture. Ce sont des ≪ sorties ≫ de la e fonction scanf. Comme ci-dessus mais avec une syntaxe plus souple (les blancs sont tol´r´s partout) : ee scanf(" ( 22 . 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. e – Dans le format. dans le format. adresse n indiquent les variables devant recevoir les donn´es lues. Les sp´cifications de format reconnues par la fonction scanf sont expliqu´es dans la table 5 (page 94). les espacements au d´but des nombres sont toujours saut´s. 33) (des blancs. Lecture d’un caract`re 59 : e char calu. le programme e e 59.. &y). sans ˆtre rang´ e e e e e dans aucune variable.2. 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). 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.2 Lecture et ´criture textuelles e 7. getc et getchar font ce travail bien plus simplement. 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 ≫. 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. Donn´es incorrectes : e (trop de blancs). Jusque-l` : a – Tout caract`re ordinaire du format. tabulation) e a e ni un caract`re faisant partie d’une sp´cification de format (commen¸ant par %). Rappelons que fgetc.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 . adresse 1 . Voir le tableau 5. doit s’identifier au cae e c ract`re courant du flot d’entr´e. Exemple 2. &y). Cependant. EOF. Le nombre de ces espacements est sans importance.7 ´ ENTREES-SORTIES 7. Donn´es correctes : e (22. et le parcours du format se poursuit.. &x. e e Fonctionnement : scanf parcourt le format. L’exp´rience ne d´¸oit pas cette a e ec attente ! Voici un bon conseil : si un programme ne fonctionne pas comme il le devrait. 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. Exemple de donn´e incorrecte : e e e e Exemple 4. . c ⃝ H. mais uniquement devant les nombres). &x). Si cette identification a lieu. Remarque.33) ( 22 . Chaque appel d’une telle fonction e commence par lire le premier caract`re que l’appel pr´c´dent n’a pas ≪ consomm´ ≫. alors les e e donn´es correspondantes dans le flot d’entr´e doivent ˆtre adjacentes. e – S’il n’y a pas d’espacement. Si l’identification n’a pas lieu. Toutes les donn´es de l’exemple pr´c´dent sont correctes. scanf("%c".%d)". . notez bien qu’ici nous e sommes oblig´s de d´clarer la variable calu de type char . l’activation de scanf se termine. entre les caract`res ordinaires ou les sp´cifications. 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.. le caract`re courant est lu.. adresse 2 . (%d 33 . Lecture d’un entier : scanf("%d". – Les sp´cifications de format commencent par %. Garreta.

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

2.. y = 33. &calu). Exemple 5. y = %d\n". char calu. scanf("%d%c". sautez tous a e les caract`res blancs qui se pr´sentent ≫... . 1988-2011 95 . y = 33 Le¸on ` retenir : dans le format de scanf. char tmp. printf("x = %d. */ ı printf("donne y : "). 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. Parce qu’on a ´crit. ! ? e 22¶ donne y : x = 11. e ` A propos de la lecture d’un caractere. 7. scanf(" %d ". Exemple 6. printf("donne x : "). . ce caract`re n’´tant pas consomm´.. &calu).. Nous avons dit que les sp´cifications qui constituent le format e peuvent ˆtre s´par´es entre elles par un ou plusieurs blancs. scanf("%c". quel qu’il soit : e e int nblu.. Garreta. &nblu. cela ne pose pas de probl`me. &x).. calu = tmp. il fait ´chouer la lecture du second nombre : e e e e donne x : 11¶ donne y : x = 11. e e e Pour la fonction scanf.. des blancs apr`s le format %d.. . Exemple : e int x. les blancs ne sont pas de la d´coration. char calu. Par exemple.. y = 22 Notez que faire suivre le premier nombre d’un caract`re non blanc conventionnel n’est pas une bonne solution e car. &y). le premier appel de scanf e e e ci-dessus signifie ≪ lisez un entier et tous les blancs qui le suivent ≫. 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. les caract`res ` lire existant tous d`s le d´part. les blancs dans le format se traduisent donc par l’ordre ≪ ` pr´sent. &tmp).2 Lecture et ´criture textuelles e solution correcte aurait ´t´ la suivante : ee int calu.4 A propos de la fonction scanf et des lectures interactives A propos de ≪ sauter les blancs ≫. Lecture d’un nombre entier et du premier caract`re non blanc le suivant : e int nblu. /* a¨e. le programme na¨ ıf suivant lit des nombres les uns ` la suite des autres et affiche le carr´ de chacun : a e c ⃝ H.. 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. 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. Si la e a e lecture se fait dans un fichier. x. peut-ˆtre sans faire attention.7 ´ ENTREES-SORTIES 7.. scanf("%d %c". scanf("%d". Lecture d’un nombre entier et du caract`re le suivant imm´diatement. . &nblu. y). 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. ils jouent un rˆle bien c a e o pr´cis.

Dans beaucoup d’environnements on peut faire cela avec la fonction e a e fflush : int x. le tampon de stdin contient (au moins) un ’\n’ while (getchar() != ’\n’) . scanf("%d". maintenant le tampon est vide c = getchar(). scanf("%d".. char s[BUFSIZ]. scanf("%d". 1988-2011 .. de e e e e ee mani`re ` consommer tous les caract`res restant dans le tampon jusqu’` la fin-de-ligne. 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. maintenant le tampon est vide c = getchar(). L’appel de getchar (qui. c. printf("un autre calcul (O/N) ? "). ce qui fait quitter la boucle. do { printf("donne un nombre : "). printf("au revoir.. &x). ici. c... L’exemple ci-dessus devient e . dans l’esprit du programmeur. Ainsi. ce caract`re devant ˆtre e a e a e e consomm´ lui aussi.\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. 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 ≫). &x). La machine n’a pas attendu la r´ponse (O ou N) de l’op´rateur.. x.. 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. 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.")..2 Lecture et ´criture textuelles e ´ 7 ENTREES-SORTIES int x. pour que a e ce programme se comporte comme son programmeur attend il faudrait qu’` la suite de l’invite ≪ donne un a nombre: ≫. &x). c = getchar().. printf("un autre calcul (O/N) ? "). bien sˆr. . ici. x.. . Garreta. ← lecture d’un caract`re e } while (c == ’O’). x * x).. printf("%d ^ 2 = %d\n". do { printf("donne un nombre : "). aurait dˆ provoquer une nouvelle lecture e u physique) trouve cette fin-de-ligne et l’affecte ` c comme r´ponse. printf("%d ^ 2 = %d\n".. printf("un autre calcul (O/N) ? "). .. 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. ..7. 96 c ⃝ H. x * x). x."). } while (c == ’O’). ← lecture d’un nombre printf("%d ^ 2 = %d\n". printf("au revoir.. le tampon de stdin contient (au moins) un ’\n’ fflush(stdin)..

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

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

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

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]. FILE *dest.4. if ((dest = fopen(nom. printf("destination : "). /* fichier de texte */ /* fichier binaire */ printf("source : "). gets(nom). prenom[32]. 100 c ⃝ H.h> #include <string. e e – une adresse sur la ligne suivante . qui peut ˆtre aussi bien le clavier d’un e e terminal qu’un fichier existant par ailleurs. gets(nom). exprimant un certain ≪ nombre de passages ≫ (` un p´age autoroutier). char adresse[80].h> #include <stdlib. – un entier seul sur une ligne. FILE *srce. Le programme est ind´pendant de la nature du fichier de texte. int nombre_de_passages. e e 7. "r")) == NULL) exit(ERREUR_OUVERTURE). if ((srce = fopen(nom. a e Exemple TOMBAL Pierre Rue de la Gaiete de Vivre 10 MENSOIF Gerard Allee ’Les verts’ 20 etc.7.4 Exemples ´ 7 ENTREES-SORTIES N fichier ` copier comporte N octets .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 . "wb")) == NULL) exit(ERREUR_CREATION). /* constitution d’un fichier d’articles */ /* ` partir d’un fichier de texte */ a #include <stdio. } art. 1988-2011 . Garreta. 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. main() { char nom[256].

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

7.5

Les fichiers de bas niveau d’UNIX

´ 7 ENTREES-SORTIES

for (;;) { if (fread(&n, sizeof n, 1, passages) != 1) break; fseek(abonnes, n * sizeof art, SEEK_SET); fread(&art, sizeof art, 1, abonnes); art.nombre_de_passages++; fseek(abonnes, n * sizeof art, SEEK_SET); fwrite(&art, sizeof art, 1, abonnes); } fclose(abonnes); exit(PAS_D_ERREUR); }

7.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. En fait, 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). 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. e Dans le syst`me UNIX chaque processus dispose, pour g´rer ses fichiers, d’une table de descripteurs de e e fichiers. Lorsque les fichiers sont g´r´s au plus bas niveau, ils sont repr´sent´s tout simplement par un indice dans ee e e cette table. Par exemple, les trois unit´s standard stdin, stdout et stderr sont respectivement repr´sent´es e e e en tant que fichiers par les descripteurs d’indices 0, 1 et 2. Les fonctions disponibles sont : int open(char *nom, int mode, int permissions) Ouvre un fichier existant ayant le nom externe indiqu´. L’argument mode doit avoir pour valeur une des e constantes symboliques (d´finies dans <ioctl.h> ou dans <sys/file.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, ou un nombre n´gatif en cas d’erreur. e e Pour l’argument permissions voir ci-dessous. int creat(char *nom, int permissions) Cr´e un fichier nouveau, ayant le nom indiqu´, et l’ouvre en ´criture. Comme pour la fonction open, e e e l’argument permissions indique que le droit de lecture, le droit d’´criture ou le droit d’ex´cution est e e accord´ ou non au propri´taire du fichier, aux membres de son groupe de travail ou ` tous les utilisateurs e e a du syst`me. Cela fait trois groupes de trois bits, soit un nombre qui s’´crit particuli`rement bien en octal. e e e Par exemple : fic = creat("tmp/monfic", 0751); 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, char *adresse, int nombre) Lit nombre octets depuis le fichier indiqu´ et les range ` partir de l’adresse indiqu´e. Renvoie le nombre e a e d’octets effectivement lus, qui peut ˆtre inf´rieur ` nombre (fin de fichier, erreur de lecture, etc.). e e a int write(int fichier, void *adresse, int nombre) Ecrit les nombre octets qui se trouvent ` l’adresse indiqu´e dans le fichier indiqu´. Rend le nombre a e e d’octets effectivement ´crits, qui peut ˆtre inf´rieur ` nombre (erreur d’´criture, etc.). e e e a e int close(int descfic) Ferme le fichier indiqu´. e

102

c ⃝ H. Garreta, 1988-2011

7

´ ENTREES-SORTIES

7.5

Les fichiers de bas niveau d’UNIX

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

c ⃝ H. Garreta, 1988-2011

103

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. Ce sont des ´l´ments importants du langage mais, sauf quelques exceptions, e ee on peut s’en passer aussi longtemps qu’on limite la pratique de C ` des exercices de petite taille. a

8.1

Le pr´processeur e

Le pr´processeur transforme le texte source d’un programme avant que la compilation ne commence. e G´n´ralement associ´es au processus de la compilation, ses actions semblent en faire partie, mais il faut sae e e voir que le pr´processeur est un programme s´par´, largement ind´pendant du compilateur. En particulier le e e e e pr´processeur ne connaˆ pas la syntaxe du langage C. Les transformations qu’il effectue sur un programme e ıt sont simplement des ajouts, des suppressions et des remplacements de morceaux de texte nullement astreints ` a correspondre aux entit´s syntaxiques du langage. e Les directives destin´es au pr´processeur comportent, dans l’ordre : e e – un signe # (qui, en syntaxe originale, doit occuper la premi`re position de la ligne) ; e – un mot-cl´ parmi include, define, if, ifdef, ifndef, else et endif qui identifie la directive. Nous e passerons sous silence quelques autres directives mineures comme pragma, line ou error ; – le corps de la directive, dont la structure d´pend de la nature de celle-ci. e 8.1.1 Inclusion de fichiers

La possibilit´ d’inclure des fichiers sources dans d’autres fichiers sources s’av`re tr`s utile, 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, structures, etc.). Ainsi ces d´finitions sont ´crites dans un seul fichier, ce qui en facilite la e e maintenance. 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. Pour que les appels de ces fonctions puissent ˆtre correctement compil´s 62 dans des programmes qui e e les utilisent, un certain nombre de fichiers en-tˆte sont livr´s avec les biblioth`ques, comprenant e e e – principalement, les prototypes des fonctions qui forment la biblioth`que ou qui incarnent une fonctionnalit´ e e particuli`re (les entr´es - sorties, la gestion des chaˆ e e ınes de caract`res, la biblioth`que math´matique, etc.) ; e e e – souvent, un ensemble de directives #define et de d´clarations struct, union et typedef qui d´finissent e e les constantes et les types n´cessaires pour utiliser la biblioth`que en question ; e e – parfois, 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 ; e e e – plus rarement, quelques d´clarations de variables externes. e Cette directive poss`de deux formes : e #include "nom-de-fichier " C’est la forme g´n´rale. 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 ; ce doit ˆtre un fichier source ´crit en C. 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´. e e La deuxi`me forme est : e #include <nom-de-fichier > Ici, le nom est incomplet ; il ne mentionne pas le chemin d’acc`s au fichier, car il est convenu qu’il s’agit d’un e fichier appartenant au syst`me, faisant partie de la biblioth`que standard : le pr´processeur ≪ sait ≫ dans quels e e e r´pertoires ces fichiers sont rang´s. Ce sont des fichiers dont le nom se termine par .h (pour header, 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. e Exemples (avec des noms de fichier en syntaxe UNIX) : #include <stdio.h> #include "/users/henri/projet_grandiose/gadgets.h"
62. 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. Pour cette raison on ne trouve jamais dans les fichiers en-tˆte ni des variables non externes, ni le corps e des fonctions.

104

c ⃝ H. Garreta, 1988-2011

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

il faut l’appeler avec un argument sans effet de bord. peut ˆtre obtenue avec une macro et avec une fonction. 1988-2011 . le corps d’une macro est recopi´ puis compil´ ` chaque endroit o` figure l’appel e e ea u de la macro. 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.1 Le pr´processeur e 8 ´ ´ AUTRES ELEMENTS DU LANGAGE C Les appels de macros ressemblent beaucoup aux appels de fonctions. Alors que le corps d’une fonction figure en un seul exemplaire dans le programme e ex´cutable dont elle fait partie. 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)). etc. puisqu’il n’y aura qu’un point du programme ` modifier. 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). au moins deux caract`res sont lus. ce qui est probablement erron´. De plus.186 * p) Le probl`me pr´c´dent est corrig´. Mais il faut comprendre que ce n’est e pas du tout la mˆme chose.3 Compilation conditionnelle Les directives de conditionnelle (compilation) se pr´sentent sous les formes suivantes : e 106 c ⃝ H.196 * p est tr`s imprudente. la macro e e e e e #define TVA(p) 0.196 * x. On notera e u e que si toupper avait ´t´ une fonction. l’expression toupper(getchar()) aurait ´t´ tout ` fait correcte. a Attention. 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. cette expression se d´veloppe en e calu = (’a’ <= (getchar()) && (getchar()) <= ’z’ ? (getchar()) + (’A’ .). 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. Exemple classique : on dispose d’une macro nomm´e toupper qui transforme e e e e toute lettre minuscule en la majuscule correspondante.186 * (p)) 2.186 * p1 + p2). elles peuvent avoir un type pour argument. 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’ . La mˆme facilit´. Il faut donc que le corps d’une macro soit r´duit. car une expression comme (int) TVA(x) sera d´velopp´e en (int) 0. } 8. Par exemple. Garreta.1. A chaque appel. on gagne ` e e ea a chaque fois le coˆt d’un appel de fonction. Voici un tr`s mauvais appel e e e de cette macro : calu = toupper(getchar()) En effet. Il ne faut jamais appeler une macro inconnue avec des arguments qui ont un effet de bord. Premi`re correction : e #define TVA(p) (0. car ceux-ci peuvent ˆtre ´valu´s plus d’une fois. comme ci-dessus. Des telles expressions peuvent figurer ` de nombreux endroits d’un programme. Par exemple : { calu = getchar(). Mais ee ee a puisque ce n’en est pas une. Le texte de la macro ´tant recopi´ ` la place de l’appel. sans quoi son utilisation peut finir par ˆtre tr`s on´reuse e e e e en termes d’espace occup´. 1. 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. calu = toupper(calu). ne pas r´crire un e e e certain code source. ce qui n’est sˆrement pas l’effet recherch´. proches du mat´riel ou sujettes ` modification. mais il y en a un autre : l’expression TVA(p1 + p2) est d´velopp´e en e e e e e e (0. e e pour ´viter des probl`mes li´s aux priorit´s des op´rateurs. et ne fait rien aux autres caract`res.’a’) : (getchar())). D’o` la forme habituelle : e u #define TVA(p) (0. ce qui vaut e e e toujours 0. elles sont ind´pendantes des types de leurs arguments ee e ou mˆme. Il est quasiment obligatoire d’´crire les arguments et le corps des macros entre parenth`ses. a e e En faveur des macros : leur efficacit´. e En d´finitive.’a’) : (c)) On voit que l’´valuation de toupper(c) comporte au moins deux ´valuations de c.

#endif .. #ifdef BINAIRES_ANSI fic = fopen(nom.. ignor´ si elle est fausse e e . 1988-2011 107 . 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. #else . si la condition est fausse aucun texte ne sera compil´. alors le texte qui se trouve entre #if et #else sera trait´ par le a e compilateur. On obtient de la sorte des textes sources qui restent e e portables malgr´ le fait qu’ils contiennent des ´l´ments non portables.8 ´ ´ AUTRES ELEMENTS DU LANGAGE C    #if expression  #ifdef identificateur   #ifndef identificateur . ignor´ si elle est vraie e e . 0).. e Les directives #ifdef identificateur #ifndef identificateur ´quivalent respectivement ` e a #if #if ≪ ≪ identificateur est le nom d’une macro actuellement d´finie ≫ e 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. #endif 8... 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. c’est le premier texte qui sera ignor´ tandis que le second sera lu par a e le compilateur. Dans la forme sans #else. texte compil´ si la condition est fausse. D´claration : e e e .. "r"). #ifdef BINAIRES_ANSI FILE *fic... Garreta. Ouverture : .1 Le pr´processeur e Lorsque le premier ´l´ment de cette construction est #if expression. tandis que celui qui figure entre #else et #endif sera purement et simplement tenu pour inexistant . texte compil´ si la condition est vraie. #else int fic. #else fic = open(nom. ok = (fic != NULL).. #endif . expression doit pouvoir ˆtre ´valu´e ee e e e par le pr´processeur.. #endif La partie else est facultative . on a donc droit aussi aux formes :    #if expression  #ifdef identificateur   #ifndef identificateur .. – si elle est fausse. c’est-`-dire non nulle.. texte compil´ si la condition est vraie. Elle sera ´valu´e et e e e – si elle est vraie. ok = (fic >= 0). ignor´ si elle est fausse e e .. 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 &... c ⃝ H......

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. 1988-2011 . fic) == 1). Suivant l’analyse de Bertrand Meyer (Conception et programmation par objets. pour le caract´riser. #endif 8. la construction #if expression1 .. e – des noms de macros d´finis dans la commande qui lance la compilation..) . #endif . #else #if expression2 .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. Garreta. #elif expression2 ... 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. au pire. #ifndef BINAIRES_ANSI ok = (fread(&art. 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´ ) . 1990) ` laquelle il semble a difficile d’ajouter ou de retrancher quelque chose.. comme celles-ci : e e e a e – les modules doivent correspondre ` des entit´s syntaxiques du langage . 1. e e ea – assurer que l’effet d’une condition anormale se produisant dans un module restera localis´ ` ce module ou. #else ok = (read(fic. on peut toutefois la cerner ` travers quelques crit`res 67 .. e e – favoriser la production de composants logiciels qui peuvent se combiner librement pour produire de nouveaux syst`mes (crit`re de composabilit´ ) . e Th´oriquement. D’autre part..2 La modularit´ de C e 8 ´ ´ AUTRES ELEMENTS DU LANGAGE C Lecture : . De plus.. sans douleur et sans artifice. e e Remarque. Par exemple dans e e e e le compilateur des syst`mes UNIX la macro UNIX est d´finie et vaut 1. elles pourront ˆtre faites par des proc´dures de commandes (scripts) donc e e automatis´es . 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´ ) . e – un ensemble de noms de macros pr´d´finis dans chaque syst`me.. Elle n’est pas facile ` e e e e a d´finir .. En pratique un langage est dit modulaire lorsqu’on peut. #if !defined ident). n’atteindra qu’un petit nombre de modules ≪ voisins ≫ ( crit`re de protection). puisque ces d´finitions se font au niveau de la e e commande de compilation. sizeof(art)) == sizeof(art)). 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´ ) . tout langage de programmation peut servir ` mettre en œuvre une m´thode de conception e a e modulaire.. sizeof(art).8. #endif #endif peut en C ANSI s’abr´ger en e #if expression1 . 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. 67.. e que le jeu n’en vaut pas la chandelle. 108 c ⃝ H. ce qui permet de changer le texte e qui sera compil´ sans toucher au fichier source.. pour un r´sultat si peu fiable. #ifndef ident) sous la forme #if defined ident (resp. &art.. en faire l’outil d’une m´thode de conception modulaire. mais dans certains cas il faut une telle dose de ruse et d’autodiscipline. InterEditions.

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

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

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

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

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

8. L’appel setjmp(contexte). 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.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. e main analyse() analyse setjmp setjmp(c) expression expression() longjmp(c. et en maˆ ıtrisant le ≪ point de chute ≫. puis renvoie z´ro. etc.. e e Le principal service rendu par ce dispositif est de permettre de programmer simplement. qui appelle une fonction facteur qui ` son tour appelle une fonction primaire laquelle a rappelle expression. 8 ´ ´ AUTRES ELEMENTS DU LANGAGE C 8. terme. Cela fonctionne de la mani`re suivante : tout d’abord il faut d´clarer une variable. jmp_buf contexte. remet le syst`me dans l’´tat qui a ´t´ enregistr´ dans la variable contexte. quel que soit le nombre de ces fonctions.. qui rend la poursuite du traitement inutile ou impossible. valeur). en rendant cette fois non pas z´ro mais la valeur indiqu´e dans l’appel de longjmp. l’abandon d’une famille de fonctions qui se sont mutuellement appel´es. void longjmp(jmp_buf contexte. etc.h> . peut ` tout moment rencontrer une erreur dans a ses donn´es.) .3. int code).. Garreta. de e e e e type jmp buf (type d´fini dans le fichier en-tˆte setjmp. – chacune des fonctions expression.. g´n´ralement globale. 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. Plus pr´cis´ment.2 Branchements hors fonction : setjmp. Ensuite. facteur. enregistre dans contexte certaines informations traduisant l’´tat du syst`me. l’appel e e e longjmp(contexte. 1988-2011 .h) : e e #include <setjmp.v) terme terme() Figure 19 – Branchement hors fonction Voici comment l’appel de expression pourrait ˆtre emball´ dans une fonction e e analyse (voir la figure 19) : 114 ≪ enveloppe ≫ nomm´e e c ⃝ H..3 Deux ou trois choses bien pratiques. Il est r´alis´ ` l’aide des deux fonctions : e ea int setjmp(jmp_buf contexte).

et elle nous apprend que la fonction signal prend deux arguments. Garreta. 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. 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´. 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). . car il utiliserait le contexte d’une e e activation de fonction (la fonction analyse) qui n’existe plus. Par exemple. 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. Cela est vrai.v) qui serait plac´ e e ee e e dans la fonction main apr`s l’appel analyse() (voir la figure 19) serait erron´.h> . D’autre part.). 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). elle est donc e e imm´diatement suivie par l’appel de expression. 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. jmp_buf contexte. pour laquelle l’espace local est encore allou´ dans la pile. notamment les variables globales : e elles ne reprennent pas la valeur qu’elles avaient lorsque longjmp a ´t´ appel´e. 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. Avec cela. facteur.3. 1). d´pendant de chaque syst`me. } else return ERREUR. qui ram`ne le contrˆle dans la fonction analyse. En g´n´ral on traite de la mˆme mani`re le vrai et le faux asynchronisme. l’argument numero doit d´signer un des ´v´nements qu’il est possible de r´cup´rer. #include <setjmp. } Fonctionnement : lorsque la fonction analyse est activ´e. ee e 2. un appel longjmp(c. sauf pour ce qui concerne la valeur des variables. Remarques. PROCEDURE *manip). e e e e e La fonction signal est d´clar´e dans le fichier signal. la d´claration pr´c´dente se r´crit plus simplement : e e e e PROCEDURE *signal(int numero. void (*manip)(int)))(int). 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. Lors d’un appel de signal. 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. ` savoir un int et l’adresse d’une PROCEDURE. dans la fonction expression et toutes celles ee appel´es ≪ au-dessus ≫ d’elle (terme.h de la fa¸on suivante : e e c void (*signal(int numero. est r´cup´rable ` travers le e e e e e e a m´canisme d´crit ici. alors elle sera automatiquement appel´e par le syst`me avec pour argument le num´ro de e e e l’´v´nement. Si le travail de expression se termine normalement. il est possible d’effectuer l’appel e longjmp(contexte. e e e e e L’effet de signal est d’enregistrer la PROCEDURE donn´e en argument.... return SUCCES. e e u car ils ne r´sultent pas d’instructions normalement ins´r´es dans la s´quence qui forme le programme. e e c ⃝ H..8 ´ ´ AUTRES ELEMENTS DU LANGAGE C 8. 8. alors e analyse rendra SUCCES et elle aura ´t´ transparente. Ce prototype n’est pas tr`s facile ` lire.3 Deux ou trois choses bien pratiques. de telle mani`re que si l’´v´nement en e e e e question se produit. ≪ Asynchrones ≫ signifie qu’on ne peut pas pr´voir le moment o` ils se produiront.3 Interruptions : signal. 1. 1988-2011 115 . etc. mˆme si cette fonction a ´t´ rappel´e depuis. setjmp est appel´e et rend 0 .. a et renvoie l’adresse d’une PROCEDURE.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.. int analyse(void) { if (setjmp(contexte) == 0) { expression().

autres op´rations e . void voirSkissPass(int numero) { int c... e e 69.dll. autres op´rations e ..4 La biblioth`que standard e La biblioth`que standard ANSI se compose de deux sortes d’´l´ments : e ee – un ensemble de fichiers objets 70 contenant le code compil´ des fonctions de la biblioth`que et participant. if (c == ’O’) exit(1). Il s’agit en r´alit´ de fichiers ≪ biblioth`ques ≫ (extensions . 1988-2011 . } 8. etc. e e 116 c ⃝ H.. en appuyant sur une touche particuli`re 69 ... . etc. printf("J’en suis a l’iteration: %d\nOn arrete? "..lib.. 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. do c = getchar().). typedef void PROCEDURE(int).8. .. 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. 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.h> int iteration_numero.. 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. /* abandonner le programme */ else return. /* reprendre le travail interrompu */ } main() { PROCEDURE *maniprec.. calculs terriblement complexes . iteration_numero).) e e SIGSEGV : acc`s m´moire ill´gal (souvent : mauvais pointeur dans l’acc`s ` une donn´e. . On souhaite donc offrir ` ce dernier la possibilit´ d’interrompre le programme e a e lorsqu’il estime que celui-ci devient excessivement long. Sur plusieurs syst`mes. . e 70. il s’agit de la combinaison des deux touches ≪ Ctrl ≫ et ≪ C ≫. /* entr´e dans la section on´reuse */ e e maniprec = signal(SIGINT.4 La biblioth`que standard e 8 ´ ´ AUTRES ELEMENTS DU LANGAGE C Six ´v´nements sont pr´vus par le standard ANSI (ils ne sont pas tous implant´s dans tous les syst`mes . while ((c = toupper(c)) != ’O’ && c != ’N’). . comme UNIX et VMS.a.) e SIGTERM : demande d’arrˆt de l’utilisateur (interruption ≪ forte ≫) e SIGFPE : erreur arithm´tique (division par z´ro. en e e e e e outre. 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. voirSkissPass). maniprec). Garreta. /* sortie de la section on´reuse */ e signal(SIGINT.. etc.

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

. affectation de s . le double) dont la chaˆ s est l’expression ´crite. lorsque les assert sont d´sarm´s.. voir rand ci-dessus. Celle-ci se fera alors comme si tous les appels de assert avaient ´t´ gomm´s. "Erreur.. comme les erreurs dans les donn´es saisies. du moins ` partir e e a a e a du deuxi`me terme.4 La biblioth`que standard e 8 ´ ´ AUTRES ELEMENTS DU LANGAGE C juste avant d’effectuer la compilation finale du programme. a Voyez srand ci apr`s. affectation de i . . scanf("%d". i). if( ! (0 <= x && x <= N)) { fprintf(stderr. /* 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. sprintf(s. assert(0 <= x && x <= N). } .. ... Deux valeurs diff´rentes de la semence e e – ´loign´es ou proches. &x). scanf("%d". Cette suite ne d´pend que de la valeur de la semence donn´e lors de l’appel de srand.4. Garreta. Ainsi – sauf pour un programme ´ph´m`re ` usage strictement e e e e e a personnel – le code suivant n’est pas acceptable : . 8.. C’est pourquoi elle doit plutˆt ˆtre programm´e avec du e e o e e ≪ vrai ≫ code : .. double atof(const char *s) Ces fonctions calculent et rendent l’int (resp. e void srand(unsigned int semence) Initialise une nouvelle suite pseudo-al´atoire.. .8. peu importe – donnent lieu ` des suites tout ` fait diff´rentes.. ee e N. 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. le long.. &x). maintenant i a la valeur num´rique dont la chaˆ e ıne s est l’expression textuelle Note. long atol(const char *s).B. 1988-2011 . "%d"...h int atoi(const char *s)... char *s. char s[80]. 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. la suite obtenue e ee e est celle qui correspond ` srand(1). qui vaut au moins 32767. e Application. e exit(-1). voir ci-dessous. N). i = atoi(s). ıne e Exemple int i. La valeur de x doit ^tre comprise entre 0 et %d\n".. 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 e et la valeur de la constante RAND MAX. 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 118 c ⃝ H. Si srand n’a pas ´t´ appel´e.2 Fonctions utilitaires : stdlib.

elle obtient e e un nouvel espace ayant la taille voulue. par exemple ` travers la fonction time 73 . La fonction time renvoie le nombre de secondes ´coul´es depuis le premier janvier 1970. e void *calloc(size t nombre. 1988-2011 119 . Cette e e e adresse doit n´cessairement provenir d’un appel de malloc. calloc ou realloc. que le programme a r´ussi ` accomplir sa mission . e void abort(void) Provoque un arrˆt anormal du programme (c’est un ´v´nement d´tect´ par la fonction signal). /* version C de randomize */ rand(). e a – EXIT FAILURE : le programme n’a pas r´ussi. i < 10.h> #include <stdlib. printf("%f\n". 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. /* le premier tirage est tr`s li´ ` la semence */ e e a for(i = 0. size t taille) R´-allocation d’espace. Garreta. e e e e e int system(const char *commande) Suspend momentan´ment l’ex´cution du programme en cours. lib`re cet espace. c ⃝ H. Si cela peut se faire sur place alors la fonction renvoie la mˆme adresse adr. 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. 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 73. ou NULL en cas d’´chec. calloc ou realloc. x). pour le syst`me sous-jacent. qu’on peut r´gler en faisant intervenir d’autres fonctions de mesure du temps. La signification de ce code d´pend du syst`me . ou e NULL en cas d’´chec. i++) { x = rand() / (float) RAND_MAX. 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. float x. Elle renvoie donc un nombre entier e e qui change toutes les secondes. srand(time(NULL)).h> main() { int i. La valeur de adr doit provenir d’un appel de malloc.h> #include <time.) du programme. etc. Sinon. on peut e e e utiliser les constantes : e – EXIT SUCCESS : la valeur conventionnelle qui indique. Une mani`re d’obtenir cela consiste ` utiliser l’heure e e a courante (elle change tout le temps !). y copie le contenu de l’espace point´ par adr. } } void *malloc(size t taille) Alloue un espace pouvant m´moriser un objet ayant la taille indiqu´e. Par e e e exemple clock compte le temps (mais c’est un temps relatif au lancement du programme) en fractions de seconde. a Par exemple. Cet espace e e e n’est pas initialis´. chacun ayant la taille indiqu´e. e void *realloc(void *adr.8 ´ ´ AUTRES ELEMENTS DU LANGAGE C 8. L’espace allou´ est initialis´ par des z´ros. size t taille) Alloue un espace suffisant pour loger un tableau de nombre objets. e e enfin renvoie l’adresse de l’espace nouvellement allou´. 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. 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).4 La biblioth`que standard e a ` lui passer un argument diff´rent chaque fois. e void exit(int code) Produit l’arrˆt normal (fermeture des fichiers ouverts. La valeur du code indiqu´ est e e transmise au syst`me d’exploitation.

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

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

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

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

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

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

You're Reading a Free Preview

Download
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->