P. 1
C

C

|Views: 63|Likes:
Published by amirsa

More info:

Published by: amirsa on Mar 26, 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

12/17/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
  • EES-SORTIES 7.5 Les fichiers de bas niveau d’UNIX
  • 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, 2004

` TABLE DES MATIERES

` TABLE DES MATIERES

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

. . . . . . . . . . .

. . . . . . . . . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

´ 2 OPERATEURS ET EXPRESSIONS

2.2

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

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

exp0 exp1

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

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

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

21

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

2

´ OPERATEURS ET EXPRESSIONS

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

2.2.5

N´gation ! e

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

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

2.2.6

Compl´ment ` 1 ~ e a

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

2.2.7

Les c´l`bres ++ et -ee

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

´ 2 OPERATEURS ET EXPRESSIONS

2.2

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

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

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

2.2.8

Moins unaire -

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

2.2.9

Indirection *

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

23

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

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

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

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

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

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

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

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

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

2

´ OPERATEURS ET EXPRESSIONS

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

2.2.20

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

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

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

´ 2 OPERATEURS ET EXPRESSIONS

2.2

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

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

2.2.21

L’op´rateur virgule , e

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

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

c H. Garreta, 2004

33

2.3 Autres remarques

2

´ OPERATEURS ET EXPRESSIONS

2.3
2.3.1

Autres remarques
Les conversions usuelles

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

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

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

34

c H. Garreta, 2004

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 ). 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.1 Les pointeurs et les tableaux Arithm´tique des adresses. 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. il semble naturel de donner ` exp 2 −exp 1 la valeur 1. e De mani`re analogue.h> : e #define NULL ((void *) 0) 6. e e c’est l’´quivalent en C de la constante nil de Pascal. 2004 . 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. ou type *t. En r´alit´ il y a bien plus qu’un simple lien de voisinage entre ces deux concepts. Par d´finition. etc. etc. distincte de toute adresse valide. Le programme suivant parcourt un tableau en utilisant un pointeur : 66 c H. a Cette valeur peut donc ˆtre utilis´e comme la valeur conventionnelle du  pointeur qui ne pointe vers rien  . Garreta. D’un point de vue technique.6. ajouter 1 ` un pointeur e e a c’est le consid´rer momentan´ment comme un entier et lui ajouter une fois la taille de l’objet point´. la structure vide. On e ee e peut alors interpr´ter p + i comme l’adresse de t[i0 + i] et q − p comme l’´cart entre les indices correspondant e e a ` ces deux ´l´ments.2 6. a – la soustraction de deux adresses (de types rigoureusement identiques). Pour e ın´ r´aliser ces structures il est n´cessaire de poss´der une valeur de pointeur. e a L’arithm´tique des adresses d´coule de ces remarques. Les pointeurs avec leur arithm´tique et l’acc`s aux ´l´ments des tableaux sont donc des ee e e ee concepts tr`s voisins. En pratique on utilise plutˆt la constante NULL qui est e o d´finie dans le fichier <stddef. 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]. 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. 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. 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 . e e e repr´sentant la fin de liste. ´ventuellement fictif. 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. Une des principales applications des pointeurs est la mise en œuvre de structures r´cursives de taille variable : listes chaˆ ees de longueur inconnue.2 Les pointeurs et les tableaux 6 POINTEURS Le pointeur NULL. Exemple. arbres de profondeur quelconque. indirection et indexation e ´ Arithmetique.2. e C garantit qu’aucun objet valide ne se trouve ` l’adresse 0 et l’entier 0 est compatible avec tout type pointeur. un programme est plus facile ` lire si on montre une certaine constance dans le choix des a notations.

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

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

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

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

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

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

"Nom de fichier incorrect". i < n.. i < nbr_mots. t[i] = w. t[i]) > 0) { char *w = t[i . } if (ok) return.6 POINTEURS 6. 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. void tri(char *t[].1]. *libre = espace. mots[i]). *mots[MAXMOTS]. n--. for (i = 1. nbr_mots). 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 ] . nbr_mots = 0. } Remarque.. 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. comme dans : e char *messages[] = { "Fichier inexistant". alors l’indication du nombre de ses ´l´ments est sans utilit´ puisqu’il n’y a pas d’allocation d’espace. 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. libre += strlen(libre) + 1.h> #define MAXMOTS 100 #define MAXCARS 3000 char char char int espace[MAXCARS]. i++) if (strcmp(t[i . 2004 73 . espace immodifiable allou´ et garni une fois pour toutes e par le compilateur. puts (´criture de chaˆ e ıne).. "Erreur physique d’entree-sortie". i++) printf("%s\n". 6. t[i . int n) { for (.h> #include <string.1] = t[i]. [Nk−1 ][Nk ] .1]. ok = 0. Garreta. for (i = 0.) { int i. ok = 1. "Autre erreur" }.2. } tri(mots. while(gets(libre) != NULL && *libre != ’\0’) { mots[nbr_mots++] = libre. tandis que messages est fait de e e pointeurs vers des cellules de la  zone de constantes . c H. "Volume ou repertoire inexistant".2 Les pointeurs et les tableaux Les fonctions gets (lecture de chaˆ ıne). a e e #include <stdio. } } void main(void) { int i.6 Tableaux multidimensionnels formels Nous avons vu que si un argument formel d’une fonction ou une variable externe est un tableau ` un seul a indice.

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

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

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

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

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

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

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

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

6.4 Structures r´cursives e

6

POINTEURS

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

libre

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

...

...

...

...

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

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

...

6 POINTEURS

6.4

Structures r´cursives e

...

...
Liste de familles Liste d'individus

...

...

Fig. 17 – Structures mutuellement r´cursives e

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

...

c H. Garreta, 2004

83

´ 7 ENTREES-SORTIES

7

Entr´es-sorties e

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

7.1

Flots

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

84

c H. Garreta, 2004

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

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

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

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

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

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

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

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

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

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

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

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

3. fread( & article. et ouvert de fa¸on ` autoriser les eae e e e e c a lectures et les ´critures : e FILE *fichier.3 Op´rations en mode binaire e 7. La premi`re lecture ou ´criture e e e e ult´rieure se fera ` partir de la nouvelle position. La principale application des fonctions de positionnement est la r´alisation de ce qu’on appelle l’acc`s relatif 60 ou parfois acc`s direct aux composantes d’un fichier : e e e l’acc`s ` une composante (on dit un enregistrement) que ce soit pour le lire ou pour l’´crire. fichier).. 0L.. . long deplacement. fseek(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... la fin ou le d´but du fichier. 2004 97 . } ARTICLE.´ 7 ENTREES-SORTIES 7.. n * sizeof(ARTICLE). Cela s’obtient g´n´ralement en d´clarant une structure qui repr´sente un article. e void fsetpos(FILE *flot.. fpos t *ptr) Place dans ptr la position courante dans le flot indiqu´ en vue de son utilisation par setpos. Un appel de cette fonction sur un fichier en ´criture produit la vidange du tampon (fseek implique e fflush). Un fichier destin´ ` ˆtre utilis´ en acc`s relatif sera d´clar´ normalement.. .h>) : SEEK SET : base = le d´but du fichier e SEEK CUR : base = la position courante SEEK END : base = la fin du fichier La fonction renvoie z´ro en cas de succ`s. Garreta. e e e e e e e Pour donner une signification utilisable ` la notion de  neme article .. cette fonction positionne le flot indiqu´ ` l’emplacement ea correspondant. 1.. SEEK_SET). la valeur de cet argument doit ˆtre une des constantes (d´finies e e dans <stdio. e a a e void fgetpos(FILE *flot. l’op´ration  lecture de l’enregistrement de rang n  se e e a e e programme alors selon le sch´ma suivant : e ARTICLE article. int origine) Cette fonction positionne le pointeur du fichier associ´ au flot indiqu´. champs dont se composent tous les articles . 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. Le type e fpos t (d´fini dans <stdio. Un appel de cette fonction e e e ´quivaut ` fseek(flot.. SEEK SET) suivi de la mise ` z´ro de tous les indicateurs d’erreur. const fpos t *ptr) La valeur de ptr provenant d’un appel de getpos. "rb+"). Lire ci-apr`s les explications sur l’acc`s relatif aux fichiers. e e void rewind(FILE *flot) Positionnement au d´but du fichier du pointeur de fichier associ´ au flot indiqu´.. C’est l’argument e e origine qui indique de quelle base il s’agit ... sizeof(ARTICLE). 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 .2 Positionnement dans les fichiers int fseek(FILE *flot. . Sur un fichier de texte. exploitation des valeurs des champs de article . e e e c H.h>) a une taille suffisante pour contenir une telle information. selon le mod`le : e e e e e typedef struct { .. le fichier restera une suite e e d’octets). il e e vaut mieux utiliser getpos et setpos. fichier = fopen(nom . Si les articles sont num´rot´s ` partir de z´ro. ` ´ ´ L’acces relatif aux elements des fichiers. L’op´ration  ´criture de la composante de rang n  ob´it au sch´ma e e e e 60 L’acc`s relatif s’oppose ` l’acc`s s´quentiel. une valeur non nulle en cas d’erreur. 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 ou r´ciproquement.. Attention. Garreta. soit par un appel d’une fonction de positionnement comme fseek.4. suivi d’un appel de fwrite pour ´crire les nombre octets effectivement lus. Elle se compose d’un appel de fread demandant la lecture de 512 octets. 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. Supposons que le e 98 c H. "wb"). fclose(dest). main() { char tampon[512]. 512. nombre. 7. il appartient au programmeur de pr´voir l’´criture effective du tampon. fichier). puisqu’il y a e e e toujours un appel de fseek entre deux acc`s au fichier. 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. . printf("source : ")... . affectation des champs de article . "rb"). gets(tampon). ıfs aussi bien pour les fichiers de texte que pour les fichiers binaires. e 7. 1. fseek(fichier. if (srce == NULL) return ERREUR_OUVERTURE. Notez que cette contrainte est satisfaite si on pratique des acc`s relatifs selon les sch´mas indiqu´s ci-dessus.7. int nombre. sizeof(ARTICLE). g´n´ralement e e une fonction du syst`me d’exploitation fait ce travail) : e #include <stdio. 1. fwrite( & article. printf("destination : ").. dest). 2004 0 1 2 /* codes conventionnels */ /* ` l’usage du syst`me */ a e /* d’exploitation */ .1 Fichiers “en vrac” Le programme suivant fait une copie identique d’un fichier (ce programme n’est pas bien utile.4 Exemples 7 ´ ENTREES-SORTIES ARTICLE article. 1. *dest. srce)) > 0) fwrite(tampon. fclose(srce). } L’essentiel de ce programme est sa boucle while. return PAS_D_ERREUR. while ((nombre = fread(tampon. srce = fopen(tampon.4 Exemples Les programmes suivants sont assez na¨ .h> #define PAS_D_ERREUR #define ERREUR_OUVERTURE #define ERREUR_CREATION FILE *srce. quel que soit leur mode. SEEK_SET). gets(tampon). soit par un appel e e e explicite de la fonction fflush.. dest = fopen(tampon. if (dest == NULL) return ERREUR_CREATION. ils se proposent d’illustrer les fonctions de traitement de fichiers. n * sizeof(ARTICLE).

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

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

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

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

sauf quelques exceptions. e Exemples (avec des noms de fichier en syntaxe UNIX) : #include <stdio. ses actions semblent en faire partie.h" 61 Notez bien qu’il ne s’agit pas de fournir les fonctions de la biblioth`que (cela est fait par l’apport du fichier objet au moment de e l’´dition de liens) mais uniquement de donner l’information requise pour que les appels de ces fonctions puissent ˆtre correctement e e traduits par le compilateur. 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. Pour cette raison on ne trouve jamais dans les fichiers en-tˆte ni des variables non externes. ifndef. – le corps de la directive. il ne mentionne pas le chemin d’acc`s au fichier.) . 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. mais il faut sae e e voir que le pr´processeur est un programme s´par´. Pour que les appels de ces fonctions puissent ˆtre correctement compil´s61 dans des programmes qui e e les utilisent. 2004 103 . largement ind´pendant du compilateur. Garreta. ce doit ˆtre un fichier source ´crit en C.h (pour header. la biblioth`que math´matique. e e La deuxi`me forme est : e #include <nom-de-fichier > Ici. En particulier le e e e e pr´processeur ne connaˆ pas la syntaxe du langage C. ce qui en facilite la e e maintenance. Ce sont des ´l´ments importants du langage mais. e e e – souvent. structures. if. Ainsi ces d´finitions sont ´crites dans un seul fichier. e Cette directive poss`de deux formes : e #include "nom-de-fichier " C’est la forme g´n´rale. e 8. le nom est incomplet . doit occuper la premi`re position de la ligne) . en syntaxe originale.h> #include "/users/henri/projet_grandiose/gadgets. Nous e passerons sous silence quelques autres directives mineures comme pragma. Les transformations qu’il effectue sur un programme e ıt sont simplement des ajouts. etc. dans l’ordre : e e e – un signe # (qui. e e e – plus rarement. 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. quelques d´clarations de variables externes.sorties. e G´n´ralement associ´es au processus de la compilation. ifdef.1 Inclusion de fichiers La possibilit´ d’inclure des fichiers sources dans d’autres fichiers sources s’av`re tr`s utile. else et endif qui identifie la directive. Elles sont essentiellement compos´es de fonctions utilitaires d´j` compil´es dont seul le fichier objet e ea e est fourni.1. – un mot-cl´ parmi include. etc. un certain nombre de fichiers en-tˆte sont livr´s avec les biblioth`ques. e e – parfois. comprenant e e e – principalement. ni le corps e des fonctions. des suppressions et des remplacements de morceaux de texte nullement astreints ` a correspondre aux entit´s syntaxiques du langage.).1 Le pr´processeur e Le pr´processeur transforme le texte source d’un programme avant que la compilation ne commence. 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 . 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. e ee on peut s’en passer aussi longtemps qu’on limite la pratique de C ` des exercices de petite taille. union et typedef qui d´finissent e e les constantes et les types n´cessaires pour utiliser la biblioth`que en question . define. e Les directives destin´es au pr´processeur comportent. a 8. les prototypes des fonctions qui forment la biblioth`que ou qui incarnent une fonctionnalit´ e e particuli`re (les entr´es . Ce sont des fichiers dont le nom se termine par . la gestion des chaˆ e e ınes de caract`res. c H. line ou error . 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 .´ ´ 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. un ensemble de directives #define et de d´clarations struct. dont la structure d´pend de la nature de celle-ci. 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 e le corps de la macro s’´tend jusqu’` la fin de la ligne. Elles se d´finissent par des expressions de la forme : e #define nom(ident 1 . short *) R´sultat de la substitution (on dit aussi d´veloppement) de la macro : e e { short * w_. 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. e e 65 Dans cet exemple.. a = b. t[j] = w_. ils apparaissent aussi dans le corps de celle-ci. } 62 Le mot macro est une abr´viation de l’expression  macro-instruction  d´signant un m´canisme qui existe depuis longtemps e e e dans beaucoup d’assembleurs. mais cette structure est parfaitement indiff´rente au pr´processeur. texte k ) Cette expression sera remplac´e par le corps de la macro. e 63 Mais il d´coule de la section 1. 2004 . Par exemple. . Voici une erreur que l’on peut faire : #define NBLIGNES 15. t[j]. il sera remplac´ e u ea e par le corps de la macro. e e 104 c H. les u ıt e chaˆ ınes de caract`res et les occurrences o` le nom de la macro est coll´ ` un autre identificateur). ` la place de a double matrice[NBLIGNES][NBCOLONNES]. les accolades { et } donnent au corps de la macro une structure de bloc qui sera comprise et exploit´e par e le compilateur. type) { type w_. 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 ee e ident 2 par texte 2 . etc. car le compilateur ne signalera ici rien de particulier.1. b. Les utilisations (on dit les appels) de la macro se font sous la forme nom(texte 1 . . 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). Exemple65 : #define permuter(a. Garreta. b = w_. Il ne faut pas confondre inclusion de fichiers et compilation s´par´e. cette expression qui paraˆ sans d´faut aura ´t´ transform´e par le pr´processeur en64 ıt e ee e e double matrice[15. e Attention. Ainsi. C’est une erreur difficile ` d´celer. dans lequel ident 1 aura ´t´ remplac´ par texte 1 . Macros avec arguments. ces remplacements se feront avant que la compilation ne commence. w_ = t[i]. Mais plus loin il donnera a e pour erron´e une d´claration comme : e e double matrice[NBLIGNES][NBCOLONNES]. e e 64 Pour permettre de trouver ce genre d’erreurs. Les identificateurs entre parenth`ses sont les arguments e a e de la macro . w_ = a. lequel est un texte quelconque n’ayant ` ob´ir ` aucune syntaxe. e sauf la derni`re. En effet.]. t[i] = t[j]. Le service rendu par la directive #include est de permettre d’avoir un texte en un seul exemplaire.][(2 * 15.2. se termine par le caract`re \. 8.. certains compilateurs C poss`dent une option (sous UNIX l’option -E) qui produit e l’affichage du programme source uniquement transform´ par le pr´processeur. (il remplacera imm´diatement 2 * 15 par 30). non de permettre de le compiler une seule fois. 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).2 D´finition et appel des “macros” e Les macros sans argument62 se d´finissent par la directive : e #define nom corps Le nom de la macro est un identificateur.1 que le corps d’une macro peut occuper en fait plusieurs lignes : il suffit que chacune d’elles. Comme pr´c´demment. Partout e a dans le texte o` le nom de la macro apparaˆ en qualit´ d’identificateur (ce qui exclut les commentaires. le compilateur trouvera double matrice[15][(2 * 15)]. Le corps de la macro s’´tend jusqu’` la fin de la ligne63 . Au moins d’un point de vue logique.1 Le pr´processeur e 8 ´ ´ AUTRES ELEMENTS DU LANGAGE C Attention.. } Exemple d’appel : permuter(t[i]. #define NBCOLONNES (2 * NBLIGNES).8.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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