Vous êtes sur la page 1sur 214

Le langage ISO C et son environnement

Versions C90 et C99 Jean-Paul R IGAULT


V 2.1 7 fvrier 2010

cole polytechnique de luniversit de Nice Sophia Antipolis Dpartement de sciences informatiques 930 route des Colles 06903 S OPHIA A NTIPOLIS Cedex, France Tl: +33 4 92 96 50 50 Fax: +33 4 92 96 50 55 URL : http://www.polytech.unice.fr/informatique

Avant-propos
n par Dennis langage C sest impos Conu laune des annesen1960bien entenduRitchie, lepartie au succs dUnix, comme rfrence matire de programmation-systme et de ralisation de logiciel de base. Ceci est li pour systme dexploitation pour lequel C a t conu. Mais cest aussi le rsultat des vertus intrinsques du langage : sufsamment simple pour tre efcace et portable, sufsamment moderne pour tre expressif. Cette origine de langage de programmation-systme a longtemps confr C la rputation dun langage laxiste , dans lequel le typage ntait quune contrainte surajoute, presque dcorative. Les habitudes et les exigences de programmation aidant, il tait ncessaire de rendre plus rigoureux loutil. Cest cette tche que sest consacr le comit de normalisation x3j11 de lansi [2]. Le rsultat fut un langage plus sr, dot du typage fort garant dune certaine scurit de programmation, sans que cela nuise lefcacit des programmes gnrs. Cest ce langage, dit ansi C, adopt par lansi en 1989 puis par liso en 1990 (iso/iec 9899:1990) qui est actuellement la norme de fait. Nous le dsignerons simplement par C90. Cette norme a subi quelques rvisions mineures jusqu ce que lvolution parallle de la normalisation du langage C++ oblige une reprise profonde de la norme. Ceci conduit en 1999, la norme iso/iec 9899:1999 reprise par lansi en 2000. Le nouveau langage, dit C99, nest pas encore ce jour (janvier 2010) compltement implment par tous les compilateurs, bien quun large sous-ensemble le soit gnralement. Le prsent texte dcrit la fois iso C90 et iso C99, en signalant les diffrences entre les deux versions. Ces diffrences sont rsumes dans lannexe 12. Sauf mention explicite du contraire, les programmes proposs sont compatibles avec les deux versions. Pour les dtails, voir la section 1.5. Tous les exemples de cet ouvrage ont t dvelopps et tests dans un environnement tout fait habituel dans les tablissements denseignement et de recherche : stations de travail sous Linux, environnement graphique X Window. Le compilateur ansi C utlis comme rfrence est celui de gnu d initialement Richard Stallman (et la Free Software Foundation), savoir gcc [1, 23]. Les programmes devraient tre compilables avec les versions de gcc partir de 2.95 au moins mais cest seulement partir des versions 3.x que lon a la compatibilit avec C99 (option -std=c99). La compilation seffectue sous le contrle de make (version de gnu l encore). La mise au point utilise gdb [19], autre produit de la Free Software Foundation, utilis travers lune de ses interfaces avec X Window (principalement ddd [20]). Ont galement t utiliss les analyseurs dexcution gprof [21] et

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

Avant-propos

valgrind [15]. Cet environnement de travail est dcrit dans le chapitre 10 o

sont galement introduits certains environnements intgrs de dveloppement pour C, sous Unix ou MS Windows. A Le document lui-mme a t compos laide de L TEX [10, 26] (distribution TEXLive 2009) avec les extensions Babel [7] et frenchb [5] pour traiter la typographie franaise, ainsi que xindy [14] pour gnrer lindex comportant des caractres accentus. Cet ouvrage est le fruit de prs de trente annes denseignement de C et dUnix, lEcole des mines de Paris, au cerics (Centre dEnseignement et de Recherche en Informatique, Communication et Systmes), lESSI (cole Suprieure de Sciences Informatiques de luniversit de Nice Sophia Antipolis, devenue Polytech,Nice-Sophia) et au cours de nombreuses sessions de formation continue inter- et intra-entreprises que jai animes. Je tiens donc remercier tous les collgues, tudiants et participants ces formations pour leur intrt, leur patience, et leur motivation.

Jean-Paul Rigault Professeur dinformatique Universit de Nice Sophia Antipolis Sophia Antipolis et La Roquette sur Siagne Fvrier 1993Janvier 2010

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Table des matires


Avant-propos Table des matires Table des gures Liste des tableaux Liste des programmes Liste des exercices 1 Introduction 1.1 Historique . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Caractristiques de C . . . . . . . . . . . . . . . . . . . . 1.2.1 Un langage contemporain de Pascal . . . . . . 1.2.2 Un langage dimplmentation de systmes . . . 1.2.3 Un langage supportant la compilation spare . 1.2.4 Un langage incomplet. . . pour tre portable . . 1.3 Plan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4 Notations utilises . . . . . . . . . . . . . . . . . . . . . 1.5 Compilateurs et compatibilit . . . . . . . . . . . . . . . 1.5.1 Environnement de dveloppement de rfrence 1.5.2 Version du langage C . . . . . . . . . . . . . . . 1.6 Ressources : ouvrages et sites Web sur C . . . . . . . . 1.7 Exercices du chapitre 1 . . . . . . . . . . . . . . . . . . . 2 Premiers pas 2.1 Monde, salut ! . . . . . . . . . . . . . . . . . . . . . . . 2.2 Boucle while ; entres-sorties simples . . . . . . . . . 2.3 Fonctions ; expressions ; entres-sorties . . . . . . . . 2.3.1 Exemple : calcul de la racine carre dun rel . 2.3.2 Entres-sorties formattes . . . . . . . . . . . . 2.4 Tableaux et instruction ditration . . . . . . . . . . . 2.5 Arguments du shell ; fonction rcursive . . . . . . . . 2.6 Exercices du chapitre 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 3 9 11 13 15 17 17 18 18 18 18 18 19 19 20 20 21 21 22 25 25 27 29 29 33 35 38 41

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

TABLE DES MATIRES

3 Bases du langage 3.1 Elments lexicaux . . . . . . . . . . . . . . . . . . . . . . 3.1.1 Jeu de caractres . . . . . . . . . . . . . . . . . . 3.1.2 Structure lexicale dun chier-source . . . . . . 3.1.3 Commentaires . . . . . . . . . . . . . . . . . . . . 3.1.4 Identicateurs . . . . . . . . . . . . . . . . . . . . 3.1.5 Mots-cls . . . . . . . . . . . . . . . . . . . . . . . 3.1.6 Constantes littrales arithmtiques . . . . . . . . 3.1.7 Chanes de caractres littrales . . . . . . . . . . 3.2 Types scalaires et dclarations simples . . . . . . . . . . 3.2.1 Panorama des types de C . . . . . . . . . . . . . 3.2.2 Type vide (void) . . . . . . . . . . . . . . . . . . 3.2.3 Types de base entiers . . . . . . . . . . . . . . . . 3.2.4 Types rels . . . . . . . . . . . . . . . . . . . . . . 3.2.5 Dnitions dobjets de type de base scalaire . . 3.2.6 Types numrs . . . . . . . . . . . . . . . . . . . 3.2.7 Synonymie de types : typedef . . . . . . . . . 3.3 Oprateurs . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3.1 Oprateurs arithmtiques . . . . . . . . . . . . . 3.3.2 Oprateurs relationnels et logiques . . . . . . . 3.3.3 Oprateurs bit bit . . . . . . . . . . . . . . . . . 3.3.4 Affectations . . . . . . . . . . . . . . . . . . . . . 3.3.5 Oprateurs sur les pointeurs . . . . . . . . . . . 3.3.6 Oprateurs sur les types . . . . . . . . . . . . . . 3.3.7 Oprateurs divers . . . . . . . . . . . . . . . . . 3.4 Evaluation des expressions . . . . . . . . . . . . . . . . 3.4.1 Ordre dvaluation : prcdence et associativit 3.4.2 Conversions . . . . . . . . . . . . . . . . . . . . . 3.4.3 Mlange de types dans les expressions . . . . . 3.5 Instructions et ot de contrle . . . . . . . . . . . . . . . 3.5.1 Instruction simple et bloc . . . . . . . . . . . . . 3.5.2 Instructions de slection . . . . . . . . . . . . . . 3.5.3 Instructions de boucle . . . . . . . . . . . . . . . 3.5.4 Instructions de rupture de squence . . . . . . . 3.6 Exercices du chapitre 3 . . . . . . . . . . . . . . . . . . . 4 Tableaux, structures et unions 4.1 Tableaux . . . . . . . . . . . . . . . . . . . . . . 4.1.1 Tableaux mono-dimensionnels . . . . . 4.1.2 Tableaux multi-dimensionnels . . . . . 4.1.3 Chanes de caractres . . . . . . . . . . 4.1.4 Tableaux de taille variables de C99 . . . 4.2 Structures . . . . . . . . . . . . . . . . . . . . . 4.2.1 Dnition du type et dclarations . . . 4.2.2 Structures compactes et champs de bits 4.2.3 Oprations sur les structures . . . . . . 4.2.4 Tableaux de structures . . . . . . . . . . 4.3 Unions . . . . . . . . . . . . . . . . . . . . . . . 4.3.1 Dnition du type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

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

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

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

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

43 43 43 44 45 45 46 46 48 50 50 50 51 53 53 54 55 56 56 58 60 63 64 64 65 66 66 68 70 71 71 72 75 77 79 83 83 83 86 88 88 90 90 93 94 95 96 96

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

TABLE DES MATIRES

4.4

4.3.2 Oprations sur les unions . . . . . . . . . . . . . . . . . 4.3.3 Structures avec variantes . . . . . . . . . . . . . . . . . Exercices du chapitre 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

97 97 100 103 103 103 105 106 108 109 110 110 111 118 118 119 122 129 129 130 130 130 131 131 131 132 134 134 135 135 135 135 137 137 138 138 138 139 141 141 141 142 142 143 143 144

5 Pointeurs 5.1 Dclarations et oprations sur pointeurs . . . . . . . . . . . . 5.1.1 Pointeurs simples . . . . . . . . . . . . . . . . . . . . . 5.1.2 Pointeurs multiples . . . . . . . . . . . . . . . . . . . . 5.1.3 Oprations arithmtiques sur pointeurs . . . . . . . . 5.1.4 Conversions de pointeurs . . . . . . . . . . . . . . . . 5.1.5 Pointeurs et constantes . . . . . . . . . . . . . . . . . 5.2 Pointeurs sur structures et unions . . . . . . . . . . . . . . . 5.2.1 Oprateur de slection che . . . . . . . . . . . . 5.2.2 Types rcursifs . . . . . . . . . . . . . . . . . . . . . . 5.3 Pointeurs et tableaux . . . . . . . . . . . . . . . . . . . . . . . 5.3.1 Tableaux de pointeurs ; lecture des dclarations de C 5.3.2 Relations entre tableaux et pointeurs . . . . . . . . . 5.4 Exercices du chapitre 5 . . . . . . . . . . . . . . . . . . . . . . 6 Le prprocesseur ANSI C 6.1 Prtraitement des programmes C . . . . . . . . . . . . . . 6.2 Inclusion de chier . . . . . . . . . . . . . . . . . . . . . . 6.2.1 Effet de linclusion de chier . . . . . . . . . . . . 6.2.2 Recherche du chier inclure . . . . . . . . . . . 6.2.3 Utilisation de linclusion de chier . . . . . . . . . 6.3 Dnition de macros . . . . . . . . . . . . . . . . . . . . . 6.3.1 Macros sans arguments : dnition de constantes 6.3.2 Macros arguments : fonctions en ligne . . . . . 6.3.3 Dnition rcursive de macros . . . . . . . . . . . 6.3.4 Concatnation et stringication . . . . . . . . 6.3.5 Annulation de dnition : #undef . . . . . . . . . 6.3.6 Macros rserves . . . . . . . . . . . . . . . . . . . 6.4 Compilation conditionnelle . . . . . . . . . . . . . . . . . 6.4.1 Les directives de compilation conditionnelle . . . 6.4.2 Inclusion unique de chiers . . . . . . . . . . . . . 6.5 Autres directives du prprocesseur . . . . . . . . . . . . . 6.5.1 Numro de ligne : #line . . . . . . . . . . . . . . 6.5.2 Messages derreur : #error . . . . . . . . . . . . 6.5.3 Commentaire excutable : #pragma . . . . . . . . 6.6 Exercices du chapitre 6 . . . . . . . . . . . . . . . . . . . . 7 Fonctions 7.1 Arguments et valeur de retour . . . . . . . . . . . . . . 7.1.1 Type de retour dune fonction . . . . . . . . . . 7.1.2 Type des arguments dune fonction . . . . . . . 7.1.3 Mode de passage des arguments . . . . . . . . . 7.2 Dclaration, dnition et appel de fonction . . . . . . . 7.2.1 Dclaration du type dune fonction : prototype 7.2.2 Dnition dune fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

TABLE DES MATIRES

7.3

7.4 7.5

7.2.3 Appel dune fonction . . . . . . 7.2.4 Fonctions en ligne de C99 . . Fonctions et pointeurs . . . . . . . . . . 7.3.1 Fonction retournant un pointeur 7.3.2 Pointeurs en argument . . . . . . 7.3.3 Pointeurs sur fonctions . . . . . La fonction main . . . . . . . . . . . . . Exercices du chapitre 7 . . . . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

145 146 146 146 147 148 151 153 155 155 156 156 158 158 158 159 161 161 162 163 164 164 165 166 167

8 Structure des programmes 8.1 Espaces de nommage . . . . . . . . . . . . . . . . . . . . . . . . 8.2 Structure des programmes C . . . . . . . . . . . . . . . . . . . 8.2.1 Compilation spare . . . . . . . . . . . . . . . . . . . . 8.2.2 Unit de compilation . . . . . . . . . . . . . . . . . . . . 8.3 Dure de vie et rgles de visibilit . . . . . . . . . . . . . . . . 8.3.1 Dure de vie . . . . . . . . . . . . . . . . . . . . . . . . . 8.3.2 Visibilit des objets . . . . . . . . . . . . . . . . . . . . . 8.3.3 Initialisation des objets . . . . . . . . . . . . . . . . . . . 8.3.4 Visibilit des fonctions . . . . . . . . . . . . . . . . . . . 8.3.5 Synthse : relation entre dure de vie et visibilit . . . 8.3.6 Autres rgles de visibilit . . . . . . . . . . . . . . . . . 8.4 Programmation modulaire en C . . . . . . . . . . . . . . . . . 8.4.1 Notion de module . . . . . . . . . . . . . . . . . . . . . 8.4.2 Objets globaux trouvs dans une interface de module 8.4.3 Exemple dorganisation modulaire . . . . . . . . . . . . 8.5 Exercices du chapitre 8 . . . . . . . . . . . . . . . . . . . . . . .

9 La bibliothque standard 171 9.1 lments gnraux . . . . . . . . . . . . . . . . . . . . . . . . . 171 9.1.1 Assertions : <assert.h> . . . . . . . . . . . . . . . . . 171 9.1.2 Codes derreur : <errno.h> . . . . . . . . . . . . . . . 171 9.1.3 Dnitions communes : <stddef.h> . . . . . . . . . . 171 9.1.4 Utilitaires gnraux : <stdlib.h> . . . . . . . . . . . 172 9.2 lments numriques . . . . . . . . . . . . . . . . . . . . . . . . 172 9.2.1 Boolens : <stdbool.h> . . . . . . . . . . . . . . . . . 172 9.2.2 Types entiers : <stdint.h> . . . . . . . . . . . . . . . 172 9.2.3 Conversion des types entiers : <inttypes.h> . . . . 172 9.2.4 Environnement pour calcul en nombres rels : <fenv.h> 172 9.2.5 Nombres complexes : <complex.h> . . . . . . . . . . 173 9.3 Fonctions mathmatiques . . . . . . . . . . . . . . . . . . . . . 173 9.3.1 Bibliothque mathmatique de base : <math.h> . . . 173 9.3.2 Bibliothque mathmatique gnrique : <tgmath.h> 173 9.4 Caractres et chanes de caractres . . . . . . . . . . . . . . . . 173 9.4.1 Manipulation de caractres : <ctype.h> . . . . . . . . 173 9.4.2 Manipulation de chanes de caractres : <string.h> 173 9.4.3 Manipulation de caractres tendus: <wctype.h> . . 174 9.4.4 Manipulation de chanes de caractres tendus : <wchar.h> 174 9.4.5 Localisation : <locale.h> . . . . . . . . . . . . . . . . 174 9.5 Entres-sorties : <stdio.h> . . . . . . . . . . . . . . . . . . . 174

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

TABLE DES MATIRES

9.6

Divers 9.6.1 9.6.2 9.6.3 9.6.4 9.6.5

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fonctions nombre variable darguments : <stdarg.h> Date et heure : <time.h> . . . . . . . . . . . . . . . . . Traitement dvnements : <signal.h> . . . . . . . . Points de reprise : <setjmp.h> . . . . . . . . . . . . . Notation alternative de certains oprateurs : <iso646.h> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

174 175 175 175 175 176 177 177 178 178 179 180 182 182 183 184 184 185 189 189 189 189 189 189 189 189 189 189 189 189 189 189 189 191 191 191 191 193 195 203

10 Environnement de dveloppement 10.1 Compilateurs . . . . . . . . . . . . . . . . . . . . . . 10.2 Dveloppement traditionnel sous Unix . . . . . . . 10.2.1 dition de source . . . . . . . . . . . . . . . . 10.2.2 Loutil make . . . . . . . . . . . . . . . . . . . 10.2.3 Mise au point avec gdb et compagnie . . . . 10.2.4 Outils de documentation . . . . . . . . . . . 10.3 Environnements intgrs de dveloppement . . . . 10.3.1 Environnements multi-plateformes . . . . . 10.3.2 Environnements spciques Unix . . . . . 10.3.3 Environnements spciques MS Windows 10.4 Remarque nale . . . . . . . . . . . . . . . . . . . . . 11 Extensions avances de C99 11.1 Prprocesseur . . . . . . . . . . . . . . . . . . . . . 11.1.1 Macros prdnies . . . . . . . . . . . . . . 11.1.2 Macros nombre variable darguments . . 11.2 langage de base . . . . . . . . . . . . . . . . . . . . 11.2.1 Nom de la fonction courante : __func__ . 11.2.2 Pointeurs restreints : restrict . . . . . . 11.2.3 Tableaux . . . . . . . . . . . . . . . . . . . . 11.3 Bibliothque standard . . . . . . . . . . . . . . . . 11.3.1 Fichiers dentte . . . . . . . . . . . . . . . 11.3.2 Type boolen . . . . . . . . . . . . . . . . . 11.3.3 Types entiers tendus . . . . . . . . . . . . 11.3.4 Nombres complexes . . . . . . . . . . . . . 11.3.5 Manipulation des caractres multiples . . 11.3.6 Rels en virgule ottante . . . . . . . . . . . . . . . . . . . . . . . .

12 C traditionnel, iso C90, iso C99 et C++ 12.1 Diso C90 C traditionnel . . . . . . . . . . . . . . . . . . . . . 12.2 Diso C90 iso C99 . . . . . . . . . . . . . . . . . . . . . . . . . 12.3 Diso C90 ou iso C99 C++ . . . . . . . . . . . . . . . . . . . . Bibliographie Glossaire Index

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

TABLE DES MATIRES

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Table des gures


2.1 2.2 3.1 4.1 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 5.10 8.1 8.2 8.3 10.1 10.2 10.3 10.4 10.5 10.6 10.7 10.8 7.1 Algorithme de tri par insertion . . . . . . . . . . . . . . . . . . Arbre de calcul rcursif de fibo(4) . . . . . . . . . . . . . . Reprsentation dune chane de caractres littrale . . . . . . . Exemple de champs de bits . . . . . . . . . . . . . . . . . . . . Pointeur simple . . . . . . . . . . . . . . . . . . . . . . . . . . . Pointeur double . . . . . . . . . . . . . . . . . . . . . . . . . . . Liste simplement chane : simple_list.h . . . . . . . . . . Insertion dans une liste avec un simple pointeur . . . . . . . . Insertion dans une liste avec un double pointeur . . . . . . . . Relation entre pointeurs et tableaux mono-dimensionnels en C Tableau multi-dimensionnel et tableau de pointeurs . . . . . . Liste doublement chane avec cellule de garde et rebouclage Structure dun arbre binaire . . . . . . . . . . . . . . . . . . . . Un exemple darbre binaire . . . . . . . . . . . . . . . . . . . . Compilation spare et units de compilations . . . . . . . . . Visibilit et dure de vie : quelques exemples . . . . . . . . . . Organisation dun module en C . . . . . . . . . . . . . . . . . . XEmacs luvre . . . . . . . . . . . . . . . . . . . . . . . . . ddd (et gdb) luvre . . . . . . . . . . . . . . . . . . . . . . Eclipse en mode dition/compilation de C/C++ . . . . . . . Eclipse en mode mise au point de C/C++ . . . . . . . . . . . Encore un IDE multiplateformes, Code::Blocks . . . . . . . . Un IDE multiplateformes lger, CodeLite . . . . . . . . . . . Visual Studio 2008 en mode dition/compilation de C/C++ Visual Studio 2008 en mode mise au point de C/C++ . . . . . . . . . . . . 37 41 49 93 104 106 112 113 118 120 122 124 126 127 157 163 165 179 181 183 184 185 186 187 187 197

Chane de compilation C . . . . . . . . . . . . . . . . . . . . . .

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

10

TABLE DES FIGURES

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Liste des tableaux


3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 8.1 Jeu de caractres ASCII . . . . . . . . . . . . . . . Mots-cls de C . . . . . . . . . . . . . . . . . . . . Mots-cls supplmentaires de Cnew . . . . . . . Squences dchappement pour les caractres . Panorama des types de C . . . . . . . . . . . . . Sous-types de int . . . . . . . . . . . . . . . . . Liste des oprateurs de C . . . . . . . . . . . . . Prcdence et associativit des oprateurs de C Relations entre dure de vie et visibilit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 46 46 47 51 52 57 67 162

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

V 2.1 7 fvrier 2010

11

c Jean-Paul R IGAULT

12

LISTE DES TABLEAUX

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Liste des programmes


2.1 2.2 2.3 2.4 2.5 2.6 2.7 3.1 3.2 3.3 3.4 3.5 3.6 4.1 4.2 4.3 4.4 5.1 5.2 5.3 5.4 7.1 7.2 8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 Le programme hello, world original . . . . . . . . . Le programme hello, world localis en franais . . . Copie de chiers . . . . . . . . . . . . . . . . . . . . . . Calcul de la racine carre dun nombre rel . . . . . . . Programme simple de lecture de scalaires . . . . . . . . Tri par insertion dune suite dentiers . . . . . . . . . . Calcul du terme de rang n de la suite de Fibonacci . . Remplacement des ns de ligne par un blanc . . . . . . Longueur utile dune chane de caractres . . . . . . . Copie de chanes de caractres . . . . . . . . . . . . . . Calcul du pgcd de deux entiers . . . . . . . . . . . . . . Pathologie du dcalage . . . . . . . . . . . . . . . . . . . Manipulation de bits . . . . . . . . . . . . . . . . . . . . Produit scalaire de deux vecteurs . . . . . . . . . . . . . Transposition dune matrice . . . . . . . . . . . . . . . . Tableaux dimension variable de C99 . . . . . . . . . . Structure avec variante (en C99) . . . . . . . . . . . . . Liste dentiers : dnition du type . . . . . . . . . . . . Liste dentiers : insertion par ordre croissant . . . . . . Liste dentiers : programme principal . . . . . . . . . . Insertion dans une liste avec un seul (double) pointeur Les pointeurs sur fonction dans tous leurs tats . . . . La commande echo modie . . . . . . . . . . . . . . . Espaces de nommage en C . . . . . . . . . . . . . . . . . Visibilit des objets locaux . . . . . . . . . . . . . . . . . Visibilit et dure de vie : fic1.c . . . . . . . . . . . . Visibilit et dure de vie : fic2.c . . . . . . . . . . . . Visibilit des noms de type en ansi C . . . . . . . . . . Spcication du module pile dentiers . . . . . . . . Corps du module pile dentiers . . . . . . . . . . . . Utilisation du module pile dentiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 26 27 30 34 36 39 47 49 50 59 61 62 84 87 89 98 112 114 116 117 150 152 156 159 163 163 164 167 168 169

V 2.1 7 fvrier 2010

13

c Jean-Paul R IGAULT

14

LISTE DES PROGRAMMES

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Liste des exercices


1.1 1.2 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 3.1 3.2 3.3 3.4 3.5 4.1 4.2 4.3 4.4 4.5 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 7.1 7.2 8.1 8.2 8.3 Vrications prliminaires . . . . . . . . . . . . . . Accs la documentation . . . . . . . . . . . . . . Mise en route . . . . . . . . . . . . . . . . . . . . . Copie de chiers . . . . . . . . . . . . . . . . . . . La commande iota . . . . . . . . . . . . . . . . . . Minimum et maximum dun ensemble dentiers . Conversion Celsius-Farenheit . . . . . . . . . . . Drcursiver le calcul de la suite de Fibonacci Amlioration du calcul de la racine carre . . . . Mauvais format . . . . . . . . . . . . . . . . . . . . Nombre de bits 1 dans un octet . . . . . . . . . . Nombre de caractres, mots et lignes dun texte . valuation dun polynme . . . . . . . . . . . . . . Nombres premiers . . . . . . . . . . . . . . . . . . Quelques oprations non portables . . . . . . . . . Manipulation de bits . . . . . . . . . . . . . . . . . Calcul matriciel simple . . . . . . . . . . . . . . . . Recherche en table . . . . . . . . . . . . . . . . . . Oprations sur les dates . . . . . . . . . . . . . . . Gestion de liste de contacts . . . . . . . . . . . . . Liste trie simplement chane . . . . . . . . . . . Liste trie doublement chane . . . . . . . . . . . Manipulation de chanes de caractres . . . . . . . Tri dun tableau de pointeurs . . . . . . . . . . . . Commande fgrep . . . . . . . . . . . . . . . . . . . Arbre binaire . . . . . . . . . . . . . . . . . . . . . Recherche dichotomique . . . . . . . . . . . . . . . Pathologie des pointeurs . . . . . . . . . . . . . . . Intgration par la mthode des trapzes . . . . . . Efcacit des fonctions inline . . . . . . . . . . . . Modication de limplmentation des piles . . . . File fifo . . . . . . . . . . . . . . . . . . . . . . . . Du module au type abstrait . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 23 41 41 42 42 42 42 42 42 79 80 80 80 80 100 100 100 101 101 122 123 123 125 126 126 127 127 153 153 167 167 167

V 2.1 7 fvrier 2010

15

c Jean-Paul R IGAULT

16

LISTE DES EXERCICES

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Chapitre 1

Introduction
1.1 Historique
Le langage C trouve son origine au dbut des annes 1970, lorsque Dennis Ritchie rejoint Ken Thomson aux Bell Laboratories. Ce dernier a dj dni en 1970 le langage B, driv de BCPL, pour implmenter de manire portable le systme dexploitation quil vient dinventer et quil a dnomm Unix. En 1972, C voit le jour et est utilis pour rcrire compltement le noyau dunix (en un t!). Le langage recevra une dnition de rfrence par la publication du livre de Brian Kernighan et Dennis Ritchie en 1978 [16]. Le langage dcrit dans ce livre, avec quelques extensions ultrieures (affectation et passage en arguments de structures et dunions, introduction du type void), constitue ce quil est convenu dappeler le C traditionnel . Le dveloppement considrable de C, dabord entrain par celui dUnix puis relativement autonome, rendit ncessaire une normalisation qui fut entreprise partir de 1983 par le comit x3j11 de lansi 1 . Les travaux se terminrent n 1988, par une norme ansi reprise par ieee puis par liso (norme iso-9899). La deuxime dition du livre de Kernighan et Ritchie [17] dcrit le langage ainsi normalis. La compatibilit arrire a t lun des soucis de lansi, et la plupart des programmes crits en C traditionnel peuvent tre recompils avec ansi C. Mais, sans rien changer la philosophie du langage, ansi C a apport de nombreuses amliorations allant la fois dans le sens de la scurit de programmation (typage fort, meilleure rigueur smantique) et dans celui de la portabilit (par exemple dnition de la bibliothque dexcution minimale). La version C99 est galement largement compatible avec iso C90. Malheureusement de nombreux compilateurs C ne la supporte pas encore compltement. Ainsi, parmi les compilateurs majeurs, gcc 3 et 4 supportent lessentiel de C99, mais pas la totalit et Microsoft Visual Studio 2005 (C++ version 8) et 2008 (version 9) ne prtendent mme pas supporter cette norme mme sils en ont des parties du fait de leur compatibilit C++. Bref, C99 nest pas encore trs rpandu dix ans aprs sa normalisation ! Cest pourquoi tous les programmes donns ici, sauf mention spciale, sont compatibles avec C90 aussi bien quavec C99.
1. American National Standards Institute

V 2.1 7 fvrier 2010

17

c Jean-Paul R IGAULT

18

1. Introduction

1.2 Caractristiques de C
1.2.1 Un langage contemporain de PASCAL
C a t conu peu prs en mme temps que des langages comme Pascal et il rete donc ltat de lart de lpoque. Cest donc un langage qui supporte la programmation structure (on dit aussi procdurale). Il dispose en particulier de types structurs dnissables par lutilisateur, analogues aux records de Pascal.

1.2.2 Un langage dimplmentation de systmes


Cependant C traditionnel garde de son origine de langage dimplmentation de systme dexploitation un certain nombre de caractristiques de bas niveau : instructions proches du langage-machine, manipulations dadresses au travers de pointeurs, typs certes, mais non controls, typage laxiste permettant pratiquement tous les mlanges. . . du moment que le compilateur est capable de gnrer du code ! partage de la mme zone de mmoire par des objets de types diffrents (unions). . . ansi C a ragit contre la plupart de ces licences tout en prservant la souplesse indispensable un langage de programmation-systme. Les possibilits prcdentes et parfois dangereuses de C restent utilisables en ansi C condition dtre explicites.

1.2.3 Un langage supportant la compilation spare


Conu pour raliser des programmes dont la taille pouvait tre relativement importante (un systme dexploitation tout entier), C se devait de supporter demble la compilation spare et la modularit. Cependant lapproche de la modularit est la mme dans les versions iso quen C traditionnel. Elle reste donc assez archaque si on la compare aux langages modernes comme Ada ou certains langages objets.

1.2.4 Un langage incomplet. . . pour tre portable


La portabilit est en fait lorigine de C : cest une des motivations pour crire le noyau dun systme dexploitation avec un langage de haut niveau. Le rsultat est un succs, et ce malgr lexistence des caractristiques de bas niveau dj mentionnes (1.2.2). Mais pour assurer cette portabilit les concepteurs du langage ont d en exclure toutes les fonctionnalits qui leur paraissaient trop dpendantes des machines ou des systmes. Cest ainsi, par exemple, que C ne comporte aucune instruction dentre-sortie, de manipulation de chanes de caractres, de gestion du paralllisme ou de la concurrence, ni de communication entre processus. Cette approche de la portabilit peut apparaitre paradoxale. Aprs tout, un langage comme Java assure sa portabilit de manire exactement inverse, en

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

1.3. Plan

19

rednissant de manire indpendante de la plate-forme les fonctions du systmes dexploitation (entres-sorties, gestion de chiers, de mmoire, gestion des threads , etc.), et jusquau processeur lui-mme (grce la machine virtuelle) ! Cependant, noublions pas que C est lorigine un langage conu pour raliser des systmes dexploitation ce qui, nous lavons vu, lamne proposer des fonctionnalits de bas niveau . Conjuger portabilit et bas niveau conduit donc ce minimalisme de C. Par ailleurs, lpoque, un des slogans favoris tait small is beautiful ! Ces fonctionnalits indispensables sont dlgues un bibliothque dexcution standard (la clbre standard library). ansi C a fait un gros effort pour normaliser cette bibliothque, au moins dans ses fonctionnalits minimales.

1.3 Plan
Le chapitre 2 permet, travers une srie dexemples simples, de faire un tour rapide du langage C. Les chapitres 3, 4 et 5 reprennent en dtail respectivement la description du langage de base (types, oprateurs et expressions, instructions de contrle), celle des agrgats (tableaux, structures et unions) et enn celle des pointeurs. Les chapitres 7 et 8 dcrivent en dtail la notion de fonction et la structure des programmes. Y est galement aborde la programmation modulaire en C. Le prprocesseur ansi C est tudi en dtail au chapitre 6. Lenvironnement dexcution avec une description succinte des bibliothques standards est lobjet du chapitre 9, et lenvironnement de dveloppement celui du chapitre 10. Enn le chapitre 11 rsume les extensions introduites par C99 et qui nont pas t traites dans le reste du texte. Quant au chapitre 12 il synthtise les diffrences entre C traditionnel , C90, C99 et C++.

1.4 Notations utilises


Les noms de systmes, langages ou organisations sont composs en petites capitales : Linux, Algol, iso. . . Les noms propres de personnes sont galement en petites capitales, avec des majuscules, comme pour Dennis Ritchie, Richard M. Stallman. . . Nous utilisons une police oblique pour les mots anglais ou techniques non traduits, comme par exemple integer ou lvalue. La police grasse sans empattement sert pour les noms de commandes au shell comme ls ou gcc. Les (brefs) extraits de programme insrs dans le texte sont composs avec une police chasse fixe et les identicateurs sont en police grasse, comme dans
while (*p1 != \0) *p2++ = *p1++;

Dans ces extraits, les identicateurs composs en police oblique, comme instruction et condition dans lextrait suivant, sont remplacer par des expressions ou instructions terminales :
while (condition) instruction

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

20

1. Introduction

Les programmes complets (ceux qui gurent dans la Liste des programmes, A page 13) sont afchs par le paquettage L TEX listings. Ils sont donc dcors syntaxiquement pour mettre en vidence les mots-cls, chanes de caractres littrales, constantes, etc. En outre, les lignes sont numrotes. Le programme 2.1 du chapitre suivant en fournit un exemple. On pourra stonner de ne pas trouver de caractres accentus dans ces programmes. Cela est d au fait que jutilise le codage des caractres Unicode (utf-8 pour tre prcis) qui nest correctement A support ni par le paquettage listings de L TEX ni par les compilateurs C90. En revanche, nous verrons en 11.3.5 quun certain support est prvu en C99. Lorsque lon prsente un exemple dinteraction entre lutilisateur et le systme, on suppose toujours que cette interaction a lieu sous un des shell dUnix (ou de Linux, de Cygwin. . .). La syntaxe suppose le shell du Steve Bourne sh ou lun de ses descendants comme bash, ksh, ou zsh. . . (personnellement jutilise zsh). Linteraction est prsente comme suit :
% ls -F Stack CVS/ Makefile % Stack* Stack.c Stack.h Stack_main.c

Ici % reprsente la chane de sollicitation (le prompt) envoy par le shell, le texte en oblique gras (comme ls -F Stack) est celui entr par lutilisateur, et le texte droit (comme CVS/) celui mis par le systme. Les caractres de contrle comme ctrl-d (simulation de n de chier au terminal sous Unix) sont reprsents sous la forme usuelle utilisant le prxe ^ (D). Par ailleurs lorsque ces exemples utilisent des accents, ils supposent un systme Unix ou Linux correctement congur. Ainsi la variable denvironnment LANG doit prciser un encodage 8 bits comme iso88591, iso88591@euro, ou utf8. Par exemple, la valeur de ma propre variable LANG est en_US.UTF-8 (je naime pas avoir les messages du systme en franais !).

1.5 Compilateurs et compatibilit


1.5.1 Environnement de dveloppement de rfrence
Lenvironnement de dveloppement de rfrence utilis dans ce document est le suivant (dcembre 2010) : processeur intel Core Duo 2.6 Mhz quad, mmoire centrale 4 Goctets, 2 disques durs 500 Goctets (SATA, 7200 tr/min) ; systme dexploitation Linux (Fedora11, noyau 2.6.30, glibc 2.10), systmes de chiers ext3 ; environnement graphique X Window 11 et gestionnaire gnome 2.26 ; diteur emacs 23.1, gnu make 3.81, mise au point avec gdb 6.8 ; compilateur gcc 4.4.3. Les programmes se compilent et fonctionnent galement sous MacOs X (versions Leopard et Snow Leopard) avec le compilateur fourni par Apple (gcc4.2) comme avec les compilateurs gcc-4.4.x. En ce qui concerne Microsoft Windows, les programmes ont t compils et excuts avec succs en utilisant Cygwin et gcc-3.4), et aussi avec les compilateurs de Microsoft Visual Studio 2005 et 2008 (en mode C++). . Voir le chapitre 10 pour les dtails.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

1.6. Ressources : ouvrages et sites Web sur C

21

1.5.2 Version du langage C


Ce document dcrit la fois les normes iso C90 et iso C99, ce qui veut dire que, sauf mention explicite, le texte sapplique aux deux versions. Lorsque lon veut attirer lattention sur une proprit du langage C diffrente entre C90 et C99, on utilise une marque marginale comme pour ce paragraphe. Si toute une section ne sapplique qu C99, son titre est ainsi. Enn, certaines extensions lourdes de C99 ne seront pas utilises dans ce cours, mais elles sont rsumes au chapitre 11. Le compilateur gcc 4.4 utilis supporte presque entirement (voir [22]) la norme C99 et cest dans ce mode quil est utilis ici (option -std=c99). Cependant, la plupart des exemples de programmes donns dans ce document nutilisent pas sauf mention explicite les fonctionnalits idiosyncratiques de C99 et doivent donc tre compilables galement avec un compilateur C90 (option -ansi de gcc) ou un compilateur C++ raisonnable (comme g++ ou Microsoft Visual C++ dj mentionns). Pour compiler on utilise ici systmatiquement la commande make. Beaucoup de programmes tant rduits une seule unit de compilation (un seul chier source .c), on peut utiliser make sous Unix sans avoir besoin de Makefile. Il suft de dnir les deux variables denvironnement CC et CFLAGS, par exemple en plaant dans lun des chiers dinitialisation du shell 2 :
export CC=gcc export CFLAGS="-g -std=c99 -Wall"

C99

Ensuite, pour compiler un programme tenant tout entier dans un seul chier source, disons prog.c, il suft dexcuter la commande make prog qui va produire (sil ny a pas derreur) un chier excutable de mme nom que le source mais sans lextension .c (donc ici, prog), quil sufra dexcuter son tour :
% make prog % prog ... affichage des resultats de prog ...

Lorsquune Makefile est ncessaire, elle est dcrite dans le texte. En outre on trouvera des complments sur make en 10.2.2.
Avertissements (warnings) lors de la compilation

Noter que nous compilons toujours avec tous les warnings activs (option de compilation -Wall). Un programme C doit tre exempt de tels warnings. Certains peuvent vous sembler sans consquence, mais il faut beaucoup de discernement et dexprience pour en tre sr.

1.6 Ressources : ouvrages et sites Web sur C


Le langage C a donn lieu une abondante littrature et je ne donnerai ici que quelques titres parmi ceux qui me paraissent les plus recommandables. En tte vient bien sr louvrage des inventeurs du langage, Brian Kernighan et Denis Ritchie, dans sa seconde dition (ansi) en anglais [17] ou dans sa traduction franaise [18]. Cest un livre assez court compar la plupart des
2. selon les cas et les shells, cest un chier comme .profile, .zshenv, .login, .bashrc... prsent dans votre rpertoire initial (home directory)

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

22

1. Introduction

autres et qui va directement lessentiel. Il contribue aussi promouvoir un certain style de programmation bien dans la philosophie du small is beautiful chre Unix. Bref, cet ouvrage est hautement recommand. Bien entendu, sil couvre la norme ansi 89 (iso C90), il naborde absolument pas les extensions de C99. Pour cette dernire version de la norme, il existe quelques ouvrages rcents comme celui de Kochan [8] ou encore, si vous aimez les gros livres trs dtaills, de Prata [3]. Parmi les (nombreux) ouvrages directement crits en franais, citons louvrage de Claude Delannoy [12], auteur renomm pour sa pdagogie, ainsi que le livre trs complet dAchille Braquelaire [27]. Ce dernier dborde le cadre de ce cours puisquil prsente aussi la programmation-systme avec lapi Posix (cest--dire avec Unix ou ses clones comme Linux). Un grand nombre de sites sur lInternet ont des sections consacrs au langage C. En particulier le trs intressant site franais developpez.com [29] et son forum [28] permettent dchanger des informations couvrant de nombreux langages et systmes, dont C et C++. Invitables dans tous ces sites interactifs, les interventions incomptentes, les jugements premptoires et non motivs, mmes les dbuts dengueulades, sans parler des orthographes douteuses sy rencontrent beaucoup moins quailleurs, favorisant des informations prcises et utiles. Hberg par developpez.com, le site de Nicolas Joseph [6] fournit dexcellents rsums sur la compatibilit entre C90 et C99, ainsi que des conseils de programmation, des piges viter et des liens sur dautres ressources concernant C. Si, au del de C, vous dcidez de vous intresser C++, le livre de linventeur de ce langage, Bjarne Stroustrup, dans sa troisme dition [24] peut tre un dbut moins que vous prfriez une approche plus progressive avec le rcent livre du mme Stroustrup, vritable introduction la programmation en gnral [25] ou encore avec [11, 4]. En ce qui concerne la compatibilit entre C(90 ou 99) et C++, on peut consulter lexcellent rsum d David Tribble [9].

1.7 Exercices du chapitre 1


Exercice 1.1 (Vrications prliminaires) Dterminez le compilateur C dont vous disposez. Sous Unix/Linux ou Cygwin, cela a des chances dtre gcc. Dterminez-en la version (commande gcc -v). Il serait bon que ce soit 3.4 ou 4.x pour avoir la compatibilit avec le standard C99. Vrier aussi la disponibilit dun outil de mise au point comme gdb ou lune de ses formes graphiques kdbg, xxgdb, ddd. . .

Choisissez votre diteur. vriez quil est correctement congur, quil sait mettre en valeur (par changement de polices ou de couleurs) la syntaxe du langage. Explorez la manire dont il collabore avec make, vous permettant de lancer vos compilations et surtout de rcuprez les messages derreurs sans quitter lditeur. Si votre diteur na pas cette dernire fonctionnalit, changezen avant quil ne soit trop tard ! Si vous dcidez dutiliser un environnement intgr de dveloppement (ide) comme kdevelop ou eclipse, apprenez lutiliser correctement, dnir et grer des projets, interagir avec make et les dbogeurs.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

1.7. Exercices du chapitre 1

23

Si vous tes sous MS Windows et que vous dcidez dutilisez Microsoft Visual Studio, sachez que vous disposerez dun excellent ide condition de savoir lutiliser. Laide en ligne sur les bibliothques C est galement de haute qualit 3 . Malheureusement, avec Visual Studio, vous naurez pas un compilateur rellement compatible avec la norme C99. Par ailleurs il vous faudra au moins la version 2005. Une autre possibilit sous MS Windows est dapprendre utiliser lun des multiples ide disponibles et qui sont souvent connects un compilateurs gcc: voir 10.
Exercice 1.2 (Accs la documentation) Elle doit se faire en ligne. Tout systme de type Unix dispose de la clbre commande man qui permet daccder la descriptions des commandes shell, des fonctions de bibliothque, des

chiers systme, etc. Apprenez lutiliser correctement (faites donc la commande man man), elle ou lune de ses interfaces comme info ou autre. Notez que les fonctions des bibliothques standards de C se trouvent dans la section 2 ou 3 du manuel. Vous pouvez galement recourir un site Web fournissant une description dtaille de la bibliothque standard. Il y en a un grand nombre que Google se fera un plaisir de vous indiquer. Un bon point de dpart, comme souvent, est Wikipdia 4 .

3. http://msdn2.microsoft.com/en-us/library/ 4. http://en.wikipedia.org/wiki/C_standard_library. Cest la version en anglais, beaucoup plus complte que celle en franais.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

24

1. Introduction

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Chapitre 2

Premiers pas
chapitre rapidement en unes des Ceprincipalespasselangagelangage etrevue quelques en dtail caractristiques du C qui seront reprises par la suite. Il constitue un tour rapide du introduit au passage quelques fonctions dentre-sortie utiles. Le but est de permettre au lecteur de rdiger rapidement quelques programmes simples. La prsentation utilise une suite dexemples assez classiques : lun des programmes les plus simples possibles, avec le clbre "hello, world!" ; un programme de copie de chier, introduisant la notion de boucle (ici une boucle while) et des fonctions simples dentre-sortie ; un programme introduisant les notions de fonction, dexpression arithmtique et dentre-sortie formatte (calcul de la racine carre dun nombre rel) ; un programme de tri par insertion an dintroduire la boucle for et la notion de tableau ; enn le calcul du terme de rang n de la suite de Fibonacci, prsent comme un exemple de fonction rcursive.

2.1 Monde, salut !


Depuis la premire dition du livre de Kernighan et Ritchie [16], de nombreux ouvrages sur C dbutent par un programme trs simple, pratiquement toujours le mme. Il nest absolument pas dshonorant de sacrier une tradition si bien tablie. Le programme 2.1 est donc le clbre hello, world! . Programme 2.1 Le programme hello, world original
/***** Fichier: hello.c *****/ #include <stdio.h>
5

int main() { printf("hello, world!\n"); }

V 2.1 7 fvrier 2010

25

c Jean-Paul R IGAULT

26

2. Premiers pas

On peut en prfrer la version franaise en 2.2. Programme 2.2 Le programme hello, world localis en franais
/***** Fichier: salut.c *****/ #include <stdio.h>
5

int main() { printf("Monde, salut !\n"); }

Ce programme (prenons la version amricaine 2.1 par exemple) est compos de trois entits : 1. un commentaire (entre /* et */), 2. une directive au prprocesseur (la ligne dbutant par #), 3. une dnition de fonction constitue de toutes les lignes suivantes. La directive au prprocesseur
#include <stdio.h>

permet dimporter les dclarations des fonctions dentre-sortie de la bibliothque standard, cest--dire les prototypes de ces fonctions qui prcisent les types des arguments et le type de retour. Ici, une seule fonction de bibliothque est directement utilise : printf. Les directives au prprocesseur sont reconnaissables au dise (#) qui doit tre le premier caractre de la ligne (autre quun blanc ou une tabulation horizontale). Le nom de cette directive (include) voque son mcanisme : il sagit dune inclusion textuelle du chier stdio.h. Les piquants (<...>) indiquent que ce chier est chercher dans des rpertoires par dfaut dpendant de linstallation (ici sans doute /usr/include). Le programme ne comporte quune seule fonction, nomme main. Par convention expresse, la fonction main est celle par laquelle commence lexcution dun programme C. La dnition de la fonction main comporte lentte de la fonction
int main()

qui en prcise le nom et surtout la signature : ici une fonction sans argument type de retour entier 1 (int pour integer). Le corps de la fonction main est un bloc, reconnaisable au fait quil est encadr par une paire daccolades :
{ printf("hello, world!\n"); }

Ici, ce bloc ne comporte quune instruction simple, linvocation (ou lappel) de la fonction printf avec un argument effectif qui est une chane de caractres littrale "hello, world!n". Cette fonction afche la chane de caractres sur la sortie standard (stdout), suivie dune n de ligne reprsente par la
1. La signature de la fonction main est impose parmi un nombre de choix limit (voir 7.4).

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

2.2. Boucle while ; entres-sorties simples

27

squence \n (newline). Noter le point-virgule nal : toute instruction simple et toute dclaration de C se termine par un point-virgule. Supposons que ce programme ait t saisi, laide dun diteur de texte quelconque, dans le chier hello.c. Il ne reste plus qu le compiler, par exemple en utilisant la commande make (que nous considrons comme la seule commande de compilation sous Unix 2 ) :
% make hello gcc -g -std=c99 -Wall -o hello hello.c %

puis excuter le chier binaire produit (qui a t nomm hello par loption -o de la commande prcdente) :
% hello hello, world! %

2.2 Boucle while ; entres-sorties simples


Le programme 2.3 lit chaque caractre de son entre standard (stdin) et le copie tel quel sur sa sortie standard (stdout). Programme 2.3 Copie de chiers
/***** Fichier: copy.c *****/ #include <stdio.h>
5

int main() { int c; while ((c = getchar()) != EOF) putchar(c); return 0; }

10

La premire ligne du corps de la fonction main est une dnition de variable locale :
int c;

Le nom de la variable est c et son type est int pour integer, cest--dire entier (sign). Le reste du corps de main est une boucle while qui a la structure suivante :
while (condition) corps
2. Pour la conguration de make, voir le chapitre 10. Ici, les rgles par dfaut doivent sufre si les variables denvironnement ont t correctement positionnes comme indiqu en 1.5.2; il ny a pas besoin de dnir de Makefile.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

28

2. Premiers pas

Une telle boucle excute son corps, constitu par une instruction simple ou un bloc, tant que la condition, qui est une expression entire quelconque, a une valeur non nulle. La condition est value et teste avant chaque itration, y compris la premire. La boucle while est une des trois formes de boucles dont dispose C (avec for et do ... while voir 3.5.3). Ici la condition
(c = getchar()) != EOF

est une expression relationnelle car != est loprateur dingalit. Dans a != b les deux expressions a et b sont values et compares : si elle sont diffrentes, la valeur de lexpression a != b est 1, sinon cest 0 3 . C possde aussi loprateur dgalit == (attention ==, deux signes = colls !), ainsi que les oprateurs de comparaison (<, <=, >, >=). Loprande droit de != est la valeur prdnie EOF (voir ci-aprs). Loprande gauche
c = getchar()

est une expression daffectation, = tant loprateur daffectation. La valeur de lexpression de droite devient la nouvelle valeur de lobjet rfrenc par lexpression de gauche (ici la variable c). La valeur de lexpression daffectation elle-mme est la nouvelle valeur de lobjet en partie gauche. Donc
(c = getchar()) != EOF

invoque la fonction getchar, copie sa valeur de retour dans la variable c, et compare cette nouvelle valeur de c la constante EOF. La boucle while sera excute tant que cette comparaison donnera une valeur non nulle (cest--dire tant que la nouvelle valeur de c nest pas EOF). Noter que les parenthses sont indispensables pour grouper laffectation, sinon
c = getchar() != EOF

sinterprte comme
c = (getchar() != EOF)

cause de la prcdence des oprateurs (voir 3.4.1). Une expression comme


(c = getchar()) != EOF

illustre bien ce type de programmation avec effet de bord puisque cette expression non seulement compare c EOF, mais encore le fait aprs avoir modi c. En liminant leffet de bord, on aurait pu crire la boucle while sous une forme plus habituelle pour un programmeur Pascal, mais certainement moins idiomatique pour un programmeur C :
c = getchar(); while (c != EOF) { putchar(c); c = getchar(); }
3. Contrairement C99 et C++, C90 ne possde pas de type de donne boolen mais que ce sont les valeurs entires 0 et 1 qui sont utilises pour jouer les rles respectifs de faux et vrai voir 3.3.2 et 11.3.2.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

2.3. Fonctions ; expressions ; entres-sorties

29

Notez la ncessit de rpter linstruction c = getchar(), une (lgre) nuisance. La fonction getchar lit le caractre suivant sur lentre standard et retourne sa valeur. En cas n de chier dtecte par le systme dexploitation, getchar retourne la valeur EOF, prdnie dans stdio.h et qui ne correspond aucune valeur possible de caractre 4 . La boucle while se terminera donc lorsque la n de chier sera atteinte sur lentre standard. Enn, le corps de la boucle est constitu par une instruction simple qui est linvocation de la fonction putchar. Celle-ci afche le caractre quelle reoit en argument (ici c) sur la sortie standard. Le programme, dont le source est dans le chier copy.c, peut donc servir recopier un chier dans un autre grce au mcanisme de redirection du shell :
% make copy gcc -g -std=c99 -Wall % copy > fic ceci est le texte qui recopi sur la sortie D % cat fic ceci est le texte qui recopi sur la sortie % copy < fic > fic1 % cat fic1 ceci est un texte qui recopi sur la sortie % cmp fic fic1 %

-o copy copy.c sera standard

sera standard

sera standard

On rappelle que le caractre ctrl-d (eot) reprsent par D simule une n de chier au terminal. Par ailleurs, la commande cmp compare 2 chiers : labsence dafchage indique que les deux chiers sont identiques loctet prs.

2.3 Fonctions ; expressions ; entres-sorties


2.3.1 Exemple : calcul de la racine carre dun rel
Le programme 2.4 calcule la racine carre dun nombre rel positif grce la mthode de Newton. Si t est un tel nombre, sa racine carre est en effet donne par la limite de la suite un = 1 2 u n 1 + t u n 1

avec la condition initiale u0 = t, par exemple. Le programme comporte, au plus haut niveau, sept lments. Trois directives au prprocesseur qui, comme prcdemment, permettent dimporter les prototypes des fonctions de la bibliothque standard :
4. Ceci explique que getchar retourne un entier (int) et non pas un caractre (char) et que la variable locale c doive tre dclare comme un entier.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

30

2. Premiers pas

Programme 2.4 Calcul de la racine carre dun nombre rel


/***** Fichier: square_root.c *****/ #include <stdio.h> #include <stdlib.h> #include <math.h> double square_root(double); int main() { double x; printf("entrez un nombre? "); scanf("%lg", &x); printf("resultat = %g\n", square_root(x)); exit(0); } const double EPS = 1.0e-06;
20

10

15

/* precision maximale */

double square_root(double t) { double previous = 0.0; double current = t;


25

30

35

if (t < 0.0) { fprintf(stderr, "argument negatif pour square_root\n"); exit(1); } while (fabs(previous - current) > EPS) { previous = current; current = 0.5 * (current + t / current); } return current; }

#include <stdio.h> #include <stdlib.h> #include <math.h>

Nous avons dj remarqu que linclusion de stdio.h permet dimporter les prototypes des fonctions dentre-sortie de la bibliothque standard ; celle de stdlib.h concerne une bonne partie du reste des prototypes de cette bibliothque (par exemple celui de la fonction exit) ; quant celle de math.h, elle importe les fonctions de la bibliothque mathmatique comme fabs. Un prototype dclarant la fonction square_root, car celle-ci est utilise avant dtre dnie (on parle de dclaration en avant (forward declaration) :

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

2.3. Fonctions ; expressions ; entres-sorties

31

double square_root(double);

Cette fonction a un argument qui est un rel double prcision (type double) et retourne une valeur de mme type. La dnition de la constante globale EPS, galement de type double, initialise 106 . La dnition des deux fonctions main et square_root. On note que le format est libre et que lindentation et le changement de ligne sont largement utiliss pour rendre le programme lisible (ils nont pas dautre signication pour le compilateur que celle de sparateurs). La fonction main utilise une variable locale x, de type double. La valeur de x est lue grce la fonction de bibliothque scanf. Le premier argument de scanf est une chane de caractres qui spcie le format de lecture : ici, la spcication %lg signie quon cherche lire un rel dans toute prsentation raisonnable (le g), et que ce rel est double prcision (le l, comme long ). Le deuxime argument de scanf est ladresse de la variable que lon cherche lire, ici celle de x. En effet loprateur & sert prendre ladresse dun objet (il retourne un pointeur). De manire gnrale, les arguments de scanf doivent tre des pointeurs 5 . Lavant-dernire instruction de main afche le rsultat de linvocation de la fonction square_root avec largument x. L encore le premier argument de printf est un format o la spcication %g indique que lon veut afcher un rel double prcision dans un format raisonnable pour sa valeur. On constate au passage que printf et scanf sont des fonctions nombre variable darguments. Bien entendu, linvocation de exit termine lexcution du programme, en renvoyant au shell la valeur de son argument comme code de retour. Par convention, la valeur 0 indique que tout sest bien pass 6 . Il nest pas ncessaire dappeler systmatiquement exit la n de main, mais ce nest pas une mauvaise pratique puisque cela permet de matriser le code de retour transmis au shell. Une autre possibilit quivalente est de remplacer exit(code) par return code (par exemple ici, return 0) mais videmment ceci nest quivalent exit que pour le retour de main, pas dune autre fonction ! Notez aussi que le chier dentte <stdio.h> dnit les deux constantes entires EXIT_SUCCESS (avec la valeur 0) et EXIT_FAILURE (1) ce qui permet de remplacer les utilisations de exit ou return prcdentes par exit(EXIT_SUCCESS) ou return EXIT_SUCCESS. Aprs la fonction main est dnie une constante globale EPS initialise la valeur 106 . Enn, on a dj remarqu que les commentaires se placent entre /* et */. Puis vient la dnition de la fonction square_root elle-mme. Elle vrie dabord que largument t nest pas ngatif. Linstruction if est lune des deux instructions de slection de C (voir 3.5.2). Elle peut prendre la forme simple suivante
5. Essayez donc dexcuter ce programme en oubliant de prendre ladresse de x, cest--dire en crivant : texttscanf("%lg", x) 6. Rappelons que ce code de retour peut tre consult par les instructions de contrle conditionnelles du shell : if, while, until...

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

32

2. Premiers pas

if (condition) instruction-si-vrai

ou encore celle dune alternative


if (condition) instruction-si-vrai else instruction-si-faux

Sa signication est vidente. Les deux instructions sont soit des instructions simples soit des blocs 7 . La fonction fprintf est analogue printf, mais son premier argument prcise le ux de sortie, ici lerreur standard stderr 8 . Le cur de square_root est un boucle while o current est la valeur courante de un et previous la valeur prcdente (i.e., un1 ). La fonction de bibliothque fabs calcule la valeur absolue relle double prcision (son prototype est dans math.h). La boucle sexcute tant que lcart en valeur absolue entre un et un1 est suprieur EPS. Le corps de la boucle tant constitu de deux instructions simples (deux instructions daffectation en fait), il faut donc l encore les englober dans un bloc. Lexpression arithmtique
0.5 * (current + t / current)

se comprend sans problme. C dispose entrautres, des oprateurs arithmtiques habituels : addition (+), soustraction (-), multiplication (*), division (/). Ces oprateurs sappliquent des valeurs entires ou relles : attention, si ses deux oprandes sont entiers, / est la division entire. A la n de la boucle, la valeur de current est le rsultat cherch et est donc transmise lappelant grce linstruction return qui termine la fonction. Il ne reste plus qu compiler et excuter ce chier (supposons quil soit nomm square_root.c) :
% make square_root gcc -g -std=c99 -Wall -o square_root square_root.c % square_root entrez un nombre? 64 result = 8 % square_root entrez un nombre? 2 result = 1.41421 % square_root entrez un nombre? 0 result = 0 % square_root entrez un nombre? 1.E+18 resultat = 1e+09 % square_root entrez un nombre? -12 argument negatif pour square_root %
7. Remarquer labsence du mot-cl then habituel dans dautres langages. 8. printf(...) est donc quivalent fprintf(stdout, ...).

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

2.3. Fonctions ; expressions ; entres-sorties

33

2.3.2 Entres-sorties formattes


On a vu que printf et scanf ont un premier argument 9 qui est un format prcisant les conversions effectuer en en sortie ou en entre. Nous donnons ici quelques indications pour permettre la comprhension des exemples venir. Pour les dtails on se reportera la documentation en ligne. Le format est une chane de caractres comportant des spcications de conversion , chacune dbutant par le caractre %. Une telle spcication indique la conversion effectuer sur largument correspondant (rappelons que les deux fonctions ont un nombre variable darguments). La correspondance entre spcication et argument seffectue donc de manire positionnelle et il doit en principe y avoir autant darguments convertir que de spcications dans le format.
Conversions en sortie

Dans le cas de printf (ou de fprintf), tout ce qui nest pas une spcication de conversion est imprim tel quel. Ainsi
printf("hello, world!\n");

est un exemple o il ny a aucune spcication de conversion dans le format. Dans


int i = 3; int j = 4; ... printf("i = %d, j = %d\n", i, j);

les valeurs de i et j sont imprimes en format dcimal (%d) produisant


i = 3, j = 4

suivi dune n de ligne. Il existe un grand nombre de possibilits de spcications de conversion; voici quelques unes des plus frquentes :
%d %o %x %c %s %lf %le %lg %% conversion dun entier en dcimal conversion dun entier en octal conversion dun entier en hexadcimal afchage dun caractre individuel afchage dune chaine de caractres conversion dun rel double en format xe conversion dun rel double en format avec exposant conversion dun rel double en format gnral le caractre % lui-mme

Le format, comme toute chane, peut contenir des squences dchappement qui reprsentent certains caractres non imprimables. Ces squences se composent toutes du caractre \ suivi dun seul caractre. Nous avons dj rencontr \n, voici les plus frquentes :
9. La discussion qui suit sapplique aussi au deuxime argument de fprintf ou de fscanf dont le premier argument dsigne le ux dentre-sortie.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

34

2. Premiers pas

\n \t \f \0

n de ligne tabulation horizontale (tab) saut de page (form feed) le caractre nul (nul)

Le caractre nul joue un rle fondamental comme terminateur de chane de caractres (voir 3.1.7). Ne pas confondre ce caractre nul avec la constante NULL dsignant le pointeur nul (voir 5.1.1).
Conversions en entre

Nous avons dj vu que les arguments de scanf sont des pointeurs. Les formats et les spcications de conversion ont pratiquement la mme syntaxe que pour printf, tout au moins en premire approximation. Le texte autre que les spcications de conversion et les espaces doit tre prsent dans lentre effective. Les espaces eux ne servent que de sparateurs et pour le reste sont ignors (le blanc, \n, \t, \f sont des espaces). Lexcution de scanf se termine lorsque toutes les spcications de conversion ont t satisfaites ; elle est abandonne prmaturment ds quune conversion ne peut tre effectue. Dans tous les cas, la valeur de retour est le nombre de conversions effectivement ralises. A titre dexemple, considrons le programme 2.5 (chier scanf.c) qui lit un entier (i) et un rel (x) et les afche avec le nombre dobjets lus (n). Programme 2.5 Programme simple de lecture de scalaires
/***** Fichier: scanf.c *****/ #include <stdio.h>
5

int main() { int i; double x; int n; n = scanf("%d toto %lg", &i, &x); printf("n = %d, i = %d, x = %lg\n", n, i, x); }

10

Voici quelques exemples de son excution :


% make scanf gcc -g -std=c99 -Wall -o scanf scanf.c % scanf 12 toto 3 n = 2, i = 12, x = 3 % scanf 12 toto 3 n = 2, i = 12, x = 3 % scanf

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

2.4. Tableaux et instruction ditration

35

12 toto 3 n = 2, i = 12, x = 3 % scanf 12toto3 n = 2, i = 12, x = 3 % scanf 12toto 3 n = 2, i = 12, x = 3 % scanf 12 3 n = 1, i = 12, x = 1.9064e-310 % scanf 12 to n = 1, i = 12, x = 1.9064e-310 % scanf aaaaaa 12 toto 3 n = 0, i = -1081747800, x = 1.9064e-310 %

Les valeurs tranges de x et i (en rouge) sont bien videmment le symptome que ces variables nont pas t initialises (dailleurs, on remarque que n est infrieur 2 dans ces cas). Les exemples prcdents montrent, sil en est besoin, que scanf est une fonction extrmement dlicate manipuler! lavenir, nous lviterons le plus possible!

2.4 Tableaux et instruction ditration


Comme troisime exemple, le programme 2.6 ralise le tri par insertion dun tableau dentiers. Cet algorithme de tri seffectue sur place (cest--dire ne ncessite aucune mmoire supplmentaire) et requiert de lordre de n2 comparaisons (O(n2 )) si n est le nombre dlments trier 10 . Cette mthode est celle quutilise naturellement quelquun qui range ses cartes (au bridge ou au poker par exemple). La gure 2.1 schmatise un pas ditration de la boucle principale. On parcourt la liste de gauche droite, et on insre llment courant (dindice i) sa place dans la sous-liste de gauche (dj trie, par construction). Au besoin, pour ce faire, on cre un espace en dcalant des lments vers la droite. On trouve, au niveau principal, cinq lments dans ce programme : deux directives #include pour le prprocesseur ; la dnition de la fonction insertion_sort ; une directive au prprocesseur qui dnit la constante NMAX gale 1000
#define NMAX 1000

la dnition de la fonction main. La fonction insertion_sort ralise lagorithme dcrit prcdemment. Si le nombre dlments trier n vaut 1 (ou moins) il ny rien faire. Linstruction return termine alors lexcution de la fonction. Cette instruction peut
10. Les meilleurs algorithmes de tri squentiels sont en O(n log n) comparaisons.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

36

2. Premiers pas

Programme 2.6 Tri par insertion dune suite dentiers


/***** Fichier: insertion_sort.c *****/ #include <stdio.h> #include <stdlib.h>
5

void insertion_sort(int tab[], int n) { int i, j, current;


10

15

20

if (n <= 1) return; for (i = 1; i < n; i++) { current = tab[i]; for (j = i - 1; j >= 0; j--) { if (tab[j] <= current) break; else tab[j + 1] = tab[j]; } tab[j + 1] = current; } }

25

#define NMAX 1000 int main() { int n, i; int t[NMAX]; printf("entrez une liste dentiers terminee par EOF? "); for (n = 0; n < NMAX && scanf("%d", &t[n]) != EOF; n++) /* Rien */; insertion_sort(t, n); /* Fait tout le travail */

30

35

40

printf("liste triee = "); for (i = 0; i < n; i++) printf("%d ", t[i]); putchar(\n); return 0; }

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

2.4. Tableaux et instruction ditration

37

Figure 2.1 Algorithme de tri par insertion

tre suivie dune expression dont lvaluation fournit la valeur retourne par la fonction. Mais ici, la fonction square_root a pour type de retour void ce qui signie quelle ne retourne aucune valeur (plutt quune fonction, cest une procdure). La boucle for permet de parcourir le tableau tab, pass en argument, dont la dimension (le nombre de composantes) est n. Malgr sa syntaxe trange, la boucle for est assez simple comprendre. En fait
for (initialisation; entretien; rebouclage) corps

est ( peu prs) quivalent


initialisation; while (entretien) { corps rebouclage }

Donc dans notre cas


for (i = 1; i < n; i++) { corps }

est trs prcisment quivalent


i = 1; while (i < n) { corps i++; }

Linstruction i++ est lincrmentation de i, ici quivalente i = i + 1. Cette boucle for excute son corps pour les valeurs de i variant de 1 n-1 inclus.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

38

2. Premiers pas

Lexpression tab[i] dsigne bien sr la composante dindice i du tableau tab. A noter quen C les indices dbutent 0. La boucle interne (indexe par j) parcourt la liste vers la gauche, partir de la position courante, en dcalant au fur et mesure les lments pour faire de la place pour insrer current. Elle se termine soit sur linstruction break qui linterrompt prmaturment, soit lorsque j vaut -1. Dans les deux cas, la valeur courante de j est, 1 prs, celle de lindice o il convient dinsrer current.
Une forme plus idiomatique de la boucle de tri

A cause de lquivalence mentionne avec la boucle while, la boucle interne (en j) aurait pu prendre une forme plus idiomatique en utilisant loprateur et logique && : for (j = i - 1; j >= 0 && tab[j] > current; j--) { tab[j + 1] = tab[j]; } Par ailleurs, le corps de la boucle ne comportant quune seule instruction, les accolades sont inutiles : for (j = i - 1; j >= 0 && tab[j] > current; j--) tab[j + 1] = tab[j]; C possde aussi loprateur ou logique reprsent par ||. Ces deux oprateurs (|| et &&) sont valus de gauche droite, et lvaluation sinterrompt ds que le rsultat est dtermin. La fonction main dnit un tableau local t de dimension maximale NMAX. Ce tableau est initialis composante par composante grce scanf. On remarque que le corps de la boucle est vide, tout le travail ayant t fait par effet de bord dans la condition dentretien. A la n de la boucle, n est le nombre dlments lus. La liste trie par invocation de insertion_sort est imprime lment par lment grce printf. On noublie pas dajouter une n de ligne la n de la liste. Noter la diffrence entre les chanes de caractres littrales (entre ") et les caractres littraux (entre ).

Voici un exemple dexcution :


% make insertion_sort gcc -g -std=c99 -Wall -o insertion_sort insertion_sort.c % insertion_sort entrez une liste dentiers termine par EOF? 12 -3 2 -1 7 -13 18 12 -27 D liste triee = -27 -13 -3 -1 2 7 12 12 18 %

2.5 Arguments du shell ; fonction rcursive


Le terme de rang n de la suite de Fibonacci est dni ainsi : u n = u n 1 + u n 2 u =0 0 u1 = 1 c Jean-Paul R IGAULT
V 2.1 7 fvrier 2010

2.5. Arguments du shell ; fonction rcursive

39

Le programme 2.7 calcule un . La fonction fibo est une implmentation directe de la formule prcdente. Elle sinvoque rcursivement. La rcursion se termine car n dcroit chaque appel et nit donc par tre infrieur 2. Programme 2.7 Calcul du terme de rang n de la suite de Fibonacci
/***** Fichier: fibo.c *****/ #include <stdio.h> #include <stdlib.h>
5

10

int fibo(int n) { if (n < 2) return n; else return fibo(n-1) + fibo(n-2); } int main(int argc, char *argv[]) { int i; if (argc == 2) { i = atoi(argv[1]); } else { fprintf(stderr, "usage: fibo n\n"); exit(1); } printf("fibo(%d) = %d\n", i, fibo(i)); exit(0); }

15

20

25

La valeur de n est obtenue grce aux arguments de la ligne de commande qui correspondent aux arguments de la fonction main. On sait que sous le shell une commande est invoque par son nom avec une ventuelle liste darguments qui sont des chanes de caractres, comme dans
% ls -lg -t -d /usr/bin /usr/local/bin

A lexcution de la commande correspond celle dun programme et donc dune fonction main si ce programme a t crit en C. Les arguments de main permettent de rcuprer les arguments positionnels de la commande shell : lentier argc est le nombre darguments positionnels y compris le nom de la commande (dans lexemple de ls, argc vaut 6) ; le tableau de chanes de caractres argv, dont la dclaration sera explicite en 5.3.2, contient lui les arguments positionnels, cest-dire ici les six chanes "ls", "-lg", "-t", "-d", "/usr/bin" et "/usr/local/bin".

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

40

2. Premiers pas

La fonction de bibliothque standard atoi 11 transforme une chane de caractres (ici le premier argument positionnel de la commande, soit argv[1]) en entier, si toutefois cela a un sens. La fonction fibo aurait pu tre crite de manire plus idiomatique en utilisant une expression conditionnelle :
int fibo(int n) { return (n < 2) ? n : fibo(n-1) + fibo(n-2);\\ }

Une expression comme


a ? b : c

se lit: si a alors b sinon c . Elle svalue comme cette lecture le laisse supposer. Voici un exemple dutilisation :
% make fibo gcc -g -std=c99 -Wall -o fibo fibo.c % fibo 20 fibo(20) = 6765 % fibo 25 fibo(25) = 75025 % fibo 1 fibo(1) = 1 %

Une modication lmentaire de la fonction fibo permet de voir la rcursivit luvre (chier fibo1.c) :
int fibo(int n) { int r; printf("entre dans fibo --- n = %d\n", n); if (n < 2) r = n; else r = fibo(n-1) + fibo(n-2); printf("sort de fibo --- n = %d, fibo = %d\n", n, r); return r; }

Voici le rsultat :
% fibo1 4 entre dans fibo -entre dans fibo -entre dans fibo -entre dans fibo -sortie de fibo -- n entre dans fibo -sortie de fibo -- n
11. pour ASCII to integer

n n n n = n =

= 4 = 3 = 2 = 1 1, fibo = 1 = 0 0, fibo = 0

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

2.6. Exercices du chapitre 2

41

sortie de fibo -- n entre dans fibo -sortie de fibo -- n sortie de fibo -- n entre dans fibo -entre dans fibo -sortie de fibo -- n entre dans fibo -sortie de fibo -- n sortie de fibo -- n sortie de fibo -- n fibo(4) = 3 %

= n = = n n = n = = =

2, fibo = 1 1, fibo 3, fibo = 2 = 1 1, fibo = 0 0, fibo 2, fibo 4, fibo

= 1 = 1 = 2

= 1 = 0 = 1 = 3

Figure 2.2 Arbre de calcul rcursif de fibo(4)

On peut reprsenter ce calcul rcursif sous forme dun arbre comme dans la gure 2.2. La fonction fibo value cet arbre de la gauche vers la droite, en profondeur dabord , cest -dire en valuant effectivement les feuilles (indiques en bleu) dabord.

2.6 Exercices du chapitre 2


Exercice 2.1 (Mise en route) Utiliser votre diteur favori pour saisir le texte de

lun des exemples de ce chapitre. Compiler et excuter cet exemple. Noter la coloration syntaxique (syntax highlighting) utilise par votre diteur (la plupart des diteurs actuels en disposent). Si votre diteur le permet ou si vous utilisez un environnement intgr comme kdevelop, eclipse, visual studio ou dev-c++, habituez-vous compiler et corriger vos erreurs sans quitter cet diteur ou cet environnement : cela fait gagner beaucoup de temps et vite les manipulations stupides 12 !
Exercice 2.2 (Copie de chiers) Modier copy (programme 2.3) pour quil im-

prime le nombre de lignes copies sur lerreur standard (stderr).


12. comme par exemple modier un chier et le compiler sans lavoir sauvegard au pralable. . .

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

42

2. Premiers pas

Exercice 2.3 (La commande iota) crire le programme iota qui, lorsquil est invoqu depuis le shell avec largument n produit la suite des entiers de 1 n ;

par exemple

% iota 7 1 2 3 4 5 6 7 %

Exercice 2.4 (Minimum et maximum dun ensemble dentiers) crire un prog-

ramme qui imprime le maximum et le minimum dune liste dentiers.


Exercice 2.5 (Conversion C ELSIUS -FARENHEIT) crire un programme qui im-

prime la table de conversion des degrs Celsius en degrs Farentheit entre -50C et +110C par pas de 5C , ainsi que la table inverse de -60F +220 F par pas de 10F. On rappelle la formule de conversion : si C est la temprature en C et F celle en F, on a F= 9 C + 32 5

Exercice 2.6 ( Drcursiver le calcul de la suite de F IBONACCI) Drcursi-

ver fibo, cest--dire remplacer les appels rcursifs par des boucles.
Exercice 2.7 (Amlioration du calcul de la racine carre) Considrez le pro-

gramme de calcul de la racine carre par la mthode de Newton (programme 2.4). Excutez-le pour de petites valeurs de x, par exemple de lordre de 106 . Que se passe-t-il ? Identiez le problme. Quelle solution peut-on trouver pour amliorer la situation ? Vriez le rsultat.
Suggestion

Si un est la suite utilise dans la mthode de Newton, que vaut la limite suivante ?
n

lim

u n +1 un

Exercice 2.8 (Mauvais format) crire un programme qui utilise printf pour

imprimer un rel (float ou double) en format %d, un entier (int) en format %f et un caractre (char) en format %d. Faites le mme exercice mais en lecture, avec scanf. Que concluez-vous ?
Remarque

Pour cet exercice, ignorez les messages davertissement (warnings) ventuels du compilateur. Pouvez-vous imaginer un moyen de faire taire ces messages ?

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Chapitre 3

Bases du langage
dcrit du langage lexiOnscalaires, ici les lments fondamentaux instructions. C. .),: lments doncaux (identicateurs, sparateurs, constantes diverses. types de nes oprateurs et expressions, enn

3.1 Elments lexicaux


3.1.1 Jeu de caractres
Lcriture dun programme C utilise un jeu de caractres qui doit au minimum comporter les lments suivants : lettres minuscules et majuscules, caractres spciaux (ponctuations, parenthses...), espaces (blanc, n de ligne, tabulations...). Les majuscules et les minuscules sont considres comme des caractres diffrents (en anglais, on parle de case-sensitivity). Le jeu de caractres le plus utilis est lascii dont la table 3.1 donne la description. Certaines machines relevant en particulier de la mouvance des gros ordinateurs ibm utilisent le code ebcdic qui contient les caractres imprimables du code ascii plus quelques autres. Dans le code ascii, les 32 premiers caractres (de nul us) ainsi que del sont non imprimables. La table 3.1 permet de trouver le code numrique correspondant un caractre : par exemple, le caractre A a pour code hexadcimal 41, soit 65 en dcimal ou encore 101 en octal. En fait la norme ansi C suppose que tous les caractres du code ascii sont reprsentables dans le jeu courant 1 , sauf @ et (la backquote).
Portabilit du jeu de caractres

Il importe de remarquer que tout programme C qui fait une hypothse implicite sur le jeu de caractres utilis est par essence non portable. Un programme qui nutilise que le code ascii est en principe sr. Les codes dans lesquels un caractre est cod sur 8 bits peuvent galement tre manipuls sans trop de problme dans les chanes de caractres et seront rendus correctement si votre environnment est adquat (variables LANG par exemple). Ds que lon veut utiliser des jeux de caractres multi-octets comme Unicode, il vaut mieux se mettre en mode C99. Les fonctions correspondantes sont dcrites en 11.3.5.
1. mais pas ncessairement dans le codage ascii!

C99

V 2.1 7 fvrier 2010

43

c Jean-Paul R IGAULT

44

3. Bases du langage

Table 3.1 Jeu de caractres ASCII 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 nul soh stx etx eot enq ack bel bs tab nl vt ff cr so si 1 dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us 2 sp ! " # $ % & ( ) * + , . / 3 0 1 2 3 4 5 6 7 8 9 : ; < = > ? 4 @ A B C D E F G H I J K L M N O 5 P Q R S T U V W X Y Z [ \ ] ^ _ 6 a b c d e f g h i j k l m n o 7 p q r s t u v w x y z { | } ~ del

Remarque sur les digraphs et les trigraphs

C99

Au cas o le jeu de caractres disponible serait insufsant (!), la norme ansi C dnit une reprsentation particulire de certains caractres spciaux, dnomme trigraph . Les trigraphs comportent 3 caractres (!) dont les deux premiers sont ?? et sont remplacs par leur caractre quivalent dans tout contexte (y compris les chanes de caractres et les constantes litrales). On se porte aussi bien en ignorant cette bizarrerie 2 mais il convient dviter le double point dintrrogation en particulier dans les commaentaires (voir 3.1.3) et constantes littrales reprsentant des caractres individuels (voir3.1.6) ou des chanes (voir 3.1.7) ! Ajoutant le saugrenu la bizarrerie, C++a aussi dni des squences de deux caractres (donc des digraphs ) permettant de reprsenter aussi une partie de caractres spciaux. C99 sest prcipit pour adopter cette ide gniale. Ainsi les crochets carrs ([ et ]) peuvent-ils tre reprsents par <: et :>, les accolades ({ et }) par <% et %>, le dise (#) par %: et le double dise (##) videmment par %:%:. Cela namliore certes pas la lisibilit ! Contraitrement aux trigraphs, les digraphs ne sont pas substitus lintrieur dun commentaire ni dune constante littrale (chane ou caractre) ; en fait leur substitution na lieu que sils reprsentent par euxmmes un lment lexical (un token voir 3.1.2). Il est peu probable que vous ayez utiliser les digraphs...

3.1.2 Structure lexicale dun chier-source


Un chier de source C est constitu dlments lexicaux qui se divisent en six catgories : mots-cls, identicateurs, constantes arithmtiques littrales, chanes de caractres littrales, oprateurs et autres sparateurs.
2. Cette bizarrerie a une raison, cependant. Les caractres correspondants sont ceux utiliss par C++ qui sont absents du jeu de caractres normalis suppos tre implant sur toutes les machines, lInvariant Code Set iso 646-1983.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.1. Elments lexicaux

45

Les sparateurs permettent disoler les autres lments lexicaux. Les oprateurs sont aussi des sparateurs. Les espaces, cest--dire le blanc (sp), la n de ligne (nl), les tabulations horizontale (tab ou ht) ou verticale (vt) et le saut de page (ff) sont des sparateurs purs en ce sens que pour le reste ils sont ignors.

3.1.3 Commentaires
En C90 les commentaires sont encadrs entre /* et */ et peuvent stendre sur plusieurs lignes mais ne peuvent pas simbriquer :
/* Un long commentaire sur 3 lignes */ /* Une /* erreur de syntaxe */ coup sr ! */

Ce type de commentaire est quivalent un espace pour le compilateur (cest donc un sparateur). C99 autorise aussi les commentaires la C++ commenant par // et stendant jusqu la n de la ligne. Il est possible dimbriquer un commentaire C90 dans un commentaire C99 ou linverse :
// un commentaire-ligne /* Toute une section en commentaire, y compris ses commentaires-lignes x = 0; // plus de x y = 1; // encore un y / *

C99

3.1.4 Identicateurs
Les identicateurs permettent de nommer des objets (constantes, variables, fonctions). Ils commencent par une lettre ou le caractre soulign (_) suivi dune squence de lettres, de chiffres ou de souligns. Par exemple :
x j21 Prix_HT _strcmp X a2ps Square_Root __DATE__ toto char_0_to_9 num_secu __STDC__ NBUF PascalStyleId _1993

En revanche, les mots suivants ne sont pas des identicateurs :


#x 2#$!**)((_+%^ 2toto Prix-TTC

On rappelle que les majuscules et les minuscules sont considres comme des caractres diffrents : Square_Root et square_root ne sont pas le mme identicateur.
Portabilit : jeu de caractres tendu pour les identicateurs

En C90, seul le code ascii peut tre utilis pour coder les identicateurs (noms de constantes, variables, fonctions, macros. . .). En revanche, C99 permet lutilisation des caractres tendus (Unicode)

C99

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

46

3. Bases du langage

dans les identicateurs. Cependant, peu de compilateurs ralisent actuellement cette fonctionalit (en particulier gcc-4.2 ne la supporte pas). On vitera donc les identifcateurs comme t42 ou .
Portabilit : identicateurs rservs

Les identicateurs dbutant par un double caractre soulign (_) sont rservs des usages spciaux (voir 6.3.6) et ne devraient pas tre utiliss en dehors de ces usages. En principe, les noms de fonctions de la bibliothque normalise (fopen, printf, exit... voir 9) ne devraient pas tre utiliss pour dautres usages. Enn, les mots-cls (voir 3.1.5) sont strictement rservs.
Portabilit : longueur dun identicateur

La norme ansi C rclame que les 31 premiers caractres au moins dun identicateur soient pris en compte sauf pour les identicateurs dobjets externes qui sont manipuls par lditeur de liens. Pour ces derniers, les 6 premiers caractres au moins doivent tre signicatifs. Les compilateurs et les diteurs de liens modernes ne placent pratiquement aucune limite sur la longueur des identicateurs. Se mer cependant : quant la modernit, les diteurs de liens sont bien plus rares que les compilateurs !

3.1.5 Mots-cls
Les mots-cls de C sont strictement rservs. Ils ne doivent pas tre utiliss comme identicateurs sinon le programme devient incompilable. Tous les mots-cls sont en minuscules ; ils sont au nombre de 32 pour C90 et le tableau 3.2 en donne la liste. Table 3.2 Mots-cls de C auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while

C99

La norme C99 ajoute 5 mots-cls supplmentaires (table 3.3). Table 3.3 Mots-cls supplmentaires de Cnew restrict inline _Complex _Imaginary _Bool

3.1.6 Constantes littrales arithmtiques

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.1. Elments lexicaux

47

Constantes littrales caractres

La squence a reprsente le caractre a. Une constante caractre est donc un caractre encadr de simples quotes (). Lorsque le caractre nest pas imprimable, on peut utiliser une squence dchappement comme \n. La table 3.4 donne la liste complte de ces squences. Table 3.4 Squences dchappement pour les caractres

\a \b \f \n \r \t \v \\ \0 \ \" \?

alarme retour arrire saut de page saut de ligne retour chariot tabulation horizontale tabulation verticale le \ lui-mme le caractre de code 0 la simple quote elle-mme la double quote elle-mme le point dinterrogation

alarm backspace form feed new line carriage return horizontal tab vertical tab backslash nul

bel bs ff nl cr tab (ht) vt nul

A titre dexemple, le programme 3.1 remplace chaque n de ligne de lentre standard par un blanc. Programme 3.1 Remplacement des ns de ligne par un blanc
/***** Fichier: rm_nl.c *****/ #include <stdio.h>
5

int main() { int c; while ((c = getchar()) != EOF) { if (c == \n) c = ; putchar(c); } putchar(\n); }

10

15

Portabilit : valeur entire dun caractre

La norme permet de reprsenter tout caractre par son code octal ou hexadcimal. Ainsi A, \101 et \x41 sont trois reprsentations quivalentes de la constante caractre A. Conformment la remarque dj faite en 3.1.1, ceci peut tre une cause de non-portabilit.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

48

3. Bases du langage

Constantes entires littrales

ansi C supporte trois reprsentations principales des constantes entires littrales : dcimale, constitue des chiffres de 0 9 (0, 123, 2812727837...) ; octale, dbutant par le caractre 0 et ne comportant que les chiffres de 0 7 (0123, 0177) ; hexadcimale, dbutant par le prxe 0x ou 0X et constitue des chiffres de 0 9 et des lettres de a f en majucules ou minuscules indiffremment (0x10 , 0xff, 0XaaBBcc00...). Une constante entire littrale peut tre prcde dun signe (+ ou -). Ce signe ne fait pas rellement partie de la constante mais dsigne en fait loprateur correspondant (voir 3.3.1). Il existe de nombreuses variations de syntaxe des constantes entires qui correspondent aux diffrents sous-types des entiers et qui seront tudies en 3.2.3.
Constantes relles littrales

Les constantes relles littrales peuvent prendre deux formes : virgule xe avec une partie entire et une partie fractionnaire (dcimales) spares par un point (2.718285, 0.314159...) ; exponentielle, identique la virgule xe avec en plus un facteur dchelle introduit par le caractre e ou E et qui reprsente un exposant en base 10 (6.02e+23 pour 6, 02 1023 , 1.e-6 pour 106 ...). Les deux remarques prcdentes sur la prsence possible dun signe et lexistence de variations sappliquent ici aussi (voir 3.2.4 pour les variations).

3.1.7 Chanes de caractres littrales


Une chane de caractres littrale est une suite de caractres encadres de doubles quotes . Les squences dchappement (3.1.6 et table 3.4) sont bien entendu utilisables :
"ceci est une chaine de caracteres litterale" "hello, world\n" "Article\tPrix unitaire\tQuantite\tPrix total" "la double quote comme \" cad avec un \ devant"

Une chane de caractres littrale ne peut tre coupe par un retour la ligne : ceci
printf("hello, world \n");

est illgal. Cependant quand des chanes littrales sont adjacentes (cest--dire uniquement spares par des espaces), elles sont concatnes par le compilateur :
printf("hello, " "world" "\n");

est quivalent
printf("hello, world\n");

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.1. Elments lexicaux

49

Figure 3.1 Reprsentation dune chane de caractres littrale

En mmoire, une chane littrale est reprsente par la suite des caractres qui la constitue (sans les doubles quotes bien sr) termine par le caractre nul (\0). Ainsi, une chane occupe-t-elle un caractre de plus que sa longueur utile (gure 3.1). Cette proprit, qui est impose par le compilateur pour les chanes littrales, est une convention qui doit tre respecte par le programmeur en manipulant des chanes variables. A titre dexemple, les deux programmes 3.2 et 3.3 utilisent cette proprit de terminaison des chanes : le premier pour dterminer la longueur utile, le second pour recopier une chane dans une autre noter que le caractre nul nal est lui aussi recopi. Programme 3.2 Longueur utile dune chane de caractres
/***** Fichier: string_length.c *****/ #include <stdio.h>
5

int string_length(const char orig[]) { int i; for (i = 0; orig[i] != \0; i++) {} return i; } int main() { printf("%d\n", string_length("hello, world\n")); }

10

15

Pour ces deux programmes, on remarque que largument orig est pass sous forme dun tableau de caractres constants
const char orig[]

ce qui signie que les lments du tableau ne sont pas modis dans le corps de la fonction (voir 7.3.2). En revanche, le tableau dest, paramtre de copy_string, est videmment modiable, dou labsence de const. Noter aussi le corps vide de la boucle de la fonction string_length : tout le travail est en effet ralis dans lentte de linstruction for.
Attention : a et "a"

On ne doit donc pas confondre la constante charactre a avec la chane

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

50

3. Bases du langage

Programme 3.3 Copie de chanes de caractres


/***** Fichier: copy_string.c *****/ #include <stdio.h>
5

void copy_string(char dest[], const char orig[]) { int i; for (i = 0; orig[i] != \0; i++) dest[i] = orig[i]; dest[i] = \0; /* copie du nul terminal */ } int main() { char str[100]; copy_string(str, "hello, world\n"); printf(str);

10

15

20

de caractres "a" rduite a et dont la reprsentation en mmoire comporte en fait deux caractres (a et \0).

3.2 Types scalaires et dclarations simples


3.2.1 Panorama des types de C
Il y a deux manires de classier les types de donnes en C : les types scalaires dune part et les agrgats dautre part ; les types de base dune part et les types utilisateurs dautre part. Le tableau 3.5 schmatise cette situation. Comme son titre lindique, cette section ne concerne que les types de base. Les agrgats et les pointeurs sont respectivement tudis aux chapitres 4 et 5.

3.2.2 Type vide (void)


Le type void (qui ne gure pas dans le tableau 3.5) joue un rle particulier en ce sens quil ny a aucun objet de ce type il dsigne en quelque sorte lensemble vide. A quoi peut-il servir? En fait, il nest utilis que dans trois circonstances qui seront tudies plus tard : pour dclarer quune fonction na pas darguments (7.2.1) ; pour dclarer quune fonction na pas de rsultat cest une procdure (7.2.1) ; enn, dans le sens conventionnel de pointeur sur void (void* 5.1.4).

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.2. Types scalaires et dclarations simples

51

Table 3.5 Panorama des types de C


Types scalaires entiers Types de base char avec variations de signe (signed et unsigned), int avec variations de longueur (short, long) et de signe (signed et unsigned) float, double, long double tableaux, structures (struct), unions (union) Types utilisateurs pointeurs, numrations (enum)

Types scalaires rels Agrgats

3.2.3 Types de base entiers


C possde une grande varit de types de nature entire (integral types). Comme indiqu dans le tableau 3.5 cela inclut bien entendu la reprsentation des entiers signs ou non de diffrentes tailles mais aussi les numrations, les caractres, sans oublier les pointeurs.
Le type int et ses variations

Le type int constitue la reprsentation naturelle 3 des valeurs entires avec les oprations habituelles (voir 3.3.1). Cependant plusieurs sous-types sont dnis, rpartis en deux catgories indpendantes selon la possibilit dtre sign ou non dune part, et selon le nombre de valeurs reprsentables dautre part. Un entier non sign (unsigned) est toujours positif et obit aux lois de larithmtiques modulo 2n , o n est le nombre de bits de sa reprsentation. Le tableau 3.6 rsume toutes les variations possibles des entiers, avec les contraintes minimales imposes par la norme ansi C sur lensemble des valeurs reprsentables. Lexamen de ce tableau appelle un certain nombre de commentaires et de remarques : 1. Les modieurs de type (signed, unsigned, short, long...) sont optionnels et signed est le dfaut, sauf pour char. Le type de base peut galement tre omis, cest alors int. Donc le type signed short int est quivalent short int ou mme short tout court. ; 2. Les charactres sont considrs comme des (petits) entiers et le fait que, par dfaut, ils soient signs ou non dpend de limplmentation ; 3. Les entiers ordinaires sont garantis tre reprsents sur 16 bits au moins ; en fait les valeurs indiques sont des contraintes minimales et une implmentation peut les dpasser : ainsi sur la plupart des processeurs mots
3. La reprsentation est naturelle au sens du matriel : cest le type dentier que le processeur manipule le plus aisment. Sur nos machines actuelles, cest gnralement un mot de 32 bits, voire de 64.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

52

3. Bases du langage

de 32 bits, un int est cod sur 32 bits (ce qui donne un intervalle de valeurs de -2 147 483 648 +2 147 483 647 pour un signed en complment 2) et de 0 4 294 967 295 pour un unsigned). 4. Les intervalles de valeurs effectivement utiliss par une implmentation sont dnis dans le chier denvironnement <limits.h>. Table 3.6 Sous-types de int Nom du type char signed char unsigned char signed short int signed int long int unsigned short int unsigned int unsigned long int Intervalle minimal de valeurs 0 .. 255 ou -127 .. +127 -127 .. +127 0 .. 255 -32 767 .. +32 767 -32 767 .. +32 767 -2 147 483 647 .. +2 147 483 647 0 .. 65 535 0 .. 65 535 0 .. 4 294 967 295

Portabilit : ensemble de valeurs

Le respect des intervalles de valeurs du tableau 3.6 permet dassurer la portabilit. Le ou sur le signe ou labsence de signe du type char doit inciter la prudence : si lon veut utiliser les caractres comme de petits entiers et faire dessus des oprations arithmtiques, il est prudent de spcier explicitement si lon veut des unsigned char ou des signed char.

Type dune constante entire littrale

Une constante caractre correspondant un caractre du jeu de caractres (ASCII par exemple) comme a ou \n est de type char et est garantie positive. Une constante entire littrale en notation dcimale, comme 123 ou encore 1 512 827 304, a le premier parmi les types suivants qui permet de reprsenter sa valeur : int, long, unsigned long. Si la reprsentation est octale ou hexadcimale, les types tests sont dans lordre : int, unsigned, long, unsigned long. On peut forcer une constante entire littrale, quelle que soit sa reprsentation, tre dun des sous-types des entiers en utilisant les sufxes suivants : u ou U pour unsigned, l ou L pour long. Voici quelques exemples :
123U 123L 123UL 123LU unsigned int long unsigned long unsigned long

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.2. Types scalaires et dclarations simples

53

3.2.4 Types rels


La norme connait essentiellement deux types de rels : simple et double prcision (float) et double). Elle permet aussi le type long double mais C90 ne prcise pas ses caractristiques. Pour les float comme pour les double, lintervalle des valeurs absolues reprsentables doit tre au minimum de 1037 10+37 ; un float doit avoir une prcision dau moins 6 chiffres, alors que cest 10 pour un double. Les valeurs effectivement utilises par une implmentation sont dans le chier denvironnement standard <float.h>. Une constante relle littrale, comme 3.141592 ou 6.02E+23 est par dfaut de type double. On peut la forcer tre float en utilisant le sufxe f ou F (3.141592f ou 6.02E+23F).
Types rels en C99

La norme C99 a fait un gros effort pour rendre plus portables et plus rigoureuses les manipulations de nombres rels. Ceci est voqu en 11.3.6.

C99

3.2.5 Dnitions dobjets de type de base scalaire


Dnitions simples

La syntaxe dune dclaration de variable dun type de base scalaire est immdiate : elle a la forme
nom-du-type liste-didentificateurs ;

Par exemple :
int i; short int i, j, k; long l1, l2;

Une variable peut tre initialise lors de sa dnition :


int i = 1; unsigned long ul1 = 0xFFFF0000; float x = 3.141592f; double y = 2.7182818284;

Linitialisation se fait individuellement pour chaque variable : ainsi, dans


int i, j = 2, k, l = 1;

seuls j et l sont initialiss. Si une variable nest pas initialise lors de sa dnition, C utilise des rgles par dfaut qui dpendent du contexte de dnition (voir 8.3.3) : dans certains cas la variable nest pas initialise du tout. Moralit : si une variable doit tre initialise, elle doit ltre explicitement.
Modieurs de type : const et volatile

Une dnition peut tre prcdes de const : lobjet est alors une constante (symbolique) : sa valeur ne pourra plus tre modie. Une consquence immdiate est quune dnition de constante comporte ncessairement son initialisation.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

54

3. Bases du langage

const int LINE_LENGTH = 100; const double PI = 3.141592; const double AVOGADRO = 6.02E+23;

Notez quil est usuel (mais conventionnel) de nommer les constantes avec des majuscules. Une dnition de variable peut galement tre prcde de volatile. Ceci signie que la variable peut changer de valeur en dehors de la volont du programme (cest--dire sans quil y ait de modication de cette variable dans le programme). Cest le cas des variables qui reprsentent des registres dentresortie de contrleurs et dont la valeur peut tre change par le monde extrieur. Un autre est constitu par les segments de mmoire partage entre processus. La prsence de volatile indique au compilateur quil doit viter certaines optimisations. Par exemple dans le cas de lextrait de programme
void test_io(void) { volatile int io_register = 0; while (io_register == 0) { sleep(1); /* attendre 1 seconde */ } }

un compilateur astucieux pourrait, en labsence de la dclaration volatile, sapercevoir que io_register nest pas modi dans le corps de la boucle, qutant une variable locale, il ne peut tre modi ailleurs, et que lon peut donc transformer la fonction en la forme quivalente suivante comportant une boucle innie et sans la variable locale puisquelle ne sert rien !
void test_io(void) { while (1) /* boucle infinie */ { } }

La prsence de volatile, en signalant la possibilit que io_register puisse tre modi lextrieur du programme empche cette optimisation : ltat de de io_register sera donc bien vri toutes les secondes jusqu ce quil devienne non nul. Une constante peut tre volatile :
const volatile double vc;

Cela signie que lobjet vc ne peut tre modi par programme mais peut ltre par lextrieur. Dans ce cas, la dnition de constante nexige pas dinitialisation.

3.2.6 Types numrs


Il sagit dun sous-type des entiers, mais dni par lutilisateur. La dnition dun type numr indique la liste des valeurs que peut prendre un objet du type : ainsi

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.2. Types scalaires et dclarations simples

55

enum color {GREEN, ORANGE, RED};

dnit-il un type, de nom enum color 4 . On peut dnir des objets du type et les affecter entre eux :
enum enum ... c1 = c2 = color c1; color c2 = GREEN; c2; RED;

On peut la fois dnir le type et un ou plusieurs objets du type :


enum first_name {BOB, CAROL, TED, ALICE} p1, p2 = CAROL;

Dans ce cas on peut mme faire lconomie du tag :


enum {BOB, CAROL, TED, ALICE} p1, p2 = CAROL;

Le type devient anonyme et il sera donc impossible de dnir plus tard dautres objets du mme type. En fait, les valeurs que peut prendre un objet de type numr sont des entiers, dbutant 0 par dfaut. Dans les exemples prcdents, VERT vaut 0 et ALICE vaut 3. On peut mme forcer les valeurs que lon souhaite pour certains lments :
enum instruction { LOAD = 0xfa, STORE, ADD = 0x10, SUB, MUL, DIV, JUMP = 0x20, HALT = 0xff, };

/* 0xfb */ /* 0x11 */ /* 0x12 */ /* 0x13 */

Dans une certaine mesure (voir cependant 3.4.2) on peut mlanger les objets de types numrs et les entiers. Deux types numrs diffrents ne peuvent avoir des valeurs de mme nom :
enum color {GREEN, ORANGE, RED}; enum fruit {APPLE, PEAR, ORANGE}; /* erreur! */

3.2.7 Synonymie de types : typedef


Linstruction typedef permet de dnir des noms de types synonymes. La syntaxe est la mme que celle dune dclaration dobjet prcde du mot-cl typedef. Ainsi
unsigned int U_int;

est la dclaration dune variable U_int, alors que


4. enum est un mot-cl de C, color est un identicateur choisi par le programmeur, le tag, et le nom du type est enum color.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

56

3. Bases du langage

typedef unsigned int U_int;

dnit le nom U_int comme tant synonyme de unsigned int. On peut donc maintenant dclarer ou dnir des objets de type U_int :
U_int u1, u2; U_int u3 = 12u; U_int add = 0xFFFF;

Voici quelques autres exemples :


typedef double Length; typedef double Surface; typedef const int C_int; typedef volatile double V_double;

Cette instruction permet de donner un nom plus manipulable aux types numrs. Par exemple, en supposant que le type enum color soit dj dni, mais pas enum doc :
typedef enum color Colors; typedef enum doc {ARTICLE, REPORT, BOOK} Document; typedef enum {MALE, FEMALE} Sex; typedef enum {FALSE, TRUE} Boolean; ... Colors c = GREEN; Document d, d1 = REPORT; Sex sx = MALE; Boolean flag = FALSE;

Noter que typedef dnit une simple synonymie, et non de la cration dun nouveau type. Ainsi, aprs
Length l; Surface s;

les deux variables s et l sont de mme type (double) et on peut par exemple crire laffectation
s = l;

3.3 Oprateurs
Les oprateurs combins avec les identicateurs et les constantes littrales ou symboliques permettent dcrire des expressions. Chaque expression a un type et une valeur, rsultat de son valuation. Le tableau 3.7 donne la liste de tous les oprateurs du langage avec leur signication et la rfrence de la section o ils sont tudis.

3.3.1 Oprateurs arithmtiques


Sauf mention explicite du contraire, les oprateurs dcrits ici sappliquent tous les objets (constants ou variables) de type entier ou rel (y compris toutes les variations). Si les entiers sont unsigned, les rgles de larithmtique modulo 2n sont utilises.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.3. Oprateurs

57

Table 3.7 Liste des oprateurs de C


Oprateur () [] -> . ! ~ + * & ++ -(type) sizeof * / % + << >> > <= > >= == != & ^ | && || ? : = += *= ... , Utilisation f(x1, x2, ...) t[i] p->champ s.champ !a ~a -a +a *p &x x++ ou ++x x-- ou --x (type)a sizeof(x) a*b a/b a%b a+b a-b a<<b a>>b a<b a<=b a>b a>=b a==b a!=b i&j i^j i|j a&&b a||b a?b:c a=b x1,x2,...,xn Signication appel de fonction indexation slection de champ de structure slection de champ de structure ngation logique complment 1 moins unaire plus unaire indirection adresse de post ou pr-incrmentation post ou pr-dcrmentation conversion explicite (cast) taille mmoire dun objet multiplication division modulo (reste de la division) addition soustraction dcalage gauche dcalage droit infrieur infrieur ou gal suprieur suprieur ou gal gal diffrent de et bit bit ou exclusif bit bit ou bit bit et logique squentiel ou logique squentiel expression conditionelle affectations valuation squentielle Rfrence 3.3.7 3.3.5 3.3.5 3.3.7 3.3.2 3.3.3 3.3.1 3.3.1 3.3.5 3.3.5 3.3.4 3.3.4 3.3.6 3.3.6 3.3.1 3.3.1 3.3.1 3.3.1 3.3.1 3.3.1 3.3.1 3.3.2 3.3.2 3.3.2 3.3.2 3.3.2 3.3.2 3.3.3 3.3.3 3.3.3 3.3.2 3.3.2 3.3.1 3.3.4 3.3.7

ansi C connait deux oprateurs arithmtiques unaires : + et -. Cinq oprateurs binaires sont dnis : addition (+), soustraction (-), multiplication (*), division (/), modulo (%). Noter quil nexiste pas doprateur dexponentiation. La division est la division entire si les deux oprandes sont entiers. Lopration modulo est possible seulement sur les entiers, pas sur les rels : a%b donne le reste de la division entire de a par b. A titre dexemple, le programme 3.4 calcule de manire rcursive le Plus Grand Commun Diviseur de deux entiers (qui sont donns en arguments de la ligne de commande). En voici quelques exemples dexcution :
% gcd 0 1 gcd(0, 1) = 1 % gcd 12 7

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

58

3. Bases du langage

gcd(12, 7) = 1 % gcd 24 12 gcd(24, 12) = 12 % gcd 27 63 gcd(27, 63) = 9 % gcd -27 63 gcd(-27, 63) = 9 % gcd -27 -63 gcd(-27, -63) = 9 % gcd 0 0 gcd(0, 0) = 0 %
Portabilit : signe du reste et du quotient

Pour la division entire et le modulo, le signe du quotient (a/b) et du reste (a%b) dpend de limplmentation. Cest bien entendu une calamit, et on doit donc viter les oprandes ngatifs pour ces deux oprations. En particulier, remarquez les prcautions qui sont prises dans le programme 3.4.

3.3.2 Oprateurs relationnels et logiques


Ces oprateurs retournent une valeur logique cest--dire, comme il nexiste pas de type boolen en C90, un entier (int) : 0 reprsente la valeur faux et 1 la valeur vrai. En revanche, C99 propose un type boolen, nomm _Bool (cest un motcl) dont les lments ne peuvent prendre que les valeurs 0 et 1. En outre, aprs avoir inclus le chier dentte <stdbool.h>, vous pouvez utiliser le synonyme bool (pour _Bool) et les deux valeurs true (1) et false (0). Notez que bool, true et false ne sont pas des mots-cls (mais des macros du pr-processeur).
Oprateurs relationnels

C99

Les oprateurs relationnels sont utilisables avec tout type scalaire entier ou rel. Ils sont au nombre de six : <, >, <=, >=, == et !=. Noter que loprateur dgalit est == et non pas = ce dernier est loprateur daffectation (3.3.4).
Remarque sur lidentit entre boolens et entiers

Le fait quune expression comme a < b retourne un entier (0 ou 1) permet dcrire des expressions tranges comme 4*(a < 0) + 8*(a < 4) + 16*(a < 8) Ce style de programmation est en gnral douteux.
Portabilit : comparaison des rels

Comme souvent dans les langages de programmation, on doit tre trs prudent dans les tests entre rels : le rsultat peut dpendre de la prcision du processeur.

Valeur de vrit dune expression

La valeur de vrit dun expression arithmtique e, note VV (e) est dnie ainsi : vrai si e = 0 VV (e) = faux si e = 0

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.3. Oprateurs

59

Programme 3.4 Calcul du pgcd de deux entiers


/***** Fichier: gcd.c *****/ #include <stdio.h> #include <stdlib.h>
5

int max(int a, int b) { return (a > b) ? a : b; }


10

int min(int a, int b) { return (a < b) ? a : b; }


15

20

int gcd(int a, int b) { int a0 = abs(a); /* abs est defini dans <stdlib.h> */ int b0 = abs(b); int p = max(a0, b0); int q = min(a0, b0); return (q == 0) ? p : gcd(q, p%q); }

25

int main(int argc, char *argv[]) { int x, y;


30

35

if (argc != 3) { fprintf(stderr, "usage: gcd a b\n"); return 1; } x = atoi(argv[1]); y = atoi(argv[2]); printf("gcd(%d, %d) = %d\n", x, y, gcd(x, y)); return 0; }

40

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

60

3. Bases du langage

En principe e peut tre de nimporte quel type arithmtique, mais, comme on la dj remarqu, on doit se mer du test 0 des rels. Il vaut donc mieux se limiter des expressions entires. Noter aussi que mme les valeurs (strictement) ngatives donnent la valeur de vrit vrai.
Ngation logique

Lexpression !a inverse la valeur de vrit de lexpression a : !a = 0 si a = 0 1 si a = 0

Connecteurs logiques squentiels

Dans lexpression a&&b, loprateur && dnote le et logique (conjonction) des deux valeurs de vrit des expressions a et b. Cependant lvaluation seffectue squentiellement de gauche droite et sinterrompt ds que la valeur de lexpression est dtermine, cest--dire au premier oprande faux. Ainsi, compte tenu de lassociativit de cet oprateur, dans
a$_1$ && a$_2$ && ... && a$_n$

les expressions ai sont values dans lordre, et lon sarrte la premire dont la valeur de vrit est faux, le rsultat global tant alors faux galement. Dans cet autre exemple
int t[100]; ... if (n < 100 && t[n] == 0) ...

lexpression t[n] nest value que si n est infrieur 100, vitant ainsi le potentiel dbordement dindice. Loprateur || est dual de && : cest le ou logique (disjonction). Son valuation est elle-aussi squentielle, de gauche droite, et sarrte donc la premire expression qui la valeur vrai.

3.3.3 Oprateurs bit bit


Ces oprateurs travaillent directement sur la reprsentation binaire de leurs oprandes.
Complment 1

Loprateur unaire ~ inverse les 0 et les 1 de la reprsentation binaire de son oprande.


ET

et

OU

bit bit

Les deux oprateurs & et | ralisent respectivement le et et le ou bit bit de leurs oprandes. Loprateur ^ ralise le ou exclusif.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.3. Oprateurs

61

Dcalages

Une expression comme a<<b dcale la valeur de a gauche de b positions de bits alors que a>>b effectue le mme dcalage mais vers la droite.
Portabilit des oprations de dcalage

La portabilit des oprations de dcalage nest garantie que si les deux conditions suivantes sont runies : 1. le premier oprande (celui que lon dcale) est unsigned int ; 2. le second oprande (la valeur du dcalage) est un entier positif ou non sign de valeur infrieure au nombre de bits correspondant la reprsentation du type du premier oprande. Dans ces cas a<<b (resp. a>>b) est quivalent la multiplication (resp. la division) par 2b . Dans tous les autres cas, la valeur du rsultat est laisse la discrtion de limplmentation ! Le programme 3.5 illustre cette pathologie du dcalage. Voici le rsultat de son excution : % chkshift ui = 2147483647, (ui << 1) = -2 i = 2147483647, (i << 1) = -2 j = -1, (j >> 1) = -1 k = -1073741825, (k >> 1) = 2147483646 %

Programme 3.5 Pathologie du dcalage


/***** Fichier: chkshift.c *****/ #include <stdio.h>
5

10

int main() { unsigned int ui = 0x7FFFFFFF; int i = ui; int j = -1; int k = 0xBFFFFFFF; printf("ui = %d, (ui << 1) printf("i = %d, (i << 1) = printf("j = %d, (j >> 1) = printf("k = %d, (k >> 1) = return 0; }

/* 01111...11 */

/* 10111...11 */

15

= %d\n", ui, ui << 1); %d\n", i, i << 1); %d\n", j, j >> 1); %d\n", k, k << 1);

Exemple

An dillustrer ces oprations de manipulation de bits, le programme 3.6 ralise des oprations courantes de test et de positionnement dun bit dans un tableau de bits (ici un unsigned char). Noter lutilisation de la fonction (en fait la macro) assert dnie dans le chier dentte <assert.h>.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

62

3. Bases du langage

Programme 3.6 Manipulation de bits


/***** Fichier: bit_ops.c *****/ #include <stdio.h> #include <limits.h> #include <assert.h> /* Les bits asont numerotes a partir de 0 */ /* Il y a exactement CHAR_BITS (defini dans <limits.h>) bits dans un unsigned char / * typedef unsigned char Bit_Array;
15

10

unsigned char set_bit(Bit_Array bits, unsigned int n) { assert(n < CHAR_BIT); return bits | (1 << n); } unsigned char reset_bit(Bit_Array bits, unsigned int n) { assert(n < CHAR_BIT); return bits & ~(1 << n); } int test_bit(Bit_Array bits, unsigned int n) { assert(n < CHAR_BIT); return bits & (1 << n); } int main() { Bit_Array b = 0x00; set_bit(b, 156); b = set_bit(b, 3); if (test_bit(b, 3)) printf("Jusquici, tout va bien\n"); b = reset_bit(b, 3); if (b == 0) printf("Cela a lair de marcher...\n"); return 0; }

20

25

30

35

40

45

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.3. Oprateurs

63

Linstruction assert(cond) value la condition cond. Si elle est vraie, rien ne se passe ; sinon lexcution se termine avec un message derreur. Par exemple, imaginons que nous invoquions set_bit(b, 156) dans le programme 3.6. Lexcution se termine alors ainsi :
% bit_ops bit_ops: bit_ops.c:17: set_bit: Assertion n < 8 failed. zsh: abort bit_ops %

3.3.4 Affectations
Affectation simple

Laffectation simple est dsigne par loprateur =. Dans une expression daffectation comme a = b, loprande de gauche ne peut pas tre une simple valeur (comme 12) ; ce doit tre une lvalue modiable cest- dire une rfrence sur une variable (par exemple le nom de cette variable). La valeur de cette variable est remplace par celle de lexpression en oprande gauche b. Tous les types scalaires sont affectables, ainsi que les structures et les unions comme nous le verrons (4.2.3). Comme toute expression, a = b a un type, celui de a, et une valeur, celle de a aprs que laffectation ait t ralise. On peut utiliser cette valeur dans une expression plus complexe comme
j = 4*(i = 5);

qui donne j la valeur 5. Ce type dexpression avec effet de bord doit tre utilis avec parcimonie. Nanmoins, dans certains contextes il est dans la logique sinon le gnie du langage ; nous avons dj rencontr en 2.2 des expressions comme
while ((c = getchar()) != EOF) ...
Attention : = et ==

La possibilit de cet effet de bord rend encore plus dangereuse la confusion entre loprateur daffectation (=) et celui dgalit (==). Dans if (a = 0) ... il ny a aucune erreur de syntaxe ; ce nest pas le test de a 0, mais laffectation de 0 a suivi du test de la nouvelle valeur de a (cest--dire 0 !). La condition ne sera donc jamais vraie ici.

Affectation compose

Pour tout oprateur binaire , C dnit un oprateur = 5 daffectation sur place tel que
a = b

est quivalent
a = a (b);
5. Il ny a pas despace entre et =.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

64

3. Bases du langage

avec cette diffrence que a nest valu quune seule fois. Cette dernire restriction ne pose pas de problme si a est une simple variable. mais nous verrons que la situation peut tre plus complexe (5.1.1). Noter les parenthses autour de b qui forcent lvaluation de cette expression avant dappliquer loprateur . Les oprateurs binaires composables avec laffectation (cest--dire ligibles pour remplacer ) sont les suivants :
* / % + << >> & ^ |

Voici quelques exemples dutilisation :


a a a a a a += 2 *= 3 %= 2 <<= 3 |= m &= m incrmentation de a de 2 triplement de a a remplac par son imparit a decal gauche de 3 bits les bits 1 de m le seront aussi dans a les bits 1 de m seront 0 dans a ...

Dans les deux derniers cas, les autres bits de a sont inchangs.
Incrmentation et dcrmentation

Lexpression ++a, o a est une lvalue modiable, est strictement quivalente a += 1 Elle rend donc la valeur de a aprs lincrmentation : on parle dincrmentation prxe. En revanche, la valeur de lexpression ++a est celle de a avant son incrmentation. On parle dincrmentation postxe. Bien entendu, loprateur de dcrmentation -- est lui aussi dni avec les deux formes, prxe et postxe.

3.3.5 Oprateurs sur les pointeurs


Il sagit des oprateurs dindexation ([]) eh oui, cest un oprateur ! de slection de champ (->), dindirection (* unaire) et dadresse (&). Ils sont tudis en 5.1.1.

3.3.6 Oprateurs sur les types


Conversions explicites (casts)

Pour (tenter de) convertir une expression e dans le type T, il suft dcrire (T)e :
float x = (float)3; ... x = 1/(double)2; ... c = (char)0; /* float x = 3.0; */ /* x = 1/2.0; */ /* c = \0; */

En C, cette conversion na de sens que si le type T et celui de a sont scalaires. La smantique de ces conversions est tudie en 3.4.2 et en 5.1.4. En C il sagit toujours dun mcanisme rsolu la compilation. On trouve le nom dun type en considrant une dclaration dun objet du type et en y retirant lidenticateur correspondant lobjet dclar :

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.3. Oprateurs

65

int i; unsigned short int ui; const double PI = ...; volatile char c;

type : int type : unsigned short int ou unsigned short type : const double type : volatile char

Nous verrons des applications de cette rgle simple dans des cas bien plus complexes plus loin.
Taille mmoire dun objet

Lexpression sizeof(x) o x est soit une rfrence sur un objet, soit un nom de type. Le rsultat est la taille mmoire occup par lobjet ou un objet du type. Par dnition
sizeof(char) == 1

Cette taille est donc exprime en nombre de caractres. En C, la taille des objets est connue la compilation, et cest donc la compilation que sizeof(x) est valu : cest un exemple dexpression statique.

3.3.7 Oprateurs divers


Les oprateurs dappel de fonction (()) et de slection de champ de structure (.) sont tudis respectivement au chapitre 7 et en 4.2.3.
Expression conditionnelle

Nous avons dj rencontr cette expression en 2.5. Elle scrit


a ? b : c

et se lit : si a alors b sinon c . Elle a donc la valeur de lexpression b si lexpression a est vrai (i.e. a est non nulle) et celle de c sinon. Une remarque supplmentaire : seule lexpression qui dtermine la valeur est value 6 . Ainsi dans
int t[100]; ... a = (n < 100) ? t[n] : 0;

t[n] nest valu que si n est infrieur 100, vitant un potentiel dbordement dindice.
Remarque : question de style

Il est clair quune expression comme la prcdente est quivalente if (n < 100) a = t[n]; else a = 0; Quelle forme prfrer? Jai tendance dans ce cas privilgier la premire qui indique clairement que a va changer de valeur. Dans la seconde forme, cest tout bonnement un hasard que a se retrouve cible de laffectation dans les deux instructions du if ; une lecture trop rapide pourrait la confondre avec
6. Non seulement cest bien naturel, mais en plus cest garanti !

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

66

3. Bases du langage

if (n < 100) b = t[n]; else a = 0; On peut cependant regretter la syntaxe cryptique de cette instruction conditionnelle.

On peut noter au passage que a&&b est quivalent a?(b!=0):0 et a||b a?1:(b!=0).
Oprateur dvaluation squentielle

Une expression comme


a1 , a2

value dabord a1 , puis a2 . La valeur et le type de lexpression tout entire sont ceux de la dernire expression, cest--dire ici a2 . Par exemple, a++ est quivalent
((tmp = a), (a += 1), tmp)

o tmp est une variable temporaire.


Attention : il y a virgule et virgule

La virgule qui spare les arguments dun appel de fonction sort(t, n); nest pas loprateur dvaluation squentielle. En fait, il ny a aucune garantie sur lordre dvaluation des paramtres dune fonction (voir 7.2.3).

3.4 Evaluation des expressions


3.4.1 Ordre dvaluation : prcdence et associativit
Le grand nombre doprateurs disponibles en C a comme consquence une certaine complexit dinterprtation des expressions, en particulier pour le dbutant. C dnit la prcdence on dit aussi la priorit des oprateurs ainsi que leur associativit. Les rgles en sont donnes dans le tableau 3.8, les oprateurs les plus liants tant cits dabord. Quand deux oprateurs ont la mme force de liaison, la dernire colonne indique le sens de lassociativit : ainsi une expression comme
a - b - c

sinterprte bien comme


(a - b) - c

puisque loprateur binaire - associe de gauche droite. Bien entendu, il est toujours possible dutiliser des parenthses pour forcer un autre ordre dvaluation ou tout simplement pour amliorer la lisibilit. Le sens dassociativit est dni pour tous les oprateurs bien que cela nait pas ncessairement de sens, lopration ntant pas naturellement associative. Par exemple

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.4. Evaluation des expressions

67

Table 3.8 Prcdence et associativit des oprateurs de C Associativit gauche droite droite gauche

()

! & * + << < == & ^ | && || ? : (conditionnelle) Affectations simples et composes = += *= ... , (virgule)

[] -> . Oprateurs unaires + * ++ -- (type) sizeof / \% >> <= > >= !=

gauche droite gauche droite gauche droite gauche droite gauche droite gauche droite gauche droite gauche droite gauche droite gauche droite droite gauche droite gauche gauche droite

a < b < c

doit sinterprter comme


(a < b) < c

ce qui compare le rsultat de (a < b) (0 ou 1) la valeur de c, ce qui nest sans doute pas ce qui est dsir. De manire plus souhaitable, on note que les affectations associent de droite gauche, ce qui permet dcrire
a = b = c

qui est interprt comme


a = (b = c)

et qui affecte a et b la mme valeur, celle de c au type prs (voir 3.4.3). Certaines priorits sont pigeantes : par exemple
if (a == b&0xff) ...

doit se lire
if ((a == b)&0xff) ...

ce qui nest pas particulirement naturel. On doit galement se mer de lexpression conditionnelle :
x = 3 + a ? b : c;

sinterprte comme
x = (3 + a) ? b : c;

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

68

3. Bases du langage

ce qui l encore nest pas trs naturel. En revanche, les affectations sont trs peu liantes, ce qui est normal. Ainsi les parenthses sont ncessaires dans
while ((c = getchar()) != EOF) ...

car la forme non parenthse se lirait


while (c = (getchar() != EOF)) ...

ce qui teste bien la n de chier, mais affecte c le rsultat de cette comparaison, 0 ou 1, et non pas le caractre lu effectivement. Cependant les affectations sont plus liantes que loprateur dvaluation squentielle :
x = a1, a2, a3;

se lit
(x = a1), a2, a3;

affectant donc a1 et non pas a3 x. Il aurait donc fallut crire


x = (a1, a2, a3);
Attention : ordre dvaluation

Prcdence et associativit ne sont pas sufsantes pour dterminer lordre dvaluation dune expression. En fait, seuls quatre oprateurs garantissent exactement lordre dvaluation de leurs oprandes : ||, &&, ?: et la virgule ,. Dans tous les autres cas, lordre dvaluation des oprandes est indni. On doit donc tre particulirement vigileant lorsque certains oprandes contiennent des effets de bord. Aucun des deux exemples ci-aprs nest portable : t[i] = i++; x = ((c = getchar()) ? c : 0) + c;

3.4.2 Conversions
ansi C permet deffectuer des conversions entre types scalaires, soit explicitement (comme en 3.3.6), soit implicitement comme on va le voir un peu plus loin (3.4.3). Cette section donne les rgles qui sont alors appliques, cest--dire la smantique des conversions. Une rgle gnrale simple est utilise pour toutes ces conversions : si la valeur initiale peut tre reprsente dans le nouveau type, la valeur est conserve 7 ; sinon le rsultat dpend de limplmentation.
Promotion entire

Cette conversion est effectue implicitement avant toute autre valuation dune expression. Elle permet dutiliser un caractre (char), un entier court (short), une valeur de type numr (enum), ou encore un champ de bits (voir 4.2.2), quils soient ou non signs, la place dun entier ordinaire. La rgle est simple : si le type int est sufsant pour contenir la valeur convertir, il est utilis ; sinon la valeur est convertie en unsigned int.
7. avec ventuellement une perte de prcision dans les conversions ayant pour cible un type rel

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.4. Evaluation des expressions

69

Conversions entre entiers

Lorsquun entier sign a est converti en non sign, la valeur obtenue u est telle que u = min(v a
v >0

(mod 2n ))

o n est le nombre de bits de la reprsentation. Dans la reprsentation des entiers en complment 2 ceci revient tronquer gauche la reprsentation binaire si le nouveau type est moins large, ou a complter gauche par des 0 dans le cas contraire. Si un entier est converti en un autre type entier sign, la valeur est prserve si le nouveau type peut reprsenter cette valeur ; sinon le rsultat dpend de limplmentation :
int i = 12715; short s = i; char c = i; /* OK: short suffisamment grand */ /* ??: resultat non portable */

Conversions entre entier et rel

Lorsquun entier est converti en rel (float ou double) le rsultat est la valeur reprsentable la plus proche ; le fait que cette valeur soit suprieure ou au contraire infrieure la valeur entire initiale nest pas dni et est dpendant de limplmentation. Dans le cas inverse dune conversion de rel en entier, la partie fractionnaire est simplement ignore (troncature vers 0 et non pas arrondi !). Si la partie entire nest pas reprsentable dans le type cibl, le rsultat dpend de limplmentation. En particulier, la conversion dun rel en entier non sign (unsigned) nest jamais portable.
double x = double y = double z = int i; ... i = x; /* i = y; /* i = -x; /* i = -y; /* i = z; /* 3.141592; 2.7182818; 1.0e+30;

i == 3 */ i == 2 */ i == -3 */ i == -2 */ ??: vraisemblablement non portable */

Conversions entre rels

Si le type cibl est plus grand par exemple conversion de float en double la valeur reste inchange. Dans le cas contraire, si la valeur initiale est dans lintervalle reprsentable, le rsultat est la plus proche valeur (il nest pas dni si elle est infrieure ou suprieure) ; sinon, le rsultat dpend de limplmentation.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

70

3. Bases du langage

3.4.3 Mlange de types dans les expressions


C autorise le mlange des types arithmtiques dans les expressions. Les conversions suivantes dsignes en gnral sous le vocable de conversions usuelles sont alors automatiquement effectues sur les oprandes : 1. Tout dabord, les promotions entires (voir 3.4.2) sont appliques, si ncessaire, aux oprandes ceci garantit que lexpression est value au moins dans le type entier ordinaire (int) et jamais dans un type plus restreint (il ny a pas en C dexpression de type char ou short) ; 2. Si lun des oprandes est long double, lautre est converti en ce type ; 3. Sinon, si lun des oprandes est double, lautre est converti en ce type ; 4. Sinon, si lun des oprandes est float, lautre est converti en ce type ; 5. Sinon, si lun des oprandes est unsigned long, lautre est converti en ce type ; 6. Sinon, si lun des oprandes est long et lautre unsigned int et si un long permet de reprsenter toutes les valeurs de type unsigned int, alors le second oprande est converti en long ; en revanche, si la dernire condition nest pas satisfaite, les deux oprandes sont convertis en unsigned long ; 7. Sinon, si lun des oprandes est long, lautre est converti en ce type ; 8. Sinon, si lun des oprandes est unsigned int, lautre est converti en ce type ; 9. Sinon, cest que les deux oprandes sont int ainsi que le rsultat.
Cas particulier des affectations

Dans une affectation simple entre types scalaires, comme


v = e

les deux oprandes sont valus chacun avec leur type propre. Puis, si ncessaire (et possible !), la valeur de loprande droit est convertie implicitement dans le type de celui de gauche.
double x = 3.141592; int i; ... i = x; /* troncature: i == 3 */ x = 5/2; /* division entiere: x == 2.0 */

On voit donc que le type de la partie gauche de laffectation na aucune inuence sur la manire dvaluer la partie droite ni sur le type de cette dernire. En C le type dune expression ne dpend que du type de ses oprandes. Pour les affectations composes, on se rappelle lquivalence de a += b avec a = a+(b), mais avec une seule valuation de a. Les rgles de conversions se dduisent de la forme expanse.
Attention : affectations en chane

Le mlange de types dans des affectations en chane comme a = b = c

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.5. Instructions et ot de contrle

71

peut donner des rsultats surprenants : aprs cette expression, a, b et c ne sont pas ncessairement gaux. Aprs double a; int b; ... a = b = 3.141592; b vaut 3 et a vaut 3.0 (et pas 3.141592!).

3.5 Instructions et ot de contrle


Il y a trois grandes catgories dinstructions en C : les instructions simples, les blocs et les instructions de contrle. Ces dernires se subdivisent en instruction de slection, boucles et instructions de rupture de squence.

3.5.1 Instruction simple et bloc


Une instruction simple est une expression que lon termine par un pointvirgule.
/* ne fait pas grand chose! */ 4; x = a + b; /* instruction daffectation */ printf("%d %d\n", a, b);

Un bloc permet de regrouper plusieurs instructions : cest une suite dinstructions encadres par des accolades.
{ x = a + b; y = a > b ? a : b; printf("%d %d\n", x, y); }

Un bloc peut comporter des dnitions de variables qui sont alors locales au bloc, cest--dire ne sont visibles et mme ventuellement nont dexistence que dans le bloc (voir le chapitre 8) :
{ int i = 0, j = 3; x = 2*i; y = j -i; }

En C90, les variables locales dun bloc doivent tre dclares 8 en tte de ce bloc, avant toute instruction excutable. Ceci nest plus ncessaire en C99 : comme en C++, les variables locales peuvent tre dclares nimporte o dans le bloc, du moment que cest avant leur utilisation. Ainsi, le bloc suivant est lgal en C99, mais pas en C90 :
{ int x = int y = }
8. En fait, cette dclaration est en fait une dnition voir 8.2.

C99

i = 0; 2*i; j = 3; j -i;

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

72

3. Bases du langage

Un bloc joue le mme rle syntaxique quune instruction simple. Dans la suite, nous dsignerons par instruction aussi bien une instruction simple quun bloc ou une instruction de contrle (objet de la section suivante). Les blocs peuvent bien entendu simbriquer (cest--dire que les instructions dun bloc peuvent elles-mmes tre des blocs).
Attention : pas de point-virgule aprs un bloc

Si une instruction simple est bien termine par un point-virgule, ce caractre ne gure pas derrire laccolade fermante dun bloc. Lerreur est le plus souvent sans consquence sauf dans certaines instructions composes (voir 3.5.2).

3.5.2 Instructions de slection


Instruction de choix binaire : if

Linstruction if a la forme gnrale suivante :


if (condition) instruction-si-vrai else instruction-si-faux

Noter que la condition qui est une expression scalaire quelconque (dont on value la valeur de vrit voir 3.3.2) est entre parenthses, et quil ny a pas en C de mot-cl then. Si les plusieurs instructions sont prsentes dans instruction-si-vrai et instruction-si-faux, il faut les regrouper dans un bloc. Enn la clause else est optionnelle.
if (a == 0) fprintf(stderr, "division par 0"); else x = b/a; if {a != b) { x = a; y = b; }

Les instructions if senchainent trs bien grce la forme else if :


if (isalpha(c)) printf("lettre\n"); else if (isdigit(c)) printf("chiffre\n"); else if (isspace(c)) printf("espace\n"); else printf("%c ni lettre, ni chiffre, ni espace\n");

Bien entendu le dernier else est optionnel. Les fonctions isalpha, isdigit et isspace permettent de dterminer quelle catgorie appartient un caractre ; elles sont dnies dans le chier denvironnement standard <ctype.h>.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.5. Instructions et ot de contrle

73

Ambigut syntaxique : if en cascade

Contrairement la plupart des langages impratifs modernes, le if nest pas termin par un mot-cl comme endif ou fi ; en consquence, dans une instruction comme if (a == b) if (c != d) x = y; else y = x; il y a une ambigut : on ne sait pas quel if se rfre le else. Bien que C ait sa propre rgle pour lever cette ambigut, ce nest pas un bon style de sy er. Il vaut mieux crire explicitement ce que lon veut : par exemple if (a == b) { if (c != d) x = y; else y = x; }
Attention : pas de point-virgule aprs une accolade

Dans lextrait de programme suivant, le point-virgule provoque une erreur de syntaxe : if (a != b) { x = a; y = b; }; else x = y = 0;

Instruction de choix multiple : switch

Cette instruction permet un aiguillage entre plusieurs possibilits ; elle correspond linstruction case des langages de la famille Pascal. Elle a la forme suivante :
switch (expression) { case val1 : instructions1 case val2 : instructions2 ... case valn : instructionsn default: instructions-default }

Les instructionsi , et linstructions-default sont des suites dinstructions quelconques (simples ou blocs) ventuellement vides. La clause default et les instructions-default sont optionnelles. Les expressions val1 , val2 , ..., valn doivent tre statiques cest--dire valuables la compilation. Lexpression est value et sa valeur est compare celle des expressions vali , dans lordre i = 1, 2, ..., n ; si lgalit est trouve, disons pour val j , alors les instructions j sont excutes puis on passe en squence pour excuter instructions j+1 , instructions j+2 , etc... jusqu

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

74

3. Bases du langage

instructions-default incluses. Si aucune galit avec un des vali nest trouve, les instructions-default sont excutes si elles existent ; sinon on sort du switch et on passe en squence. Lorsque certaines des instructionsi sont vides ce comportement trange prend un sens : on obtient alors le ou logique des diffrents cas :.
switch (c) { case \n: printf("fin de ligne"); /* fall through ... */ case : case \t: case \f: printf("espace\n"); }

Dans cet extrait de programme, si c est une n de ligne on obtient les deux messages
fin de ligne espace

Si c est un blanc, un caractre de tabulation horizontal ou un saut de page, on obtient le seul message
espace

et dans tous les autres cas, on na aucun message. Le commentaire /* fall through ... */ ou quelque chose dquivalent est assez souvent rencontr dans cette situation pour bien marquer que le programmeur souhaite effectivement ce passage en squence. Mais dans limmense majorit des cas, on veut retrouver le comportement habituel de linstruction case de Pascal, cest--dire sortir du switch ds que lune des instructionsi celle qui correspond la valeur de lexpression a t excute ; on utilise alors linstruction break qui est une rupture de squence (un saut) la n du switch :
switch (c) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: printf("chiffre\n"); break; case : case \t: case \n: case \f: printf("espace\n"); break; default: printf("ni chiffre, ni espace\n"); break; }

Le dernier break est bien videmment inutile, mais il a t plac l par souci dhomognit.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.5. Instructions et ot de contrle

75

3.5.3 Instructions de boucle


Ces trois instructions permettent deffectuer des itrations controles par une condition dentretien de boucle.
Boucle while

Cette instruction a la forme suivante :


while (condition) instruction

Le corps de la boucle, cest--dire instruction, est excute tant que la condition une expression entire ou convertible en entier a la valeur de vrit vrai. La condition est value et teste avant chaque itration, y compris la premire.
Boucle do ... while

Linstruction do ... while dont la syntaxe est


do instruction while (condition) ;

effectue linstruction jusqu ce que la valeur de vrit de lexpression entire ou convertible en entier condition devienne faux. La condition est value en n de boucle. Le corps de la boucle, cest-dire instruction, est donc toujours valu au moins une fois. En ce sens, la boucle do ... while est donc quivalente la forme suivante :
instruction while (condition) instruction

Notez le point-virgule la n de linstruction, juste aprs while (condition).


Boucle for

Cest la boucle ditration controle, trs particulire C. Sa syntaxe est la suivante :


for (initialisation ; entretien ; rebouclage) instruction

Elle est quivalente


initialisation ; while (entretien) { instruction rebouclage ; }

Ainsi, lextrait de programme suivant o i est une variable entire, et t et u des tableaux de mme type ou de types compatibles et de dimension au moins gale a 10,

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

76

3. Bases du langage

for (i = 0; i < 10; i++) { t[i] = u[i]; u[i] = 0; }

est-il quivalent
i = 0; while (i < N) { { t[i] = u[i]; u[i] = 0; } i++; }

ce qui a pour effet de transfrer les 10 premiers lments du tableau u dans le tableau t et de mettre les 10 premiers lments de u zro. Noter quen sortie de boucle, la variable i vaut 10.
Remarque : variable de controle de boucle

C99

En C90, La variable de contrle de la boucle, i dans lexemple ci-dessus, doit tre dclare avant la boucle for elle-mme. Sa porte est donc strictement plus grande que celle de la boucle elle-mme ; cest souvent une variable locale du bloc englobant. En C99, il est possible comme en C++ de dclarer que la variable de boucle est locale la boucle : for (int i = 0; i < N; ++i) t[i] = 0.0; Dans ce cas, la variable de boucle nest pas connue lextrieur de la boucle : for (int i = 0; i < N; ++i) if (t[i] == 0.0) break; /* sortie, voir plus loin */ printf("element nul en i = %d\n", i); /* NON ! i inconnu ici */ Enn, dans tous les cas, il est possible, bien que de style douteux, de modier cette variable dans le corps de la boucle.

Si la boucle for est trs souvent utilis dans des cas simples comme ceux de lexemple ci-dessus, elle est dune grande richesse dexpression. Dans lextrait suivant
for (i = 0; i < 10; i += 2) t[i] = 0;

un lment sur deux du tableau t est mis zro (lindice de boucle avance par pas de 2). On peut mme avoir plusieurs indices pour une seule boucle :
for (i = 0, j = 0; i < 10; i++, j += 2) t[i] = u[j];

C99

En C99, ces deux indices peuvent tre dclars locaux :


for (int i = 0, j = 0; i < 10; i++, j += 2) t[i] = u[j];

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.5. Instructions et ot de contrle

77

Ici un lment sur deux du tableau u est transfr dans les dix premiers lments (conscutifs) du tableau t.
Attention : boucles simples et imbriques

Dans lexemple prcdent, il ny a quune seule boucle et les deux indices i et j progressent en mme temps, bien quavec des pas diffrents. Le nombre ditrations est donc de 10. Il ne faut pas confondre cet exemple avec celui des boucles for imbriques comme for (i = 0; i < 10; i++) for (j = 0; j < 20; j += 2) ... Dans ce dernier cas, il y a deux boucles et 200 itrations.
Remarque : boucle for incomplte Lun ou plusieurs des trois lments de contrle dune boucle for peuvent tre omises :

for ( ; i < 10; i++) ... for (i = 0; i < 10; ) ... for (i = 0; ; i++) ...

/* pas dinitialisation */ /* pas de rebouclage */ /* pas de condition dentretien */

Dans le dernier cas, la condition dentretien, absente, est suppose valoir 1, cest--dire vrai : la boucle est a priori innie. Ainsi for (;;) {} est une vraie boucle innie quivalente while (1) {}

La boucle for de C dconcerte souvent les dbutants dans ce langage ; ils prfrent alors utiliser la forme while quivalente. Ceci est bien entendu une question de got. Cependant, jai tendance prfrer chaque fois que possible la forme for car elle a lavantage de concentrer syntaxiquement les trois lments de contrle de la boucle : point de dpart, condition de passage litration suivante, instruction de progression.

3.5.4 Instructions de rupture de squence


Abandon de corps de boucle : break

Linstruction break que nous avons dj rencontr dans le cas de switch (3.5.2) a une autre signication : elle permet dabandonner prmaturment une boucle (for, while ou do ... while). Ainsi
for (i = 0; i < 10; i ++) { if (t[i] == k) break; }

est une boucle qui se termine lorsque t[i] vaut k. Noter quen sortie de boucle, la valeur de i est conserve.
Remarque

Lexemple prcdent peut aussi scrire

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

78

3. Bases du langage

for (i = 0; i < 10 && t[i] != k; i ++) {} avec un corps de boucle vide et pas de break.

Linstruction break ne permet de sortir que dun niveau de boucle : dans


for (i = 0; i < 10; i++) { while (j > 0) { //... if (j == i) break; } }

on ne sort que de la boucle interne ; en C le seul moyen de sortir de deux (ou plus) boucles imbriques est dutiliser un goto. De plus, comme break est aussi utilise pour sortir dun switch, il nest pas possible, sans utiliser un goto de sortir prmaturment dune boucle depuis un switch interne :
for (i = 0; i < 10; i++) { switch (t[i]) { case 0: ... case 1: ... // sort de switch, pas de for break; ... } }

Rebouclage prmatur : continue

Linstruction continue est analogue break, mais au lieu dabandonner la boucle, on passe litration suivante (ou plutt on tente de passer en valuant la condition dentretien). Dans le cas dune boucle for les instructions de rebouclage sont bien entendu excutes :
for (i = 0; i < 10; i++) { ... if (i == 0) continue; /* va a literation suivante (i == 1) */ ... }

Rupture de squence inconditionnelle : goto

Linstruction goto permet de transfrer le contrle un point dsign par une tiquette.
... goto La_Bas;

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.6. Exercices du chapitre 3

79

... La_Bas: ...

/* La_Bas, cest ici */

Une tiquette est un identicateur quelconque. Par exemple, pour sortir la fois dun switch et de la boucle qui lenglobe :
for (i = 0; i < 10; i++) { switch (t[i]) { case 0: ... case 1: ... goto out; ... } } out: ...

On peut faire un goto uniquement vers une tiquette dnie dans le mme bloc ou dans un bloc englobant. Il est interdit de faire des goto de lextrieur vers lintrieur dun bloc, du corps dune boucle, dun if ou dun switch. Enn signalons que linstruction goto est considre comme nuisible depuis un article clbre de E. W. Dijsktra [13]. En tout tat de cause, son utilisation est trs souvent lindice dune mauvaise organisation du code.
Retour de fonction: return

Nous avons dj souvent utilis cette instruction qui permet de terminer lexcution dune fonction. Elle a deux formes possibles :
return;

si la fonction ne rend pas de rsultat (son type de retour est void) ou


return expression;

si la fonction une valeur de retour. Lexpression sera convertie dans le type de retour, si ncessaire et possible sinon, cest une erreur.
Parenthsage de lexpression suivant return

Comme return nest pas une fonction (contrairement, par exemple, exit()), il ny pas besoin de parenthses autour de lexpression retourner.

3.6 Exercices du chapitre 3


Exercice 3.1 (Nombre de bits 1 dans un octet) crire un programme qui im-

prime combien de bits sont 1 dans un octet.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

80

3. Bases du langage

Exercice 3.2 (Nombre de caractres, mots et lignes dun texte) crire le pro-

gramme word_count qui lit son entre standard et imprime sur la sortie standard le nombre de lignes, mots et caractres lus sur lentre standard. Votre programme doit avoir le mme comportement que la commande dUnix wc(1) mais, videmment, ne doit pas utiliser cette dernire ! On pourra utiliser les fonctions dnies dans <ctype.h> en particulier isspace pour tester quun caractre est un espace.
Remarque 1

On prcisera ce quon entend par un mot et donc quels sont les sparateurs de mots. Ce doit tre la mme chose que pour la commande standard wc(1).
Remarque 2

Attention la n de chier !
Remarque 3

On pourra ajouter au programme des options -l, -c... comme dans wc(1).
Exercice 3.3 (valuation dun polynme) crire un programme qui reoit en

ligne de commande les coefcients a0 , a1 , . . . , an dun polynme de degr n, imprime le polynome obtenu, rclame une valeur de la variable x et value le polynme pour cette valeur. Pour ce programme, vous crirez deux fonctions dvaluation, une o vous utiliserez la fonction pow de <math.h> et lautre o vous appliquerez le schma de Horner. On rappelle que ce schma consiste remarquer quun polynme de degr n peut scrire sous la forme

(. . . (( an x + an1 ) x + an2 ) x + . . . + a1 ) x + a0
et peut donc tre valu par une procdure rcursive ou itrative (que nous vous laissons le soin de dnir et de mettre en place) ne ncessitant que n multiplications. Voici un exemple dutilisation :
% poly 2 3 4 polynome: 2 + 3 * x1 + 4 * x2 valeur de x ? 2 rsultat direct: 24 rsultat Horner: 24 %

Comme vous pouvez le constater, on a aussi crit une fonction qui permet dafcher le polynme sous une forme raisonnable.
Exercice 3.4 (Nombres premiers) crire un programme qui reoit une suite

dentiers en ligne de commande et pour chacun de ces entiers imprime sil est premier ou non.
Exercice 3.5 (Quelques oprations non portables) tudiez comment votre plate-forme se comporte pour certaines oprations non portables sur les entiers. En particulier : que se passe-t-il en cas de dpassement de capacit entire ?

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

3.6. Exercices du chapitre 3

81

que se passe-t-il en cas de division par zro entire ? que se passe-t-il lorsque loprande droit dune opration de dcalage est ngatif ? le dcalage droit est-il arithmtique ou logique, cest--dire conserve-t-il le signe ou non de son oprande gauche ? quen est-il du dcalage gauche ? que se passe-t-il lorsque le diviseur dune opration modulo est ngatif ? lorsque le dividende est ngatif ? lorsque les deux oprandes sont ngatifs ?

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

82

3. Bases du langage

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Chapitre 4

Tableaux, structures et unions


chapitre dcrit les CedesCtypes-utilisateurs.trois constructeurs de types agrgats disponibles en : tableaux, structures et unions. Ces constructeurs permettent de dnir

4.1 Tableaux
4.1.1 Tableaux mono-dimensionnels
Dnition et dclaration

Un tableau est bien sr une collection dobjets qui sont tous du mme type. Nous avons dj rencontr des dclarations et dnitions de tableaux monodimensionnels (2.4). En voici quelques exemples :
int t[10]; double vec[100]; ... #define NBUF 10000 char buffer[NBUF];

La dimension, cest--dire le nombre dlments, doit tre une expression statique : la taille des tableaux de C90 doit donc tre connue la compilation (voir 4.1.4 pour C99). Il y a cependant exception lorsquun tableau est pass en argument dune fonction 1 : la dimension du tableau peut tre laisse libre, condition que la fonction puisse la connaitre par ailleurs. La dimension dun tableau de C nest en effet pas conserve lexcution : en particulier, C ne sait pas vrier les dbordements dindice 2 . A titre dexemple, dans le programme 4.1, la fonction prod_scal calcule le produit scalaire des deux vecteurs de dimension n donns en arguments.
Remarque : dimension dun tableau et const

En C90, on ne peut malheureusement pas xer la dimension dun tableau laide dune const : const int N = 100; /* ERREUR!! */ double t[N];
1. Nous verrons plus tard (5.3.2) que ce nest pas vraiment le tableau qui est pass en paramtre mais plutt un pointeur, ladresse de son premier lment. 2. Certains compilateurs, ou certains outils de lenvironnement de programmation, peuvent cependant proposer un tel service, mais il nest pas dni dans la norme.

V 2.1 7 fvrier 2010

83

c Jean-Paul R IGAULT

84

4. Tableaux, structures et unions

Programme 4.1 Produit scalaire de deux vecteurs


/***** Fichier: prod_scal.c *****/ #include <stdio.h> #include <stdlib.h>
5

double prod_scal(int n, double v1[], double v2[]) { int i; double p;


10

for (i = 0; i < n; i++) p += v1[i]*v2[i]; return p; }


15

20

int main() { double x1[5] = {0, 1, 2, 3, 4}; double x2[5] = {3, 1, -1, -1, 1}; printf("<x1, x2> = %g\n", prod_scal(5, x1, x2)); return 0; }

Bien entendu, il est toujours possible dutiliser les macros du prprocesseur et #define : #define N 100; ... double t[N]; mais ceci est loin davoir les mmes avantages (voir 6.3.1).

C99

En revanche, C99 permet ce type de dimensionnement par une constante, mais uniquement pour les variables locales (voir 4.1.4).

On peut bien sr dnir un synonyme pour le nom dun type tableau ; la dimension fait partie du type :
typedef double Vector[1000]; Vector v1, v2;

Ici Vector est le type tableau de 1000 double .


Initialisation dun tableau

Un tableau peut tre initialis au moment de sa dnition :


int t[5] = {1, 1, 1, 1, 1}; int u[5] = {1, 2}; int v[] = {1, 2, 3, 4, 5, 6};

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

4.1. Tableaux

85

La syntaxe utilise est dite initialisation dagrgat . Dans lexemple du tableau u seules les deux premires composantes sont initialises. Pour v, la dimension est laisse libre ; cest le compilateur qui la calculera en fonction du nombre dlments dans la liste dinitialisation (ici la dimension est donc 6).
Taille dun tableau et operateur sizeof

Noter que pour tout tableau, le rapport entre la taille du tableau et la taille dun composante (ces tailles tant fournies par loprateur sizeof 3.3.6) est gal au nombre de composantes. Ainsi pour v plus haut : const int n_elems_v = sizeof(v)/sizeof(int);

Bien entendu, un tableau peut tre constant (resp. volatile), ce qui revient dire que toutes ses composantes le sont :
const int ctab[] = {1, 0, 0, 0}; const volatile unsigned status_registers[NDEVICES];

Dans ce cas, ctab[i] ne peut tre modi et status_registers[i] peut changer de valeur sans que le programme ne le modie explicitement.
Opration dindexation

Lopration dindexation est la principale et de fait la seule opration sur un tableau : pour un tableau t, t[i] dsigne une rfrence sur la composante dindice i : lindice doit tre une expression entire, ou convertible en entier, la premire composante a comme indice 0. Comme loprateur retourne une rfrence, lexpression dindexation peut donc apparaitre en partie gauche dune affectation, condition que le tableau ne soit pas constant, bien sr :
int t[10] = {...}; int u[10]; ... u[0] = 0; for (i = 1; i < 10; i++) u[i] = t[i - 1];

Copie de tableaux

Comme nous le justierons plus tard (5.3.2), on ne peut pas affecter les tableaux entre eux. Cet extrait de programme est incompilable :
int t[10], u[10]; ... u = t; /* ERREUR */

Pour faire la copie il faut utiliser une boucle


for (int i = 0; i < 10; ++i) u[i] = t[i];

ou alors utiliser une fonction systme telle que memcpy :


memcpy(u, t, 10*sizeof(int));

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

86

4. Tableaux, structures et unions

4.1.2 Tableaux multi-dimensionnels


En C, un tableau bi-dimensionnel est en fait un tableau de tableaux et les syntaxes de dclaration et dindexation retent ce fait :
double mat[10][100]; for (i = 0; i < 10; i++) for (j = 0; j < 100; j++) mat[i][j] = 0.0;

mat est une matrice de double 10 lignes et 100 colonnes. En mmoire, une telle matrice est range par lignes, cest--dire que cest le dernier indice qui varie le plus vite :
mat[0][0] mat[1][0] ... mat[9][0] mat[0][1] mat[1][1] mat[9][1] mat[0][2] ... mat[0][99] mat[1][2] ... mat[1][99] mat[9][2] ... mat[9][99]

La syntaxe dinitialisation dagrgat rete aussi cette notion de tableau de tableaux :


int ident[3][3] = { {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, };

On lit sur cette initialisation les diffrentes lignes du tableau ident (la matrice identit de dimension 3). A titre dexemple trs simple, le programme 4.2 effectue la transposition sur place dune matrice carre 3 3 lue sur lentre standard. En voici un exemple dexcution :
% transpose ligne 0 (entrez 3 valeurs): 1 -1 -1 ligne 1 (entrez 3 valeurs): 1 2 -2 ligne 2 (entrez 3 valeurs): 1 2 3 Resultat: 1 1 1 -1 2 2 -1 -2 3 %

En C, le nombre de dimensions dun tableau nest pas limit : on peut donc avoir des tableaux 2, 3, n dimensions qui sont autant de tableau de tableaux :
int t[3][5][7]; double u[][2][2] {{1, 2}, {2, {{2, 3}, {3, {{3, 4}, {4, }; = { 3}}, 4}}, 5}},

Noter, avec lexemple de u, que seule la premire dimension peut tre laisse libre (ici sa valeur calcule par le compilateur sera 3).

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

4.1. Tableaux

87

Programme 4.2 Transposition dune matrice


/***** Fichier: transpose.c *****/ #include <stdio.h>
5

#define N 3 typedef double Mat3x3[N][N]; void Transpose(int n, Mat3x3 m) { int i, j; double x; for (i = 0; i < N; i++) for (j = i + 1; j < N; j++) { x = m[i][j]; m[i][j] = m[j][i]; m[j][i] = x; } } int main() { int i, j; Mat3x3 mat; for (i = 0; i < N; i++) { printf("ligne %d (entrez %d valeurs): ", i, N); for (j = 0; j < N; j++) scanf("%le", &mat[i][j]); } Transpose(N, mat);

10

15

20

25

30

35

40

printf("Resultat:\n"); for (i = 0; i < N; i++) { for (j = 0; j < N; j++) printf("%g ", mat[i][j]); putchar(\n); } }

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

88

4. Tableaux, structures et unions

4.1.3 Chanes de caractres


En C, une chane de caractres est simplement un tableau mono-dimensionnel de caractres. Cependant, comme nous lavons dj indiqu (3.1.7), il est conventionnel dindiquer la terminaison dune chane par le caractre null (\0). Une chane de caractres occupe donc en mmoire un caractre de plus que sa longueur utile . Toutes les fonctions de la bibliothque standard (9.4.2) respectent et renforcent cette convention (voir aussi les programmes 3.2 et 3.3). En particulier la fonction strlen de la bibliothque retourne toujours la longueur utile ; cest aussi le cas de la fonction string_length du programme 3.2. On peut initialiser un tableau par une chane littrale :
char msg[] = "hello, world\n";

Aprs cette initialisation on a donc


sizeof(msg) == 14 strlen(msg) == 13

Il est bien entendu possible de dnir des tableaux multi-dimensionnels de caractres (voir aussi 5.3.2) :
const char Days[][10] = { "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche", };

Ici Days est une matrice de char dont la dimension est 7 10 soit 9 lignes et 10 colonnes (en fait 7 9 sufrait, mais rappelons nous que seule la premire dimension peut tre calcule automatiquement par le compilateur).
Affectation de chanes de caractres

Les chanes de caractres tant des tableaux, comme ces derniers elles ne sont pas affectables. Ainsi, avec les exemples prcdents, les affectations suivantes sont illgales : msg = "Bonjour le monde!"; Days[0] = "Monday";

4.1.4 Tableaux de taille variables de C99


C99

Nous avons vu que la (les) dimension(s) dun tableau en C90 devaient tre des constantes de compilation. Ce nest plus vrai en C99, au moins pour les tableaux qui sont des variables locales automatiques dune fonction ou dun bloc. En C99, il est possible de dimensionner un tableau local laide dune variable, comme dans
void g(int n) { int t[n]; ... }

Notez cependant que la taille nest toujours pas un attribut du tableau : il est ncessaire de la connaitre par ailleurs (ici grce n). Le programme 4.3 montre un exemple un peu plus compliqu. Voici le rsultat de son excution :

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

4.1. Tableaux

89

% c99_var_array n? 10 main: n = 10 sizeof(tab) = 40 f: n = 10 sizeof(t) = 4 sizeof(u) = 4000 %

Programme 4.3 Tableaux dimension variable de C99


/***** Fichier: c99_var_array.c *****/ #include <stdio.h>
5

/* Quatre prototypes equivalents pour f */ void f(int n, int t[n]); void f(int, int t[*]); void f(int, int t[]); void f(int, int *t); int main() { int n; printf("n? "); scanf("%d", &n); int tab[n]; printf("main: n = %d sizeof(tab) = %d\n", n, sizeof(tab));

10

15

20

f(n, tab); } void f(int n, int t[n]) { int u[n][n*n]; printf("f: n = %d sizeof(t) = %d sizeof(u) = %d\n", n, sizeof(t), sizeof(u)); }

25

On peut faire plusieurs remarques sur ce code : Les quatre formes du prototype de pr-dclaration de f (lignes 5 8) sont quivalentes en C99 ; seules les deux dernires sont correctes en C90. Dans la fonction main, le tableau tab reoit la dimension n lue juste avant ; on voit ici au passage lintrt de pouvoir mlanger dclaration et code (3.5.1) au lieu dtre oblig de mettre toutes les dclarations en tte de bloc. Dans main toujours, noter le calcul de sizeof lexcution et non pas, comme la plupart du temps la compilation. Dans la fonction f, on constate que plusieurs dimensions peuvent tre variables (tableau u). Enn, f montre aussi que lorsquon passe un tableau dimensions variable en paramtre (tableau t), en fait rien ne change par rapport C90. En particulier, la taille est perdue (sizeof(t) retourne ici 4, cest--dire la taille dun pointeur !).

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

90

4. Tableaux, structures et unions

Limitations du mcanisme de dimensionnement variable

Comme il a t indiqu au dbut, ce mcanisme de dimension variable ne sapplique quaux variables locales (automatiques), et certainement pas aux variables statiques. Par exemple, ceci ne compile pas : int n = 10; int tab[n]; int main()} { ... } car tab nest pas locale. Le fait que n soit constante ou pas ne change rien laffaire. Notons que mcanisme permet au passage de dimensionner un tableau local par une constante : en fait il permet dj de le faire par une variable et donc, qui peut le plus, peut le moins.

4.2 Structures
La structure C est lanalogue du record des langages de la famille de Pascal. Elle permet de regrouper en un seul type composite une collection dobjets dont les types sont possiblement diffrents.

4.2.1 Dnition du type et dclarations


La dnition dun type structure a la forme suivante :
struct tag { liste de champs };

Chaque champ de la liste a la mme syntaxe quune dclaration 3 dobjet. Par exemple
struct Address { int street_number; char street_name[100]; short zip_code; char city_name[50]; };

dnit un nouveau type dont le nom est struct Address. Le tag Address est un identicateur choisi par lutilisateur. Un objet de ce type est constitu de quatre champs : un entier (street_number), une chane de 100 caractres 4 (street_name), un entier court (zip_code) et une chane de 50 caractres 5 (city_name). Ce type tant dni, on peut en crer des objets :
struct Address addr1, addr2; struct Address addr_epunsa = { 930, "route des Colles",
3. une dclaration, pas une dnition : en particulier, il ne peut pas y avoir dinitialisation. 4. dont 99 utiles 5. dont 49 utiles

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

4.2. Structures

91

6903, "Sophia Antipolis", };

On remarque lutilisation de la syntaxe dinitialisation dagrgat. Ici encore, cette intialisation peut tre partielle : seuls les premiers champs sont alors initialiss.
Remarque: nom dun type structure

Le nom du type, struct Address, conserve le mot-cl struct. Cest parfois pnibleet lon peut toujours faire une synonymie de type grce typedef : typedef struct aaaa { int street_number; char street_name[100]; short zip_code; char city_name[50]; } Address; ou mme (voir 8.1) typedef struct Address { int street_number; char street_name[100]; short zip_code; char city_name[50]; } Address; et dans les deux cas, le nom du type sera Address tout court : Address addr_epunsa = { 930, "route des Colles", 6903, "Sophia Antipolis", };

Comme toujours, on peut crer un type et un objet de ce type (et mme linitialiser) en une seule instruction :
struct Person { char first_name[50]; char last_name[50]; enum {FACULTY, ADMIN, STUDENT} position; struct Adresse addr; } jpr = { "Jean-Paul", "Rigault", FACULTY, { 930, "route des Colles", 6903, "Sophia Antipolis", } };

On peut mme dans certains cas faire lconomie du tag. Le type devient alors anonyme.
struct { int a; double x; } s1, s2 = {3, 3.141592};

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

92

4. Tableaux, structures et unions

Cependant, dans ce dernier cas, le type tant anonyme, on ne pourra pas crer dautres objets de ce type structure. Combiner dans la mme instruction la fois la dnition dun type et la dnition dobjets de ce type est de style douteux, et devrait tre vit.
Remarque : initialisation dagrgat

En C90, dans la dnition de lobjet tel que jpr ci-dessus, en supposant lobjet addr_epunsa pralablement initialis, il est impossible dcrire simplement (quel que soit le contexte) struct Person jpr = { "Jean-Paul", "Rigault", FACULTY, addr_epunsa, }; En effet, dans une initialisation daggrgat de C90, tous les initialiseurs doivent tre des constantes statiques.

Initialisation de structure en C99 C99

C99 a apport deux modications linitialisation des structures. Tout dabord, il est possible dinitialiser une variable locale (automatique) avec des expressions non statiques. Ceci est donc maintenant permis :
void f() { struct Person jpr = { "Jean-Paul", "Rigault", FACULTY, addr_epunsa, }; int i = random(); struct { int a; double x; } s = {i, i * 5}; // ... }

Dans lextrait prcdent, addr_epunsa aurait pu aussi bien tre une variable gobale, mais jpr doit tre locale. Ainsi
struct Person jpr = { "Jean-Paul", "Rigault", FACULTY, addr_epunsa, }; int main() { ... }

ne compile pas, ni en C90, ni en C99. La deuxime extension apport par C99 est la possibilit dinitialiser les champs de la structure en les nommant :

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

4.2. Structures

93

Figure 4.1 Exemple de champs de bits

struct Person jpr = { .position = FACULTY, .last_name = "Rigault", .first_name = "Jean-Paul", };

On voit dans ce cas que lordre des champs est sans importance et aussi que linitialisation peut ntre que partielle (pas dadresse par exemple). Cette syntaxe est galement utilisable pour les tableaux de structures (voir 4.2.4).

4.2.2 Structures compactes et champs de bits


Un objet de type structure occupe en mmoire une place au moins gale la somme de celles de ses champs. La taille peut tre plus grande que la somme des sizeof des champs cause des contraintes dalignement de certains processeurs. On peut cependant compacter une structure en indiquant le nombre de bits consacrs un champ (bit eld). Par exemple, la structure
struct Instruction { unsigned code_op : 8; unsigned index_reg : 4; unsigned base_reg : 4; int offset : 16; };

dcrit un format dinstruction dun langage-machine, schmatis dans la gure 4.1. Le nom du champ peut tre omis : le champ de bits sert alors despace dalignement (padding).
Type des champs de bits

Un champ de bits ne peut tre que de type entier (int) sign ou non. En outre, en C90, ce ne peut pas tre un type numr, et cest parfois bien dommage! C99 a lev cette impossibilit. Le code suivant compile donc en C99 mais pas en C90 : enum Op_Code {MOVE, ADD, SUB, BRANCH}; struct Instruction { unsigned code_op : enum Op_Code; unsigned index_reg : 4;

C99

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

94

4. Tableaux, structures et unions

unsigned base_reg : 4; int offset : 16; };


Portabilit des champs de bits

Limplmentation des champs de bits est extrmement dpendante du compilateur et du processeur. Les programmes utilisant les champs de bits peuvent tre portables, cependant, sils ne font aucune hypothse sur limplmentation, mais cela rduit de beaucoup leur intrt. En effet, il est clair que lutilisation des champs de bits ne peut se justier que pour se rapprocher de la machine , et dans ce cas, on sait que lon a des chances de perdre toute portabilit.

4.2.3 Oprations sur les structures


Slection de champ de structure

Cest lopration la plus importante : elle est reprsente par loprateur . (dot) et permet de rcuprer une rfrence sur lun des champs. Avec les exemples prcdents:
struct Person pers; ... if (pers.position == FACULTY) { printf("%s %s est un enseignant\n", pers.first_name, pers.last_name); }; ... char initial = pers.last_name[0]; ... pers.position = ADMIN; ... int zip = pers.addr.zip_code;

On remarque lutilisation en cascade de loprateur de slection dans le dernier exemple. Nous verrons (5.2.1) quil existe une autre oprateur de slection applicable aux pointeurs, la che (->)
Attention: initialisation dagrgat et affectation

La syntaxe dinitialisation dagrgat ne peut tre utilise, comme son nom lindique, que dans une initialisation. Elle est illgale pour une affectation : struct Address addr_epunsa = { 930, "route de Colles", 6903, "Sophia Antipolis", }; /* OK: initialisation */ adr_epunsa = { 930, "route de Colles", 6903, "Sophia Antipolis", }; /* ILLEGAL: affectation */

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

4.2. Structures

95

Affectation de structures

Contrairement aux tableaux, les structures sont affectables ; laffectation revient faire une copie champ par champ :
struct Person jpr = { "Jean-Paul", "Rigault", { 930, "route des Colles", 6903, "Sophia Antipolis", } }; struct Person autre_jpr; autre_jpr = jpr;

Nous verrons aussi (7.1.3) que les structures peuvent tre passes en argument ou retournes par une fonction.
Retour sur laffectation des tableaux

On voit que si les tableaux ne sont pas affectables directement (4.1.1), ils le sont quand on les embarque dans une structure !

4.2.4 Tableaux de structures


On peut bien entendu dnir des tableaux de structures :
struct Person foule[100];

Les expressions de slection peuvent devenir un peu plus complexes:


zip = foule[i].addr.zip_code; initial = foule[i].last_name[0];

Pour lire ces expressions, et dcider si lon doit utiliser des parenthses, une seule solution: consulter le tableau de prddence et dassociativit des oprateurs (3.8). Sachez cependant que les priorits ont t bien choisies et quon a rarement besoin de parennthses dans ce type dexpression.
Intiailisation des tableaux de structures en C99

En C99, linitialisation des structures en nommant les champs est tendu aux tableaux de la faon suivante : struct Person faculty[] = { [0].first_name = "Jean-Paul", [0].last_name = "Rigault", [1].first_name = "Paul", [1].last_name = "Franchi", }; L encore, lordre na pas dimportance (mais vitez de perdre le lecteur !). En outre comme la dimension de faculty est laisse libre, le compilateur la calculera automatiquement (ici 2).

C99

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

96

4. Tableaux, structures et unions

4.3 Unions
Les agrgats de type union sont une des particularits du langage C, bien que lon puisse trouver des constructions analogues dans dautres langages. Ils permettent de placer un mme emplacement mmoire des objets de types diffrents mais, contrairement aux structures, un seul objet la fois.

4.3.1 Dnition du type


La dnition dun type union se prsente de manire analogue celle dune structure (voir 4.2.1) en remplaant le mot-cl struct par union :
union tag { liste de champs };

Par exemple :
union Real_Int { int int_view; double real_view; };

La diffrence avec une structure est la suivante : une structure corrrespond un emplacement mmoire assez grand pour contenir lensemble de tous ses champs ; une union, au contraire, correspond un emplacement mmoire sufsant pour contenir le plus grand des champs et donc nimporte lequel dentre eux. Dans lexemple prcdent, union Real_Int est donc le type dun objet qui peut tre soit un int, soit un double. Les objets de type union se dclarent et se dnissent comme les structures :
union Real_Int u1, u2;

Linitialisation dune union doit utiliser la syntaxe dinitialisation dagrgat :


union Real_Int u3 = {3};
Attention : initialisation dune union

Une prcaution cependant : on ne peut initialiser un objet de type union quavec un objet du type du premier champ de lunion. Ceci peut engendrer des surprises en cas de conversions implicites ; ainsi lextrait suivant union Real_Int u4 = {3.141592}; passe bien la compilation. Malheureusement, il ne range pas la valeur relle 3.141592 dans lobjet u4, mais la valeur 3 (rsultat de la conversion de 3.141592 dans le type du premier champ !).

C99

Cependant, en C99, si on utilise la possibilit de nommage des champs comme pour les structures (4.2.1), on peut prciser le type que lon veut utiliser linitialisation: union Real_Int u4 = {.real_view = 3.141592};

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

4.3. Unions

97

4.3.2 Oprations sur les unions


Comme pour les structures (4.2.3) la slection de champs est lopration la plus importante : elle est galement reprsente par loprateur . et permet de rcuprer une rfrence sur lun des champs. Mais ici la slection ne permet que de dterminer le type avec lequel on interprte le contenu de lunion. Ainsi, aprs
union Real_Int u;

lexpression u.int_view est une rfrence sur le contenu de u interprt comme un entier, alors que u.real_view est le mme objet mais interprt comme un rel double prcision. Loprateur de slection che (->) tudi en 5.2.1 est galement applicable aux unions.
Attention : type courant dune union

Il ny a aucun mcanisme en C qui permette de conserver, lexcution, la trace du type de lobjet effectivement prsent dans une union. De plus, aucune conversion nest effectue lors de la slection dun champ dunion. Lextrait de programme suivant est trs certainement erron : union Real_int u; int i; u.real_view = 3.141592; i = u.int_view; En effet, on range dabord la valeur (relle) de dans u, puis lon interprte cet objet comme un entier dans linstruction immdiatement suivante. Le rsultat de linterprtation de la conguration binaire de interprt comme un entier a trs peu de chances davoir une valeur voisine de 3 (par exemple, sur ma machine, la valeur de i est dans ce cas -57 999 238) ! Cest donc le programmeur qui a la responsabilit de garder la trace des types des objets quil place dans une union (voir 4.3.3).
Portabilit des unions

Lutilisation des unions est portable, sauf si le programme fait des suppositions sur limplmentation interne (cadrage, disposition interne, etc.).

Pour toutes les raisons discutes ici, lutilisation des unions en C doit tre rservs des cas particuliers trs spciques (par exemple programmation systme) et doit tre faite avec soin, par des programmeurs trs prudents !

4.3.3 Structures avec variantes


Lune des utilisation les plus courantes des unions est de permettre de raliser des structures variantes, analogues au records avec variant de Pascal ou dAda. Le programme 4.4 constitue un exemple de telle structure. Notez quil utilise des facilits propres C99. Dans ce programme, la structure Person contient 4 champs : le nom, le prnom et le statut (enseignant, administratif ou tudiant) de la personne ; le quatrime champ, info, correspond un objet choisi parmi lune des trois structures dnies juste au dessus (Faculty, Admin et Student). Compte

C99

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

98

4. Tableaux, structures et unions

tenu du choix des noms, il est assez clair, au moins dans la tte du programmeur et sans doute aussi du lecteur, que cest la valeur du champ position qui dtermine la manire dinterprter le contenu de cette union. Mais, faut-il le rpter, rien dans la syntaxe ne lexprime. La fonction print_person rend cette hypothse plus explicite grce linstruction switch. Il est dailleurs extrmement frquent de voir associs une union lutilisation dun type numr et de son corollaire quasi-invitable switch. En outre noter que C requiert une qualication complte du chemin conduisant la variante, comme dans
pers.info.faculty_info.CNU_section

C99

Ici .info slectionne le champ union de pers, .faculty_info slectionne linterprtation du contenu de cette union, et .CNU_section slectionne un champ dans la structure correspondante. Ouf ! Enn, la fonction main contient un cas de test. Notez la manire dont lobjet a_student est initialis. Nous utilisons la possibilit de nommer les champs propres C99, mais pour ce qui concerne la structure Student qui est simple, nous nous contentons de linitialisation dagrgat ordinaire de C90. Programme 4.4: Structure avec variante (en C99)
/***** Fichier: struct_variant.c --- ATTENTION: C99 *****/ #include <stdio.h>
5

10

/* Informations specifiques a un membre du corps enseignant */ struct Faculty { enum {PR, MDC, ATER, MONITOR} grade; char CNU_section[10]; }; /* Informations specifiques a un personnel administratif */ struct Admin { enum {A, B, C, D} category; // categorie administrative char fonction[20]; // fonction exercee }; /* Informations specifiques a un etudiant */ struct Student { enum {Y1 = 1, Y2, Y3} year; // annee detude _Bool repeater; // redoublant ? }; /* Inforamtions pour une eprsonne de lune quelconque * des categorie precedentes */ struct Person { char first_name[50]; // nom char last_name[50]; // prenom enum {FACULTY, ADMIN, STUDENT} position; // statut

15

20

25

30

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

4.3. Unions

99

35

union { struct struct struct } info; // };

Faculty faculty_info; Admin admin_info; Student student_info; informations specifiques selon statut

40

45

/* Impression des informations */ void print_person(const struct Person pers) { printf("%s %s\n", pers.first_name, pers.last_name); switch(pers.position) { case FACULTY : printf("\tenseignant : %s\n", pers.info.faculty_info.CNU_section); break; case ADMIN: printf("\tadministratif : %s\n", pers.info.admin_info.fonction); break; case STUDENT: printf("\tetudiant : annee %d %s\n", pers.info.student_info.year, pers.info.student_info.repeater?"redoublant":""); break; } }

50

55

60

65

70

/* Test simple */ int main() { struct Person a_student = { .last_name = "Glandu", .first_name = "Anthelme", .position = STUDENT, .info.student_info = {Y2, 1}, }; print_person(a_student); return 0; }

75

Voic le rsultat de lexcution de ce programme :


% struct_variant Anthelme Glandu etudiant : annee 2 redoublant %

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

100

4. Tableaux, structures et unions

4.4 Exercices du chapitre 4


Exercice 4.1 (Manipulation de bits) Modier le programme 3.6 an de traiter

des tableaux de bits de longueur maximum NBITS pouvant tre suprieur au nombre de bits dans un char (CHAR_BIT dni dans <limits.h>).
Exercice 4.2 (Calcul matriciel simple) Raliser quelques oprations de calcul

matriciel simple : addition et produit de deux matrices rectangulaires, impression dune matrice, etc.
Utilisation des facilits de C99 C99

Il peut tre agrable de proter des facilits des tableaux de taille variable de C99 (voir 4.1.4). Par exemple le prototype de la fonction daddition peut tre le suivant :
void matrix_sum(int m, int n, double mat1[m][n], double mat2[m][n], double mres[m][n]);

Soyez cependant conscient que le fait de spcier les dimensions dans le prototype nentraine pas de vrication de ces dimensions et quil vous appartient toujours dassurer que vous passez des matrices de taille correcte (et compatible avec les dimensions indiques). En particulier, cest un excellent exercice de faire tourner votre programme en passant volontairement des tableaux de taille incorrecte. Essayez mme de lexcuter plusieurs fois avec les mmes donnes (incorrectes).
Exercice 4.3 (Recherche en table) Un enseignant anglo-saxon note habituellement ses examens en pourcentage (donc de 0 100), mais son administration peut lui demander des notes reprsentes pas les lettres de A E. La table de correspondance est la suivante :

% 060 6170 7180 8190 91100

Note E D C B A

crire un programme qui imprime la table de correspondance entre les notes de 0 100 et les lettres. Amliorer ce programme pour ajouter un + ou un - aprs la lettre en fonction du dernier chiffre du pourcentage selon la tableau suivant : Chiffre 13 47 80 Modieur +

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

4.4. Exercices du chapitre 4

101

Ce tableau nest valable que pour les notes de A D, E ne peut avoir de modieur !
Exercice 4.4 (Oprations sur les dates) On souhaite reprsenter les dates par

une structure trois champs entiers reprsentant respectivement lanne, le mois, et le jour du mois an de faire des oprations arithmtiques. Les oprations suivantes doivent tre ralises (d, d1 et d2 sont des dates et n un entier) : date_valid(d) Retourne un boolen indiquant si la date est valide : lanne doit tre suprieure ou gale 1900 (Posix ne gre pas de dates infrieures), le mois doit tre compris entre 1 et 12, et le jour entre 1 et 28, 29, 30 ou 31 selon le mois et lanne... is_leap(y) Retourne un boolen indiquant si lanne y est bissextile ; une anne est bissextile si elle est divisble par 4, moins que ce soit une n de sicle auquel cas elle doit tre divisble par 400 (1900, 2100 ne sont pas bissextiles, 2000 lest). today() Retourne la date du jour. On utilisera les fonctions time et localtime de <time.h> pour obtenir cette information. print_date(d) Imprime une version humainement lisible de la date comme 23/01/2008. compare_date(d1, d2) Retourne 0 si les deux dates sont gales, une valeur ngative si la premire est infrieure la seconde, une valeur positive sinon. add_date(d, n) Retourne la date n jours aprs d ; ainsi add_date(today(), 1) retourne demain. sub_date(d, n) Retourne la date n jours avant d ; ainsi sub_date(today(), 1) retourne la veille. diff_date(d1, d2) Retourne le nombre algbrique de jours entre d1 et d2. Ainsi la diffrence entre aujourdhui et demain est -1 alors quentre demain et aujourdhui, cest +1.
Remarque

Pour la ralisation des fonctions arithmtiques add_date, sub_date et diff_date nessayez pas dtre trop malin ! Utilisez des algorithmes simples, par exemple des boucles jour par jour, en grant correctement les passages de mois et danne.
Exercice 4.5 (Gestion de liste de contacts) crire une gestion dagenda, cest-

-dire dune collection dindividus avec leur nom, prnom, adresse et numro de tlphone. Les fonctions suivantes doivent au minimum pouvoir tre effectues : recherche sur le nom et le prnom,

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

102

4. Tableaux, structures et unions

afchage de la liste complte, introduction dun nouveau contact condition quil ne gure pas dj dans la base, destruction dun enregistrement tant donns son nom et son prnom. Dans cette premire version on se contentera dune structure de donnes simple et en mmoire. Dautres exercices viendront plus tard complter celui-ci.
Remarque

Pour cette exercice, vous utiliserez les fonctions de manipulation de chanes de caractres de la bibliothque standard (<string.h>) en particulier la fonction de comparaison de chanes strcmp.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Chapitre 5

Pointeurs
pointeur est un objet (variable ou dont la est ladresse Undun autre(types access). Cependantconstante) dans desvaleurbien plus gobjet. Cette notion est bien connue langages comme Pascal ou Ada les pointeurs de C sont nraux que ceux de ces langages, dans la mesure o il est possible de prendre ladresse de tout objet et pas seulement des objets allous dynamiquement. En contre-partie de cette gnralit, la manipulation de pointeurs en C est dlicate et souvent source de graves erreurs. Maitriser C, cest dabord maitriser lutilisation des pointeurs.

5.1 Dclarations et oprations sur pointeurs


5.1.1 Pointeurs simples
Un pointeur simple contient ladresse dun objet qui nest pas lui-mme un pointeur. Lobjet point peut tre un scalaire, une structure, une union et aussi, comme nous le verrons plus tard, une fonction (7.3.3). Pour une raison qui apparaitra en 5.3.2, ce ne peut pas tre un tableau.
Dclaration dun pointeur simple

Les pointeurs de C sont typs : un pointeur sur un entier na pas le mme type quun pointeur sur un double ou quun pointeur sur une structure ; des pointeurs sur des structures de types diffrents nont pas le mme type. On ne peut donc pas mlanger des types de pointeurs diffrents dans les expressions. La syntaxe de la dnition et de la dclaration de pointeurs doit indiquer le type de lobjet point :
type-point *identificateur;

Par exemple :
int *pi; double *px; struct Person *ptr_pers; union Real_Int *pu;
Attention: factorisation des dclarations de pointeurs

Lorsque lon veut dclarer plusieurs pointeurs en une seule dclaration (ce qui est par ailleurs de style souvent douteux), il importe de remarquer

V 2.1 7 fvrier 2010

103

c Jean-Paul R IGAULT

104

5. Pointeurs

Figure 5.1 Pointeur simple

que ltoile est attache lidenticateur (mme sil peut y avoir des espaces entre cette toile et le nom de lobjet). Ainsi int *pi, *pj; dclare bien deux pointeurs sur int, alors que int * pi, pj; dclare un pointeur sur entier (pi) et un simple entier (pj).

Initialisation de pointeurs; oprateur adresse (&)

Une dclaration de pointeur peut bien sr comporter une initialisation. Loprateur & unaire permet de prendre ladresse dun objet. Plus prcisment, pour un objet a de type T, lexpression &a est un pointeur constant 1 sur a. Par exemple, dans
int i = 4, j; int *pi = &i;

pi est un pointeur sur entier qui contient ladresse de lobjet entier i. La gure 5.1 illustre la situation. Loprateur adresse ne peut sappliquer qu une rfrence sur un objet, et pas une valeur : &4, &(x + 2) ou encore &sin(3.14) sont des erreurs. Dans le chier dentte standard <stddef.h> est dnie la constante NULL : on peut donner cette valeur tout pointeur, quel que soit son type. On peut galement comparer tout pointeur cette valeur. Par convention, un pointeur qui a la valeur NULL ne pointe sur aucun objet. Dans une expression entre pointeurs, la valeur entire 0 est galement convertie implicitement en NULL 2 . On peut donc crire, par exemple,
1. car ladresse dun objet est une constante et ne peut pas tre modie par le programme 2. En fait, en ansi C, NULL est dni ainsi: #define NULL 0

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

5.1. Dclarations et oprations sur pointeurs

105

int *pi = 0;

au lieu de
int *pi = NULL;

Oprateur dindirection (*)

Loprateur unaire * est linverse de &. Il sapplique un pointeur et rend une rfrence sur lobjet point. Ainsi, aprs les dnition de i, j et pi prcdentes
j = *pi; *pi = 5; (*pi)++; /* j vaut 4 */ /* i vaut maintenant 5 */ /* i vaut maintenant 6 */

Noter les parenthses indispensables cause de la prcdence des oprateurs (voir 3.4.1 et 5.1.3). Il est clair que loprateur dindirection ne doit tre appliqu un pointeur que si ce dernier pointe effectivement sur un objet. Sinon, lexcution du programme sera sanctionne (le plus souvent par un core dumped !) En particulier, appliquer loprateur dindirection un pointeur qui a la valeur NULL provoque invitablement une telle catastrophe.
Remarque : partie gauche daffectation et valuation unique pour les affectations composes

Avec les pointeurs, la partie gauche dune affectation peut devenir une expression assez complexe. En voici quelques exemples : int *p, *p1, *p2; ... /* lire: *(p++) = 3 */ *p++ = 3; *(a > b ? p1 : p2) = 4; On note la prcdence des oprateurs dans le premier exemple ; dans le second, selon le rsultat de la comparaison a < b, cest lobjet point par p1 ou celui point par p2 qui prend la valeur 4. Dans le cas dune affectation compose, on voit lintrt de la remarque sur lvaluation unique (3.3.4). Considrons en effet *(a < random() ? p1 : p2) += 2; o random est une fonction qui retourne une valeur alatoire. Cette instruction value une seule fois random, et ajoute 2 soit lobjet point par p1, soit celui point par p2. Il est clair que ceci est un comportement radicalement diffrent de celui de *(a < random() ? p1 : p2) = *(a < random() ? p1 : p2) + 2;

5.1.2 Pointeurs multiples


Un pointeur est un objet qui a lui mme une adresse : on peut donc lui appliquer loprateur &, ce qui donne une valeur de type pointeur sur pointeur . Ainsi dans

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

106

5. Pointeurs

Figure 5.2 Pointeur double

int i = 4; int *pi = &i; int **ppi = &pi;

&pi est une constante de type pointeur sur pointeur sur entier ou encore double pointeur sur int , alors que ppi est une variable de ce mme type. La situation est schmatise dans la gure 5.2. Sur un pointeur double comme ppi on peut appliquer une ou deux fois loprateur dindirection : *ppi est une rfrence sur un pointeur sur entier (int *), alors que **ppi est une rfrence sur un entier.

5.1.3 Oprations arithmtiques sur pointeurs


Comparaison de pointeurs

Des pointeurs de mme type peuvent tre compars pour lgalit (==) ou lingalit (!=) ; ces mmes oprateurs permettent de comparer un pointeur, quel que soit son type, la valeur NULL. On peut aussi comparer les pointeurs grces aux oprateurs <, <=, > et >=. Cette comparaison na pas vraiment de sensla plupart du temps. Cependant elle a un sens lorsque les deux pointeurs pointent sur un mme agrgat, par exemple un tableau, comme dans lexemple suivant :
int t[100]; int *p1 = &t[0]; int *p2 = &t[5]; ... if (p1 < p2) {...}

/* OUI */

En revanche, dans le cas suivant :


int t1[100]; int *p1 = &t1[0]; int t2[10];

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

5.1. Dclarations et oprations sur pointeurs

107

int *p2 = &t2[5]; ... if (p1 < p2) {...}

/* ??? */

le rsultat dpend de limplmentation. Et quel peut bien tre lintrt de savoir si &t1[0] est infrieure ou suprieure celle de &t2[5] ?
Addition dun entier un pointeur

Pour un pointeur p sur un type T (ou une expression de type pointeur sur T) et une expression entire n, lexpression p+n a comme valeur un pointeur sur T qui pointe sur le nme objet de type T suivant si n > 0 (resp. prcdant si n < 0) celui point par p. Illustrons cette opration par lextrait de programme suivant :
struct Person *px; struct Person tab[100]; ... px = &tab[5];

Ici px pointe sur la structure Person qui est la composante dindice 5 du tableau tab. Dans ces conditions, px+3 pointe sur le troisime lment de type struct Person suivant tab[5], cest--dire tab[8]. Et px-2, quant lui, pointe sur tab[3]. On voit donc que laddition dun entier un pointeur nest pas la simple addition la valeur de ladresse mmoire. Il y a en fait une mise lchelle en fonction de la taille de lobjet point. Notons, une fois encore, quil ny a aucune vrication de dbordement dindice : dans lexemple prcdent, px-10 est certainement une erreur, mais elle nest dtecte ni la compilation, ni lexcution sauf option spciale de mise au point. Bien entendu, les cas particuliers daddition et de soustraction entires reprsents par les affectations composes (+= et -=) et les incrmentations et dcrmentations (++ et --) sont applicables aux pointeurs.
Soustraction de deux pointeurs (de mme type)

Si p1 et p2 sont deux pointeurs pointant sur des objets de mme type T, lexpression p2-p1 rend une valeur entire (int) qui est le nombre algbrique dobjets de type T contenu entre celui point par p2 et celui point par p1. Ce nombre est bien sr nul si p2 == p1, positif si p2 > p1, ngatif si p2 < p1. Comme pour la comparaison de pointeurs mentionne ci-dessus, une telle diffrence na vraiment de sens que si p1 et p2 sont non seulement de mme type, mais pointent sur des lments dun mme tableau : dans ce cas en effet on est sr quil y a un nombre entier exact dlments de mme type entre les deux pointeurs :
double t[10]; int u[3]; double v[5]; ... double *p1 = &t[3]; double *p2 = &t[7];

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

108

5. Pointeurs

int n = p2 - p1; // OUI : n == 4, le nombre de double entre p2 et p1 ... n = &v[2] - p1; // cela compile, mais quest-ce que cela signifie ?
Attention : addition de deux pointeurs

Lopration daddition de deux pointeurs, mme sils sont de type identique na pas de sens. Le compilateur ansi C met dailleurs un message derreur. Quant aux programmeurs qui voudraient calculer un pointeur sur un objet qui est exactement au milieu des objets points par les deux pointeurs p1 et p2, ils devront dabord se rendre compte que la notion dtre exactement au milieu nest pas compltement dnie : elle dpend de la parit de p2-p1. Une fois le choix fait, ils pourront essayer des expressions de la forme p1 + (p2 - p1)/2 qui ont un sens, car il sagit de laddition dun entier un pointeur.

5.1.4 Conversions de pointeurs


Pointeurs gnriques : void*

Par convention 3 , un pointeur sur void est un pointeur brut , une adresse machine. Tout pointeur peut tre converti en void* sans perte dinformation. Ceci signie que si le void* est nouveau converti dans le type initial, il retrouve la mme valeur (i.e., pointe sur le mme objet). Ces conversions, dans les deux sens, peuvent tre implicites ou bien sr explicites (cast) comme dans les exemples suivants :
int i = 4; double x = 3.14; int *pi = &i; double *px = &x; void *pv; ... pv = pi; pi = pv; ... pv = (void *)px; px = (double *)pv;

/* OK: pi pointe sur i */

/* OK: px pointe sur x */

Mais attention aux incohrences :


pv = pi; px = pv; /* Catastrophe annonce !!!! */

Dans le dernier exemple, pv prend la valeur dun pointeur sur int puis est converti en pointeur sur double. Si lon essaie de prendre une rfrence sur lobjet maintenant point par px (*px), cela revient interprter lentier point par pi comme un double, mais sans conversion ! Cest un problme analogue celui des unions (4.3.2) : le programmeur a la responsabilit de garder la trace du type exact point par un pointeur sur void. Il est noter que ce type derreur est indtectable dans le cas gnral, la compilation comme lexcution.
3. Ce ne peut tre quune convention, puisquil ny a aucun objet de type void.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

5.1. Dclarations et oprations sur pointeurs

109

Enn, et pour des raisons videntes, les seules oprations permises sur un void* sont laffectation et les tests dgalit et dingalit, ainsi que la prise de ladresse du pointeur lui-mme. Les oprations dindirection (*), laddition et la soustraction dun entier, la soustraction de deux pointeurs, loprateur -> (5.2.1) ou encore lindexation (5.3.2) sont interdites pour les void*, car elle ncessitent toutes la connaissance du type, ou au moins de la taille, de lobjet point.

5.1.5 Pointeurs et constantes


Considrons le cas dun pointeur simple. Il y a deux notions de constante possibles pour un tel pointeur : le pointeur lui mme est constant, lobjet point est constant. Les dclarations correspondantes sont les suivantes :
int i; ... /* pointeur constant */ int *const pconst = &i; const int *p_sur_const = &i; /* pointeur sur constante */

Dans le premier cas, cest la valeur de pconst qui est constante ; pconst ne peut donc pas apparaitre gauche dune affectation. En revanche, lobjet point qui sera donc toujours le mme nest pas constant, et *pconst peut apparaitre gauche dune affectation. Noter que linitialisation est ici ncessaire (cest la seule chance de donner une valeur pconst). Dans le second cas, cest lobjet point qui est constant : *p_sur_const ne peut donc apparaitre gauche dune affectation, mais p_sur_const en revanche pourra pointer sur plusieurs objets au cours de sa vie. Linitialisation nest donc pas indispensable ici. Dire, dans le cas de p_sur_const, que lobjet point est constant est un raccourci, voire un abus de langage. En fait, pour tre plus prcis, lobjet ne peut tre modi lorsquil est accd au travers du pointeur ; il peut cependant exister dautres voies daccs qui permettent de modier lobjet point. Cest clair dans lexemple prcdent, puisque p_sur_const pointe sur i qui est une variable. Donc lobjet en question est modiable qand il est accd par le nom i mais ne lest pas quand on utilise *p_sur_const. Un pointeur simple peut la fois tre constant et pointer sur une constante : la dclaration devient alors
const int *const pconst_sur_const = &i;

Ni pconst_sur_const, ni *pconst_sur_const ne peuvent apparaitre en partie gauche dune affectation. L encore linitialisation est ncessaire. Pour les pointeurs multiples, la combinatoire des possibilits augmente. Par exemple pour un double pointeur sur entier, on a les possibilits suivantes :
int **p; const int **p; int **const p; const int **const p; int *const *p; const int *const *p; etc... double pointeur variable **p est constant p est constant p et **p sont constants *p est constant *p et **p sont constants

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

110

5. Pointeurs

On laisse au lecteur le soin de dterminer les cas o les initialisations sont indispensables. Le compilateur ansi C est extrmenent vigilant lors des affectations et initialisations mettant en jeu des pointeurs sur constante. Il impose la rgle naturelle suivante : il est toujours possible de restreindre laccs un objet, mais jamais de ltendre. On peut donc dcider de faire implicitement une constante partir dune variable, mais pas linverse. Les exemples suivants rsument les possibilits et les impossibilits dans le cas de pointeurs simples :
int i; const int ic = 4; int *p = &i; int *p = &ic; const int *p = &ic; const int *p = &i; int *const p = &i; int *const p = &ic; const int *const p = &i; const int *const p = &ic;

OUI: pointeur sur variable NON: objet point constant OUI: pointeur sur une constante OUI: i non modiable travers p OUI: pointeur constant NON: pointeur sur une variable OUI: i non modiable travers p OUI

int *pp; int *const pconst = &i; const int *p_sur_const; const int *pconst_sur_const = &ic; pp = pconst; pconst = pp; p_sur_const = pp; pp = p_sur_const; p_sur_const = pconst; p_sur_const = pconst_sur_const;

OUI: NON: affectation interdite OUI: restriction daccs NON: largissement daccs OUI OUI

Noter que dans la premire srie dexemples, &i est de type int *const, alors que &ic est de type const int *const. Un cas particulier dinitialisation est celui du passage darguments une fonction tudi en 7.3.2.

5.2 Pointeurs sur structures et unions


5.2.1 Oprateur de slection che
C permet bien entendu davoir des pointeurs sur des structures ou des unions :
struct Person somebody; struct Person *ps = &somebody; ... union Real_Int ri; union Real_Int *pu = &ri;

Si lon veut slectionner un champ de structure ou dunion travers le pointeur on peut utiliser loprateur de slection habituel (.), mais ceci oblige appliquer auparavant loprateur dindirection :
(*ps).position = ADMIN; (*pu).int_view = 3;

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

5.2. Pointeurs sur structures et unions

111

Compte tenu de la prcdence des oprateurs (3.4.1), les parenthses sont indispensables et lcriture devient vite pnible. Cest pourquoi C introduit loprateur de slection de champ travers un pointeur, dnot ->. Lexpression p->champ dsigne une rfrence sur le champ de la structure ou de lunion pointe par p ; elle est strictement quivalente lexpression (*p).champ, mais moins lourde. Avec cet oprateur, les exemples prcdents scrivent de manire plus lgre :
ps->position = ADMIN; pu->int_view = 3;

Nous verrons dans la sous-section suivante (5.2.2) lorigine et tout lintrt de cette notation.

5.2.2 Types rcursifs


Un type rcursif est un type qui contient un composant un champ du mme type que lui-mme. Au sens strict ceci est interdit en C : un champ de structure ou dunion ne peut pas tre du type de cette structure ou union. Ainsi ceci
struct Recur { int i; struct Recur r; };

est interdit. La raison principale est que C insiste pour connaitre la taille des objets la compilation, et qu lvidence ceci est ici impossible. En revanche, la taille dun pointeur est connue la compilation. Il est alors possible quun champ dune structure ou dune union soit de type pointeur sur cette structure ou union. Lexemple prcdent peut donc scrire correctement
struct Recur { int i; struct Recur *pr; };

Exemple: liste simple dentiers

Lun des exemples les plus vidents de type rcursif est celui des listes simplement chanes. Le chier dinclusion simple_list.h en 5.1 prsente la dnition dun tel type : une liste simplement chane de valeurs entires. Ce chier contient aussi le prototype de la fonction insert, dont le corps est prsent plus loin. La structure List_Cell 4 comporte donc deux champs : une valeur entire (val) et un pointeur (next) sur un objet de mme type soit List_Cell* . Ceci va permettre de chaner linairement de tels objets comme schmatis sur
4. On voit que List_Cell est la fois le tag et le nom du type dni par typedef. Ceci est autoris, comme nous le verrons en 8.1. Noter aussi que le tag est ici obligatoire car le nom du type dni par typedef nest pas encore connu lintrieur de la dnition de structure.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

112

5. Pointeurs

Programme 5.1 Liste dentiers : dnition du type


#ifndef _SIMPLE_LIST_H_ #define _SIMPLE_LIST_H_ /***** Fichier: Simple_List/simple_list.h *****/
5

10

typedef struct List_Cell { int val; struct List_Cell *next; } List_Cell; struct List_Cell *insert(List_Cell *, int); #endif

Figure 5.3 Liste simplement chane : simple_list.h

la gure 5.3. Par convention mais cette convention est assez universelle la liste sarrte quand le pointeur next vaut NULL. La liste tout entire est reprsentable par un pointeur (type List_Cell *) sur son premier lment (phead).
Remarque: origine de la notation che

Sur la gure 5.3, on voit lorigine et mme llgance de la notation ->. En effet, dans un expression comme phead->next->next->val qui dsigne la valeur porte par le troisime lment de la liste (le second ave la valeur 3), les -> correspondent exactement aux ches du schma.

La fonction insert est dnie dans un autre chier prsent en 5.2. On doit bien entendu inclure le chier simple_list.h (programme 5.1) an dimporter la dnition du type List_Cell. La directive #include effectue ce travail : noter lutilisation des doubles quotes ("...") la place des piquants

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

5.2. Pointeurs sur structures et unions

113

Figure 5.4 Insertion dans une liste avec un simple pointeur

(<...>) qui indiquent ici de chercher ce chier dabord dans le rpertoire courant, avant dexaminer les rpertoires standards du systme (voir 6.2.2). La fonction insert insre la valeur entire v de son deuxime argument dans la liste dont la tte est pointe par son premier argument. Le point dinsertion est devant le premier lment de la liste dont la valeur est suprieure ou gale v. Ainsi, si la liste tait range par ordre croissant avant insertion, elle le demeure aprs. La liste peut comporter des rptitions. La fonction retourne un pointeur 5 sur la nouvelle tte de liste. En effet, au cas o v est infrieur toutes les valeurs de la liste, cest la nouvelle cellule qui devient la tte de liste. Le parcours de liste et linsertion, schmatiss en 5.4, mettent en jeu trois pointeurs : 1. pcurr visite les cellules lune aprs lautre ; 2. pprev pointe toujours sur la cellule prcdant immdiatement celle pointe par pcurr, sauf au dbut quand pcurr vaut phead, o il est NULL ; 3. enn pnew pointe sur une nouvelle cellule de liste qui est alloue dynamiquement pour contenir la valeur v. Lallocation dynamique est effectu par lappel de la fonction malloc. Cette fonction reoit en argument la taille dun objet, obtient du systme lallocation dune zone mmoire sufsamment grande pour contenir un tel objet et retourne un pointeur sur cette zone (voir 9.1.4). Nous ignorons ici le traitement du cas derreur o la mmoire serait indisponible : malloc retournerait alors la valeur NULL. Le parcours de liste sinterrompt soit en n de liste (pcurr devient NULL), soit ds que la valeur contenue dans la cellule courante (pcurr->val) devient suprieure ou gale v. On note que tout le travail est effectu dans la partie
5. Pour une explication de la dclaration de cette fonction, et en particulier de son type de retour, voir 7.3.1.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

114

5. Pointeurs

Programme 5.2 Liste dentiers : insertion par ordre croissant


/***** Fichier: Simple_List/simple_list.c *****/ #include <stdlib.h>
5

#include "simple_list.h" List_Cell *insert(List_Cell *phead, int v) { List_Cell *pcurr; List_Cell *pprev; List_Cell *pnew; pnew = malloc(sizeof(List_Cell)); pnew->val = v;

10

15

20

for (pcurr = phead, pprev = NULL; pcurr != NULL && v > pcurr->val; pprev = pcurr, pcurr = pcurr->next) { /* Vide */ } /* Ici, soit pcurr est NULL, soit pcurr pointe sur le premier element superieur ou egal a v / * if (pprev != NULL) { /* La liste netait pas vide (phead != NULL) */ pnew->next = pcurr; pprev->next = pnew; return phead; } else if (pcurr != NULL) { /* La nouvelle cellule devient la tete de liste */ pnew->next = pcurr; return pnew; } else { /* La liste etait vide (phead == NULL) */ pnew->next = NULL; return pnew; } /*NOTREACHED*/ }

25

30

35

40

45

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

5.2. Pointeurs sur structures et unions

115

contrle de la boucle for (le corps est vide) : cest videmment une question de style et de got. Lorsque la boucle sarrte trois cas sont possibles : 1. si pprev nest pas NULL on insre pnew 6 juste derrire la cellule pointe par pprev noter que pcurr peut tre NULL si le nouvel lment devient le dernier de la liste ; la tte de liste est inchange (gale phead) ; 2. si pprev est NULL, mais pas pcurr, cest que pcurr pointe sur le premier lment de la liste (pcurr est gal phead) et que pnew devient la nouvelle tte de liste ; 3. enn si pprev et pcurr sont tous les deux NULL, cest que phead ltait aussi et pnew devient la tte de liste et aussi la seule cellule. Enn, le programme 5.3 sert de test pour la fonction insert. Il lit une liste de nombres entiers, les insre un un et imprime le rsultat qui doit tre une liste trie. Noter ici que le source du programme tient sur deux chiers ; pour obtenir un excutable, on peut utiliser la commande
% gcc -o simple_list simple_list_main.c simple_list.c

mais il est conseill dutiliser make (10.2.2). Ici, on a besoin dun chier de nom makefile (ou Makefile) dans le mme rpertoire que les chiers-source. En voici le contenu (en supposant que vous avez dni les variables denvironnement CC et CFLAGS) :
# Makefile pour la liste simplement chaine # Cible finale : excutable simple_list simple_list : simple_list_main.o simple_list.o T A B $(CC) -o simple_list simple_list_main.o simple_list.o # Dpendances individuelles simple_list_main.o simple_list.o : simple_list.h # Nettoyage clean : T A B -rm *.o simple_list

Les tab reprsentent effectivement le caractre tab du clavier. Il suft alors dinvoquer make depuis le shell puis dexcuter le programme produit :
% make gcc -g -std=c99 -Wall -c simple_list_main.c simple_list.c gcc -g -std=c99 -Wall -c simple_list.c gcc -o simple_list simple_list_main.o simple_list.o % simple_list Entrez une suite dentiers terminees par EOF 1 2 5 7 0 6 4 2 6 9 -1 -3 Liste triee: -3 -1 0 1 2 2 4 5 6 6 7 9 %
Allocation dynamique ou variable locale ?

Dans le programme prcdent les cellules de la liste sont alloues dynamiquement, grce lappel de malloc. Ceci est invitable. Comme on ne
6. abus de langage pour la cellule pointe par pnew !

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

116

5. Pointeurs

Programme 5.3 Liste dentiers : programme principal


/***** Fichier: Simple_List/simple_list_main.c *****/ #include <stdio.h>
5

#include "simple_list.h" int main() { List_Cell *phead = NULL; List_Cell *p; int n; printf("Entrez une suite dentiers terminees par EOF\n"); while (scanf("%d", &n) != EOF) phead = insert(phead, n); printf("Liste triee:\n"); for (p = phead; p != NULL; p = p->next) printf("%d ", p->val); putchar(\n); return 0; }

10

15

20

connait pas lavance le nombre (maximal) de cellules ncessaires, il nest pas possible de les allouer, par exemple, dans un tableau dont la taille devrait tre connue la compilation ; il faut donc les crer au fur et mesure des besoins. Cette cration ne peut pas tre sous la forme dune variable locale (automatique voir 8.3.1), dans la fonction insert, car une telle variable disparaitrait ds que la fonction se terminerait alors que les cellules et la liste doivent perdurer (aussi longtemps que le programmeur le dsire).

Parcours de liste et insertion avec double pointeur

La fonction dinsertion prcdente utilise deux pointeurs pour parcourir la liste : sur la cellule courante et sur la cellule immdiatement prcdente. On peut se demander sil nest pas possible dutiliser un unique pointeur. La rponse est positive, comme le montre le programme 5.4. Le pointeur unique est en fait un double pointeur pp qui pointe tour tour, non pas sur chaque cellule, mais sur le champ next de la cellule. Il faut aussi modier la structure de donnes elle mme : on introduit une cellule de liste xe qui sert de tte de liste et dont le champ val nest pas utilis. Avec cette hypothse, la tte de liste est xe et la fonction insert na plus besoin de retourner une valeur. En outre, pour reprsenter la liste vide, il suft de faire boucler la tte de liste sur elle-mme (gure 5.5(a)). On constate que le code est plus court, sinon plus simple comprendre. Il est aussi plus homogne puisquil ne traite aucun cas particulier comme le faisait la programmation prcdente. Bien sr, sa lecture et plus encore son cri-

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

5.2. Pointeurs sur structures et unions

117

Programme 5.4 Insertion dans une liste avec un seul (double) pointeur
/***** Fichier: Simple_List/simple_list_double_ptr.c *****/ #include <stdlib.h> #include <stdio.h>
5

10

typedef struct List_Cell { int val; struct List_Cell *next; } List_Cell; void insert(List_Cell *phead, int v) { List_Cell **pp; List_Cell *pnew; pnew = malloc(sizeof(List_Cell)); pnew->val = v;

15

20

25

for (pp = &phead->next; *pp != NULL && v > (*pp)->val; pp = &(*pp)->next) { /* Vide */ } pnew->next = *pp; *pp = pnew; }

30

35

int main() { List_Cell head = {0, NULL}; List_Cell *p; int n; printf("Entrez une suite dentiers terminees par EOF\n"); while (scanf("%d", &n) != EOF) insert(&head, n);

40

printf("Liste triee :\n"); for (p = head.next; p != NULL; p = p->next) printf("%d ", p->val); putchar(\n);
45

return 0; }

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

118

5. Pointeurs

Figure 5.5 Insertion dans une liste avec un double pointeur

(a) Reprsentation de la liste vide

(b) Insertion dune cellule

ture ncessitent un peu dhabitude de C. La gure 5.5(b) schmatise le parcours de boucle.

5.3 Pointeurs et tableaux


Cette section tudie les relations entre tableaux et pointeurs. Tout dabord, C permet de dnir des tableaux de pointeurs, ce qui fait lobjet de la premire sous-section. Compte tenu de la forme particulire de la dclaration de tels tableaux, nous sommes conduits donner aussi un rgle pour la lecture des dclarations de C. La deuxime sous-section traite dun des problmes trs particuliers du langage C : lquivalence partielle entre tableaux et pointeurs.

5.3.1 Tableaux de pointeurs ; lecture des dclarations de C


Dans les dclarations suivantes
int *ptab[10]; int **p2tab[100];

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

5.3. Pointeurs et tableaux

119

ptab est un tableau de 10 pointeurs sur entiers (int) et p2tab un tableau de 100 doubles pointeurs sur entiers. Pourquoi en est-il ainsi ? Comment interprter ces dclarations? Un petit retour en arrire simpose. Lorsque nous dclarons un entier par
int i;

nous disons simplement que lobjet dnot i, quand il est rencontr dans une expression, a une valeur de type int. De mme une dclaration comme
int t[10];

indique que si lon rencontre t[e] dans une expression, o e est une expression entire, alors cette forme dsigne un int. Toujours de manire analogue, la dclaration
int *pi;

signie que dans une expression, *pi est un int. Comme * est loprateur dindirection, ceci revient bien dire que pi est un pointeur sur int. Revenons maintenant aux dclarations
int *ptab[10]; int **p2tab[100];

Compte tenu de cette rgle dinterprtation, la premire dclaration signie que *ptab[e], o e est une expression, est un int. A cause de la prcdence des oprateurs (3.4.1), *ptab[e] doit se lire comme *(ptab[e]). Ceci signie que ptab[e] est un pointeur sur int, puisquon peut lui appliquer loprateur dindirection. Et nalement ptab est lui-mme un tableau de pointeurs sur int, puisquon peut lui appliquer loprateur dindexation. Cette rgle de lecture des dclarations de C est toujours valable et elle sera prcieuse lorsque nous tudierons les dclarations de pointeurs sur fonctions (7.3.3).

5.3.2 Relations entre tableaux et pointeurs


Indexation des pointeurs

Revenons sur lopration daddition dun entier un pointeur (5.1.3) dans le cadre de cet extrait de programme :
int t[10]; int *p = &t[0];

Le pointeur p pointe donc sur le premier lment du tableau t. Conformment la dnition de laddition dun entier un pointeur, pour tout entier i (suprieur ou gal 0 et infrieur 10), p+i est un pointeur sur llment dindice i de t. Donc p+i == &t[i]. Donc encore, *(p+i) et t[i] rfrencent le mme objet. A cause de ces remarques, le langage C permet dappliquer lopration dindexation un pointeur. Pour tout pointeur p et toute expression i, p[i] est dni comme tant quivalent *(p+i). Ceci permet donc de manipuler des pointeurs comme des tableaux mono-dimensionnels. Par exemple, aprs
struct Person *ppers = malloc(10*sizeof(struct Personne));

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

120

5. Pointeurs

Figure 5.6 Relation entre pointeurs et tableaux mono-dimensionnels en C

le pointeur ppers pointe sur une zone de mmoire pouvant accueillir un tableau de 10 structures Person. On peut dsigner les diffrentes composantes sous la forme ppers[0], ppers[1]... On pourra donc crire des instructions comme
ppers[i].age = 18;

Relation entre tableaux et pointeurs

Toujours dans le cadre de lexemple prcdent o p pointe sur le premier lment du tableau t, on peut donc dsigner les lments de t par t[0], t[1]... bien sr, mais aussi par p[0], p[1]..., et encore par *(p+0), *(p+1).... Si lon peut traiter un pointeur comme un tableau, on doit par cohrence traiter un tableau comme un pointeur ! Ceci revient dire que lon doit aussi pouvoir dsigner les lments de t par *(t+0), *(t+1)... ou encore que lon doit considrer un nom de tableau comme un pointeur sur sa premire composante. C fait donc hardiment cette assimilation des noms de tableaux des pointeurs. Ainsi, lextrait prcdent aurait-il pu scrire
int t[10]; int *p = t; /* t == &t[0] */

La gure 5.6 illustre cette relation trs particulire entre pointeurs et tableaux en C. Comme les chanes de caractres ne sont que des tableaux particuliers, C est aussi amen considrer quune chane de caractres littrale comme "bonjour\n" est aussi de type pointeur (sur caractre, donc char *).
Diffrence entre tableaux et pointeurs

Lquivalence entre tableau et pointeur nest cependant pas totale : Un pointeur comme p est une variable ; en revanche, t est un pointeur sur un objet dont le compilateur est maitre de ladresse, pas le program-

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

5.3. Pointeurs et tableaux

121

meur. Un nom de tableau est donc un pointeur constant (t est de type int *const). Dautre part, une dnition de tableau comme celle de t rserve effectivement la place pour toutes les composantes (ici 10), alors que la dnition de p ne rserve que la place dun pointeur. La deuxime remarque est fondamentale : on peut manipuler des pointeurs comme des tableaux, mais aprs stre assur quils pointent effectivement sur des tableaux ! Une consquence de cette remarque est que sizeof(t) est la taille du tableau tout entier, alors que sizeof(p) est la taille dun simple pointeur. Ainsi,
sizeof(t) == 10*sizeof(int) sizeof(p) == sizeof(int *)

Sur une machine 32 bits, sizeof(t) vaut 40, alors que sizeof(p) ne vaut que 4 ! Une autre consquence est que C ne sait pas distinguer un pointeur sur un objet individuel dun pointeur sur un tableau dobjets !
Cas des tableaux multi-dimensionnels

Si un nom tableau mono-dimensionnel a comme type celui de pointeur sur le type de ses composantes, quen est-il dun tableau bi-dimensionnel comme par exemple Days dj rencontr en 4.1.3 ?
const char Days[][10] = { "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche", };

La rponse est vidente : Days est de type double pointeur sur charactre (char**). Lexpression Days[i] devient possible (quivalente *(Days+i), et donc de type char* : elle dsigne tout naturellement la ligne dindice i de Days, cest--dire un tableau mono-dimensionnel. Considrons la dnition suivante :
const char *Jours[] = { "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche", };

Cette fois, Jours est un tableau de 10 pointeurs sur caractres. Linitialisation est correcte, puisque nous avons dj remarqu que les chanes comme "lundi" taient des pointeurs sur caractres. Les noms Days et Jours sont des expressions de mme type, savoir char**. Ils sont tous les deux simplement indexables : Days[1], comme Jours[1], pointe sur la chane "mardi". Ils sont galement tous les deux doublement indexables : Days[1][3], comme Jours[1][3], est une rfrence sur le caractre d de "mardi". Il y a cependant une diffrence : Days occupe en mmoire une taille de 70 caractres (sizeof(Days) vaut 70), alors que Jours noccupe que la place de 7 pointeurs (par exemple sizeof(Jours) vaut 28 sur ma station de travail). La gure 5.7 montre les reprsentations diffrentes en mmoire des deux objets Days et Jours.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

122

5. Pointeurs

Figure 5.7 Tableau multi-dimensionnel et tableau de pointeurs

(a) Une matrice de caractres

(b) Un tableau de pointeurs sur caractre

Attention : double indexation de pointeurs

Une autre diffrence entre tableaux et pointeurs concerne la double (ou plus) indexation. La double indexation dun double pointeur pur cest--dire qui nest pas lui mme soit un tableau de pointeurs, soit un tableau bi-dimensionnel na pas de sens : dans char **pp = Days; /* probleme identique avec Jours */

comment pourrait-on calculer pp[i][j] ? Le calcul dindice ncessite de connaitre le nombre de colonnes du tableau la compilation et cette information nest pas disponible pour le pointeur pp.

5.4 Exercices du chapitre 5


Exercice 5.1 (Liste trie simplement chane) Complter le type liste den-

tiers trie par ordre croissant en dnissant les fonctions suivantes : chercher si une valeur est dans la liste ; retourner le nime lment, sil existe ;

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

5.4. Exercices du chapitre 5

123

dtruire une valeur dans la liste, si elle sy trouve (une seule occurrence et toutes les occurences) ; fusionner une liste avec une autre, cest--dire ajouter la premire liste une copie des valeurs de la seconde ; bien entendu la liste nale doit aussi tre trie ; dupliquer une liste, cest--dire retourner une nouvelle liste dont les valeurs sont des copies des valeurs de la premire liste ; imprimer une liste.
Exercice 5.2 (Liste trie doublement chane) Mme exercice que prcdem-

ment, mais avec des listes doublement chaines. La cellule de liste a donc la forme suivante :
struct DList_Cell { int val; struct DList_Cell *prev; /* pointeur arriere */ struct DList_Cell *next; /* pointeur avant */ };

Par ailleurs, limplmentation des listes avec une n marque par un pointeur nul comme dans lexercice 5.1 est dj maladroite pour des listes simplement chane mais elle est carrment dbile pour des listes doubles. Vous utiliserez donc limplmentation dcrite dans la gure 5.8. Ici la liste contient toujours une cellule de garde (dont la valeur ne fait pas partie de la liste). Lorsque la liste est vide, les deux pointeurs de cette cellule pointent sur elle-mme (gure 5.8(a)). Sinon, les cellules sont chanes naturellement, et la liste reboucle sur la cellule de garde. Si phead est ladresse de la cellule de garde (gure 5.8(b)), la premire cellule utile est donc pointe par phead->next et la liste se termine la cellule phead exclue. Bien entendu, un lger inconvnient par rapport limplantation directe de lexercice 5.1 est que vous avez maintenant besoin de dnir une fonction dinitialisation de liste pour crer la cellule de garde et vous ne devez pas oublier dinvoquer cette fonction avant de manipuler le liste.
Remarque sur le test de ce programme

Un bon moyen pour (commencer ) tester ce programme est de prendre comme programme de test exactement le mme que pour lexercice 5.1, de rcuprer les sorties des deux programmes dans des chiers et de vrier que ces chiers sont identiques, par exemple en utilisant les commandes cmp ou diff du shell.
Exercice 5.3 (Manipulation de chanes de caractres) Les programmes 3.2 et

3.3 montrent comment on peut manipuler les chanes de caractres en C, sous lhypothse (forte) que les dites chanes se terminent par le caractre nul. La bibliothque standard de C fournit un certain nombre de fonctions de manipulation de chanes qui reposent toutes sur cette hypothses. Ces fonctions sont dclares dans le chier dentte <string.h> et documentes dans la page de manuel correspondante (pour vous familiariser avec elles, faites man string). Lexercice consiste utiliser quelques unes de ces fonctions pour raliser des oprations de sous-chanes, oprations peu reprsentes dans la bibliothque standard.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

124

5. Pointeurs

Figure 5.8 Liste doublement chane avec cellule de garde et rebouclage

(a) Reprsentation de la liste vide

(b) Cas gnral

Dans le mme esprit (et sous la mme hypothse), crire (et testez !) les fonctions suivantes (s, s1 et s2 dsignent des chanes, i et n des entiers, c un caractre) : string_trim(s) Enlve les espaces initiaux et naux de la chane. substring(s, i, n) Retourne la sous-chane de s de longueur maximale n commenant lindice i ; si n est trop grand, on ne retourne que ce qui est possible. substring_search(s1, s2) Recherche la premire occurrence de la chane s1 dans s2 et retourne lindex correspondant dans s2 ou -1 (si s1 nest pas une sous-chane de s2). insert_substring(s1, s2, i) Insre la chane s1 devant le caractre dindice n de s2 ; ne fait rien si n est invalide.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

5.4. Exercices du chapitre 5

125

delete_substring(s, i, n) Dtruit la sous-chane de s de longueur maximale n commenant lindice i ; si n est trop grand, on ne dtruit que ce qui est possible. replace_substring(s1, i, n, s2) Remplace la sous-chane de s1 de longueur maximale n commenant lindice i par la chane s2; si n est trop grand, on ne remplace que ce qui est possible. tokenize_string(s, c, ts[], n, empty_fields) tant donne une string s compose de champs spars par le caractre c, copie ces champs un par un dans les lments du tableau de chanes ts et retourne le nombre de champs ; n est la taille maximale du tableau ts. Si le boolen empty_fields est vrai et si deux sparateurs c sont adjacents, la chane correspondante de ts doit tre la chane vide. Ainsi aprs
char *ts[10]; char s[] = "/bin:/usr/bin::/usr/local/bin:/home/jpr/bin"; int n = tokenize_string(s, :, ts[], 10, b);

si b est vrai, n vaut 5, et ts[0] est "/bin", ts[1] est "/usr/bin", ts[2] est la chane vide, ts[3] est "/usr/local/bin", etc. En revanche is b est faux, il ny a pas de champ vide (ts[2] est "/usr/local/bin", etc.
Remarque 1

Commencez par dnir prcisment les prototypes (type des arguments et de la valeur de retour de ces fonctions), et placez les dans un chier dentte substring.h. Puis, dans substring.c, incluez substring.h et dnissez chacune des fonctions. Compilez et testez vos fonctions au fur et mesure : inutile dessayer de tout crire dabord et de se retrouver devant des pages de messages derreur de compilation, ou alors face un crash (memory fault, segmentation violation, etc.) dont on ne sait o il a eu lieu. Soyez peu prs srs que vous allez effectivement crasher dans vos premires versions de certaines fonctions.
Remarque 2

Pour certaines de ces fonctions, vous serez sans doute obligs de faire des copies de chanes de caractres et, comme leur longueur nest connue qu lexcution, dutiliser malloc. Quel inconvnient(s) voyez vous cela ? Pourquoi la bibliothque standard ne dnit-elle pas plus de fonctions du type de celles que vous venez de raliser ?
Remarque 3 : compatibilit
ISO

Il y a beaucoup de fonctions disponibles dans <string.h> et elle ne sont malheureusement pas toutes standardises. Nutilisez que les fonctions conformes la norme iso. Elle sont indiques comme conforme iso 9899 dans les pages du manuel (man).
Exercice 5.4 (Tri dun tableau de pointeurs) Modier le tri par insertion du

programme 2.6 pour trier des chanes de caractres.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

126

5. Pointeurs

Figure 5.9 Structure dun arbre binaire

Exercice 5.5 (Commande fgrep) Utilisez les fonctions de lexercice 5.3 pour crire une version simplie de la commande fgrep. Cette commande reoit

une chane de caractre en argument, lit lentre standard et en afche toutes les lignes qui contiennent la chane.
Remarque

En quoi la commande fgrep fournie par le systme est-elle suprieure la votre ?


Exercice 5.6 (Arbre binaire) On dnit un nud darbre binaire par
struct Binary_Tree_Cell { int val; struct Binary_Tree__Cell *left; struct Binary_Tree__Cell *right; };

Larbre complet est bien entendu dni par un pointeur sur sa racine et a la forme dcrite dans la gure 5.9. Ecrire la procdure dinsertion suivante pour un entier n donn : si la valeur porte par la racine est infrieure (resp. suprieure) n, insrer la valeur n dans le sous-arbre de gauche (resp. de droite) ; si les deux valeurs sont gales, ne rien insrer ; cette procdure est applique rcursivement sur les sous-arbres. Ensuite crire une fonction de recherche qui explore larbre pour trouver une valeur donne et retourne un pointeur sur la cellule correspondante (ou bien le pointeur nul si la valeur nest pas dans larbre). Enn, crivez une fonction dimpression de larbre en profondeur dabord : tire dexemple, larbre de la gure 5.10 devra tre imprim ainsi :
10 -6 --3 ---EMPTY

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

5.4. Exercices du chapitre 5

127

Figure 5.10 Un exemple darbre binaire

---EMPTY --9 ---7 ----EMPTY ----EMPTY ---EMPTY -12 --EMPTY --14 ---EMPTY ---17 ----EMPTY ----EMPTY

Le nombre de tirets exprime la profondeur.


Exercice 5.7 (Recherche dichotomique) crire un programme de recherche di-

chotomique dun entier donn dans une liste trie dentiers. La fonction retourne lindice de la valeur dans la liste, ou -1 si la recherche choue.
Recherche dichotomique

Soit a la valeur rechercher dans la liste trie u0 u1 ... un ; si a < un/2 (resp. a > un/2 ) alors a doit tre recherch dans la liste u0 , u1 , ..., un/21 (resp. un/2+1 , ..., un ) ; si a = un/2 alors le rsultat est bien videmment n/2.
Remarque 1

On traitera correctement les problmes lis la parit de n.


Remarque 2

On pourra crire une version rcursive et/ou une version non-rcursive de ce programme.
Exercice 5.8 (Pathologie des pointeurs) Les pointeurs fournissent des possibi-

lits trs puissantes mais sont aussi une des principales cause derreur dans les programmes C. Parmi les erreurs frquentes : Utiliser lobjet point par un pointeur sans que ce pointeur ait t initialis (ou aprs quil ait t initialis NULL). Se tromper lors de larithmtique des pointeurs en dbordant les limites et en pointant ainsi sur des objets de type diffrent de ce que lon croit.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

128

5. Pointeurs

Ne pas librer la mmoire alloue par malloc lorsque lon nen a plus besoin ( fuite de mmoire ). En particulier, lorsque lon utilise une fonction (de bibliothque standard ou pas) dont certains des paramtres sont des pointeurs, on doit toujours se poser les questions suivantes (et obtenir la rponse) : le paramtre peut-il tre un simple pointeur (possiblement non initialis) ? ou doit-il tre un pointeur sur un objet effectivement allou ? et dans ce cas, est-ce un pointeur sur un objet individuel ou sur un tableau ? Si la fonction retourne un (ou plusieurs) pointeurs, il faut galement toujours se demander si les objets correspondants ont t allous et comment (i.e., par malloc ou autre ?). Testez la robustesse des fonctions que vous avez ralises lors de lexercice 5.3 par rapport ces problmes.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Chapitre 6

Le prprocesseur ANSI C
prprocesseur constitue premire phase dun proLegramme C.textuelles. sur lelasource, chier par de la compilationnombre de Il effectue chier, un certain transformation Prsent depuis lorigine en C, il a subit un nettoyage complet lors de la normalisation dansi C. Le nouveau prprocesseur est donc largement incompatible avec lancien (dit traditionnel ). Ce dernier nexiste pratiquement plus et nous ne parlerons ici que du prprocesseur ansi C et de ses extensions en C99. Ce chapitre ne prtend pas tre une tude exhaustive de toutes les possibilits du prprocesseur ansi C : ce serait trop long et mon avis sans grand intrt. Les possibilits sont trop nombreuses, souvent obscures, parfois inadquates. On doit rserver lutilisation du prprocesseur des oprations o il est incontournable, savoir : dnition de constante, inclusion de chiers, compilation conditionnelle, et, en C90 uniquement, en prenant des prcautions, dnition de fonctions en ligne .

6.1 Prtraitement des programmes C


La norme ansi C dcrit prcisment les transformations qui doivent tre effectues sur les programmes source avant quils soient compils. Ce sont dans lordre : 1. les trigraphs sont substitus, que nous ignorons dlibrment ; 2. les lignes-suites sont traites, ce qui revient supprimer les squences o un \ est suivi dune n de ligne ; 3. le source est dcoup en lments lexicaux (tokens), cest--dire en mots spars par des espaces ; les commentaires sont remplacs par un blanc ; 4. les directives du prprocesseur sont excutes ; 5. les squences dchappement (3.1.6) dans les littraux (caractres et les chanes) sont remplacs par leurs quivalents ; 6. les chaines de caractres littrales adjacentes sont concatnes ; 7. la compilation puis ldition de liens peuvent alors avoir lieu.

V 2.1 7 fvrier 2010

129

c Jean-Paul R IGAULT

130

6. Le prprocesseur ANSI C

La norme ne prcise pas comment sont raliss ces diffrents traitements. Dans la plupart des implmentations, les quatre premires phases sont de la responsabilit du prprocesseur C, un programme qui effectue des transformations textuelles sur le source. Outre les transformations cites ci-dessus, le prprocesseur permet lutilisateur de spcier certaines transformations particulires : linclusion de chiers (source), le remplacement de mots (i.e. de tokens voir 3.1.2), la compilation conditionnelle. Ltude de ces transformations est lobjet de ce chapitre. Les directives au prprocesseur tiennent sur une ligne (logique 1 ) dont le premier caractre qui nest ni un blanc, ni une tabulation horizontale, doit tre un dise (#). Noter quaucun point-virgule ne termine les directives au prprocesseur.

6.2 Inclusion de chier


6.2.1 Effet de linclusion de chier
La directive dinclusion de chier a lune des formes suivantes :
#include "nom-de-fichier" #include <nom-de-fichier>

Le nom-de-fichier ne doit pas contenir lun des caractres (ou lune des squences) suivants, sinon le rsultat risque dtre indni : > (au moins pour la forme en <...>), n de ligne, , ", \, /*. Leffet de cette directive est simple : tout se passe comme si la ligne correspondante tait remplace par le contenu du chier. Les chiers inclus peuvent eux-mmes comporter des directives #include : linclusion sera alors rcursive (voir cependant 6.4.2).

6.2.2 Recherche du chier inclure


Les deux formes de la directive dinclusion diffrent par la mthode de recherche du chier inclure. Cette mthode dpend de limplmentation. Nous donnons ici les rgles utilises sous Unix. Dans les deux formes, si le nom-de-fichier commence par un /, cest un chemin absolu qui dsigne sans ambigut le chier inclure. Sinon la forme
#include <nom-de-fichier>

cherche le chier dans un liste de rpertoires dpendant de linstallation du systme, mais qui peut tre modie par loption -I de la commande de compilation C. Le nom-de-fichier est alors un nom relatif ces rpertoires. Sur la plupart des systmes Unix, la liste par dfaut est rduite au rpertoire /usr/include. Par exemple :
#include <stdio.h> #include <sys/signal.h> /* defaut: /usr/include/stdio.h */ /* defaut: /usr/include/sys/signal.h */

1. qui peut tre constitue de plusieurs lignes physiques, si lon prcde chaque n de ligne du caractre \

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

6.3. Dnition de macros

131

La forme
#include "nom-de-fichier"

quant elle, commence par chercher dans le rpertoire o se trouve le chier source qui comporte cette directive dinclusion 2 . Si le chier nest pas trouv, le fonctionnement est identique la forme avec des piquants (<...>).

6.2.3 Utilisation de linclusion de chier


On peut utiliser linclusion de chier pour inclure nimporte quel suite dinstructions, de dclarations ou de commentaires. Cependant, la seule utilisation rellement intressante est limportation de chiers dentte (headerle). Ces chiers contiennent des dnitions de types, de macros (voir section suivante), dobjets externes (voir 8.3.2) ou encore des prototypes de fonctions partages par plusieurs chiers source. Lutilisation des chiers dentte permet alors de ncrire quune seule fois ces dnitions ou dclarations partages (voir 8.4.1).

6.3 Dnition de macros


La directive #define est hlas la directive la plus importante du prprocesseur C. Elle permet de raliser le remplacement de tokens.

6.3.1 Macros sans arguments : dnition de constantes


La forme la plus simple de dnition de macro
#define ident chane-dfinition

permet de remplacer lidenticateur ident par toute la chane-dfinition. Noter que cette chane commence au premier blanc rencontr aprs ident. Lidenticateur ident nest pas remplac sil est lintrieur dune chane de caractres littrale. Par exemple, le chier source suivant
#define NBUF 512 #define NBUF2 NBUF*2 #define MESSAGE "NBUF = " int t[NBUF]; int u[NBUF2]; int main() { printf(MESSAGE "%d\n", NBUF); }

donne aprs passage travers le prprocesseur


int t[512 ]; int u[512 *2 ]; int main()
2. et pas le rpertoire courant comme on le croit trop souvent !

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

132

6. Le prprocesseur ANSI C

{ printf("NBUF = " } "%d\n", 512 );

A noter que la ligne contenant printf deviendra


printf("NBUF = %d\n");

aprs concatnation des chanes littrales adjacentes. On remarque aussi que la macro NBUF est correctement expanse dans la dnition de NBUF2. Lutilisation la plus frquente de cette forme de dnition de macros est la dnition de constantes, en particulier celles servant dimensionner des tableaux. Nous avons en effet dj remarqu (4.1.1) quen C90 il ntait pas possible de le faire avec une const int et que ce ntait que partiellement possible avec C99 (4.1.4).
Attention : une macro nest pas vraiment une dnition de constante

Une macro telle que #define N 10 ne dnit pas vraiment une constante, mais simplement une substitution textuelle. Certaines substitutions conduisent donc des expressions incorrectes : par exemple, ladresse de N, &N, na pas de sens, car cest quivalent &10 et la valeur 10 na pas dadresse. En revanche une const int N = 10 est un vritable objet de C qui a un type (const int videmment) et, si ncessaire, une adresse (&N).

6.3.2 Macros arguments : fonctions en ligne


La deuxime forme de #define permet de donner des arguments la macro. Par exemple aprs
#define MAX(a, b) ((a) > (b) ? (a) : (b))

toutes les occurrences de lidenticateur MAX 3 suivi dune paire de parenthses entourant deux suites de mots spares par une virgule (ouf !) seront remplaces. Ainsi
x = MAX(y + 4, z - 1);

deviendra
x = ((y + 4) > (z - 1) ? (y + 4) : (z - 1));

C99

On voit donc que ce type de dnition permet dobtenir des sortes de fonctions qui sont expanses en ligne, plutt que dtre invoques avec une squence dappel normale. En gnral, on utilise cette possibilit pour des raisons defcacit. Notez que C99 a introduit la possibilit davoir des macros avec un nombre variable dargument (variadic macro voir 11.1.2).
Attention : une macro nest pas une fonction !

Les diffrences entre macro et fonction sont nombreuses et doivent inciter la prudence dans lutilisation des macros arguments. Illustrons ces diffrences laide de la macro MAX ci-dessus et dune vraie fonction Max :
3. Noter quil ny pas despace entre MAX et la parenthse ouvrante (pourquoi ?).

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

6.3. Dnition de macros

133

int Max(int a, int b) { return a > b ? a : b; } Tout dabord la fonction est type, et le compilateur vrie le bon usage des types. Il nen est pas de mme pour une macro : MAX est valable aussi bien pour des entiers que des rels par exemple. Cest une puissance supplmentaire, mais cest aussi, parfois, une perte de scurit. Ensuite, un appel de fonction est syntaxiquement quivalent une valeur de son type de retour : x = 2 + Max(y, z); se lit bien x = 2 + (Max(y, z)); Avec la macro, lanalyse de lexpression nest faite quaprs le remplacement textuel, ce qui peut provoquer quelques suprises. Supposons par exemple quau lieu de dnir la macro MAX avec ce qui parait tre un sur-parenthsage, nous ayons crit #define BAD_MAX(a, b) a > b ? a : b alors x = 2 + BAD_MAX(y, z); est expans en x = 2 + y > z ? y : z; qui, compte tenu de la prcdence des oprateurs (3.4.1), se lit x = (2 + y) > z ? y : z; ce qui, pour le moins, nest pas trs naturel. Le sur-parenthsage de la dnition de MAX vite ce genre de surprise. Enn la troisime diffrence est quune fonction nvalue ses arguments quune seule fois ; comparer int y = 0; int z = 0; x = Max(y++, ++z); o les incrmentations nont lieu quune fois (y et z valent 1 aprs lappel) avec int y = 0; int z = 0; x = MAX(y++, ++z); qui va tre expans en x = ((y++) > (++z) ? (y++) : (++z)); avec comme consquence quune des incrmentations (savoir laquelle dpend du rsultat de la comparaison) sera effectue deux fois ! Pour toutes les raisons invoque ci-dessus, C99 a emprunt C++la notion de fonction inline (mot-cl inline). Voir 7.2.4 pour les dtails.

C99

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

134

6. Le prprocesseur ANSI C

6.3.3 Dnition rcursive de macros


ansi C ne permet pas au moins directement les dnitions rcursives. Ainsi
#define A A*2 i = A*A;

donne lexpansion
i = A*2 *A*2;

6.3.4 Concatnation et stringication


Concatnation : ##

ansi C introduit dans le prprocesseur loprateur de concatnation de symboles dsign par ##. Ainsi dans
#define cat(a, b) a ## b ... int cat(toto, _old) = toto;

la dernire ligne sera transforme en


int toto_old = toto;

et toto_old sera considr comme un identicateur par le compilateur.


Stringication : #

Dans une macro avec arguments, un argument formel nest pas remplac si dans la chaine dnissante il gure lintrieur dune chane littrale. Par exemple, dans
#define message(a) "le message est: a" ... printf(message(bonjour));

la dernire ligne est expanse en


printf("le message est: a");

ce qui nest pas forcment ce quon veut. Loprateur unaire # appliqu un argument de macro en effectue la stringication 4 cest--dire transforme cet argument en chane littrale. Ainsi, avec
#define message(a) "le message est: " #a ... printf(message(bonjour));

la dernire ligne sera expanse en


printf("le message est: " "bonjour");

qui deviendra
printf("le message est: bonjour");

aprs concatnation des chanes littrales adjacentes.


4. Je manque dimagination pour trouver une traduction franaise !

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

6.4. Compilation conditionnelle

135

6.3.5 Annulation de dnition : #undef


La porte dune directive #define va jusqu la n du chier source courant. On peut annuler une dnition laide de la directive #undef :
#define NBUF 100 ... int t[NBUF]; ... #undef NBUF const int NBUF = 10; ...

6.3.6 Macros rserves


En principe, les noms de macros encadrs de deux souligns (__) sont rservs par ansi C. En particulier les identicateurs suivants sont prdnis :
__LINE__ __FILE__ __DATE__ __TIME__ __STDC__ numro de ligne source courante (un int) nom du chier source courant la date du jour lheure vaut 1 si le prprocesseur est la norme ansi C

En C99 est aussi dnie la macro __STDC_VERSION__ qui a la valeur (entire longue) 199901L. Voici un exemple dutilisation inspir de la macro assert dnie dans le chier <assert.h>.
#define ASSERT(cond) \ if (!(cond))\ {\ printf("%s %s: ", __DATE__, __TIME__);\ printf("%s, line %d: ", __FILE__, __LINE__);\ printf("assertion failed: " #cond "\n");\ exit(1);\ }

Si ASSERT est invoqu avec une condition fausse (noter une fois de plus le sur-parenthsage indispensable), on obtient un message comme
Jan 14 2008 15:25:02: cpp-test1.c, line 22: assertion failed: a < b

6.4 Compilation conditionnelle


6.4.1 Les directives de compilation conditionnelle
Les directives de compilation conditionnelles permettent de choisir des lignes sources en fonction de certaines expressions. La forme canonique est la suivante :
#if expression-statique lignes-si-vrai #else

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

136

6. Le prprocesseur ANSI C

lignes-si-faux #endif

On value lexpression-statique qui est constitue laide des oprateurs habituel de C et sa valeur de vrit dtermine les lignes source que lon considre. Cette valeur de vrit doit bien entendu tre valuable la compilation.
#define NBUF 512 #define NBUF2 NBUF*2 ... #if NBUF > 512 && NBUF2 > 1024 int t[NBUF]; int u[NBUF2]; #else int t[512]; int u[1024]; #endif

Bien entendu, la clause #else est optionnelle. En outre la clause #elif permet denchaner les conditions :
#if NBUF < 512 ... #elif NBUF < 1024 ... #elif NBUF < 2048 ... #else ... #endif

La encore, la clause #else est optionnelle. Le prprocesseur fournit un oprateur permettant de dterminer si un symbole a t dni (cest--dire a fait lobjet dun #define non encore annul par un #undef). Cest loprateur defined :
#if defined(LINUX) || defined(MSWINDOWS) || defined(MACOS) ... /* code commun a ces systemes */ #endif

La forme suivante
#if defined(__STDC__) double cos(double); #else double cos(); #endif

est si frquente quelle peut aussi scrire


#ifdef __STDC__ double cos(double); #else double cos(); #endif

De la mme manire,

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

6.5. Autres directives du prprocesseur

137

#if !defined(getchar) int getchar(void); #endif

scrit aussi
#ifndef getchar int getchar(void); #endif

6.4.2 Inclusion unique de chiers


Lune des utilisations systmatiques de la compilation conditionnelles est de grer correctement linclusion unique de chiers dentte (header-le). Supposons que le chier main.c, inclut A.h et B.h
/**** fichier main.c ****/ #include "A.h" #include "B.h" ...

Supposons aussi que A.h et B.h aient chacun besoin des dnitions contenues dans un troisime chier dentte, Common.h ; ils vont tout naturellement inclure chacun ce dernier chier. La consquence est que ce chier va tre inclus deux fois dans main.c. Ceci peut tre grave car certaines de dnitions de Common.h peuvent provoquer des erreurs si elles sont rencontres plusieurs exemplaires dans le mme chier source cest le cas par exemple des dnitions de type. Comment viter ce phnomne de double inclusion ? En protgeant le chier Common.h de la manire suivante :
/**** fichier Common.h ****/ #ifndef _Common_h_ #define _Common_h_ ... /* contenu du fichier Common.h */ ... #endif /* _Common_h_ */

Par le jeu de la compilation conditionnelle, le contenu utile du chier ne sera effectivement inclus que lors de la premire directive #include. Bien entendu, lidenticateur _Common_h_ doit tre choisi de manire unique. On utilise trs souvent, comme ici, un identicateur qui rappelle le nom du chier. Cette technique doit tre utilise systmatiquement sur tous les chiers dentte, moins quune raison premptoire soit donne de ne pas le faire.

6.5 Autres directives du prprocesseur


Signalons dabord quune ligne rduite un dise et des blancs et/ou tabulations hoirizontales est ignore. Par ailleurs, trois autres directives compltent la description du prprocesseur C.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

138

6. Le prprocesseur ANSI C

6.5.1 Numro de ligne : #line


Cette directive qui prend lune des formes
#line constante "nom-de-fichier" #line constante

fait croire au compilateur que la prochaine ligne source a comme numro la constante (qui est soumise macro-expansion si ncessaire). Cette directive nest en gnral pas utilise directement par le programmeur, mais plutt par les compilateurs et prprocesseurs de toute nature an dassurer la remonte dans le source des messages derreur.

6.5.2 Messages derreur : #error


Cette directive qui a la forme
#error ce que vous voulez

crit le ce que vous voulez sur lerreur standard et interrompt la compilation :


#ifndef __STDC__ #error Abandon: compilateur non ANSI #endif

provoque ventuellement larrt de la chane de compilation avec le message


cpp-test1.c:15: #error Abandon: compilateur non ANSI

6.5.3 Commentaire excutable : #pragma


Enn, la dernire directive a la forme
#pragma nimporte-quoi

et, en C90, son effet est entirement laiss limplmentation ! En fait lide est de permettre de donner des commentaires qui on un sens pour un compilateur particulier et qui peuvent lui suggrer un comportement spcial. En tout tat de cause, ce comportement spcial ne devrait pas altrer la smantique profonde du programme ! titre dexemple, un environnement peut proposer la vrication optionnelle des dbordements dindices. An que le compilateur gnre le code correspondant, on lui prcise par un #pragma. On peut imaginer, par exemple, ce type de syntaxe :
#pragma CHECK_INDEX_RANGE ON int t[10]; ... t[i] = t[j]; ... #pragma CHECK_INDEX_RANGE OFF

C99

La norme exige quun #pragma non reconnu par un compilateur soit simplement ignor, an de permettre la portabilit des sources. C99 introduit des pragmas pr-dnis qui ont trait la manipulation des nombres rels et complexes (voir 9.2.4).

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

6.6. Exercices du chapitre 6

139

6.6 Exercices du chapitre 6


la rigueur un exo de comparaison entre fonctions inline et macros avec des problmes pathologiques de macros (valuation multiple, non atomicit de lval des paramtres...).

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

140

6. Le prprocesseur ANSI C

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Chapitre 7

Fonctions
dcrit ici la que nous dj largement Onutilise dansenlesdtail en notion de fonctionfonction estavonsdes mcanismes chapitres prcdents. La lun principaux de modularit C ; cest aussi lune des bases de la programmation structure. En ansi C, chaque fonction a un type, dtermin la fois par le nombre et le type de ses arguments et par celui de sa valeur de retour. Comme pour tout objet, ce type doit tre connu avant toute utilisation. Trois contextes diffrents sont associs la notion de fonction : 1. la dclaration dune fonction, qui en dnit le type et qui est mise en uvre en ansi C, grce la notion de prototype ; 2. la dnition dune fonction, qui reprend son type, mais surtout en dnit le corps, cest--dire la liste des instructions excutes chaque appel ; 3. lappel ou linvocation de la fonction.

7.1 Arguments et valeur de retour


7.1.1 Type de retour dune fonction
Un appel de fonction est syntaxiquement quivalent une valeur de son type de retour. Ainsi, dans
double cos(double); double x = 1.0 + cos(2*PI*t);

linvocation cos(2*PI*t) est quivalente une valeur de type double. En ansi C, les types de retour possibles dune fonction sont les suivants : Le type void : la fonction ne retourne pas de valeur dans dautres langages on dirait que cest une procdure ; Tout type scalaire, entier ou rel, y compris les numrations et les pointeurs ; Les structures et les unions. A lexception de void, ce sont en fait les types affectables . Les tableaux ny gurent donc pas : une fonction ne peut donc retourner un tableau tout entier ; en revanche, elle peut retourner un pointeur sur le (premier lment du) tableau.

V 2.1 7 fvrier 2010

141

c Jean-Paul R IGAULT

142

7. Fonctions

7.1.2 Type des arguments dune fonction


Un fonction peut ne pas avoir dargument du tout (voir 7.2.1). Quand elle en a, chacun des arguments peut tre dun des types suivants : Tout type scalaire, entier ou rel, y compris les numrations et les pointeurs ; Les structures et les unions. Ici encore, ce sont les types affectables . Un appel de fonction retournant une valeur, il ne peut jamais tre une lvalue, cest--dire une rfrence.

7.1.3 Mode de passage des arguments


Passage par valeur

En C traditionnel comme en ansi C, il ny a quun seul mode de passage dargument : le passage par valeur. En fait on devrait plutt parler de passage par copie de la valeur. Le mcanisme exact est le suivant. Soit f une fonction un argument de type T dont la dnition ressemble :
void f(T t) { ... }

Soit un appel de cette fonction :


f(tt);

Lexpression tt (largument effectif) est dabord value ; si ncessaire elle est convertie dans le type T 1 ) ; la valeur obtenue sert initialiser une variable locale au corps de la fonction f correspondant largument formel t. Donc si largument formel t est modif dans le corps de la fonction, cette modication reste locale, et na aucune inuence sur la valeur de largument effectif.
Passage des tableaux et des chanes de caractres

Il ny a aucune exception cette rgle de passage par valeur. Cependant, cause de lassimilation des tableaux aux pointeurs (5.3.2), les tableaux paraissent tre passs par adresse comme dans
void raz(int n, int t[]); int tab[100]; raz(100, tab);

Il ny a pas de contradiction : un nom de tableau est une constante de type pointeur, et ce pointeur est pass par valeur. En fait, le prototype de raz peut tout aussi bien scrire :
void raz(int n, int *t);

et lappel
raz(100, &tab[0]);
1. Si cette conversion est impossible, il y a erreur de compilation.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

7.2. Dclaration, dnition et appel de fonction

143

Cette mme remarque sapplique galement aux chanes de caractres littrales qui paraissent passes par adresse : en fait ce sont aussi des pointeurs (sur leur premier caractre).
void string_copy(char *dest, const char *orig); char str[100]; string_copy(str, "bonjour");

Rappelons que la fonction doit avoir un moyen de dterminer le nombre dlments du tableau. Cest pourquoi, dans le cas de la fonction raz, ce nombre (100) est pass en argument. Ce nest pas ncessaire pour les chanes de caractres puisque la convention de les terminer par le caractre nul permet den connaitre la longueur.
Passage par valeur des structures et unions

Le passage par valeur sapplique aussi quand une structure ou une union est en argument :
void print_person(struct Person who); struct Person somebody; print_person(somebody);

Ce passage par valeur implique donc une recopie champ par champ de largument effectif somebody dans largument formel who. Si la structure est de grande taille, cela peut tre inefcace. Nous examinerons en 7.3.2 un moyen davoir le mme effet mais plus efcacement.

7.2 Dclaration, dnition et appel de fonction


7.2.1 Dclaration du type dune fonction : prototype
Le prototype dune fonction donne le nom de la fonction, le nombre et le type de chacun des arguments, et le type de la valeur de retour. Il permet dutiliser la fonction alors quelle na pas encore t dnie. Sous sa forme la plus simple on pourra trouver en 7.3.3 des prototypes plus complexes , un prototype a la syntaxe suivante :
type-retour nom-fonction(liste-args);

Si la liste dargument est vide, liste-args doit tre rduite au mot-cl void comme dans
int getchar(void);

La liste est une suite de noms de types spars par des virgules comme dans
double cos(double); int strcmp(const char *, const char *);

Les noms des arguments formels peuvent tre prsents comme dans
char *strcpy(char *dest, const char *orig);

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

144

7. Fonctions

Ils ne jouent alors pas dautre rle que celui de faciliter la lisibilit en autodocumentant le source ; pour le reste, ils sont ignors. On peut avoir dans un chier source plusieurs prototypes pour la mme fonction, du moment quils sont identiques (aux noms ventuels des arguments formels prs).
Liste variable darguments

On a parfois besoin de fonction pour lesquelles le nombre et le type des arguments est inconnu, totalement ou partiellement. Un cas typique est celui de printf : nous savons que le premier argument, le format, est une chane de caractres, dailleurs constante ; en revanche les autres arguments sont en nombre et types variant chaque appel. En ansi C, son prototype est
int printf(const char *, ...);

Lellipse (...) indique au compilateur de ne faire de vrication ni de type ni de nombre sur les arguments ventuels correspondants. En revanche, il y aura bien ici vrication du type du premier argument. On ne peut lider ainsi quun segment terminal de la liste dargument. Ceci
int f(int, int, ..., double); int g(int, ..., double, ..., int);

est interdit. Pour crire le corps dune fonction dont la liste darguments est lide, il faut pouvoir trouver quelque part une information sur le type des arguments. Dans le cas de printf, ce sont les spcications de format, contenues dans le premier argument, qui jouent ce rle. Une fois ces types connus, les macros standards denvironnement dnies dans le chier <stdarg.h> (voir 9.6.1) permettent de dcomposer ces listes variables dargument.
Compatibilit avec C traditionnel et C++

Pour des raisons de compatibilit arrire, en ansi C, un prototype comme int f(); est quivalent int f(...); et non pas int f(void). Ceci est une cause dincompatibilit avec C++ o int f() est le prototype dune fonction sans argument.
Type de retour implicite dune fonction en C99

C99

La rgle encore valable en C90 selon laquelle une fonction dont le type de retour ntait pas connu tait suppose retourner un entier (int) nest plus vraie en C99. Dans cette dernire version, labsence de dclaration du type de retour est une erreur.

7.2.2 Dnition dune fonction


La dnition dune fonction comporte deux parties :

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

7.2. Dclaration, dnition et appel de fonction

145

Lentte de la fonction qui est une simple reprise du prototype : cette fois-ci cependant, il est utile, voire ncessaire, de donner un nom aux arguments formels si on veut pouvoir les rfrencer dans le corps ; Le corps de la fonction qui est un simple bloc. La dnition dune fonction ne peut pas tre situe dans un bloc. Elle est ncessairement au niveau 0 de la hirarchie de bloc. Il ny a donc pas dimbrication de fonctions, pas de fonction locale en C. Voici quelques exemples de dnition de fonctions ::
void raz(int n, int t[]) { int i; for (i = 0, i < n; i++) t[i] = 0; } void string_copy(char *dest, const char *orig) { while ((*dest++ = *orig++) != \0) {} } /* entete */ /* corps */

7.2.3 Appel dune fonction


Lappel de fonction est tout fait naturel. On doit fournir des arguments effectifs en nombre et type compatibles avec le prototype de la fonction. Si les types ne sont pas rigoureusement identiques, le compilateur effectuera les conversions correspondantes si celles-ci sont possibles ; par exemple,
void f(short); void g(void *); int i; struct Person somebody; f(i); /* f((int)i): troncature */ g(&somebody); /* g((void *)&somebody) */
Attention : il y a virgule et virgule

La virgule qui spare les arguments formels lors de lappel nest pas loprateur dvaluation squentielle (3.3.7). En fait lordre dvaluation des arguments nest pas dni en ansi C. On devra donc se mer des effets de bord dans les arguments effectifs. Ainsi, ceci int f(int); int Max(int, int); int i, j; j = Max(i++, f(i)); est non portable : on ne sait pas si f est invoqu aprs que i ait t incrment, ou avant.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

146

7. Fonctions

7.2.4 Fonctions en ligne de C99


Nous avons vu (6.3.2) que les macros du prprocesseur (#define) permettaient de dnir des fonctions dont lappel tait remplac par lexpansion en ligne. Ceci permet de faire lconomie de la squence dappel/retour de fonction, particulirement bienvenue dans le cas de fonctions dont le corps est bref. Malheureusement, nous avons vu aussi que les macros faisaient de bien pitres fonctions. Pour pallier cet inconvnient, C99 a emprunt C++ la notion de fonction inline. Dclarer une fonction inline comme
static inline int min(int a, int b) { return a < b ? a : b; }

C99

cest une sugggestion au compilateur de faire lexapansion en ligne. Notez le mot suggestion : le compilateur est libre de ne pas obir, car il sait, en gnral mieux que le programmeur, valuer le compromis entre le gain en temps dexcution et laugmentation de la taille mmoire du code.
Combinaison de inline et static

Une fonction inline devrait toujours tre dclare static. En outre si cette fonction doit tre utilise par plusieurs units de compilation, elle doit tre dans un chier dentte (.h) inclus dans ces units. Voir 8.4.2 pour plus de dtails.

7.3 Fonctions et pointeurs


7.3.1 Fonction retournant un pointeur
Nous avons dj rencontr des fonctions qui retournaient des pointeurs comme la fonction insert du programme 5.2 :
List_Cell *insert(List_Cell *, int);

Linterprtation dune telle dclaration utilise la rgle introduite en 5.3.1. Le prototype signie simplement que, dans une expression,
*insert(lc, i);

est une List_Cell ; compte tenu de la prcdence des oprateurs (3.4.1), insert(lc, i) est donc un pointeur sur List_Cell et nalement insert est une fonction qui retourne un pointeur sur List_Cell et qui a deux arguments, un pointeur sur List_Cell et un int.
Attention : pointeurs en lair

Lorsquune fonction retourne un pointeur, lobjet point doit avoir une dure de vie suprieure celle du corps de la fonction, sinon on court la catastrophe. Ainsi dans int *f(void) { int tmp = 1; ... return &tmp; }

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

7.3. Fonctions et pointeurs

147

la sanction sera svre : lobjet local tmp nexiste plus aprs le retour de la fonction ! La plupart des compilateurs donneront un warning dans des cas aussi simples, mais ce nest pas toujours possible en gnral.

7.3.2 Pointeurs en argument


Il y a deux raisons pour lesquelles on peut vouloir passer un argument sous forme dune adresse, cest dire dun pointeur : on veut modier largument effectif dans le corps de la fonction; on veut viter de passer par valeur des objets trs gros, ce qui conduirait de nombreuses copies en mmoire.
Modication dun argument effectif dans le corps dune fonction

On veut raliser une fonction qui change deux entiers. Les entiers ne peuvent pas tre passs par valeur car leur changement de valeur dans le corps de la fonction serait sans effet sur les arguments effectifs :
void bad_swap(int i, int j) { int temp = i; // les variables locales sont bien echangees... i = j; j = temp; // mais pas les parametres effectifs ! }

On doit donc utiliser des pointeurs :


void swap(int *pi, int *pj) { int temp = *pi; *pi = *pj; *pj = temp; }

et lors de lappel, on doit faire attention prendre les adresses des objets changer :
int i, j; swap(&i, &j);

Si cest un pointeur qui doit tre modi, il faut utiliser un double pointeur comme dans la fonction alloc_mem suivante 2 :
void alloc_mem(size_t sz, void **ptr) { if ((*ptr = malloc(sz)) == NULL) { fprinft{stderr, "Alloc_Mem: memoire saturee\n"); exit(1); } }
2. Stylistiquement, la fonction alloc_mem serait plus lgante si son prototype tait void *alloc_mem(size_t), mais cest juste pour lexemple.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

148

7. Fonctions

Passage de grands objets par adresse

On peut passer des structures (ou des unions) par valeur


void print_person(struct Person};

mais cela risque dentrainer une lourde copie de mmoire mmoire. Si lon passe brutalement un pointeur, comme dans
void print_person(struct Person *);

on vite ces copies, mais en contre-partie largument effectif pourra tre modi dans la fonction, ce qui rduit la scurit de programmation. La bonne solution est videmment de passer un pointeur sur une constante :
void print_person(const struct Person *);

ce qui empche la modication de lobjet point dans le corps de la fonction. Noter que le compilateur effectue dans le cadre des appels de fonctions les mmes vrications que celles voques en 5.1.5. Ainsi, dans
void f(struct Person *); void print_person(const struct Person *somebody) { ... f(somebody); ... }

lappel de f provoque une erreur : en effet largument de f nest pas un pointeur sur une constante, mais sur une variable, et donc laccs lobjet point par somebody serait largi dans le corps de f. Chaque fois que lon passe un pointeur en argument, on doit se demander si lobjet point pourra ou non tre modi dans le corps de la fonction. Si la rponse est non, ne pas hsiter passer un pointeur sur une constante. Cest plus quune clause de style : cela renforce la scurit de la programmation et cest un indice de qualit.

7.3.3 Pointeurs sur fonctions


Type pointeur sur fonction

Supposons que lon souhaite passer une fonction en argument une autre fonction. Par exemple, on a crit un programme dintgration numrique qui calcule
b a

f ( x )dx

pour toute fonction f (raisonnable !) argument et rsultat rels. La fonction dintgration, disons integr va donc avoir trois arguments, deux rels et la fonction. En C on passe en fait un pointeur sur fonction :
double integr(double a, double b, double (*pf)(double));

Interprtons la dclaration de pf avec notre rgle habituelle (5.3.1). La dclaration

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

7.3. Fonctions et pointeurs

149

double (*pf)(double)

signie quune expression comme


(*pf)(3.14)

est un rel double ; donc *pf est une fonction un argument double qui retourne un double ; et nalement, pf est bien un pointeur sur une telle fonction.
Utilisation dun pointeur sur fonction

Tout dabord, un nom de fonction rencontr seul cest--dire sans liste darguments associe est de type pointeur sur fonction ; ce pointeur est dailleurs constant puisque la fonction a une adresse qui est choisie par le compilateur et lditeur de liens et qui nest pas modiable. Ainsi, notre fonction integr peut tre invoque correctement par
double cos(double); r = integr(0.0, PI, cos);

ce qui effectue le calcul de


0

cos x dx

Dans le corps de la fonction integr, il est possible dinvoquer la fonction pointe sous la forme complte
(*pf)(x)

mais ansi C autorise aussi la forme raccourcie et syntaxiquement moins obscure


pf(x)

Un pointeur sur fonction est un type (scalaire) comme un autre et, comme tel, peut tre soumis nimporte quelle construction de type. On peut en dnir des tableaux, il peut tre le type de retour dune fonction, il peut tre un champ de structure ou dunion. Le programme 7.1 illustre quelques unes de ces possibilits. Dans ce programme, on trouve quelques types intressants 3 : funct_tab est un tableau de (3) pointeurs sur fonction argument double et rsultat double ; funct_names est un tableau de (3) structures ; le premier champ de cette structure est une chane de caractres (banal !), et le second un double pointeur sur fonction ( argument double et rsultat double) ; La fonction select na pas dargument et retourne un pointeur sur fonction ( argument double et rsultat double) choisie au hasard ; La fonction main choisit une fonction excuter grce la fonction select ; elle applique cette fonction la valeur , puis cherche dans la table funct_names le nom de la fonction qui a t choisie en comparant la valeur du pointeur pcurrf au contenu du champ ppf.
3. On peut essayer de rcrire ce programme avec des typedef pour le rendre plus lisible.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

150

7. Fonctions

Programme 7.1 Les pointeurs sur fonction dans tous leurs tats
/***** Fichier: ptr_fonct.c *****/ #include <stdlib.h> #include <stdio.h> #include <math.h> double (*funct_tab[])(double) = {sin, cos, tan}; struct Funct_Name { const char *name; double (**ppf)(double); /* double pointeur sur fonction */ } funct_names[] = { {"sinus", &funct_tab[0]}, {"cosinus", &funct_tab[1]}, {"tangente", &funct_tab[2]}, }; const int N_FUNCT_NAMES = sizeof(funct_names)/sizeof(struct Funct_Name); double (*select(void))(double) { extern long int random(void);
25

10

15

20

return funct_tab[random() % 3]; } const double PI = 3.141592;

30

int main() { double (*pcurrf)(double); double res; int i; while (1) { pcurrf = select(); res = pcurrf(PI); for (i = 0; i < N_FUNCT_NAMES; i++) { if (*funct_names[i].ppf == pcurrf) { printf("Fonction: %s Resultat: %g\n", funct_names[i].name, res); break; } } } }

35

40

45

50

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

7.4. La fonction main

151

Voici un exemple dexcution :


% ptr_fonct Fonction: sinus Resultat: 6.5359e-07 Fonction: sinus Resultat: 6.5359e-07 Fonction: cosinus Resultat: -1 Fonction: tangente Resultat: -6.5359e-07 Fonction: cosinus Resultat: -1 Fonction: sinus Resultat: 6.5359e-07 Fonction: sinus Resultat: 6.5359e-07 Fonction: cosinus Resultat: -1 Fonction: cosinus Resultat: -1 Fonction: cosinus Resultat: -1 Fonction: sinus Resultat: 6.5359e-07 Fonction: tangente Resultat: -6.5359e-07 C %

7.4 La fonction main


La fonction main joue un rle trs particulier puisque cest par elle que commence lexcution dun programme 4 . La norme interdit dappeler explicitement la fonction main. Le type de retour de main doit tre int. Une instruction comme
return c;

dans main termine le programme en transmettant le code de retour c au processus pre (par exemple le shell ; elle est quivalente exit(c). On peut dnir main avec de 0 3 arguments. La totale sous Unix est
main(int argc, char *argv[], char *envp[])

o
argc argv envp le nombre darguments positionels un tableau de pointeurs sur caractre de dimension argc un tableau de chanes de caractres qui se termine au premier i tel que envp[i] == NULL

Les chanes de caractres argv[0], argv[1], ..., argv[argc - 1] correspondent aux arguments de la ligne de commande la manire des arguments positionnels $0, $1, ... du shell. En particulier, argc vaut toujours au moins 1, et argv[0] est le nom de la commande. Il est garanti que argv[argc] est gal au pointeur NULL. Le troisime argument est rarement utilis ; cest aussi un tableau de chanes de caractres dont chacune est analogue chane reprsentant une initialisation de variable denvironnement du shell, par exemple
"PATH=/usr/ucb:/bin:/usr/bin"

Ceci permet de passer main un environnement particulier. A titre dexemple, le programme 7.2 ralise la commande echo du shell avec quelques modications. On rafche les arguments que lon a reu. La commande accepte trois options :
4. au moins du point de vue du programmeur

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

152

7. Fonctions

-n -l -u

pas de n de ligne les majuscules sont transformes en minuscules les minuscules sont transformes en majuscules

Les fonctions de transformation en majuscules et minuscules utilisent les fonctions de bibliothque standard toupper et tolower dont le prototype est dans le chier dentte <ctype.h> (voir 9.4.1). Programme 7.2: La commande echo modie
/***** Fichier: newecho.c *****/ #include <stdio.h> #include <ctype.h>
5

int no_new_line = 0; int upper_case = 0; int lower_case = 0;


10

15

void to_upper(char str[]) { int i; for (i = 0; str[i] != \0; i++) str[i] = toupper(str[i]); } void to_lower(char str[]) { int i; for (i = 0; str[i] != \0; i++) str[i] = tolower(str[i]); } int main(int argc, char *argv[]) { int nopt; int narg; for (nopt = 1; nopt < argc && argv[nopt][0] == -; nopt++) { switch (argv[nopt][1]) { case n: no_new_line++; break; case u: upper_case++; break; case l: lower_case++; break; default: fprintf(stderr, "%s: option inconnue: %s\n", argv[0], argv[nopt]); break; } }

20

25

30

35

40

45

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

7.5. Exercices du chapitre 7

153

50

55

for (narg = nopt; narg < argc; narg++) { if (upper_case) to_upper(argv[narg]); else if (lower_case) to_lower(argv[narg]); printf("%s ", argv[narg]); } if (!no_new_line) putchar(\n); return 0; }

7.5 Exercices du chapitre 7


Exercice 7.1 (Intgration par la mthode des trapzes) crire
s = integr(a, b, f, delta_x);

une fonction dintgration par la mthode des trapzes invocable de la manire suivante :

o f est une fonction argument double et rsultat double et o s, a, b et delta_x sont des double. Cette fonction doit retourner une approximation de
b a

f ( x ) dx

en intgrant f par la mthodes des trapzes avec un pas de delta_x.


Exercice 7.2 (Efcacit des fonctions inline) Il nest pas sr que loptimisation

-O ne soit pas meilleure... quand elle est possible.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

154

7. Fonctions

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Chapitre 8

Structure des programmes


chapitre dcrit la grands en pluCesieurs sparment.manire de,structurer deplusieursprogrammes(.h)(.c) units de compilation cest--dire chiers-sources compilables Le rle et le contenu des chiers dentte est galement mis en vidence, comme moyen de partager des dclarations entre plusieurs units de compilation. Enn, on montre comment lutilisation de ces mcanismes permet de dcouper un programmes en modules regroupant des lments smantiquement proches.

8.1 Espaces de nommage


ansi C classe les identicateurs en quatre espaces de nommage indpendants dcrits ci-aprs : 1. E1 : les noms dobjets (variables et constantes), de fonctions, de types (dnis par typedef) et les valeurs de tous les types numrs (enum) ; 2. E2 : les tiquettes (pour goto ); 3. E3 : les tags de structures, unions et numrations ; 4. E4 : les noms de champs pour chaque structure ou union individuellement. Ceci signie par exemple quun identicateur, disons ident, peut tre la fois le nom dune variable, une tiquette, un tag et un nom de champ de structure sans quil y ait dambigut. En revanche, ident ne peut pas dsigner la fois une variable et une fonction, ni un type et une constante numre. Pour le dernier espace (E4), individuellement signie que les noms de champs dune structure ou dune union sont dans un espace diffrent de celui dune structure ou union dun autre type. Le programme 8.1 donne un exemple assez maximaliste illustrant lindpendance de ces espaces. Dans ces espaces de nommage, nous ne prenons pas en compte celui des noms de macros du prprocesseur, dnis par #define. Ces noms de macros ont une porte qui englobe les espaces ci-dessus puisquil sagit de remplacement textuel. Cet espace du prprocesseur prend le pas sur ces quatre espaces et la porte dun nom de macro stend de son point de dnition jusqu la n du chier source courant ou jusqu une directive #undef pour cette macro (voir 6.3.5).

V 2.1 7 fvrier 2010

155

c Jean-Paul R IGAULT

156

8. Structure des programmes

Programme 8.1 Espaces de nommage en C


/***** Fichier: espace_noms.c *****/ int var;
5

/* E1 */ /* E3 */ /* E4 */ /* E1 */ /* E3 */ /* E4 */ /* E4 */

typedef struct st { int st; } st; struct var { double st; double var; }; enum e { A, B } x; struct A { int A; }; int main() { goto var;

10

15

/* E3 */ /* E1 */ /* E1 */ /* E1 */ /* E3 */ /* E4 */

20

25

/* E1 */ /* E2 */ /* /* /* /* /* E2 E2 E2 E2 E2 */ */ */ */ */

30

35

var: st: A: B: goto A; }

8.2 Structure des programmes C


8.2.1 Compilation spare
La compilation spare permet au texte dun programme C dtre dispers dans plusieurs chiers-sources. Ces chiers-sources sont de deux types : les chiers directement compils (dont lextension est .c) et les chiers inclus (dont lextension est traditionnellement .h) 1 . Une chier .c constitue une unit de compilation. En fait la dite unit est constitu du contenu du chier .c et de tous les chiers quil inclus, directe1. En fait la sparation entre chiers .c et .h est une pure tradition et na absolument aucun caractre normatif. La norme de C ne connait en fait que la notion dunit de compilation. Cependant la tradition est trs bien tablie et il convient de la respecter.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

8.2. Structure des programmes C

157

Figure 8.1 Compilation spare et units de compilations

ment ou indirectement (voir la directive #include en 6.2). La gure 8.1 schmatise ce concept. Chaque unit (il y en a trois dans la gure 8.1) est compile sparment par une excution du compilateur : ceci comporte lexcution du prprocesseur (6), lanalyse lexicale et syntaxique, la vrication des types et la gnration de

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

158

8. Structure des programmes

code. Chaque unit de compilation produit en rsultat un chier-objet binaire (extension .o). Toutes les excutions sont indpendantes et en consquence chaque unit doit comporter toute linformation ncessaire au compilateur pour pouvoir effectuer sa tche. Puis les chiers-objets obtenus sont envoys lditeur de liens qui les regroupe en un chier binaire excutable unique en les combinant avec bibliothques ncessaires.

8.2.2 Unit de compilation


Une unit de compilation constitue galement une unit de visibilit (voir 8.3.2) et aussi, le plus souvent un module logique (voir 8.4). Chacune de ces units de compilation est constitue dune suite dlments pris parmi les suivants : directives au prprocesseur ; dnition de types (enum, structures, unions, typedef) ; dclarations ou dnitions de variables ou de constantes, de fonctions. Nous avons dj signal la diffrence quil y avait entre une dnition et une dclaration de fonction (chapitre 7). Il existe la mme diffrence entre une simple dclaration de variable ou de constante, qui se contente den donner le nom et le type, et sa dnition o en outre on procde lallocation de lespace mmoire et ventuellement son initialisation. Pour chaque objet, il peut y avoir autant de dclarations que lon souhaite du moment quelles sont compatibles mais il doit y avoir une et une seule dnition de lobjet dans tout le programme. Cest ce quansi C appelle la rgle de dnition unique (odr ou One denition Rule).

8.3 Dure de vie et rgles de visibilit


Cette section est consacre essentiellement la visibilit et la dure de vie des variables et constantes ; les rgles correspondantes concernant les fonctions seront vues en 8.3.4.

8.3.1 Dure de vie


Le langage C dnit trois classes de dure de vie des objets : les objets statiques dont la dure de vie est la dure totale dexcution du programme ; ces objets sont crs (allous et initialiss) au dbut de lexcution et dtruits la n ; les objets automatiques dont la dure de vie est celle dun bloc ; les objets allocation dynamique explicite 2 qui sont allous et dsallous par le programmeur en gnral laide des fonctions de la famille de malloc et free (voir 9.1.4).
2. Ces objets ne sont pas vraiment dans le langage, puisquils sont allous et dsallous par des fonctions de bibliothque, mais on les considre ici car leur rle pratique est important.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

8.3. Dure de vie et rgles de visibilit

159

8.3.2 Visibilit des objets


La visibilit des objets est rgie par un ensemble de rgles qui permettent de relier de manire unique un identicateur sa dclaration. Il existe en C deux classes de visibilit 3 : la visibilit de bloc, o la dclaration de lobjet doit se trouver dans le bloc o il est utilis ; on parle dobjet local ; la visibilit de chier 4 , o lobjet est visible de sa dclaration jusqu la n de lunit de compilation o se trouve cette dclaration ; la dclaration de lobjet doit donc tre hors bloc ; on parle alors dobjet global bien que cette globalit soit restreinte lunit de compilation courante. Dans un bloc, les objets locaux prennent le pas sur les objets globaux de mme nom et sur les objets locaux des blocs englobants, comme illustr dans lextrait de programme 8.2. Programme 8.2 Visibilit des objets locaux
/***** Fichier: visib_local.c *****/ int g = 1;
5

/* Objet global */ /* Definition de fonction */ /* Masque le g global */ /* Objet local */ /* Le s local */ /* Bloc imbrique */ /* Masque le s precedent */

10

15

void f(int i) { double g; short s; /* ... */ s = 3; /* ... */ { long s; /* ... */ } /* ... */ s = 4; }

/* On retrouve le premier s */

Objet locaux

Les objets locaux se subdivisent en deux catgories : ceux qui sont statiques, cest--dire dure de vie permanente, et dont la dclaration (locale) est prxe par le mot-cl static ; on parle parfois dobjets rmanents ; ceux qui sont automatiques, cest--dire dure de vie de bloc ; aussi leur dclaration a la forme simple habituelle, ventuellement prcde du mot-cl auto ou du mot-cl register. Lextrait suivant illustre ces deux types dobjets locaux :
int f(int i)
3. En fait trois, car on a aussi la visibilit de fonction applicable aux tiquettes (voir 8.3.6) 4. Ce terme de visibilit de chier est passablement impropre : il vaudrait mieux parler de visibilit dunit de compilation.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

160

8. Structure des programmes

{ int a; static int n; register int r; auto int b; ... } /* /* /* /* Objet Objet Objet Objet local local local local automatique */ statique (remanent) */ automatique */ automatique */

Un objet rmanent (statique local) comme n conserve sa valeur dune entre du bloc lautre. Ainsi la fonction suivante conserve dans une variable locale rmanente le nombre de fois o elle a t appele :
int f(int i) { static int nb_calls = 0; nb_calls++; ... }
Modieurs de type register et auto

Le modieur de type register suggre au compilateur de placer la variable correspondante dans un registre machine (quoi que cela signie...). Il ne peut sappliquer qu une variable automatique. Le compilateur est libre dignorer cette suggestion qui ne devrait dailleurs pas faire partie du langage et qui est battue en brche par la plupart des optimiseurs de code modernes. Sauf cas exceptionnel, on ignorera donc register. Quant au mot-cl auto, il ne sert absolument rien ! Mais, comme ces deux mots sont rservs, vous ne pouvez pas les utiliser pour autre chose (par exemple, nom de variable, de fonction, de type...).

Objets globaux

Les objets globaux tombent eux-mmes dans deux catgories : ceux dont la visibilit est uniquement limite au chier (en fait lunit de compilation) o ils sont dnis ; on parle alors dobjets privs (au module) ; ceux qui sont potentiellement visibles dans tout le programme ; on parle alors dobjets externes. Les objets privs sont des objets globaux dont la dnition est prcde du mot-cl static comme dans
static int g = 3; void f() { ... }

Des objets privs de mme nom, mais dans des chiers source diffrents nentrent pas en conit. Comme les objets externes sont potentiellement visibles dans tout le programme, cause de la compilation spare, chaque chier source doit en connaitre le type ; ceci est ralis par une dclaration dexterne comme par exemple :

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

8.3. Dure de vie et rgles de visibilit

161

extern char Days[7][10];

Il sagit bien ici dune dclaration et non pas dune dnition. Il peut y avoir de nombreuses dclarations de cette forme, mais il ne peut y avoir, dans tous les chiers source dun programme, quune seule dnition et seule cette dnition pourra effectuer linitialisation ; elle aura la forme suivante, dj rencontre :
char Days[7][10] = {...};

8.3.3 Initialisation des objets


Linitialisation dun objet ne peut avoir lieu que lors de sa dnition. Les objets statiques privs, rmanents ou externes sont initialiss par dfaut 0 (zro !). Sils sont initialiss explicitement, ils doivent ltre par une expression statique, cest--dire valuable la compilation. Linitialisation zro des objets statiques na pas forcment de sens. Il est donc de bon style dinitialiser explicitement ce qui doit ltre. Les objets automatiques ne sont pas initialiss par dfaut. Sils sont initialiss explicitement, ils peuvent ltre par une expression valuable lexcution :. Pour les objets allocation dynamique, malloc neffectue aucune initialisation, mais certaines fonctions de sa famille comme calloc le font (voir 9.1.4). Lorsquun objet statique est initialis explicitement, cette initialisation na lieu quune fois en dbut de programme. En revanche pour un objet (local) automatique, linitialisation a lieu chaque entre dans le bloc 5 :
int f(int i) { static int s = 3; int j = 3 * i; ... }

/* Local statique (remanent) */ /* Local automatique */

Noter que lobjet s nest ici initialis quune seule fois, au dbut du programme. Lexpression dinitialisation doit donc tre statique, cest--dire valuable la compilation. En revanche, pour un objet automatique comme j, linitialisation est excute chaque entre dans le bloc et peut donc utiliser une expression valuable lexcution.
Visibilit des objets allocation dynamique

Les objets allocation dynamique explicite sont manipuls travers des pointeurs. Leur rgles de visibilit sont donc celles de ces pointeurs.

8.3.4 Visibilit des fonctions


Les fonctions ont bien entendu une dure de vie permanente. Elles sont externes par dfaut, mais il est possible de les rendre prives en prxant leur dnition par le mot-cl static :
static int f(int i) { ... }
5. En fait un nouvel objet est cr et initialis chaque entre dans le bloc.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

162

8. Structure des programmes

Il peut donc exister plusieurs fonctions de mme nom dans le programme, du moment quelles sont toutes dclares static et dnies dans des units de compilation diffrentes.

8.3.5 Synthse : relation entre dure de vie et visibilit


Le tableau 8.1 rsume les diffrentes relations qui existent entre la dure de vie et les rgles de visibilit des objets. Table 8.1 Relations entre dure de vie et visibilit
Objets statiques Visibilit de bloc (locale) Visibilit de chier (globale) Visibilit dans tout le programme (potentiellement) (externe) Objet rmanent static Objet priv static Objet externe extern Objets dynamiques automatiques Objet local automatique auto, register IMPOSSIBLE IMPOSSIBLE

La gure 8.2 illustre quelques unes des situations possibles. La variable v est externe. Elle est dnie (et initialise 2) la ligne 1 de fic1.c. Elle est visible globalement dans tout ce chier. Comme elle fait lobjet dune dclaration dexterne la ligne 1 de fic2.c, elle est galement visible globalement dans tout ce dernier chier. La variable vv est galement externe. Elle est dnie la ligne 2 de fic1.c. Elle est donc visible globalement partir de cette ligne et dans tout ce chier. Nayant pas dinitialisation explicite, elle est initialise 0, comme tout objet allocation statique. Comme elle fait lobjet dune dclaration dexterne locale la fonction f3 de fic2.c (ligne 7), elle est visible dans cette fonction. Et elle est galement visible partir de sa dclaration dexterne globale (ligne 18) du mme chier, jusqu la n. En revanche elle nest pas visible lintrieur de la fonction f4. Les paramtres x, y de f1 (ligne 8 de fic1.c), x de f2 (ligne 17 de fic1.c), x de f3 (ligne 3 de fic2.c) ainsi que les variables z (ligne 11 de fic1.c) et i (ligne 19 de fic1.c) sont des variables locales automatiques. Leur porte est le bloc de la fonction dans laquelle elles sont dnies. Ces variables automatiques ne sont pas initialises par dfaut. La variable v de f2 (ligne 20 de fic1.c) est locale automatique. Sa porte est le bloc de f2. lintrieur de cette fonction elle masque la variable globale externe v. Les deux variable nommes z sont toutes les deux globales statiques. Comme elles sont dnies dans deux units de compilation diffrentes (ligne 15 de fic1.c et 17 de fic2.c), elles dsignent des objets distincts, de types dailleurs galement diffrents. Nayant pas dinitialisation explicite, elles sont initialiss 0. Elles sont visibles de leur point de dnition jusqu la n du chier o elles sont dnies. Elles ne peuvent pas tre lobjet dune dclaration dexterne. Les fonctions f1, f2 et g sont externes. Elles sont visibles de leur point de

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

8.3. Dure de vie et rgles de visibilit

163

Figure 8.2 Visibilit et dure de vie : quelques exemples Programme 8.4 Visibilit et dure de vie : fic2.c
extern int v; static int { double static extern /* ... } f3(int x) cos(double); int w; int vv; */

Programme 8.3 Visibilit et dure de vie : fic1.c


int v = 2, vv; int main() { /* ... */ } void f1(int x, int y) { extern double g(); int z; /* ... */ } static double z; double f2(int x) { register int i; int v; /* ... */ }

static void f4(void) { /* ... */ }

static int z[10]; extern int vv; extern int f1(int, int); double g(void) { /* ... */ }

dnition jusqu la n du chier o elles sont dnies. En outre f1 est galement visible partir de la ligne 19 du chier fic2.c. La fonction g a aussi une dclaration dexterne (ligne 10 de fic1.c) qui la rend visible dans le bloc de la fonction f1. Enn les fonctions f3 et f4 de fic2.c (lignes 3 et 11) sont statiques. Elles ne sont donc visibles qu partir de leur point de dnition jusqu la n du chier courant. Elles ne peuvent pas tre lobjet dune dclaration dexterne.

8.3.6 Autres rgles de visibilit


Les tiquettes ont une visibilit limite la fonction o elles sont dnies. Les noms de types (obtenus par typedef), les tags de structure, dunion, dnumration, les constantes symboliques des numrations ont une visibilit globale si leur dnition lest et locale si leur dnition se trouve dans un bloc. Le programme 8.5, bien quun peu pathologique, illustre ce dernier type de visibilit.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

164

8. Structure des programmes

Programme 8.5 Visibilit des noms de type en ansi C


typedef struct A { short a; struct B { int b; } b; } A; struct B b; int main() { typedef struct A { int a; } A; enum e {X, Y, Z}; A a; } void f() { typedef struct A { double a; } A; A a; struct A b; enum e {X, Y, Z}; } /* Global */

/* Global */

10

/* Local */

15

/* Local */

20

25

/* Local */

30

/* Local */

8.4 Programmation modulaire en C


8.4.1 Notion de module
Dans le sens gnral, un module est un regroupement (syntaxique) de types, de variables, de constantes et de fonctions logiquement (ou smantiquement) lies. On distingue classiquement deux parties dans un module : linterface du module, qui contient lensemble des informations dont ont besoin les fonctions qui veulent utiliser le module ; le corps du module qui contient limplmentation des diffrentes fonctions et donnes. Il doit tre possible de changer le corps limplmentation du module sans changer son interface, cest--dire la manire de lutiliser. Dans ces conditions, les programmes utilisant le module nauront pas besoin dtre recompils ; seule ldition de liens devra tre refaite.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

8.4. Programmation modulaire en C

165

Figure 8.3 Organisation dun module en C

En C, il est possible avec une certaine discipline de raliser ce type de programmation modulaire. Linterface est constitue par un chier dentte comportant les dnitions de type utilises, les dclarations dobjets externes et les prototypes des fonctions externes de manipulation du module. Ce chier sera inclus par tous les chiers-sources qui veulent utiliser le module. La gure 8.3 schmatise cet organisation. Elle dcrit un module de nom mod. Son interface est un chier dentte mod.h et son implmentation une unit de compilation mod.c. Bien entendu, linterface elle-mme peut avoir besoin de dclarations ou dnitions emprunts dautres modules, elle peut donc en inclure les chiers dentte (.h) correspondants. De mme, limplmentation mod.c peut avoir besoin directement dautres ressources qui ne sont pas dans mod.h ni dans les chiers que ce dernier inclus. Enn le code client (client.c) qui utilise le module mod ne doit avoir inclure que linterface de ce module (mod.h). Bien entendu, ldition de liens, les deux chiers objets client.o et mod.o doivent tre fournis ainsi que ceux correspondant aux autres modules quils utilisent. Lart du concepteur de modules est dtablir les dpendances minimales entre ces diffrents composants. Le code client dpend de tout qui est dans linterface (directement ou inclus). Toute modication directe ou indirecte de cette interface risque donc dinduire des modications et en tout tat de cause une recompilation du code client. Il faut donc ne mettre dans linterface que ce qui est ncessaire et sufsant au code client pour utiliser le module.

8.4.2 Objets globaux trouvs dans une interface de module


Noter quun chier dentte comme mod.h qui constitue linterface dun module ne doit jamais contenir de dnition dobjets externes (variables, constantes, ou fonctions). Il peut (doit) en revanche en contenir des dclarations. En revanche un -

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

166

8. Structure des programmes

chier dinterface peut contenir des dnition de constantes et variables globales ainsi que des dnitions de fonctions du moment que ces objets sont privs (static) lunit de compilation. Tout se passera comme si chaque unit de compilation avait sa propre copie des objets en question, mais toutes ces copies seront de mme type. En particulier, les fonctions inline (7.2.4) doivent toujours tre dclare aussi statiques et tre dnies dans un chier dentte .h inclut par toutes les units de compilation qui souhaitent en partager la dnition
#ifndef _Utilitaires_h_ #define _Utilitaires_h_ /***** Fichier: utilitaires.h *****/ /* * Quelques fonctions inline dusage general et * destinees a etre utilisees dans de nombreux modules. */ static inline int max(int a, int b) { return a > b ? a : b; } static inline int min(int a, int b) { return a < b ? a : b; } static inline int abs(int a) { return a < 0 : -a : a; } #endif

Enn, noter que comme tout chier .h, mais encore plus que pour les autres un chier dinterface doit tre protg en inclusion unique (6.4.2).

8.4.3 Exemple dorganisation modulaire


Par exemple, le chier Stack.h (programme 8.6) dcrit linterface dun module pile dentiers , gr selon une stratgie dernier entr, premier sortie . Cette interface se rduit au prototype des quatre fonctions de base : empiler, dpiler, tester si la pile est pleine, tester si elle est vide, ainsi quune fonction dinitialisation. Le corps du module est dans le chier Stack.c (programme 8.7) : la pile est ralise laide dun tableau qui est allou dynamiquement (par malloc) puisque lon veut xer la taille de la pile lexcution. On remarque que, lexception des cinq fonctions externes de linterface, tous les autres objets sont privs. Enn le programme 8.8 utilise ce module de pile en empilant puis dpilant une suite de nombres. En voici un exemple dexcution :

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

8.5. Exercices du chapitre 8

167

Programme 8.6 Spcication du module pile dentiers


/***** Fichier: Stack.h *****/ /* Interface du module Stack */
5

#ifndef _Stack_h_ #define _Stack_h_ void stack_init(int stack_size); /* Alloue et intialise */

10

void stack_push(int elem); int stack_pop(void); int stack_is_full(void); int stack_is_empty(void); #endif

/* /* /* /*

Placer un element */ Retirer un element */ Pile pleine ? */ Pile vide ? */

15

% Stack Taille de Entrez un Entrez un Entrez un Entrez un Entrez un Entrez un Entrez un Entrez un Entrez un Entrez un Resultat: %

la pile? 10 nombre: 1 nombre: 2 nombre: 3 nombre: 4 nombre: 5 nombre: 6 nombre: 7 nombre: 8 nombre: 9 nombre: 10 10 9 8 7 6 5 4 3 2 1

8.5 Exercices du chapitre 8


Exercice 8.1 (Modication de limplmentation des piles) Changer le corps du

module pile dentiers en utilisant une liste la place dun tableau.


Exercice 8.2 (File

Ecrire un module fifo (cest--dire une le premier entr, premier sorti ) la manire du module pile dentiers .
FIFO )

Exercice 8.3 (Du module au type abstrait) ADT Stack et Fifo.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

168

8. Structure des programmes

Programme 8.7 Corps du module pile dentiers


/***** Fichier: Stack.c *****/ /* Implemetnation du module Stack */
5

#include <stdio.h> #include <stdlib.h> #include "Stack.h" static int *the_stack; /* La pile elle-meme */ static int stack_top = 0; /* Indice du sommet */ static int stack_size; /* Taille amximale */ static void error(const char *msg) { fprintf(stderr, "Stack: ERREUR: %s\n", msg); exit(1); } void stack_init(int size) { if (size < 0) error("taille"); if ((the_stack = malloc(size*sizeof(int))) == NULL) error("malloc"); stack_size = size; } void stack_push(int elem) { if (!stack_is_full()) the_stack[stack_top++] = elem; else error("pile pleine"); } int stack_pop(void) { if (!stack_is_empty()) return the_stack[--stack_top]; else error("pile vide"); /*NOTREACHED*/ return 0; // pour eviter un warning... } int stack_is_full(void) { return stack_top == stack_size; } int stack_is_empty(void) { return stack_top == 0; }

10

15

20

25

30

35

40

45

50

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

8.5. Exercices du chapitre 8

169

Programme 8.8 Utilisation du module pile dentiers


/***** Fichier: Stack_main.c *****/ /* Utilisation du module Stack */
5

#include <stdio.h> #include "Stack.h" int main() { int n = 10; printf("Taille de la pile = %d\n", n); stack_init(n);

10

15

for (int i = 0; !stack_is_full(); ++i) stack_push(i); printf("Resultat: "); while (!stack_is_empty()) printf("%d ", stack_pop()); putchar(\n); return 0; }

20

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

170

8. Structure des programmes

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Chapitre 9

La bibliothque standard
n ne ici donner description complte toutes Opagesprtend pas(commande une deou aPoursite enillignedecelui deles fonctions de la bibliothque standard C. cela, vaut mieux se reporter aux du manuel ) un : Wikipeman

dia est un bon point de dpart (cherchez "c standard library manual"). Le livre de Prata [3] contient galement une description trs complte de la bibliothque standard.

9.1 lments gnraux


9.1.1 Assertions : <assert.h>
Dnition de la macro assert : voir 3.3.3 et 6.3.6. noter que si lon dnit la macro NDEBUG
#define NDEBUG

les assertions disparaissent (aucun code nest gnr).

9.1.2 Codes derreur : <errno.h>


Ce chier dnit de possibles codes derreurs positionns par les fonctions de la bibliothque. Seuls trois codes sont dnis dans le standard et il nest pas requis que les fonctions les positionnent ! En C pur, ce chier est donc de peu dintrt. En revanche, en programmation-systme sous Unix ce chier est trs important car il contient les codes derreur positionnes par les primitives systme de la norme Posix

9.1.3 Dnitions communes : <stddef.h>


Ce chier contient un certain nombre de dnitions utiles, comme celle du type size_t (le type de retour de loprateur sizeof) ou de la valeur NULL du pointeur de mme proprit. En gnral, ce chier <stddef.h> est inclus dans <stdlib.h> et donc na pas tre inclus directement.

V 2.1 7 fvrier 2010

171

c Jean-Paul R IGAULT

172

9. La bibliothque standard

9.1.4 Utilitaires gnraux : <stdlib.h>


Ce chier contient un grand nombre dlments et dutilitaires gnraux. Il est rare que lon nen ait pas besoin dans un programme. Parmi ces lments ceux de <stddef.h> ; les constantes EXIT_SUCCESS (0) et EXIT_FAILURE (1) utilisables comme paramtre de la fonction exit ou comme valeur de retour de la fonction main ; des fonctions comme atoi, atof, etc. pour des conversions entre chaines de caractres et types numriques ; des gnrateurs de nombres au hasard comme rand, srand, etc. les fonctions dallocation mmoire dynamique de la famille malloc, calloc et free ; les fonctions de terminaison du programme comme exit et abort ; et bien dautres choses encore...

9.2 lments numriques


9.2.1 Boolens : <stdbool.h>
C99

C99 dnit le mot rserv _Bool qui est une numration avec deux valeurs 0 et 1. Si, en plus, vous incluez le chier <stdbool.h>, alors _Bool est rendu synonyme de bool et vous gagnez aussi le deux nouvelles macros true et false. Il est fortement conseill de le faire. Outre une meilleure lisibilit, cela assure la compatibilit avec C++.

9.2.2 Types entiers : <stdint.h>


Ce chier dnit une ensemble de types entiers signs ou non dont le nombre de bits de la reprsentation est garanti : int8_t, int32_t, uint64_t, etc.

9.2.3 Conversion des types entiers : <inttypes.h>


C99

Ce chier dnit un ensemble de macros pour faciliter la conversion entre les types entiers nombre de bits garantis de <stdint.h>.

9.2.4 Environnement pour calcul en nombres rels : <fenv.h>


C99

Ce chier dnit un certain nombre de types et de fonctions permettant de manipuler des exceptions lors des calculs en nombres ottants. Le pragma
#pragma STD FENV_ACCESS on

active le mcanisme dexception (et le mme pragma avec off le dsactive). Les exceptions en question capturent des erreurs de calcul comme le dpassement de capacit, la division par zro, les valeurs illgales, etc. On peut aussi contrler la manire dont seffectuent les arrondis. Tout ceci est rserv aux spcialistes du calcul scientique...

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

9.3. Fonctions mathmatiques

173

9.2.5 Nombres complexes : <complex.h>


Ce chier dclare le type complex comme synonyme de _Complex et les oprations sur ce type (y compris les fonctions trigonomtriques). Noter que ni _Complex ni a fortiori complex ne sont des types de base : ils font juste rfrence une structure 2 champs, la partie relle et la partie imaginaire ; en outre les oprations arithmtiques sont ralises sous forme de fonctions et non doprateurs (comme + ou *).
C99

9.3 Fonctions mathmatiques


9.3.1 Bibliothque mathmatique de base : <math.h>
Le chier <math.h> contient la dclaration dun grand nombre de fonctions mathmatiques (fonctions trigonomtriques, de Bessel, logarithmes et exponentielles, arrondis, comparaisons entre rels, etc.). Si vous utilisez ces fonctions vous devez diter les liens avec la bibliothque mathmatique en fournissant loption -lm votre commande ddition de liens :
gcc -o prpg prog1.o prog2.o prog3.o -lm

9.3.2 Bibliothque mathmatique gnrique : <tgmath.h>


Ce chier dnit un ensemble de macros qui permettent dutiliser les fonctions de <math.h> et <complex.h> en utilisant un nom unique pour chaque fonction. Ainsi, aprs inclusion de ce chier, lexpression sin(x) invoquera la fonction sinus adapte au type de x : csin sil sagit dun complexe, sinf si cest un float, etc. Il sagit l dune manire rustique de compenser labsence de surcharge des fonctions et oprateurs en C !
C99

9.4 Caractres et chanes de caractres


9.4.1 Manipulation de caractres : <ctype.h>
Ce trs utile chier contient des fonctions permettant de dterminer la nature dun caractre : espace (isspace), lettre (isalpha) ou chiffre (isdigit), ponctuation (ispuct), etc. Il permet aussi certaines transformations comme le passage en majuscules ou minuscules (toupper, tolower)... Le fonctionnement de ces fonctions dpend des conventions locales (voir 9.4.5 plus loin) et nest correct quavec un encodage de caractres sur 8 bits.

9.4.2 Manipulation de chanes de caractres : <string.h>


Indispensable, ce chier dclare les fonctions de manipulations de chanes de caractres (encods sur 8 bits) : copie (strcpy, strcat), comparaison lexicographique (strcmp), etc. Les chanes doivent tre bien videmment termines par le caractre nul (voir gure 3.1).

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

174

9. La bibliothque standard

Ce chier dclare aussi des fonctions de manipulation brutales de zones de mmoire comme la copie (memcpy), la comparaison (memcmp), etc. Voici par exemple une manire trs efcace (beaucoup plus efcace quune boucle lment par lment) de copier un tableau de double dans unn autre (de mme taille) :
double t1[100]; double t2[100]; // initialisation des elements de t2 memcpy(t1, t2, 100 * sizeof(1)); // copie t2 dans t1

9.4.3 Manipulation de caractres tendus: <wctype.h>


C99

Ce chier est lquivalent de <ctype.h> mais pour les caractres encods sur plus dun octet (type wchar_t), par exemple Unicode.

9.4.4 Manipulation de chanes de caractres tendus : <wchar.h>


C99

Ce chier est lquivalent de <string.h> mais pour les caractres encods sur plus dun octet (type wchar_t), par exemple Unicode. Il contient galement des opration de conversions entre les diffrents encodages ainsi que lquivalent des oprations dE/S de <stdio.h>) pour les caractres wchar_t.

9.4.5 Localisation : <locale.h>


Ce chier dclare un ensemble dlments permettant la localisation des programmes, cest--dire le respect dun certain nombre de convention locales : point dcimal, symbole montaire, format de la date et de lheure, jeu de caractres, etc. La manipulation des locales est un monde en soi, quil ne nous appartient pas de dcrire ici !

9.5 Entres-sorties : <stdio.h>


Lun des plus utiles de la bibliothque standard, ce chier dclare les fonctions dentres-sorties : ouverture et fermeture de chier : fopen, fclose... accs direct au chier : fseek... E/S en mode caractre : getchar, putchar... E/S en binaire : fread, fwrite... E/S formattes : printf, scanf...

9.6 Divers

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

9.6. Divers

175

9.6.1 Fonctions nombre variable darguments : <stdarg.h>


Ce chier dnit des macros permettant dextraire chacun des arguments dune liste variable darguments. la page de manuel (man vararg) est trs bien faite.

9.6.2 Date et heure : <time.h>


Ce chier permet de consulter, dimprimer, de transformer des dates et heures (entre gmt et lheure locale). Bien utile...

9.6.3 Traitement dvnements : <signal.h>


Oubliez celui-l ! Il vaut mieux tudier le problme de la gestion dvnements dans un cadre systme (e.g., Posix).

9.6.4 Points de reprise : <setjmp.h>


Permet de positionner des points de reprise pour y revenir plus tard. Cela permet de simuler un mcanisme dexception rustique comme dans lextrait de programme suivant :
#include <stdio.h> #include <setjmp.h> jmp_buf env; double sqrt(double x) { if (x < 0) longjmp(env, 1); // ... algorithme de calcul de la racine carree ... return ...; } int main() { double t[] = {1.5, 3.5, -2.5, 4.0}; const int N = sizeof(t) / sizeof(t[0]); for (int i = 0; i < N; ++i) { if (setjmp(env) != 0) fprintf("valeur = %lg est negative!\n", t[i]); printf("sqrt(%lg) = %lg\n", t[i], sqrt(t[i])); } }

Lisez donc la page de manuel (man setjmp) pour comprendre ce que fait ce programme... Inutile de dire quil sagit l dun horreur absolue par rapport un vrai mcanisme dexception comme en Java ou C++.!

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

176

9. La bibliothque standard

9.6.5 Notation alternative de certains oprateurs : <iso646.h>


Ce chier est utile si on veut utiliser les mot-cls dnis par C++ pour certains oprateurs. Il dnit les macros quivalentes pour les oprateurs suivants : Macro and and_eq bitand bitor compl not_eq or or_eq xor xor_eq Prfrer
if (not p and i < N and p[i] not_eq 0) ...

Oprateur && &= & | ~ != || |= ^ ^=

if (!p && i < N && p[i] != 0) ...

est videmment une question de gout et de couleur ! Ma propre prfrence va en fait


if (p != 0 and i < N and p[i] != 0) ...

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Chapitre 10

Environnement de dveloppement

Pour dvelopper du logiciel avec un langage on a besoin doutils pour supporter les diffrentes activits : un compilateur du langage, capable de produire du code pour le processeur et le systme dexploitation vis, un diteur de texte, de prfrence connaissant la syntaxe du langage et capable de la faire ressortir par le jeu des couleurs et/ou des polices, un gestionnaire de conguration permettant denchaner harmonieusement et efcacement les diffrentes tapes de la production de code, des outils de mise au point ( debuggers ), des outils de gestion et de gnration de documentation.

10.1 Compilateurs
Quils soient libres ou propritaires, il existe un grand nombre de compilateurs pour le langage C. Wikipedia 1 en liste la plupart. Il est noter que le clbre Visual C++ de Microsoft 2 nest pas, dans ses versions rcentes tout au moins, un compilateur C99. La suite gcc 3 , le gnu c compiler, est universellement rpandue. Elle possde un certain nombre davantages : elle est libre, sous licence gpl 4 ; il sagit non pas dun simple compilateur dun langage donn, mais dun multi-compilateurs, supportant plusieurs langages diffrents : C et C++, Objective C et C++, Ada, Fortran, Java... la suite gcc volue de manire trs efcace et ractive ce qui lui permet de suivre de trs prs, voire danticiper, le travail de normalisation de ces diffrents langages ; elle produit du code pour virtuellement tout processeur et la plupart des systmes disponibles sur le march ; sa qualit globale nest plus dmontrer (mme si certains compilateurs propritaires peuvent tre suprieurs sur certains points particuliers : e.g., la suite de compilateurs Intel et larchitecture multi-curs) ;
1. 2. 3. 4. http://en.wikipedia.org/wiki/List_of_compilers http://msdn.microsoft.com/en-us/library/60k1461a(VS.80).aspx http://gcc.gnu.org/ http://www.gnu.org/licenses/gpl.html

V 2.1 7 fvrier 2010

177

c Jean-Paul R IGAULT

178

10. Environnement de dveloppement

cest la suite favorite du monde du logiciel libre, disponible par dfaut sur tout les Linux et autres UnixBSD ; cest le compilateur par dfaut qui est derrire la plupart des environnements intgrs de dveloppement (IDEs) multi-plateformes actuellement disponibles : Eclipse, Code::Blocks, CodeLite, etc.

10.2 Dveloppement traditionnel sous U NIX


Cest videmment sous Unix que lon a commenc dvelopper en C. Au dpart on utilisait des outils assez disparates pour supporter les diffrentes activits. Assez rapidement, des passerelles ont t tablies entre ces outils, allant mme, dans certains cas, jusqu une vritable intgration.

10.2.1 dition de source


Plus personne nutilise un diteur ligne comme ed pour crire du code ; on souhaite au moins pouvoir afcher et travailler directement sur toute un page de code. Le successeur 2 dimensions ded, vi, remplit bien ce rle, surtout sous sa forme rcente vim qui permet dditer plusieurs chiers simultanment et de dcorer la syntaxe. Cependant, mme si vim a son fan club, de mme que gedit ou kate, les rois des diteurs dans ce domaine sont ceux de la famille Emacs, soit emacs lui-mme (gnu emacs pour tre correct), soit son clone xemacs. Ces diteurs disposent depuis fort longtemps dune suite impressionnante de proprits : dition et afchage simultans de plusieurs chiers, afchage simultan de parties du mme chier avec synchronisation automatique des vues, intgration parfaite avec le systme de fentrage natif : X11 sous Unix/Linux, MS Windows, aquamacs sous Mac OS X... extensibilit, avec un puissant dialecte de Lisp qui permet dajouter nimporte quelle fonction ddition, mais aussi de communiquer avec quasiment nimporte quelle application extrieure. Cest cette dernire proprit qui donne sa pleine puissance Emacs. Elle a permis de dvelopper un ensemble considrable doutils facilitant le vie du programmeur : modes langages, avec coloration syntaxique, aide la saisie, compltion de code, etc. pour virtuellement nimporte quel langage connu ou moins connu, intgration de loutil make avec remonte automatique des messages derreurs de compilation vers les lignes correspondantes des chiers-source, intgration du debugger gdb, intgration des outils de gestion de documentation comme doxygen... On peut dire que emacs est, depuis fort longtemps, un vritable IDE. La gure 10.1 montre cet aspect dintgration. Trois chiers source sont en cours ddition, dont deux afchs (simple_list.h et simple_list_main.c ; la troisime fentre, tout en bas montre lexcution de la commande make. Si des erreurs de compilation taient intervenues, une simple commande lditeur aurait permis dafcher le chier source correspondant, la ligne en erreur.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

10.2. Dveloppement traditionnel sous Unix

179

Figure 10.1 XEmacs luvre

Pendant longtemps, les diteurs de type Emacs ont eu la rputation dtre complexes utiliser et de ncessiter lapprentissage dun grand nombre de raccourcis clavier grand renfort de touches control ou escape. Ce ntait pas faux mais il faut reconnaitre que, de nos jours, mme si la maitrise totale de toutes les fonctionnalits dEmacs reste videmment difcile, grce la souris, aux touches spciales du claviers, aux boutons et aux menus, lutilisation quotidienne en est devenue trs aise.

10.2.2 Loutil make


Lutilitaire make est une des perles qua produite Unix. Cette commande exploite les dpendances chronologiques entre chiers source (fondes sur la date de dernire modication) pour dclencher les actions ncessaires la reconstruction dun produit nal, comme par exemple un binaire excutable. Son rle nest certes pas limite la compilation et ldition de liens en C, mais dans ce cas comme ailleurs elle y est prcieuse. Les dpendances entre chiers doivent tre fournies dans un chier nomm makefile (ou Makefile ou MAKEFILE) dans le rpertoire o vous lancez make. Cependant make peut dduire certaines dpendances grce ses rgles

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

180

10. Environnement de dveloppement

par dfaut. Par exemple, si vous programme tient dans un seul chier source, disons prog.c et que vous avez correctement dnit les variables denvironnement CC et CFLAGS, vous navez mme pas besoin de makefile ; tapez simplement
% make prog gcc -g -Wall -std=c99 -o prog prog.c %

Par ailleurs, si vous ntes pas sr de vos dpendances entre chiers, le compilateur gcc vous aidera : utilisez loption -MM sur la liste des units de compilation (chiers *.c) et il vous fournira ces dpendances sous une forme telle que vous naurez plus qu la couper-coller dans votre makefile :
% gcc -MM *.c random_list.o: random_list.c simple_list.o: simple_list.c simple_list.h simple_list_double_ptr.o: simple_list_double_ptr.c simple_list_main.o: simple_list_main.c simple_list.h %

10.2.3 Mise au point avec gdb et compagnie


Le gnu debugger gdb est loutil de mise au point le plus utilis sous Unix ou Linux. Il permet la mise au point au niveau source, sous rserve que vous ayez utilis loption -g lors de la compilation. Il permet de placer des points darrt, de progresser pas pas ou en continu, de visualiser des variables, des ts de contrle ( threads ), la pile dappel, etc. On peut lutiliser en mode ligne, mais il est plus agrable et efcace le le faire travers lune de ses interfaces graphiques comme emacs, xxgdb, ddd ou lun des multiples environnements intgrs qui lutilisent (voir section 10.3). La gure 10.2 prsente son utilisation traver ddd. Elle montre deux fentres, celle de ddd proprement dite et, en bas, la fentre dexcution du programme (une fentre xterm normale). Dans le fentre de ddd se trouvent trois sous-fentres : tout en bas, celle qui permet de donner des ordres au debugger ; au dessus, la visualisation du chier source en cours o lon voit que lexcution est bloque sur un point darrt sur une ligne invoquant la fonction malloc. La troisime fentre est une originalit de ddd : ce debugger permet en effet de dessiner les structures de donnes avec pointeurs (ici une simple liste) de manire naturelle : sympa dans les cas simples, mais gadget dans les cas plus complexes ? Il existe bien dautres outils pour la mise au point. Les prolers comme gprof vous permettent de mesurer lors de lexcution le nombre dappels et le temps pass dans chaque fonction. Vous pourrez ainsi savoir ce qui vous coute le plus cher en temps et donc ncessite le plus gros de vos efforts doptimisation. Un autre type doutils a une importance considrable en particulier dans les programmes utilisant lallocation de mmoire dynamique (malloc and Co). Ce sont les dtecteurs de fuite de mmoire. Les plus connus sont Purify de Rational (maintenant IBM), trs efcace et puissant, mais cher, et valgrind qui lui est sufsamment puissant, facile dutilisation et... libre.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

10.2. Dveloppement traditionnel sous Unix

181

Figure 10.2 ddd (et gdb) luvre

Pour utilisr valgrind, aucune option de compilation ou ddition de liens nest ncessaire. Vous invoquez juste lutilitaire avec le nom de votre excutable :
% valgrind simple_list ==4576== Memcheck, a memory error detector. ==4576== Copyright (C) 2002-2008, and GNU GPLd, by Julian Seward et al. ==4576== Using LibVEX rev 1884, a library for dynamic binary translation. ==4576== Copyright (C) 2004-2008, and GNU GPLd, by OpenWorks LLP. ==4576== Using valgrind-3.4.1, a dynamic binary instrumentation framework. ==4576== Copyright (C) 2000-2008, and GNU GPLd, by Julian Seward et al. ==4576== For more details, rerun with: -v ==4576== Entrez une suite dentiers terminees par EOF 1 3 2 Liste triee: 1 2 3 ==4576== ==4576== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 2) ==4576== malloc/free: in use at exit: 2,156 bytes in 3 blocks. ==4576== malloc/free: 4 allocs, 1 frees, 2,172 bytes allocated. ==4576== For counts of detected errors, rerun with: -v

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

182

10. Environnement de dveloppement

==4576== ==4576== ==4576== ==4576== ==4576== ==4576== ==4576== ==4576== ==4576== %

searching for pointers to 3 not-freed blocks. checked 71,744 bytes. LEAK SUMMARY: definitely lost: 32 bytes in 2 blocks. possibly lost: 0 bytes in 0 blocks. still reachable: 2,124 bytes in 1 blocks. suppressed: 0 bytes in 0 blocks. Rerun with --leak-check=full to see details of leaked memory.

Pendant lexcution qui se droule normalement (lignes marque en bleue 5 ), les fonctions de la famille malloc sont instrumentes. la n un rapport est mis. On constate un dsquilibre entre le nombre dallocation (4) et de dallocation (1) ; on nous indique aussi que 2 blocs totalisant 32 octets sont non rcuprables. Rien de dramatique ici puisque le programme se termine et que donc lensemble de son espace mmoire est retourn au systme. Mais dans des cas plus complexes, cela pourrait tre lindication dune catastrophe venir... Sachez que valgrind possde dautres options et bien dautres possibilits. Nhsitez pas lutiliser et apprenez interprter ses rapports.

10.2.4 Outils de documentation


Lune des qualits majeures que lon peut attendre de la documentation interne dun programme est dtre en permanente synchronisation avec le code luimme. Cela est difcile imposer mais certains ont pens quon pouvait au moins linciter en plaant cette documentation dans les commentaires du code. Ainsi en modiant le code, peut-tre penserait-on aussi mettre jour la doc 6 . Parmi les outils permettant de grer une telle documentation dans les commentaires, les plus connus sont Javadoc (malheureusement limit au langage... Java) et doxygen 7 . Ce dernier permet de documenter du code source en C, C++, Java, Objective C, Python, idl, Fortran, vhdl, php et C#. Il produit une documentation dans une foultitude de langues diffrentes (dont le FranA ais !) et dans de nombreux formats (html, L TEX, rtf (i.e., MS Word), pdf, man dUnix...). Il est disponible sous Unix/Linux, MS Windows et Mac OS X. Et il est libre. Bref un outil universel et prcieux. Apprenez lutiliser...

10.3 Environnements intgrs de dveloppement


Un environnement intgr de dveloppement (IDE) regroupe en une seule application tous les outils supports des activits de dveloppement de logiciel et les dote dun ensemble cohrent et homogne dinterfaces utilisateur. Un IDE peu tre limit un type de plateforme (i.e., systme dexploitation ) ou sexcuter sur plusieurs plateformes. Il existe de nombreux IDE pour C, commerciaux ou non. Nous nous limitons ici aux non-commerciaux, souvent aussi, sinon plus, puissants que leurs concurrents payants.
5. Les couleurs sont dues lauteur, non valgrind, malheureusement ! 6. Pour tre honnte, cette ide na pas que des partisans inconditionnels et lexprience permet de dire que cest sans doute juste titre ! 7. http://www.doxygen.org

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

10.3. Environnements intgrs de dveloppement

183

Figure 10.3 Eclipse en mode dition/compilation de C/C++

10.3.1 Environnements multi-plateformes


Dans le domaine des IDEs multi-plateforme eclipse 8 est la Rolls, mais un prix ridicule, puisque nul. Support par IBM et une large communaut dutilisateurs et de dveloppeurs, cest bien plus quun simple IDE multi-plateformes, multi-langages, multi-activits, multi-tout. Cest un environnement de dveloppement dapplications extensible (grce Java) et adaptable virtuellement nimporte quelle activit interactive. Sa communaut la dot de capacits de dveloppement de logiciel (dans de nombreux langages) mais aussi de modlisation, de documentation, de dveloppement graphique, etc. quipe du greffon ( plugin ) CDT, eclipse est un vritable environnement de dveloppement en C (et C++) fonctionnant sous Unix/Linux, MS Windows et Mac OS X. Par dfaut, les outils utiliss sont gcc, make et gdb, mais ceci est largement paramtrable. La gure 10.3 montre eclipse luvre en mode dition et compilation, alors que 10.4 en montre la vue de mise au point (interface avec gdb). Bien sr, eclipse nest pas le seul IDE multi-plateformes pour C. Il en existe dautres, souvent plus lgers dutilisation, comme Code::Blocks 9 , gure 10.5, ou CodeLite 10 , gure 10.6, qui eux aussi sinterfacent par dfaut avec les outils gnu (gcc, make, gdb...).
8. http://www.eclipse.org 9. http://http://www.codeblocks.org/ 10. http://codelite.org/

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

184

10. Environnement de dveloppement

Figure 10.4 Eclipse en mode mise au point de C/C++

10.3.2 Environnements spciques U NIX


Il existe aussi certains IDEs spciques certaines versions dUnix. Cest le cas de kdevelop 11 , lenvironnement de dveloppement de kde, disponible sous Linux uniquement, de anjuta 12 un IDE issu du projet gnome et donc galement ddi Linux, ou encore XCode 13 , lIDE propre Apple pour Mac OS X.

10.3.3 Environnements spciques MS W INDOWS


Le plus connu et lun des meilleurs IDEs toutes plateformes confondues est certainement Visual Studio. Son intgration aux bibliothques standard et celles de Microsoft, ses modes ddition, la qualit exceptionnelle de son debugger en feraient un outil parfait si il tournait aussi sur dautres
11. http://www.kdevelop.org/ 12. http://projects.gnome.org/anjuta/ 13. http://developer.apple.com/tools/xcode/

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

10.4. Remarque nale

185

Figure 10.5 Encore un IDE multiplateformes, Code::Blocks

plates-formes, si il ntait pas pollu (parfois insensiblement) par les extensions propres Microsoft, si il respectait les normes internationales, comme C99... La gure 10.7 montre Visual Studio en mode dition et compilation et la gure 10.8 en mode de mise au point.

10.4 Remarque nale


De mon point de vue lutilisation dun diteur puissant et intgrant linvocation de make comme emacs associ un debugger de type ddd ou xxgdb est ce que lon peut faire de plus productif pour des projets de dveloppement de petits moyens. La mise en uvre est simple et ncessite peu de ressources et lapprentissage est relativement rapide. Ds que lon recourt un IDE, le cot dentre augmente. Il faut apprendre lutiliser, comprendre la notion de projet qui lui est propre, les diffrentes activits quil permet. Ensuite, il ne faut pas se leurrer : la puissance ou lagrment des IDEs se paye souvent par une complexit importante et surtout une hyper-consommation de ressources. De ce point de vue, sachez que eclipse et Visual Studio sont vraiment trs gourmands !

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

186

10. Environnement de dveloppement

Figure 10.6 Un IDE multiplateformes lger, CodeLite

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

10.4. Remarque nale

187

Figure 10.7 Visual Studio 2008 en mode dition/compilation de C/C++

Figure 10.8 Visual Studio 2008 en mode mise au point de C/C++

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

188

10. Environnement de dveloppement

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Chapitre 11

Extensions avances de C99

n mentionne ici quelques extensions propres C99 et dont la plupart nont pas eu tre utilises dans le reste du texte. Cette section est complter.

11.1 Prprocesseur
11.1.1 Macros prdnies 11.1.2 Macros nombre variable darguments

11.2 langage de base


11.2.1 Nom de la fonction courante : __func__ 11.2.2 Pointeurs restreints : restrict 11.2.3 Tableaux
Tableaux de taille variable Tableaux incomplets

11.3 Bibliothque standard


11.3.1 Fichiers dentte 11.3.2 Type boolen 11.3.3 Types entiers tendus 11.3.4 Nombres complexes 11.3.5 Manipulation des caractres multiples 11.3.6 Rels en virgule ottante

V 2.1 7 fvrier 2010

189

c Jean-Paul R IGAULT

190

11. Extensions avances de C99

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Chapitre 12

C traditionnel, ISO C90, ISO C99 et C++

e chapitre liste brivement les diffrences entre C dit traditionnel , C90, C99 et C++. Ce chapitre est complter.

12.1 DISO C90 C traditionnel 12.2 DISO C90


ISO

C99

inline functions variable declaration no longer restricted to le scope or the start of a compound statement several new data types, including long long int, optional extended integer types, an explicit boolean data type, and a complex type to represent complex numbers variable-length arrays support for one-line comments beginning with //, as in BCPL or C++ new library functions, such as snprintf new header les, such as stdbool.h and inttypes.h type-generic math functions (tgmath.h) improved support for IEEE oating point designated initializers compound literals support for variadic macros (macros of variable arity) restrict qualication to allow more aggressive code optimization

12.3 DISO C90 ou

ISO

C99 C++

V 2.1 7 fvrier 2010

191

c Jean-Paul R IGAULT

192

12. C traditionnel, iso C90, iso C99 et C++

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Bibliographie
[1] von Hagen, W. The Denite Guide to GCC, 2e dition. Apress, 2006. [2] ansi Committee x3j11. Rationale for the ansi C Programming language. Silicon Press, 1990. [3] Prata, S. C Primer Plus, 5e dition. Sams Publishing, 2005. [4] Prata, S. C++ Primer Plus, 4e dition. Sams Publishing, 2001. [5] Flipo, D. Documentation sur le module frenchb de babel. Site Web, http://daniel.flipo.free.fr/frenchb/frenchb2-doc.pdf, 2007. [6] Joseph, N. Divers articles sur C http://nicolasj.developpez.com/. et C++. Site Web,

[7] Braams, J. Babel, a multilingual package for use A with L TEXs standard document classes. Site Web, http://css.ait.iastate.edu/Tex/Sp/babel.pdf, 2005. [8] Kochan, S. G. Programming in C, 3e dition. Sams Publishing, 2005. [9] Tribble, D. Incompatibilities between iso C and iso C++. Site Web, http://david.tribble.com/text/cdiffs.htm.
A [10] Lamport, L. LTEX, a Document Preparation System. Addison-Wesley, 1986. [11] Lippman, S. B., Lajoie, J. C++ Primer, 3e dition. Addison Wesley, 1998.

[12] Delannoy, C. Langage C, 4e dition. Eyrolles, 1999. [13] Dijkstra, E. W. Go to statement considered harmful. Communications of the ACM 11, 3 (March 1968), 147148. Version annote par David Tribble en http://david.tribble.com/text/goto.html. [14] Institut fr Theoretische Informatik Darmstadt. xindy, a exible indexing system. http://xindy.sourceforge.net/. at THSite Web,

[15] Valgrind Developpers. Valgrind. Site Web, http://valgrind.org/. [16] Kernighan, B. W., Ritchie, D. M. The C Programming Language, 1re dition. Software Series. Prentice Hall, 1978. [17] Kernighan, B. W., Ritchie, D. M. The C Programming Language (ansi C edition, 2e dition. Software Series. Prentice Hall, 1988. [18] Kernighan, B. W., Ritchie, D. M. Le langage C : norme ansi, 2e dition. Sciences Sup. Dunod, 2004. [19] gnu Project. Gdb: The gnu project debugger. http://sourceware.org/gdb/. Site Web,

V 2.1 7 fvrier 2010

193

c Jean-Paul R IGAULT

194

BIBLIOGRAPHIE

[20] gnu Project. Ddd: Data display http://www.gnu.org/software/ddd/.

debugger.

Site Site Site Site

Web, Web, Web, Web,

[21] gnu Project. Gprof: The Gnu proler. http://www.gnu.org/software/binutils/. [22] gnu Project. Status of C99 features in GCC. http://gcc.gnu.org/c99status.html. [23] gnu Project. Programmation http://gcc.gnu.org/. et langages.

[24] Stroustrup, B. The C++ Programming Language, 3e dition. AddisonWesley, 1991. [25] Stroustrup, B. Programming: Principles and Practice Using C++. AddisonWesley, 2009. [26] Mittelbach, F., Goossens, M., Braams, J., Carlisle, D., Rowley, C. The A LTEX Companion, 2e dition. Addison Wesley, 2004. [27] Braquelaire, A. Mthodologie de la programmation en C : norme C 99 api Posix, 4e dition. Dunod, 2005. [28] Forum interactif. Programmation et http://www.developpez.net/forums/. langages. Site Web,

[29] Multiples auteurs. Diverses ressources sur la programmation. Site Web, http://www.developpez.com/.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Glossaire
tente ici de donner dun certain nombre de termes et Ondetraductiongnrauxla (une) dnitionde programmationtreauignore par concepts lis aux langages et processus de leur en langage-machine. Elle peu sans dommage linformaticien expriment. affectation Lune des instructions 1 fondamentales de la programmation procdurale. Laffectation permet de modier la valeur dun objet (qui doit tre variable bien sr). Voir aussi rfrence et valeur. agrgat Un type dont les objets sont composs dautres objets. En C, il y a trois sortes dagrgats: les tableaux, les structures et les unions. analyse lexicale La premire phase de la compilation, o lon reconnait les lments lexicaux (token), cest--dire les mots dun langage de programmation (identicateurs, oprateurs, constantes...). analyse statique (ou de smantique statique) Elle est effectue par le compilateur aprs lanalyse syntaxique. On y vrie en particulier ce qui relve du typage (voir type). analyse syntaxique La deuxime phase de la compilation o lon recherche la structure des phrases cest--dire celle des instructions et dclarations. archive Voir bibliothque. argument formel et effectif dune fonction Largument effectif dune fonction est celui fourni lappel (linvocation) de la fonction; sa valeur sert initialiser largument formel correspondant, qui est un objet local la fonction. En cas de typage fort le type de largument formel et celui de largument effectif doivent tre identiques ou alors il doit exister une conversion implicite du type de largument effectif en celui de largument formel. En C, la correspondance entre arguments effectifs et formels seffectue de manire positionnelle (cest--dire selon lordre des arguments dnis dans le prototype ou lentte de la fonction). assembleur Forme symbolique du langage-machine. Les compilateurs C produisent souvent de lassembleur plutt que du langage-machine directement.
1. En fait en C, laffectation est une expression et non une instruction (voir 3.3.4).

V 2.1 7 fvrier 2010

195

c Jean-Paul R IGAULT

196

GLOSSAIRE

On dsigne aussi par assembleur le programme qui traduit ce langage lui-mme en binaire objet. bibliothque Un ensemble de chiers-objets regroups en un chier unique (library ou archive en anglais) utilisable lors de ldition de liens. Tout environnement C comporte plusieurs bibliothques dont le contenu est prdni: la bibliothque standard, la bibliothque mathmatique, etc... (voir le chapitre 9). binaire excutable Le rsultat de ldition de liens. Sous UNIX le chier binaire excutable porte par dfaut le nom a.out. Il contient le code (les instructions) du programme et la valeur initiale des donnes. Si ldition de liens sest correctement passe, ce chier est effectivement excutable, cest--dire invoquable directement depuis le shell. binaire objet Voir chier-objet. compilation Le processus de traduction dun chier-source en chier-objet 2 . Elle comporte quatre phases principales (en principe successives bien quune certaine imbrication soit possible): 1. lanalyse lexicale, 2. lanalyse syntaxique, 3. lanalyse statique (analyse du typage), 4. la gnration de code. Optionnellement, il peut exister une cinquime phase doptimisation. En C, la compilation est prcde dun prtraitement effectu par le prprocesseur et est suivie dune dition de liens. La gure 7.1 schmatise la chane de compilation dun programme C constitu de 3 chiers-sources fic1.c, fic2.c et fic3.c. Le sufxe .c caractrise par convention les chiers contenant du texte C. Chaque chier-source est analys sparment, dabord par le prprocesseur qui effectue des remplacements textuels (voir 6), puis par le compilateur proprement dit qui produit un chier-objet de mme nom de base que le source mais avec le sufxe .o. Enn lditeur de liens (ld sous UNIX) assemble les diffrents chiersobjets en un chier binaire excutable unique de nom conventionnel par dfaut a.out. Cest lditeur de liens daller chercher dans les bibliothques standards ou utilisateurs les modules demands par le programme. En fait, au moins sous UNIX, ces diffrentes phases sont enchaines par
2. En fait la compilation, dans lacception la plus large, recouvre la traduction dun langage de programmation en un autre. La dnition donne ici (traduction dun langage de haut niveau en langage-machine ou assembleur) est cependant trs rpandue et la seule utile ici.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

GLOSSAIRE

197

Figure 7.1 Chane de compilation C

une commande unique, dnomme en gnral cc 3 comme par exemple sous le shell 4 :
% cc fic1.c fic2.c fic3.c

On peut aussi renommer le chier a.out en fic:


% cc -o fic fic1.c fic2.c fic3.c
3. Lorsque les deux versions, C traditionnel et ANSI C, coexistent, cc est souvent la commande de compilation en C traditionnel. Sur notre systme, la commande de compilation ANSI C est gcc. 4. Dans ces exemples dutilisation de commande, nous faisons lhypothse que le shell utilis est celui de Steve Bourne (sh) ou lui est compatible (comme ksh, le shell d David Korn). Ce que tape lutilisateur est en police penche alors que ce quafche le systme est en police normale. Le caractre % est l invite (le prompt) du shell.

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

198

GLOSSAIRE

compilation spare Lorsque le texte dun mme programme est reparti dans plusieurs chiers-sources diffrents, chacun de ces chiers est trait sparment par le compilateur. Ceci signie que chaque chier-source doit contenir toute linformation ncessaire au compilateur, et en particulier toute celle qui permet lanalyse de smantique statique (typage). constante Un objet qui ne peut tre modi. constante littrale Un objet qui ne peut tre modi et dont la valeur peut tre value lors de la compilation (on parle aussi de constante lexicale, ou encore statique). Ainsi en C, 12, -4, "bonjour" sont des constantes littrales. contrle (ot de contrle, instruction de contrle) Le t de contrle est dtermin par lordre dexcution des instructions. En programmation procdurale Ce ot est en gnral squentiel (les instructions sont excutes dans lordre o elles sont crites) mais cette squentialit peut tre brise par les instructions de contrle qui sont le plus souvent de trois types: slection (if et switch en C), boucles (while, do ... while et for), la rupture de squence (goto). corps dune fonction Voir fonction. corps dun module La partie du module contenant la dnitions des donnes et des fonctions du module. Certaines de ces donnes et de ces fonctions peuvent tre prives au module (encapsulation) et seront donc invisibles dans le reste du programme. Les autres sont exportes dans les parties du programmes qui voient les dclarations contenues dans la spcication du module. dclaration dun objet La dclaration dun objet indique simplement le type de cet objet au compilateur. Il peut en gnral y avoir autant de dclarations que lon veut pour un mme objet du moment quelles sont identiques (ou compatibles). dnition dun objet La dnition dun objet, outre quelle indique aussi son type, rserve et ventuellement initialise la zone mmoire correspondant lobjet. Il y a une et une seule dnition dans tout le programme pour chaque objet. dynamique Une notion quelconque est dynamique si elle ne peut tre vrie, value, utilise... qu lexcution. Sinon, elle peut ltre la compilation et est dite alors statique. dition de liens En cas de compilation spare, lditeur de liens rassemble tous les chiers-objets en un chier binaire excutable unique. Cest donc lui qui rsoud les rfrences externes. Certaines de ces objet externes peuvent tre dnis dans des bibliothques.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

GLOSSAIRE

199

Sous UNIX, lditeur de liens sappelle ld (le loader). Il est noter quen gnral aucune information sur le type des objets nest transmise lditeur de liens. encapsulation Un mcanisme syntaxique qui permet de rendre des donnes ou des fonctions prives un module invisibles dans les autres modules. Lencapsulation renforce la scurit de programmation et vite la pollution de lespace des noms globaux. entte dune fonction Voir fonction. expression statique Une expression valuable la compilation (voir aussi constante statique). externe Voir rfrence externe. chier-objet Le rsultat de la compilation dun chier-source. Cest un chier qui contient le code gnr (binaire objet) et la valeur initiale des donnes; en cas de compilation spare, il nest pas en gnral directement excutable, car il peut contenir des rfrences externes non rsolues, cest-dire des rfrences des objets dnis dans dautres chiers-sources. Le nom du chier-objet est celui du chier-source qui la engendr o lon remplace le sufxe (.c) par .o. chier-source Un chier contenant le texte du programme (ou dune partie du programme). Noter que compilateur analyse le chier selon lordre lexical (lordre de lecture). Par convention expresse, les chiers de source C ont un nom sufx par .c. fonction Llment principal de structuration du ot de contrle en programmation procdurale. En C, la dnition dune fonction comporte deux parties: lentte qui en reprend la signature et prcise le nom des arguments formels. le corps qui contient les dnitions dobjets locaux la fonction et les intructions excuter chaque appel de la fonction. gnration de code La dernire phase obligatoire de la compilation pour produire le binaire objet. En fait, trs souvent les compilateurs C produisent de lassembleur quil est ncessaire de traduire encore en langage-machine. langage de haut niveau Par opposition au langage-machine ou lassembleur, un langage de haut niveau a une structure et une forme plus proche des applications que de la machine. C est un langage de haut niveau pour la programmation systme, mme sil comporte quelques caractristiques de bas niveau (voir 1.2.2).

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

200

GLOSSAIRE

langage-machine Le langage des instructions et des donnes interprtables directement par le matriel. Il constitue le contenu du binaire excutable. library Voir bibliothque. lvalue Voir rfrence. modularit La possibilit de dcouper le source dun programme en units syntaxiquement indpendantes regroupant chacune des donnes et les fonctions de manipulation directes de ces donnes. La modularit dsigne aussi la qualit dun programme ainsi dcoup. La modularit en C fait lobjet du chapitre 8. module Une unit syntaxique de modularit. Un module comporte en gnral deux parties: la spcication, qui indique les dclarations des objets et les dnitions des types que le module rend visible (quil exporte), le corps qui contient les dnitions de ces objets. Les parties du programme qui veulent utiliser un module doivent en voir la spcication (elles limportent). En C, le corps dun module est contenu dans un chier-source .c, alors que sa spcication est en gnral dans un chier dentte (de sufxe conventionnel .h). Limportation est effectue grce la directive dinclusion de chier du prprocesseur. nom (dun objet) Dans un langage de programmation, la plupart des objets sont dsigns par un nom reprsent par un identicateur. Un identicateur constitue une rfrence sur lobjet. Cependant, certains objets sont anonymes: ils peuvent tre temporaires (crs automatiquement par le compilateur), ou alors allous dynamiquement et accessibles uniquement au travers de pointeurs. objet Un objet 5 est en fait une zne de mmoire, quil sagisse de mmoire de donnes ou de mmoire dinstruction. En C, un objet peut tre une constante, une variable ou une fonction; chaque objet a un type qui doit tre connu la compilation avant toute utilisation; un objet aussi une valeur (le contenu de la zne mmoire).

objet (chier-objet) Voir chier-objet. optimisation de code Cest la dernire phase, en gnral optionnelle, de la compilation.
5. Cette notion dobjet est plus large que la forme spcialise et plus riche laquelle on se rfre dans des expressions comme langage orient-objets ou programmation objets .

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

GLOSSAIRE

201

pointeur Un pointeur est un objet dont la valeur est ladresse dun autre objet. En C, les pointeurs ont un type diffrent selon le type de lobjet point. Les pointeurs jouent un rle central en C, mais ils constituent aussi un danger potentiel pour la scurit de programmation. prprocesseur Avant la compilation, un chier-source de C est analys par le prprocesseur (cpp) qui effectue un certain nombre de remplacement textuels (cest--dire lexicaux). Le prprocesseur est tudi au chapitre 6. Profondment remodel lors de la normalisation, il constitue une des causes dincompatibilit principales entre ANSI C et le C traditionnel. procdure En C, il sagit simplement dune fonction qui na pas de valeur de retour (on dclare ce type void). programmation (procdurale, structure, imprative) Cest le type de programmation habituelle avec les langages comme FORTRAN, Pascal, Ada, et bien sr C. Le ot de contrle y est squentiel sauf si lon utilise des instructions de contrle (slection, boucles, goto...); les procdures et fonctions permettent de structurer le ot de contrle. La programmation structure est en fait un ensemble de rgles ou de conseils pour structurer le ot de contrle. Lradication du goto en est son trait le plus connu. prototype de fonction Voir signature dune fonction et type. rfrence Un rfrence est la dsignation dun objet. La forme la plus simple de rfrence est donc le nom dun objet (un identicateur). Mais C permet bien dautres formes qui seront tudies au fur et mesure de leur introduction. Une rfrence ne doit pas tre confondue avec la valeur de lobjet rfrenc. Dans le jargon de C, on designe une rfrence par le terme lvalue qui rappelle quune rfrence est requise en partie gauche dun affectation (lvalue comme abrviation de left value); la valeur de lobjet est alors dsigne, pour la mme raison, par le vocable rvalue (comme right value). Lobjet rfrenc peut tre une variable ou une constante (dans le second cas, la lvalue ne peut pas apparaitre en partie gauche dune affectation!). Bien sr une rfrence rencontre en partie droite dun affectation est drfrence pour rendre la valeur de lobjet. Ainsi dans laffectation suivant entre deux entiers (type int)
int i, j; ... i = j;

i est une rfrence (lvalue) alors que j est une valeur (rvalue).

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

202

GLOSSAIRE

rfrence externe En cas de compilation spare, une rfrence externe est une rfrence dans un chier, disons fic.c, un objet possiblement dni dans un autre chier de compilation. Si lobjet est effectivement dni ailleurs, alors le chier-objet fic.o contiendra une rfrence externe non rsolue. rfrence (externe) non rsolue Voir rfrence externe et dition de liens. smantique dexcution La signication du programme, ce quil est cens faire, la fonction quil calcule, la transformation quil effectue sur ses donnes dentre pour produire les rsultats en sortie, etc... smantique statique Voir analyse statique. signature dune fonction Cest le type dune fonction qui est dni par le type de la valeur de retour (cest--dire du rsultat) de la fonction, le nombre et le type de chacun des arguments. En ANSI C, la signature dune fonction est dclare grce au prototype (qui prcise en outre le nom de la fonction). spcication dun module La partie dun module qui contient les dclarations des objets exports par le module (leur dnitions sont dans le corps du module). Si un autre module veut utiliser ces objets, il doit rendre visible (importer) la spcication. En C, ceci est ralis par le directive #include du prprocesseur. source Voir chier-source. statique Voir dynamique. typage fort Voir type. type Un type dnit un ensemble dobjets partageant un mme ensemble doprations. Par exemple, lensemble des entiers avec les oprations usuelles (arithmtiques, logiques...). On distingue en C les types de base (entiers et rels) et les types utilisateur qui sont obtenus par des oprations de construction de type. Ces types utilisateur sont des agrgats (tableaux, structures, unions). En ANSI C, tout objet a un type qui doit tre connu avant lutilisation de lobjet. Le compilateur vrie (donc de manire statique) le bon usage des types (typage fort). valeur (dun objet) Voir objet. variable Un objet modiable, le contraire dune constante.

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Index
Symboles
, 71 ., 94, 97, 110 ..., 144 -, 32, 57, 64, 107 unaire, 57 , 60 #, 130, 134 ##, 134 #define, 84, 131135, 155 #elif, 136 #else, 136 #endif, 136 #error, 138 #if, 136 #ifdef, 136 #ifndef, 137 #define, 26, 130, 131, 157, 202 #line, 138 #pragma, 138 #undef, 135, 155 %, 33, 57 %=, 64 & unaire, 104 , 32, 57, 119 * unaire, 105 =, 64 * +, 32, 57, 107 unaire, 57 ++, 37, 64, 107 +=, 64, 107 ,, 66, 68 ,=<=, 106 /, 32, 57 /=, 64 1 complment , 60 2 complment , 69 ;, 27, 71 <, 28, 58, 106 <<=, 64 <=, 28, 58 <assert.h>, 61 <stdbool.h>, 58 =, 28, 63 -=, 64, 107 ==, 28, 58, 106 >, 28, 58, 106 ->, 94, 97, 110 >=, 28, 58, 106 >>=, 64 [], 119 __DATE__, 135 __FILE__, 135 __LINE__, 135 __STDC__, 135 __STDC_VERSION__, 135 __TIME__, 135 {}, 26

A
abort, 172 accolade, 26 Ada, 18 addition, 57 de pointeurs, 108 dun entier un pointeur, 107 adresse dun objet, 31 oprateur , 104 affectation, 195 compose, 63 de tableau, 85 simple, 63 conversion lors dune , 70 expression d, 28 agrgat, 50, 83, 195, 202 initialisation d, 85, 86, 91, 96 alignement, 93 allocation dynamique, 113, 158, 161 alternative, 32 analyse lexicale, 195 statique, 195 syntaxique, 195 and, 176

V 2.1 7 fvrier 2010

203

c Jean-Paul R IGAULT

204

INDEX

and_eq, 176 anjuta, 184 anonyme type , 55, 91 ansi, 1, 17 -ansi, 21 a.out, 196 appel de fonction, 145 dune fonction, 26 aquamacs, 178 archive, 196 argc, 39 argument de fonction, 195 effectif, 142, 195 formel, 142, 195 passage d, 142 pointeur en , 147 arguments de main, 39 liste variable d, 144 nombre variable d, 31 ordre dvaluation des , 145 type des dune fonction, 142 argv, 39 arithmtique oprateur , 32 ascii, 43 assembleur, 195 assert, 61, 135 <assert.h>, 135, 171 associativit des oprateurs, 66 atof, 172 atoi, 40, 172 auto, 159 automatique objet , 158 avant dclaration en , 30

binaire excutable, 196 objet, 199 bit bit et , 60 oprateur , 60 ou , 60 bitand, 176 bitor, 176 bits champ de , 93 bloc, 26, 71, 145, 158 _Bool, 46, 58, 172 bool, 58, 172 boolen, 58 bord effet de , 28, 38, 63, 68 boucle instruction de , 75 Bourne, Stephen, 20, 197 break, 74, 77

C
C traditionnel, 17 calloc, 161, 172 caractre multi-octet, 43 constante , 52 caractres chane de , 88 chane de littrale, 26, 48, 120, 143 case, 73 case-sensitivity, 43 cast, 64, 108 CC, 21, 115, 180 cc, 197 CDT, 183 CCFLAGS, 21, 180 CFLAGS, 115 champ de bits, 93 dune structure, 90 slection de de structure, 94 slection de dunion, 97 chane de caractres, 88 de caractres littrale, 26, 48, 120, 143 chanes concatnation des littrales adjacentes, 48, 129

B
b, 17 backquote, 43 base type de , 202 types de , 50 types scalaires de , 5056 bash, 20 bcpl, 17 bibliothque, 196 standard, 19, 26

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

INDEX

205

char, 51, 68 char*, 120 code gnration de , 199 Code::Blocks, 183 CodeLite, 183 coloration syntaxique, 41 commentaire, 26, 31 comparaison de pointeurs, 106 compilation, 196 conditionnelle, 135137 spare, 18, 156, 198 unit de , 21, 156 compl, 176 complment 1, 60 2, 69 _Complex, 46 complex, 173 <complex.h>, 173 compose affectation , 63 concatnation des chanes littrales adjacentes, 48, 129 oprateur de , 134 conditionnelle compilation, 135137 expression , 40, 65, 67 conjonction, 60 const, 53, 83, 85 constante, 198 caractre, 52 dcimale, 48, 52 entire, 48, 52 hexadcimale, 48, 52 lexicale, 198 littrale, 4650, 198 octale, 48, 52 relle, 48 statique, 198 dnition de , 132 pointeur et , 109 pointeur sur une , 148 continue, 78 contrle ot de , 198 instruction de , 7279, 198 conversion, 68 de pointeur, 108 entre entier et rel, 69 entre entiers, 69 entre rels, 69

explicite, 64 lors dune affectation, 70 usuelle, 70 spcication de , 33 copie de tableau, 85 corps dun module, 164, 198 dune fonction, 26, 145, 199 cpp, 201 <ctype.h>, 72, 152, 173 Cygwin, 20

D
ddd, 1, 180, 185

dcalage oprateur de , 61 dcimale constante , 48, 52 dclaration, 158 de pointeur simple, 103 dexterne, 160 dun objet, 198 en avant, 30 dclaration d, 160 declarationdeclaration forward forward , 30 dcrmentation oprateur de , 64 defined, 136 dnition, 53, 158 de constante, 132 de fonction, 26, 144 de macro, 131 dun objet, 198 rcursive de macro, 134 diffrence entre tableau et pointeur, 120 digraph, 44 dimension dun tableau, 83 tableau variable, 88 directive au prprocesseur, 26 disjonction, 60 division, 57 do ... while, 75 dot, 94 double, 31, 53, 69, 70 doxygen, 178 dure de vie, 158 dynamique

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

206

INDEX

allocation , 113, 158, 161

E
ebcdic, 43 chappement squence d, 129 eclipse, 183, 185 ed, 178 diteur de liens, 158 dition de liens, 196, 198 effectif argument , 142, 195 effet de bord, 28, 38, 63, 68 lment lexical, 44 ellipse, 144 else, 72 else if, 72 emacs, 178, 180, 185 encapsulation, 199 entte de fonction, 145 dune fonction, 26, 199 chier d, 131, 137, 165 entier addition dun un pointeur, 107 conversion entre et rel, 69 entire promotion, 68 constante , 48, 52 entiers, 51 conversion entre , 69 enum, 55, 68, 155, 163 numr type , 54 environnement variable d, 21 EOF, 29 quivalence entre tableau et pointeur, 120 erreur standard, 32 <errno.h>, 171 espace, 45 de nommage, 155 et bit bit, 60 logique, 38, 60 squentiel, 60 tiquette, 78, 163

valuation oprateur d squentielle, 66, 68 ordre d des arguments, 145 excutable binaire , 196 excution smantique d, 202 exit, 31, 79, 172 EXIT_FAILURE, 172 EXIT_SUCCESS, 172 explicite conversion , 64 exponentiation, 57 expression conditionnelle, 40, 65, 67 daffectation, 28 relationnelle, 28 statique, 65, 73, 199 valeur de vrit dune , 58 expressions, 5671 externe objet , 160 rfrence , 202

F
F, 53 f, 53 fabs, 32 false, 172 fclose, 174 <fenv.h>, 172 ff, 45 Fibonacci, Leonardo Pisano dit, 38 chier dentte, 131, 137, 165 -objet, 158 inclusion de , 130, 131 chier-objet, 196, 199 chier-source, 196, 199 float, 53, 69, 70 <float.h>, 53 ot de contrle, 198 fonction, 141153, 199 en ligne, 132, 146 local, 145 rcursive, 38 retournant un pointeur, 146 appel de , 145 appel dune , 26 argument de , 195 corps dune , 26, 145, 199 dnition de , 26, 144

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

INDEX

207

entte de , 145 entte dune , 26, 199 invocation dune , 26 nom de , 149 pointeur sur , 148 prototype de , 201 signature dune , 26, 202 type de retour dune , 141 type des arguments dune , 142 fonctions imbrication de , 145 fopen, 174 for, 37, 75, 115 form feed, 34 format, 33 formel argument , 142, 195 fort typage , 202 forward declaration, 30 fread, 174 free, 158, 172 Free Software Foundation, 1 fseek, 174 fuite de mmoire, 180 fwrite, 174

G
gcc, 1, 21, 177, 183 gdb, 1, 178, 180, 183 gedit, 178

gnration de code, 199 gnrique pointeur , 108 getchar, 29, 174 global objet , 159 gnu, 1 goto, 78, 155, 201 gprof, 1, 180

H
header-le, 131, 137, 165 hexadcimale constante , 48, 52 ht, 45

I
V 2.1 7 fvrier 2010

-I, 130 identicateur, 45, 200 ieee, 17 if, 31, 72 _Imaginary, 46 imbrication de fonctions, 145 imprative programmation , 201 inclusion de chier, 130, 131 unique, 137 incrmentation postxe, 64 prxe, 64 oprateur d, 64 indexation, 85 de pointeur, 119 indirection oprateur d, 105, 119 initialisation, 53 dagrgat, 85, 86, 91, 96 de pointeur, 104 de tableau, 84 dune union, 96 inline, 46, 133, 146, 166 static , 146 instruction, 26 de boucle, 75 de contrle, 7279, 198 de rupture de squence, 77 de slection, 31, 72 simple, 29, 71 instructions, 7179 int, 27, 51, 70 sous-types de , 51 int8_t, 172 int32_t, 172 integral type, 51 interface dun module, 164 <inttypes.h>, 172 Invariant Code Set, 44 invite, 197 invocation dune fonction, 26 isalpha, 72, 173 isdigit, 72, 173 iso, 17 iso 646-1983, 44 <iso646.h>, 176 iso-9899, 17 ispuct, 173 isspace, 72, 173 itration, 75

c Jean-Paul R IGAULT

208

INDEX

J
Java, 18

long, 51, 52, 70 long double, 53, 70 lvalue, 63, 142, 201

K
kate, 178 kde, 184 kdevelop, 184

M
machine langage- , 200 MacOs X, 20 macro, 58, 61, 84, 131135 dnition de , 131 dnition rcursive de , 134 variadic , 132 main, 26, 151, 172 arguments de , 39 majuscule et minuscule, 43 make, 21, 27, 115, 178, 179, 183, 185 Makefile, 21 malloc, 113, 125, 158, 161, 172, 180 <math.h>, 173 mlange de types, 70 memcmp, 174 memcpy, 85, 174 mmoire partage, 54 fuite de , 180 taille , 65 Microsoft Windows, 20 minuscule majuscule et , 43 modieur de type, 53 modulaire programmation , 164 modularit, 18, 200 module, 158, 164, 200 corps dun , 164, 198 interface dun , 164 spcication dun , 202 modulo, 57 mono-dimensionnel tableau, 83 multi-dimensionnel tableau, 86 multi-octet caractre , 43 multiple pointeur , 105 multiplication, 57

Kernighan, Brian W., 17 Korn, David, 197 ksh, 20

L
L, 52 l, 52 LANG, 20 langage-machine, 200 ld, 196, 199 lexical ment , 44 lexicale analyse , 195 constante , 198 library, 200 standard , 19 liens diteur de , 158 dition de , 196, 198 ligne fonction en , 132, 146 ligne-suite, 129 <limits.h>, 52 Linux, 1 liste, 111 variable darguments, 144 littrale chane de caractres , 26, 48, 120, 143 constante , 4650, 198 local fonction , 145 objet , 159 locale variable , 71, 88, 92 <locale.h>, 174, 175 logique et, 60 oprateur, 58 ou, 60 et , 38 ou , 38

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

INDEX

209

,NDEBUG, 171 ngation, 60 newline, 27 Newton, Sir Isaac, 29 bit eld, 93 nl, 45 nom de fonction, 149 de tableau, 120 de type, 64 dun objet, 200 nombre variable darguments, 31 nommage espace de , 155 not_eq, 176 NULL, 104106, 112 nul, 34, 49 NULLNULL, 171

asociativit des , 66 prcdence des , 28, 66 priorit des , 66 optimisation, 200 or, 176 or_eq, 176 ordre dvaluation des arguments, 145 ou bit bit, 60 exclusif, 60 logique, 38, 60 squentiel, 60

P
padding, 93 panorama des types, 50 partage mmoire , 54 Pascal, 18 passage dargument, 142 par valeur, 142 pointeur, 18, 31, 103122, 201 en argument, 147 et constante, 109 gnrique, 108 multiple, 105 simple, 103 sur fonction, 148 sur une constante, 148 sur void, 108 addition dun entier un , 107 conversion de , 108 dclaration de simple, 103 diffrence entre tableau et , 120 quivalence entre tableau et , 120 fonction retournant un , 146 indexation de , 119 initialisation de , 104 tableau de , 118 pointeurs addition de , 108 comparaison de , 106 soustraction de , 107 point-virgule, 71 portabilit, 18 Posix, 171, 175 postxe incrmentation , 64 #pragma, 172 prcdence

O
objet, 200 automatique, 158 externe, 160 global, 159 local, 159 priv, 160 rmanent, 159 statique, 158 adresse dun , 31 binaire , 199 dclaration dun , 198 dnition dun , 198 chier- , 158, 196, 199 nom dun , 200 octale constante , 48, 52 odr, 158 Onedenition Rule, 158 oprateur adresse, 104 arithmtique, 32 bit bit, 60 de concatnation, 134 de dcalage, 61 de dcrmentation, 64 de slection, 110 dvaluation squentielle, 66, 68 dincrmentation, 64 dindirection, 105, 119 logique, 58 relationnel, 58 oprateurs, 5671

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

210

INDEX

des oprateurs, 28, 66 prxe incrmentation , 64 prprocesseur, 26, 129138, 201 directive au , 26 prtraitement, 129 printf, 26, 32, 144, 174 priorit des oprateurs, 66 priv objet , 160 procdurale programmation , 18, 201 procdure, 37, 141, 201 programmation imprative, 201 modulaire, 164 procdurale, 18, 201 structure, 18, 201 promotion entire, 68 prompt, 197 prototype, 26, 143, 144 de fonction, 201 putchar, 29, 174

relationnel oprateur, 58 relationnelle expression , 28 rmanent objet , 159 rsolue rfrence non , 202 reste signe du , 58 restrict, 46 retour type de dune fonction, 141 return, 32, 35, 79 Ritchie, Dennis, 1, 17 rupture instruction de de squence, 77 rvalue, 201

S
scalaire type , 50 scalaires types scalaires de base, 5056 scanf, 31, 174 scope, 159 slection de champ de structure, 94 de champ dunion, 97 instruction de , 31, 72 oprateur de , 110 smantique dexcution, 202 statique, 195 spare compilation , 18, 156, 198 squence dchappement, 129 instruction de rupture de , 77 squentiel et, 60 ou, 60 squentielle oprateur dvaluation , 66, 68 sh, 20 shell, 20, 31, 39 redirection du , 29 short, 51, 68 <signal.h>, 175 signature dune fonction, 26, 202 signe du quotient, 58

Q
quotient signe du , 58

R
rand, 172 record, 18, 90, 97 rcursif type , 111 rcursive dnition de macro, 134 fonction , 38 rcursivit, 39 redirection du shell, 29 rel conversion entre entier et , 69 relle constante , 48 rels, 53 conversion entre , 69 rfrence, 63, 85, 94, 97, 201 externe, 202 non rsolue, 202 register, 159

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

INDEX

211

du reste, 58 signed, 51 simple affectation , 63 dclaration de pointeur , 103 instruction , 29, 71 pointeur , 103 size_t, 171 sizeof, 85, 89, 93, 171 sortie standard, 26 source chier- , 196, 199 soustraction, 57 de pointeurs, 107 sous-types de int, 51 spcication de conversion, 33 dun module, 202 srand, 172 Stallman, Richard, 1 standard library, 19 bibliothque , 19, 26 erreur , 32 sortie , 26 static, 146, 159161, 166 inline, 146 statique analyse , 195 constante , 198 expression , 65, 73, 199 objet , 158 smantique , 195 -std=c99, 21 <stdarg.h>, 144 <stdbool.h>, 172 <stddef.h>, 171 stderr, 32 stdin, 27 <stdint.h>, 172 <stdio.h>, 26, 174 <stdlib.h>, 104, 172 stdout, 26, 27 strcat, 173 strcmp, 173 strcpy, 173 <string.h>, 173 stringication, 134 strlen, 88 struct, 90 structure, 9095 avec variante, 97 champ dune , 90

slection de champ de , 94 tableau de , 95 structure programmation , 18, 201 surcharge, 173 switch, 73, 98 syntaxique analyse , 195 coloration , 41

T
tab, 34, 45 tableau, 8390 dimension variable, 88 de pointeur, 118 de structure, 95 mono-dimensionnel, 83 multi-dimensionnel, 86 (, 83 affectation de , 85 copie de , 85 diffrence entre et pointeur, 120 dimension dun , 83 quivalence entre et pointeur, 120 initialisation de , 84 nom de , 120 tag, 55, 90, 111, 155, 163 taille mmoire, 65 <tgmath.h>, 173 Thomson, Ken, 17 <time.h>, 175 token, 129, 195 tolower, 152, 173 toupper, 173 traditionnel C , 17 trigraph, 44, 129 true, 172 toupper, 152 typage fort, 202 type, 202 anonyme, 55, 91 de base, 202 de retour dune fonction, 141 des arguments dune fonction, 142 enumr, 54 rcursif, 111 scalaire, 50 utilisateur, 83, 202

V 2.1 7 fvrier 2010

c Jean-Paul R IGAULT

212

INDEX

integral , 51 modieur de , 53 nom de , 64 typedef, 55, 155, 163 types de base, 50 scalaires de base, 5056 utilisateur, 50 mlange de , 70 panorama des , 50

U
U, 52 u, 52 uint64_t, 172 unaire - , 57 & , 104 * , 105 + , 57 Unicode, 174 Unicode, 20, 43, 45 union, 9699 initialisation dune , 96 slection de champ de , 97 unique inclusion , 137 unsigned, 51 unit de compilation, 21, 156 Unix, 17 unsigned, 52, 69 unsigned int, 70 unsigned long, 70 usuelle conversion , 70 utf-8, 20 utilisateur type , 83, 202 types , 50

nombre darguments, 31 tableau dimension , 88 variadic macro, 132 variant, 97 variante structure avec , 97 vrit valeur de , 72 valeur de dune expression, 58 vi, 178 vie dure de , 158 vim, 178 visibilit, 159 Visual Studio, 20, 23 void, 37, 50, 79, 141, 143, 201 pointeur sur , 108 void*, 108 volatile, 54, 85 vt, 45

W
-Wall, 21

warning, 21 wchar_t, 174 <wchar.h>, 174 <wctype.h>, 174 while, 32, 75

X
X Window, 1 x3j11, 1, 17 XCode, 184 xemacs, 178 xor, 176 xor_eq, 176 xxgdb, 180, 185

V
valeur, 202 de vrit, 72 de vrit dune expression, 58 passage par , 142 valgrind, 2 variable, 202 denvironnement, 21 locale, 71, 88, 92 liste darguments, 144
zsh, 20

c Jean-Paul R IGAULT

V 2.1 7 fvrier 2010

Vous aimerez peut-être aussi