Universit´ du Luxembourg e

2005–2006

Cours de programmation avanc´e. e Le langage C

S´bastien Varrette <Sebastien.Varrette@imag.fr> e Nicolas Bernard
<n.bernard@lafraze.net>

Version : 0.4

2

Table des mati`res e
1 Les bases 1.1 Un langage compil´ . . . . . . . . . . . . . . . . . . . . e 1.1.1 G´n´ralit´s sur les langages de programmation e e e 1.1.2 Le C comme langage compil´ . . . . . . . . . . e 1.1.3 Notes sur la normalisation du langage C . . . . 1.1.4 C en pratique : compilation et debuggage . . . 1.2 Les mots-cl´s . . . . . . . . . . . . . . . . . . . . . . . e 1.3 Les commentaires . . . . . . . . . . . . . . . . . . . . . 1.4 Structure g´n´rale d’un programme C . . . . . . . . . e e 1.5 Notion d’identificateur . . . . . . . . . . . . . . . . . . 1.6 Conventions d’´critures d’un programme C . . . . . . e 1.7 Les types de base . . . . . . . . . . . . . . . . . . . . . 1.7.1 Les caract`res . . . . . . . . . . . . . . . . . . . e 1.7.2 Les entiers . . . . . . . . . . . . . . . . . . . . 1.7.3 Les flottants . . . . . . . . . . . . . . . . . . . 1.7.4 Le type void . . . . . . . . . . . . . . . . . . . 2 La syntaxe du langage 2.1 Expressions et Op´rateurs . . . . . . . . e 2.1.1 Op´rateurs arithm´tiques . . . . e e 2.1.2 Op´rateurs d’affectation . . . . . e 2.1.3 Op´rateurs relationnels . . . . . e 2.1.4 Op´rateurs logiques . . . . . . . e 2.1.5 Op´rateurs bit a bit . . . . . . . e ` 2.1.6 Op´rateurs d’acc`s ` la m´moire e e a e 2.1.7 Autres op´rateurs . . . . . . . . e 2.2 Les structures de contrˆle . . . . . . . . o 2.2.1 Instruction if...else . . . . . . . 2.2.2 Instruction for . . . . . . . . . . 2.2.3 Instruction while . . . . . . . . 2.2.4 Instruction do...while . . . . . 2.2.5 Instruction switch . . . . . . . . 2.2.6 Instruction goto . . . . . . . . . 2.2.7 Instruction break . . . . . . . . 2.2.8 Instruction continue . . . . . . 2.3 La r´cursivit´ . . . . . . . . . . . . . . . e e 3 2 2 2 3 4 4 5 6 7 9 9 10 10 11 13 14 15 15 15 16 16 17 18 18 19 19 20 20 20 21 22 23 23 24 24

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1 Les ´num´rations .1 Les situations de la conversion de type . . . . . .2. . . . . . . . e 4.1 Initialisation d’un tableau . . . . . . 2. . . . . . . . . . . . . . . . . . . . . . . 2. . . . . . . . . . . . . . . . . . . . . . . . .2. . .2. . . . . . . . . . . . . . . . . . . e 3. . . . .2. . . . .5 Les conversions de types . . e 2. . .4. . . . . . . . .4.4 Arithm´tique des pointeurs . . . .2. . . . . .2 Op´rateurs de manipulation des pointeurs . . . .2. . .4 2. .3. . . .4 Les unions . . 4. . . . . e 3. . . . .2 Tableaux multidimensionnels . . . . . . . . . . . .4 La fonction d’´criture ` l’´cran formatt´e printf e a e e 2. . . .5 Allocation dynamique de m´moire .6 Lib´ration dynamique avec la fonction free e . .5 Cas des tableaux de chaˆ ınes de caract`res . . . 4. . . . . . 2. . . . . . . . . . . . . . . .3 Tableau de structures . . . . .4 Pointeur vers une structure . . . . . . . . . . . . . . . . . . . .6 Gestion des arguments de la ligne de commande 4. . . . . . . .3 Passage de tableau en param`tre . . . . . . . . . .4. . . . . .1 La fonction getchar . . . . . . . . . . . . . . . . . . . 25 25 26 26 27 27 28 28 28 29 31 33 34 34 34 35 36 37 38 40 41 41 41 42 44 45 47 49 50 51 52 52 52 53 53 55 55 56 57 57 58 3 Les pointeurs 3. . . . . . .4. . .6 D´finition de synonymes de types avec typedef .2 Utilisation pratique des unions . . . . . . . . . . . . . . . . . . . . . .2 Comparaison de structures . . . . . . . . . e 3. . . . .3 Les structures . . . .4 Relation entre tableaux et pointeurs .2 La r`gle de ”promotion des entiers” . . . . . . . 4. . . . 4. .1 Initialisation et affectation d’une structure .2 L’op´rateur ’contenu de’ : * . . . . . . . . . . . . . . . . . . e 2. . . .3. . . . . . . . . . . . . . . . . . . . 4. . . . . . . . 4. 4. . . . . . . . . . . . . . . . . . . . . . . . . . Principales fonctions d’entr´es-sorties standard . 2. . . . .5 La fonction de saisie scanf . . . e 4. .1 D´claration d’une union . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . .3 La fonction puts . . . . .5.3. . . . .5 Structures auto-r´f´r´es . . . . . . e 3. . . . . . . . . . . . 4 Les types d´riv´s e e 4.3. . . . . . . . e 4. . . . . . e 2. .4. . . . . . . . . .5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3 Une m´thode pour all´ger l’acc`s aux membres . . . . . . . . . 4. . . . . .3. e 3. . . . . . . . . . . . . . . .2 Les tableaux . . 4. . . .4 Les surprises de la conversion de type . . . . . . . . . . . . . . . . . e 4 . . . 4. . .5. . .3 Initialisation d’un pointeur . . .5. .2. . . . . . . . . .2. . . . . . e e 4. . . . . . 4. . . . . . . . . . . e 3. . .4. . . 4. . . . e e e 4. .1 D´claration d’un pointeur . . . .2 La fonction putchar . . . . .5 Les champs de bits . . . . eee 4. . . . . . . .4. . . . . . . . .3 Les conversions arithm´tiques habituelles . . . . . .1 L’op´rateur ’adresse de’ & . . 2. . . . 3. . . . .5. . . . . . . . . . .

. . . e 7. . . . . . . .2 Les entr´es-sorties format´es . . . . . . . . . . . . . . . 5. . . . . . . . . . . a e 5. . . 5.2 Les macros avec param`tres . . . . . . . . . . . . . . . e e 6. . . . . . .3. . . .1. . . . .4 Passage de tableau en param`tre . . . . .2 Lecture et ´criture optimis´es par caract`re : getc et putc e e e 6. . . . . . .2. . . . . . . .2. . . . . . . e e 5. e 7.5 Retour sur les fonctions 5. . . . . . . e 6. .1 Condition li´e a la valeur d’une expression e ` 7. . . . .4 R´sum´ des r`gles de programmation modulaire e e e 5 . . . .1 Ouverture de fichiers : la fonction fopen . . ee 8. . . . . .2 Passage des param`tres par valeur . . . . . . . . . . . . . . . .8 Pointeurs sur une fonction . . . . . . . . . . . . . . . . .3 Relecture d’un caract`re . . rewind et ftell 7 Les directives du pr´processeur e 7. . . . . . . . 7. . . . . . 7. . . . . . . . . . . . . . . . . . . . . . . .3 L’op´rateur defined . . . . . . e 6. . . . . .2 Variables locales . . . . . . . . . .7 Fonction ` nombre variable de param`tres . . . . . . . . . . e 5. . . . . . . . . . . . . . . . . . . . . . . . . e e 5. . .5 Positionnement dans un fichier : fseek. . . . .5 La commande #pragma . . . .4. . . . . .6. . . . . . . . . . . . . . . .3. . . .3. .1 Lecture et ´criture par caract`re : fgetc et fputc . . . . . . . . . . . . .4 Port´e des variables . . . . . . . . . . . . . . . 6. . . . . e 5. . . . .2 Eviter les erreurs d’inclusions multiples . e e e 5. . . . . . . . . . .1 D´finition de constantes symboliques . . . . . . . . e 5. . . . . . . .2 Fermeture de fichiers : la fonction fclose .3. . .1 G´n´ralit´s . . . . . . 6. . . . . . . . . . . . . . .3. . . . . . .3. . . e 7. . . . . . . . . . . .1 Ouverture et fermeture de fichiers .3 La compilation conditionnelle . . . . .1 La fonction d’´criture en fichier fprintf . . . . . .2.2 Condition li´e a l’existence d’un symbole e ` 7. . . . . e 6. . . . . . . . . . . .2 La fonction de saisie en fichier fscanf .3 Impression et lecture de caract`res dans un fichier . .4 Les entr´es-sorties binaires : fread et fwrite . . . . 6. . . .4. . .3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2 Appel d’une fonction . . . . . . . . . . . . . . . . . . . . .6. .1 D´claration et d´finition d’une fonction . . . . .6 Passage de param`tres ` une fonction . . . . . . . .3 Dur´e de vie des identificateurs .1 La directive #include . . 59 59 61 61 62 62 63 64 64 64 65 65 66 67 69 71 71 71 73 73 73 73 74 74 74 75 76 77 79 79 79 80 80 81 82 82 83 83 84 85 85 87 88 88 6 Gestion des fichiers 6. . . . . .2 La directive #define . . . . . . .2.3. e e 6. . . 8. . . . . . . . . . .1 Principes ´l´mentaires . . . . . . . . . . . .3 La compilation s´par´e . . . . . . . . . e a 5.1 Variables globales . . . . . . . . . . 6. . . . . . . .3 Passage des param`tres par adresse . . . . . . . . . . . 8 La programmation modulaire 8. . . . . . . . . . e e 8. . . . 5.3. . . . . . . . . .6. . . . . . . . e 6. . . . . . .5 R´cursivit´ . . . . . . . . . . . . . . . .3. . . . . . . . . e 5. .6. . e 5.4 La commande #error . . 7. . . . . . . . .1. . . 7. . . . . . . . . . . .

. . . .11. . . . . . . . .5 Recherche et tri . . e e 6 . . . . . .11. . . . . . . . . . . . . . . . . . . . .2 Cr´ation d’un Makefile . .18. . . . . . . . .5. . . . . . . .h> . . . . .1 Principe de base . . . . . . . . .19. . . . .15 D´finition du type bool´en <stdbool. . .9 Intervalle de valeur des types entiers <limits. .h> . . e 9. e 9. . . 89 89 89 90 93 93 94 94 95 95 95 96 96 96 96 97 97 97 98 98 98 98 98 99 99 99 99 100 100 100 100 100 101 101 101 101 102 102 102 103 103 104 104 9 La biblioth`que standard e 9. . . .h> . . . . . . . . . . . . .7 D´finitions de types entiers de taille fix´e <inttypes.h> e e e 9. . .h> . . . . . . . . . . . . . . .10 Gestion de l’environnement local <locale.3 Gestion des environnements en virgule flottante. . . . . .h> . . . . . . . . . . .h> . .3 G´n´ration de nombres pseudo-al´atoires .2 Gestion des arrondis . . . . . . . . . 8. . . . . . . . . . . . . . . .1 Fonctions trigonom´triques et hyperboliques . . . e e 9. . . .5. . . 9. . .2 Fonctions exponentielles et logarithmiques . 9. . . .h> .3 Utilisation de macros et de variables . .16 D´finitions standards <stddef. . . . e e 9. . .5. .h> . . . . . . . 9.h> . 9. . . .1 Gestion des exceptions . .h> . . . 9. . . . . .3 Classification de caract`res et changements de casse <ctype. .h> . . . . . . . . .19. .19 Utilitaires divers <stdlib. . .23 Manipulation de caract`res ´tendus <wchar.h> . . . . 9.13 Manipulation des signaux <signal. . .h> . . . . . . . . e 9. . . e e 9. . 9. . . . 9. . . . . . .1 Diagnostics d’erreurs <assert. . . . . . . . . .3 Impression et lecture de caract`res . . . . . . .5. 9. . . . . e 9. . . .19. . . . . . e 8. .h> . . . . . e 9. . . . . e 9. . . . . . . . . . . . . . .4 Valeur du dernier signal d’erreur <errno. . . e 9.19. . .6 Intervalle et pr´cision des nombres flottants <float. . . . . . .8 Alias d’op´rateurs logiques et binaires <iso646. . . .h> . . .2 Entr´es et sorties format´es . . . . . . . . . . . . . . . . .1 Manipulation de fichiers . . . . . . e e e 9. . .h> . . . . e 9. . . . .22 Date et heure <time.5. . . . .20 Manipulation de chaˆ ınes de caract`res <string.h> . . . .5 Gestion d’un environnement ` virgule flottante <fenv. 8. . . . . . . .h> . . . . . . .h> . . . . . 9.14 Nombre variable de param`tres <stdarg. . . . . . . .6 Communication avec l’environnement . . . . . .2 Gestion des nombres complexes <complex. . . . . . . . . . . . . . . . . 9. .5. . e 9. . . . . . . . .1 Allocation dynamique . . . e 9. . .18. 9. . . . . . . . . . . . . . . .8. e 9. . . .18 Entr´es-sorties <stdio. .18. . . . e 9. . . . . . . .2 Conversion de chaˆ ınes de caract`res en nombres .21 Macros g´n´riques pour les fonctions math´matiques <tgmath. . . . . . 9. . . . 9. . . a 9. . . 9. . . . .h> . .12 Branchements non locaux <setjmp. . . . . . . . .h> . .h> . . . . . . . . . . . . . . . . . . . . . . . . .3 Fonctions diverses . . . . 9. . . 9. . . . . . . . . . .19. . . . . . . .19. . . e 9. . . .4 Arithm´tique sur les entiers . . . . .11 Les fonctions math´matiques de <math. . .5 L’outils ’make’ .17 D´finitions de types entiers <stdint. . 9.11. . . . . . . . . . . . . . . . . . . . . . . . . . . . .h> et <wctype. . . . .

. . C. . . . C. . . . . . . . . .1.6. . . . . . e C. . . . . . . . . . . . . . . 110 ın´ B Makefile g´n´rique e e C Le bˆtisier e C. . . . . . .3 Erreur sur macro avec param`tres . . . . . . . .2. . . . . . .9 Liens utiles . 116 . C. . . .2 Erreur sur l’affectation . . . . . . . . . . . .2 Erreur sur le default . . . . . . . . . . . C. . . . . C. . C. . . . . . .A Etude de quelques exemples 106 A.2 Erreurs avec les macros . . . . . . . . . . .1 Oubli du break . . . .6 Erreur avec l’instruction switch . . . . . . . . . 119 .2 Exemple de liste chaˆ ee . C. . . . . . . . . .2. . . . . . . 117 . e e C. . . . . . C. . . . 119 . . .1. . . . . . . . . .2.5 Erreurs avec les priorit´s des op´rateurs . . . . . . 119 . 117 . . . . 121 7 . 117 . . . 116 . . . . . . . . . . .1 Erreur sur une comparaison . . . . . . . 117 . . . . . .1 Erreur avec les op´rateurs .8 Erreur avec la compilation s´par´e . . . . . .3 Erreurs avec l’instruction if . . . . . . . . . . 116 . . . . . C.4 Erreur avec les effets de bord . . . . . . . . . . . . 116 . . . e C. . . . . . . . . . . . . . . . . . . . . . .2. e C. . 117 . . . . . . . . . 119 . 119 . . . . . . . . . . . . . . . . . 118 . . . . . . . 112 116 . 118 . . . . . . .4 Erreurs avec les commentaires . . . 106 A. . .1 Un #define n’est pas une d´claration . . . . . . . . . . . . . . .6. .7 Erreur sur les tableaux multidimensionnels . e e C. . . . . . . .1 Gestion des arguments de la ligne de commande . C. . . . . . . . . . .2 Un #define n’est pas une initialisation C. . . . . . .

En aucun cas il e ne dispense de la pr´sence en cours.imag.fr/~svarrett/ 1 . Le design de C lui conf`re un certain nombre d’avantages : e – Le code source est hautement portable – Le code machine est tr`s efficace e – les compilateurs C sont disponibles dans tous les syst`mes courants. voir par exemple [KR88]). Il puise ses origines dans e le langage de programmation sans type BCPL (Basic Combined Programming Language. developp´ par M.Avant-propos Ce polycopi´ vient en compl´ment au cours de programmation avanc´e dispens´ e e e e au sein de l’universit´ du Luxembourg en DUT d’informatique. e Pour la petite histoire. Il ee doit simplement permettre au lecteur d’appr´hender rapidement ce langage. le langage de programmation C a ´t´ d´velopp´ dans les ee e e ann´es 70 par Dennis Ritchie aux laboratoires Bell. e La version courante de ce document ainsi que le code source des exemples abord´s dans ce polycopi´ sont disponibles sur mon site e e http://www-id. Ce document n’a pas pour vocation d’ˆtre e e un ouvrage de r´f´rence du Langage C (pour cela. Thompson). Richards) et dans B (developp´ par K. e e En 1978. Brian Kernighan et Dennis Ritchie produisent la premi`re description e officielle de C.

Il e est traduit une seule fois.1 Un langage compil´ e G´n´ralit´s sur les langages de programmation e e e Dans le domaine de la programmation. Ce come portement est illustr´ dans la figure 1.. Il est n´anmoins n´cessaire d’op´rer une op´ration de traduction du langage e e e e de haut niveau vers le langage machine (binaire). Java. Parmi les exemples de langages de e haut niveau. l’ex´cution des programmes se fait sous ee e un ”terminal” ` la vol´e.. le compilateur qui traduit les programmes dans leur ensemble : tout le programme doit ˆtre fourni en bloc au compilateur pour la traduction.. sauf erreur. on utilise aujourd’hui des langages de programmation de haut niveau (i.Chapitre 1 Les bases 1. Ainsi. a e En outre. On distingue deux types de traducteurs : 1. l’interpr´teur qui traduit les programmes instruction par instruction dans e le cadre d’une int´raction continue avec l’utilisateur. – les langages compil´s : les source du code est dans un premier temps pass´ e e dans une ”moulinette” (le compilateur) qui va g´n´rer. on peut citer les langages C. permettant 2 .1. Il en r´sulte deux grands types de langages de programmation : e – les langages interpr´t´s : dans ce cas.1 1. ` e 2. etc. Ce type de langage est donc adapt´ au d´veloppement a e e e rapide de prototypes (on peut tester imm´diatement ce que l’on est en train e de faire) On citera par exemple les langages Scheme. un ex´cue e e table qui est un fichier binaire compr´hensible par votre machine. le programme e est traduit a chaque ex´cution.1.e ”facilement” compr´hensibles par le proe grammeur) par opposition aux langages de bas niveau (de type assembleur) qui sont plus orient´s vers le langage machine. C++. Ce type de langage est donc adapt´ e e a ` la r´alisation d’applications plus efficaces ou de plus grande envergure (il y e a une optimisation plus globale et la traduction est effectu´e une seule fois et e non ` chaque ex´cution). sans pour autant imposer la diffusion le source du code. Ruby. un langage compil´ permet de diffuser les programmes sous forme e binaire. Cobol etc..

. Quelques exemples ee de langages compil´s : C. Le fichier produit par l’assemblage est appel´ fichier e objet (et porte l’extension . appel´ fichier source.c) est analys´ par le pr´processeur qui effectue des transformations puree e ment textuelles (remplacement de chaˆ ınes de caract`res.c void f(int a){} int main(){ .. la compilation et l’assemblage e e se font dans la foul´e. C++.. G´n´ralement.ainsi une certaine protection de la propri´t´ intellectuelle. Ainsi. La compilation : la compilation proprement dite traduit le fichier g´n´r´ e ee par le pr´processeur (d’extension . il faut donc lier entre eux les diff´rents fichiers e e objets. prog. e e 4. 1. L’´dition de liens produit alors un fichier dit ex´cutable (a. Java etc. c’est-`-dire en instructions directement compr´a e hensibles par le processeur.out e e par d´faut). La compilation se d´compose en fait en 4 phases e e successives1 : 1. un programme C est d´crit par un fichier e e texte. 3. c’est-`-dire en une e a suite d’instructions du microprocesseur qui utilisent des mn´moniques rene dant la lecture possible.1.i) en assembleur.out COMPILATION source du programme executable binaire Fig..1 – Compilation d’un code source 1. Les fichiers objets correspondant aux librairies pr´-compil´es ont pour suffixe . e a e ee comme par exemple CAML. L’´dition de liens : un programme est souvent s´par´ en plusieurs fichiers e e e sources (voir le chapitre 8 consacr´ ` la programmation modulaire).o). pour ea des raisons de clart´ mais aussi parce qu’il fait g´n´ralement appel ` des e e e a librairies de fonctions standards d´j` ´crites.s) en un fichier binaire. } a.2 Le C comme langage compil´ e Le langage C est donc un langage compil´ (mˆme s’il existe des interpr´teurs e e e plus ou moins exp´rimentaux).a. L’assemblage : cette op´ration transforme le code assembleur (extension e .). inclusion d’autres e fichiers sources. e e 3 . 2. Le traitement par le pr´processeur : le fichier source (portant l’extension e . sauf si l’on sp´cifie explicitement que l’on veut e e le code assembleur.. e 1 Les extensions mentionn´es peuvent varier selon le compilateur utilis´ (gcc ici).. Une fois chaque fichier de ea e code source assembl´. e A noter que certains langages peuvent ˆtre ` la fois compil´s et interpr´t´s. Les diff´rentes commandes e qui peuvent ˆtre fournies au pr´processeur seront expos´es dans le chae e e pitre 7. compilation conditionnelle .

disponible sur quasiment toutes les plateformes UNIX. Devant la popularit´ du C.1.ansi./prog dans une fenˆtre de shell). e – -Wall : active des messages d’avertissements (warnings) suppl´mentaires . l’American National Standard Institut2 charge en e 1983 le comit´ X3J11 de standardiser le langage C.o Ces deux commandes ont donc permis de g´n´rer l’ex´cutable prog qui peut e e e alors est lanc´ (. e – -c : demande au compilateur de ne faire que la premiere ´tape de compilation . variant de 0 ` 3 e a sp´cifie le degr´ d’optimisation demand´. traduction du langage C dans le langage de la machine (binaire) sous forme d’un fichier objet prog.c se d´roule en g´n´ral en deux ´tapes : e e e e 1.c -o prog Quelques explications sur les options de gcc s’imposent : – -O : effectue des optimisations sur le code g´n´r´. regroupement des variables et des fonctions du programme avec les fonctions fournies par des bibliotheques du systeme ou faites par l’utilisateur (edition de liens) : gcc -o prog prog. car e e e a e rappelons-le. ANSI est une organisation nationale. Linux. en r´f´rence ee aux 2 auteurs). l’option -O3 permet d’otenir e e e le code le plus optimis´.o : gcc -O3 -Wall -c prog.. Un niveau. certaines fonctionnalit´s d´crites dans ce document h´ritent de la norme C99 e e e et n’´tait pas pr´sentes avant. MAC etc. le standard ANSI C89 est n´. les deux come e e e mandes pr´c´dentes peuvent ˆtre regroup´es en une seule : e e e e gcc -O3 -Wall prog. et non internationale comme l’ISO.4 C en pratique : compilation et debuggage Compilation d’un programme C Il existe ´videmment un grand nombre de compilateurs C pour chaque platee forme (Windows. e e Si un seul fichier source est utilis´ pour g´n´rer l’ex´cutable. La compilation du fichier prog.ch 4 . Ce standard ISO remplace le pr´c´dent standard ANSI (C89) mˆme ` l’int´rieur des USA.org www. Les standards ISO. Ritchie e a fait office de norme pendant longtemps (on parle alors de C K&R. sont sujets ` des r´visions. Ainsi. notamment en a e 1995 (C95) et en 99 (on parle alors de C99). Le compilateur d´taill´ dans cette section e e correspond au compilateur libre gcc.). Comme on le verra dans la suite.iso.1. Le fruit de ce travail est e finalement ´t´ approuv´ en d´cembre 1989.c 2. e e 1. Kernighan et Denis M. ee e e e L’International Organization for Standardization3 a adopt´ en 1990 ce standard e en tant que standard international sous le nom de ISO C90. La compilation peut alors e ee prendre plus de temps et utiliser plus de m´moire..1. publi´ initialement en 1978 par Brian W. e 2 3 www.3 Notes sur la normalisation du langage C [KR88]. en tant que tel.

org/software/binutils/manual/gprof-2.gnu.html 5 . – -pg : n´cessaire pour l’utilisation du profiler GNU : gprof5 (qui permet de e d´terminer par exemple quelles sont les parties du code qui prennent le plus e de temps d’ex´cution et qui doivent donc ˆtre optimis´es).1. e – les erreurs de resolution des symboles produites lors de la deuxi`me phase e lorsque des variables ou des fonctions utilis´es ne sont definies nulle part. a e e Note sur les erreurs de compilation : A ce stade. on peut classer les erreurs de compilation en deux categories : – les erreurs de syntaxe produites lors de la premi`re phase lorsque le code n’est e pas ´crit en C correct.2/manual. Ainsi.org/software/ddd/) A noter ´galement l’existence de valgrind qui permet de d´tecter les fuites de e e m´moire6 e 1.gnu.html) – sous forme d’une application graphique : – xxgdb (http://www.html) – ddd (http://www. de mauvaise pratique du langage ou d’usage involontaire de conversion implicite.– -o : permet de pr´ciser le nom du fichier produit.gnu. Avant de come e mencer. Il existe bien sˆ r de nombreuses autres options disponibles4 .edu/Help/xxgdb. il convient donc d’en donner la liste exhaustive : 4 5 man gcc si vous avez du courage http://www.h (voir chapitre 8). Exemple : -Iinclude/ – -Werror : consid`re les messages d’avertissements (warnings) comme des ere reurs – -g : est n´cessaire pour l’utilisation d’un debugger.kde. e e e Enfin.uni.2 Les mots-cl´s e Un certains nombres de mots sont r´serv´s pour le langage C.org/software/gdb/gdb. Il est possible de foure nir plus ou moins d’informations au debugger. e Outils de debuggage Il existe un certain nombre de debugger qui peuvent ˆtre utilis´s pour corriger e e les erreurs d’un programme : – en ligne de commande : gdb (http://www.9.5. l’utilisation du niveau e maximal (option -g3) assure que le maximum d’informations sera fourni au debugger .org/~sewardj/docs-2.cs.1/ 6 Voir http://developer. En voici certaines u qui pourront ˆtre utiles : e – -Idir : ajoute le r´pertoire dir dans la liste des r´pertoires ou se trouvent e e des fichiers de header . e il est important de noter que l’option -Wall est indispensable puisqu’elle permet de detecter la plupart des erreurs d’inattention. Ce crit`re est caract´ris´ par e e e un niveau variant entre 1 et 3 (2 par d´faut). il est possible d’automatiser la compilation de l’ensemble des fichiers d’un projet ` l’aide de l’utilitaire make qui sera pr´sent´ au §8.

Objective-C.. les droits de copyright. le fichier : pour indiquer le nom de l’auteur.3 Les commentaires Les commentaires sont tr`s important dans n’importe quel langage de programe mation car ils permettent de documenter les fichiers de sources. Il permet de g´n´rer facilement une documentation compl`te sur un e e e code source en utilisant un format de commentaires particulier. Dans l’exemple suivantes. d´claration ou instruction : le plus bas niveau de commentaire. la proc´dure : pour indiquer les param`tres et la raison d’ˆtre de la proe e e c´dure e 3. les dates et auteurs des modifications ainsi que e la raison d’ˆtre du fichier . Python. groupe d’instruction : pour exprimer ce que r´alise une fraction significae tive d’une proc´dure.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 1. Instruction n /* deuxi`me commentaire */ e Il convient ´galement de prendre la bonne habitude de comment´ son code e e source ”` la mode Doxygen”7 .org 6 . la date de cr´ation. e 2. les instructions 1 ` n seront ignor´es ` la compilation : a e a /* premier commentaire Instruction 1 . e On consid´rera ainsi l’exemple fourni en annexe A. Doxygen est un syst`me de documentation pour a e de nombreux langages de programmation (C++... C. /* Ceci est un commentaire qui peut s’´taler sur plusieurs lignes */ e // Ceci est un commentaire qui ne peut s’´taler que sur une ligne e Attention car le commentaire sur une ligne est une fonctionnalit´ qui n’a ´t´ e ee d´finie qu’avec la norme C99 ! e Une bonne habitude ` prendre dans le domaine g´n´ral de la programmation a e e est de d´couper chaque fichier source selon plusieurs niveaux de commentaires : e 1.doxygen.. du fichier. e e Attention : Une erreur classique est d’oublier la s´quence fermante */.1 page 106. Java.). IDL etc. 7 www. e 4.

Les noms des variables sont des identificateurs quelconques (voir §1. } /* Fonction main: point d’entree de l’ex´cution*/ e void main() { float HT. a e e 8 7 . les autres fonctions doivent ˆtre ´crites par le programmeur.C. Chaque fonction e r´alise une certaine tˆche8 .T.2f\n". } 2. : ").lib).h> // Directive de preprocesseur void main() { printf("Hello world!"). e a Pour pouvoir s’ex´cuter.5). le petit classique qui affiche ` l’´cran ”Hello World !” : a e #include <stdio.&HT). une directive doit ˆtre issue au compilateur e a e indiquant d’inclure les header files (extension *. Dans tous les cas.la TVA au Luxembourg float prix_TTC. ces fonctions ´tant contenues dans des fichiers sp´ciaux appel´s library files (exe e e tension *. : %. Une A noter qu’il existe en C des fonctions pr´compil´es qui peuvent ˆtre incluses dans le e e e programme.prix_TTC).h e prix_TTC = ajoute_TVA(HT). renvoie le prix TTC */ float ajoute_TVA(float prix_HT) { return prix_HT*(1 + (TVA/100)). faisant apparaˆ ıtre toutes les composantes d’un programme C : #include <stdio. Un exemple un peu plus complet. un programme C consiste en la construction de blocs indie e e viduels appel´es fonctions qui peuvent s’invoquer l’un l’autre. //appel de notre fonction printf("prix T.1.T. un programme C doit contenir une fonction sp´ciale e e appel´e main qui sera le point d’entr´e de l’ex´cution (c’est ` dire la premi`re e e e a e fonction invoqu´e au d´marrage de l’ex´cution). Voici deux exemples significatifs : 1. //d´claration d’une variable externe e /* Exemple de d´claration d’une fonction secondaire */ e /* ajoute la TVA au prix HT. D’abord. Pour acc´der ` ces fonctions. // d´finies dans stdio.h> // Directive de preprocesseur #define TVA 15 // idem . Toutes les autres fonctions sont e e e en fait des sous-routines. //d´claration d’une variable interne e puts("Entrer le prix H.h) contenant les d´clarations correspondantes e ` ces fonctions. Ces emplacements permettent de a e stocker des valeurs qui peuvent ˆtre utilis´es/modifi´es par une ou plusieurs e e e fonctions. avant la compilation. // appel de fonctions scanf("%f". Un programme C comporte ´galement la d´claration de variables qui correse e pondent ` des emplacements en m´moire. les directives du pr´processeur : elles permettent d’effectuer des mae nipulations sur le texte du programme source.4 Structure g´n´rale d’un programme C e e De mani`re g´n´rale. } On voit sur ces deux exemples les diff´rentes composantes d’un programme C : e 1.

Les variables peuvent ˆtre d´clar´es soit de fa¸on externe (c’est ` dire en e e e c a dehors d’une fonction). La d´claration d’une e e fonction est de la forme : type_resultat nom fonction (type1 arg1 . on utilise les fonctions puts. la d´finition de fonctions. Ces instructions seront d´taill´es dans le ¸ e e e e chapitre 7 mais on distingue d´j` les deux directives les plus utilis´es : ea e – #include qui permet d’inclure un fichier. e e 3. Les trois types scalaires de base du C a u sont l’entier (int). Dans cet exemple.. typen argn ) { <d´claration de variables locales > e <liste d’instructions > } En C. a chaque fois que le compilateur e rencontrera le mot TVA..h d´finit e (on pr´f`re dire d´clare) les fonctions standards d’entr´es/sorties (en ee e e anglais STanDard In/Out). les d´clarations de variables : Ces d´clarations sont de la forme : e type nom variable [= <valeur>] . Ce sont des sous-programmes dont les inse tructions vont d´finir un traitement sur des variables. soit de fa¸on interne (on dit aussi locale) ` une c a fonction.directive du pr´processeur est une ligne de programme source commene cant par le caract`re di`se (#). Ici. contee e nant : 8 . Par exemple. une fonction est d´finie par : e (a) une ligne d´clarative qui contient : e – type_resultat : le type du r´sultat de la fonction. . Ici. typen argn : les types et les noms des param`tres de e la fonction (b) un bloc d’instructions d´limit´ par des accolades { . e printf et scanf (voir §2.7. le r´el (float) et le caract`re (char) et seront abord´s e e e au §1. On peut bien sˆ r d´finir ses propres types e e u e (voir chapitre 4). d’o` ce nom). – type1 arg1 . . de type float (le type des nombres r´els e e dit ` virgule flottante. c e e Enfin. qui feront le lien entre le programme et la console (clavier/´cran). on a d´fini la variable e e externe identifi´e par prix_TTC. on ne peut jamais utiliser de variable sans l’avoir d´clar´e. dans la d´claration float prix_TTC . }. Cela permet notamment de savoir quelle e place la variable occupera en m´moire et comment il faudra interpr´ter la e e suite de bits stock´e en m´moire. Ces types de base sont d´taill´s dans la section 1. e – nom fonction : le nom qui identifie la fonction. Toute variable C est typ´e. e 2. il le remplacera par 15. le fichier stdio. . mais il existe un certain nombre de types disponibles de fa¸on native.7. . – #define qui d´finit une constante.5).

Des commentaires (voir §1. Ils sont form´s d’une suite de lettres (’a’ ` ’z’ et ’A’ ` ’Z’).5 page 27. D’autres exemples de fichiers source sont fournis en annexe A page 106. e e Par d´finition.6 Conventions d’´critures d’un programme C e Avant d’aller plus loin. le premier caract`re de cette suite ne peut pas ˆtre un chiffre . Ainsi. 1. a e e Ces fonctions seront d´taill´es au §2. les lettres accentu´es sont interdites . permet de donner un nom ` une a entit´ du programme (qu’il s’agisse d’une variable ou d’une fonction). C’est un e ordre ´l´mentaire que l’on donne ` la machine et qui manipulera ee a les donn´es (variables) du programme.5 Notion d’identificateur Un identificateur. toute fonction en C fournit un r´sultat dont le type e e doit ˆtre d´fini.. Dans notre exemple. #endif : voir le chae pitre 7). – puts affiche ` l’´cran le texte fourni en argument. comme son nom l’indique. – <d´claration de variables locales > : les d´clarations des donn´es e e e locales (c’est ` dire des donn´es qui sont uniquement connues ` a e a l’int´rieur de la fonction) e e – <liste d’instructions > : la liste des instructions qui d´finit l’action qui doit ˆtre ex´cut´e. on a e vu deux types de manipulation : l’appel de fonctions d’une part (puts. _start et InitDB sont des identificateurs valides. a e – scanf attend que l’on entre une valeur au clavier.. Ils sont e sujets aux r`gles suivantes : e 1. sous format r´el (%f). e e e 1.”. printf. e – printf affiche ` l’´cran un texte formatt´. S_i. scanf ou ajoute_TVA) et une affectation (=). les noms var1. e e e Une instructions est une expression termin´e par un ”. e 9 Ces r`gles peuvent largement ˆtre ´tendues ` tout langage de programmation e e e a 9 . Le retour du r´sultat se fait en g´n´ral ` la fin de e e e e e a la fonction par l’instruction return. En particulier.7. puis la met dans la variable HT. e e 3.4).e e e 4. Le type d’une fonction qui ne fournit pas de r´sultat est d´clar´ comme void (voir §1. A noter que pour ignorer une partie de programme il est pr´f´rable d’utiee liser une directive du pr´processeur (#ifdef .3) : ils sont ´limin´s par le pr´processeur. tandis que i:j ou 1i ne le sont pas. il convient d’´tablir un certain nombre de r`gles de e e pr´sentation que devra suivre tout bon programmeur qui souhaite ´crire des e e programmes C lisibles9 : – ne jamais placer plusieurs instructions sur une mˆme ligne . les identificateurs sont case-sensitive. e 2. de chiffres (0 e a a a ` 9) et du signe ’_’.

1 Les types de base Les caract`res e On utilise le mot-cl´ char pour d´signer une variable de type char. – laisser une ligne blanche entre la derni`re ligne des d´clarations et la premi`re e e e ligne des instructions . E O Y c m w Tab. // D´claration d’une variable c1 de type char e // a laquelle on affecte la valeur ’a’ // A noter l’utilisation du simple quote char c2 = 97.7 1. Il s’agit e e en fait d’un entier cod´ sur 8 bits interpr´t´ comme un caract`re utilis´ sur la e ee e e machine (il s’agit en g´n´ral du code ASCII de ce caract`re).1 donne la liste des principaux codes ASCII en d´cimal (pour e ˆtre pr´cis.– utiliser des identificateurs significatifs . l’autre moiti´ e e e e e e ¨ correspondants en g´n´ral ` des caract`res ´tendus comme A). il s’agit de la premi`re moiti´ des caract`re ASCII.1 – Codes ASCII en d´cimal e 10 . faire ressortir la structure syntaxique du a a programme . 8 B L V ‘ j t 7 BEL DC1 ESC % / 9 C M W a k u DEL 8 BS DC2 FS & 0 : D N X b l v 9 HT DC3 GS ’ 1 .7. – bien commenter les listings tout en ´vitant les commentaires triviaux. 6 @ J T ^ h r | 5 ENQ SI EM # 7 A K U i s } 6 ACK DLE SUB $ . – a´rer les lignes de programme en entourant par exemple les op´rateurs avec e e des espaces . e e e Ex : char c1 = ’a’. e e a e e code 0 10 20 30 40 50 60 70 80 90 100 110 120 0 NUL LF DC4 RS ( 2 < F P Z d n x 1 SOH VT NAK US ) 3 = G Q [ e o y 2 STX NP SYN SP * 4 > H R \ f p z 3 ETX CR ETB ! + 5 ? I S ] g q { 4 EOT SO CAN ” . 1. – grˆce ` l’indentation des lignes. la lecture de [Her01] devrait permettre au lecteur d’aqu´rir de bon e r´flexes de programmation. e A ce propos. //c2 correspond ´galement au caract`re ’a’ e e Le tableau 1. e 1.

e e le caract`re " doit ˆtre utilis´ dans les chaˆ e e e ınes de caract`re (et la s´quence ’\"’ e e permet de repr´senter ce caract`re dans une chaˆ e e ıne). e e 1. 1. Ex : e char * chaine = "Hello World !". les chaˆ e ınes de caract`res sont vues comme un e pointeur sur des caract`res et sont donc de type char *. // la variable a est initialis´e ` la valeur 14 e a /* Utilisation des pr´cisions (cas le + g´n´ral)*/ e e e short int b. 1. De mˆme. Par exemple. // d est cod´ sur 32 bits e // la possibilit´ de l’´criture suivante d´pend du compilateur e e e long long int e. Trigraphe Signification ??= # ??( [ ??/ \ ??) ] ??’ ^ ??< { ??! | ??> } ??~ Tab.Caract`res particuliers e Il existe un certain nombre de caract`res particuliers dont les principaux sont e r´sum´s dand le tableau 1.// une chaine de caract`re e // noter l’utilisation du double // quote Plus de d´tails dans le chapitre 3 consacr´ aux pointeurs.2 – Quelques caract`res sp´ciaux e e Ces caract`res ont une signification particuli`re pour le langage C.3 – Les 9 trigraphes disponibles en norme ANSI Et les chaˆ ınes de caract`res ? e En empi´tant un peu sur la suite. // b est cod´ sur 16 bits e int c.7.2. e 11 .3 d´taille les e e e trigraphes disponibles. // d est cod´ sur 64 bits. e e Caract`re e S´mantique e ’\n’ retour a la ligne ` ’\t’ tabulation ’\b’ backspace ’\’’ ’ ’\"’ ” \? ? Tab.2 Les entiers On utilise alors le mot-cl´ int. le caract`re ? e e est utilis´ dans les trigraphes qui sont des s´quences de trois caract`res permete e e tant de d´signer les caract`res # [ ] \ ^ { } | ~. Exemple : e /* d´claration la plus courante d’une variable de type int */ e int a = 14. Le tableau 1. // c est cod´ sur 16 ou 32 bits e long int d.

536 31 = 2.255] [-128 . //n est un entier non sign´ sur 32 bits e Par d´faut. 231 − 1] [0 . un short int peut contenir tout entier donc la valeur est comprise entre −215 et 215 − 1.616 2 2 Type char unsigned char signed char int unsigned int short int unsigned short int long unsigned long long long(*) unsigned long long(*) Taille m´moire e 1 octet 1 octet 1 octet 2 ou 4 octets 2 ou 4 octets 2 octets 2 octets 4 octets 4 octets 8 octets 8 octets Intervalle de valeurs [-128 . 0x5A2b. 0X5a2b.294.744. Voici quelques valeurs num´riques pour avoir un meilleur ordre d’id´e e e des intervalles utilis´s : e 215 = 32. On peut aussi sp´cifier e e e explicitement le type en ajoutant l’un des suffixes L ou l (pour le type long).709.215 − 1] ou [−231 . un unsigned short int (non sign´) pourra e 16 − 1. En principe.295 . e Par d´faut.h> (voir §9. LL or ll (pour le type long long).768 216 = 65.967.127] [-215 . e e e – octale (base 8) : on commence par un 0 suivi de chiffres octaux. Ex : 0x5a2b./* Pr´cision du signe */ e unsigned long int n.036.4 regroupe les types entiers standards avec quelques informations suppl´mentaires (la taille utilis´e en m´moire et l’intervalle des valeurs pose e e sibles. les entiers sont en repr´sentation sign´e (principalement en repr´e e e e sentation par compl´ment ` 2 pour les acharn´s).446.223.232 − 1] [−215 .4 – Les types entiers standards.551. 215 − 1] [0.372. Des exemples sont fournis dans le tableau suivant : 12 . Au contraire.216 − 1] ou [0 . on a sizeof(short) <= sizeof(int) <= sizeof(long) Cas des constantes enti`res e On distingue 3 notations : – d´cimale (´criture en base 10) : c’est l’´criture usuelle. (*) : d´pend du compilateur utilis´ e e Remarque : les valeurs limites des differents types sont indiqu´es dans le fichier e header <limits. 63 =9. – h´xad´cimale (base 16) : on commence par 0x (ou 0X) suivis de chiffres h´xae e e d´cimaux (0-9 a-f). le type des constantes enti`res est le type int.854. Ex : 0477 . 231 − 1] [0. 1.147. contenir un entier compris entre 0 et 2 Le tableau 1. 263 − 1] [0.648 2 232 =4.808 64 =18. On peut aussi ajouter le suffixe U ou u (pour unsigned).483. 216 − 1] [−231 . Ex : 372 .9).775.255] [0 .127] ou [0. 232 − 1] [−263 . 264 − 1] Tab. e a e Ainsi.073.

L’exposant est stock´ avec un biais (qui vaut 127 pour le type e e e float) Un petit exemple pour bien comprendre : −2.3 Les flottants On distingue trois types de flottants allant de la pr´cision la plus faible ` la e a plus forte (qui d´pend de l’impl´mentation) : float. 1 ∗ 104932 ] Pr´cision e 6 chiffres d´cimaux e 15 chiffres d´cimaux e 19 chiffres d´cimaux e Tab. d’une mantisse m et un exposant exp en base 2 tels que : e x = s ∗ m ∗ 2exp avec 1. double et long double. 3 ∗ 10−308 . 5 = −1 ∗ 1.5 – Les types flottant pour les nombres r´els. 1. Representation Interne La taille de stockage et la repr´sentation interne des nombres flottant d´pend e e du compilateur utilis´ mais la plupart utilise le standard IEEE 754-1985[Ste90]. 3.5 d´taille ces informations pour chaque type e e flottant. 7 ∗ 10308 ] [3. 4 ∗ 10−4932 . e Un nombre flottant x est ainsi compos´ d’un signe s (s = 1 pour un nombre e n´gatif.D´cimal e 15 32767 10U 32768U 16L 27UL Octal 017 077777 012U 0100000U 020L 033ul Hexa Oxf 0x7FFF 0xAU 0x8000u 0x10L 0x1BUL Type int int unsigned int unsigned int long unsigned long 1. 0 sinon) . 1. Les valeurs stock´es seront donc : S = 1 . le premier bit de la mantisse non nulle sera toujours 1 : il n’est donc pas repr´sent´.2 montre le format de stockage d’un nombre de type float dans la repr´sentation IEEE : e s Position du bit: 31 Exposant exp 22 Mantisse m 0 Fig. Type float double long double Taille m´moire e 4 octets 8 octets 10 octets Intervalle de valeurs [1. alors l’intervalle de valeur est d´termin´ par le nombre de bits utie e lis´s par l’exposant. 4 ∗ 1038 ] [2. 1. 1.25 e 13 . e e Ex : double Pi = 3.7.2 – Format de stockage IEEE pour un nombre de type float En binaire.14159. Le tableau 1. e La figure 1. exp = 127 + 1 . 25 ∗ 21 .0 ≤ m < 2 ou m=0 La pr´cision du nombre flottant d´termine le nombre de bits utilis´e pour la e e e mantisse. m = 0. 2 ∗ 10−38 .

voir §2. les expressions de type void : on les rencontre principalement dans la d´claration de fonctions qui n’ont pas de valeur de retour. c’est ` dire qu’il est capable de pointer vers n’importe quel type e a d’objet.4 ∗ 10−4 notation C .3e4 correspondance 0. On utilise aussi dans le cadre d’une conversion de type (on parle de cast. e A noter qu’en pratique.Cas des constantes flottantes Des exemples d’utilisation des constantes flottantes sont r´sum´es dans le tae e bleau suivant : notation C 2. Il repr´sente donc l’adresse de l’objet et non son type. les pointeurs vers le type void : le type void * est le type pointeur g´e n´rique.e4 1. le prototype de fonctions sans param`tres : int rand(void).4) vers le type void.3 ∗ 104 1. on utilise le mot-cl´ void. ce qui n’a de sens que si la valeur r´sultat e n’est pas utilis´e.4e-4 correspondance 2 1. de mˆme que toute valeur de retour e e e d’un fonction.4 Le type void On a vu que toute variable C ´tait typ´e. e e 14 . Ex : (void)printf("Un exemple.").3 2 ∗ 104 0. Plus de e pr´cisions dans la section3 page 33 consacr´e aux pointeurs. on ´crira plus simplement : int rand(). Ce type est utilis´ e e e dans trois situations : 1.4 2 ∗ 104 1. e 3.3 2e4 . 1. e 2.4 2. e Ex : void exit (int status). Mais il peut arriver qu’aucune valeur ne soit disponible.7. Pour exprimer l’id´e de ”aucune valeur”.

0 // Resultat: 1. une valeur. Des expressions peuvent ´galement ˆtre utilis´es comme op´randes e e e e et ˆtre jointes ensembles par des op´rateurs pour obtenir des expressions plus e e complexes.0/3 est ´quivalent ` 2. Quelques exemples d’expressions : 4 * 512 // Type: int 4 + 6 * 512 // Type: int. 2.1 Op´rateurs arithm´tiques e e Les principaux op´rateurs arithm´tiques sont r´sum´s dans le tableau 2.0 / 4. type: void (int*)malloc(count*sizeof(int)) // Appel de fonction. e – Le r´sultat d’une division d’entiers est aussi un entier ! Ex : e 6 / 4 // Resultat: 1 6 % 4 // Resultat: 2 6. type: int * Les op´rateurs peuvent ˆtre unaires (` une op´rande) ou binaires (` deux op´e e a e a e randes). Toute expression a un type et si ce type n’est pas void.5 1 Seul l’op´rateur % requiert des types entiers e 15 . Les e e e e op´randes de ces op´rateurs peuvent appartenir ` tout type arithm´tique1 e e a e Quelques remarques : – On peut effectuer une conversion de type aux op´randes. type: int 1.1 Expressions et Op´rateurs e Une expression est une combination d’op´rateurs et d’op´randes. une expression se r´sume ` une constante.1. Le r´sultat de l’op´e e e ration prend le type de cette conversion. e e 2.Chapitre 2 La syntaxe du langage 2. Ainsi .0 e a et le r´sultat est de type float.0/3. Il existe un seul op´rateur ternaire (il s’agit de ?:) Les paragraphes e qui suivent d´taillent les op´rateurs disponibles. une variable ou un appel e a de fonction. Dans les cas les e e plus simples.0 + sin(x) // Type: double srand((unsigned)time(NULL)) // Appel de fonction.1. equivalent to 4 + (6 * 512) printf("Un exemple!\n") // Appel de fonction.

y x * y x / y x % y +x -x x++ ou ++x --(unaire) Decr´ment e x-.3 Op´rateurs relationnels e Toute comparaison est une expression de type int qui renvoie la valeur 0 (false) ou 1 (true).2 Op´rateurs d’affectation e Traduction affectation simple affectation compos´e e Exemple x = y x += y R´sultat e assigne la valeur de y ` x a x (op)=y est ´quivalent e x = x (op) y Op´rateur e = (op)= a ` Tab. Dans l’´criture x(op)=y. suffixe x--) d´cr´mente e e e x avant (resp.Op´rateur e + * / % +(unaire) -(unaire) ++(unaire) Traduction Addition Soustraction Produit Division Reste Signe positif Signe n´gatif e Incr´ment e Exemple x + y x .1. R´sultat : N = 6 et X = 5 e e – Incr´mentation pr´fixe : X = ++N . (op) est un e e e op´rateur arithm´tique ou de manipulation de bits Les op´rateurs d’affectation e e e compos´s sont donc : e += -= *= /= %= &= ^= |= <<= >>= 2. la variable x n’est ´valu´e qu’une seule fois.ou --x R´sultat e l’addition de x et y la soustraction de x et y la multiplication de x et y le quotient de x et y Reste de la division euclidienne de x par y la valeur de x la n´gation arithm´tique de x e e x est incr´ment´ (x = x + 1). 2. apr`s) de l’´valuer e e x est d´crement´ (x = x − 1). voici un petit exemple pour bien e e comprendre : Supposons que la valeur de N soit ´gale ` 5 : e a – Incr´mentation postfixe : X = N++ . L’op´rateur e e e pr´fixe ++x (resp. suffixe x++) incr´mente e e x avant (resp. e 16 . L’op´rande de gauche doit ˆtre une expression qui d´signe un objet e e e (une variable par exemple). 2.2.2 – Les op´rateurs d’affectation e On distingue deux types d’op´rateurs d’assignement qui sont r´sum´s dans le e e e tableau 2.1. Il faut que les op´randes soient du mˆme type arithm´tique (ou des e e e pointeurs sur des objets de mˆme type).1 – Les principaux op´rateurs arithm´tiques e e – Concernant l’incr´mentation pr´/postfixe. L’op´rateur e e e pr´fixe --x (resp. apr`s) de l’´valuer e e Tab. R´sultat : N = 6 et X = 6 e e e 2.

c -o erreur l’option de compilation ’-Wall’ a quand mˆme soulev´ un warning e e 17 .1).e e e e arithm´tique ou pointeur). e e e e Attention : Une erreur ultra classique est de confondre l’op´rateur d’´galit´ e e e (==) avec celui d’affectation (=). x et y sont diff´rents et on s’attend ` obtenir un message le confirmant.h> int main () { int x=14. Toute valeur diff´rente de 0 est interpr´t´e comme e e ee vraie (et 0 correspond ` ’faux’).1. Comme pour les expressions relationnelles.y). 2.4 Op´rateurs logiques e Les op´rateurs logiques. Ainsi. Cette valeur est consid´r´e comme ’fausse’ pour la condition test´e dans ee e le if si bien que les instruction de la branche else sont ex´cut´es (voir §2. les a expressions logiques renvoient une valeur enti`re (0=false . e return 0.x. Ainsi.4 permettent de combiner le e e r´sultat de plusieurs expressions de comparaison en une seule expression logique. consid´rons le code suivant2 : e /* Fichier: erreur. e a else printf("x est diff´rent de y (%i!=%i)\n".y=1.c */ #include <stdio. 1=true). l’op´rande de droite n’est e e e e 2 3 Compiler ce code avec la commande gcc -Wall erreur. } A priori. on aurait obtenu : x est diff´rent de y (14!=1) e e Remarque : en initialisant y a 0 (et non a 1). e e 2.3.2. e a N´anmoins. l’erreur d’ecriture a permis d’effectuer une affectation de la valeur e de x si bien que ce programme renvoit la ligne3 : x est ´gal ` y (1=1)) (not´ e a e la valeurs de x) Avec une ´criture correcte.3 – Les op´rateurs relationnels e Les diff´rents op´rateurs relationnels sont d´taill´s dans le tableau 2.Op´rateur e < <= > >= == != Traduction inf´rieur e inf´rieur ou ´gal e e sup´rieur e sup´rieur ou ´gal e e ´galit´ e e in´galit´ e e Exemple x < y x <= y x > y x >= y x == y x != y R´sultat e 1 si x est inf´rieur ` y e a 1 si x est inf´rieur ou ´gal ` y e e a 1 si x est sup´rieur ` y e a 1 si x est sup´rieur ou ´gal ` y e e a 1 si x est ´gal ` y e a 1 si x est diff´rent de y e Tab.y). list´s dans le tableau 2. e Les op´randes des op´rateurs logiques peuvent ˆtre n’importe quel scalaire (i.x. e Remarque : les op´rateurs && et || ´valuent les op´randes de gauche ` droite et e e e a le r´sultat est connu d`s l’op´rande de gauche. on aurait obtenu le message x est diff´rent de y (0 !=0) car le r´sultat de l’affectation est la valeur affect´e e e e (ici 0). // x est diff´rent de y e if (x = y) //erreur!!! il faudrait ´crire ’if (x == y)’ e printf("x est ´gal ` y (%i=%i)\n".

Ainsi. Les op´randes sont ine a e e terpr´t´es bits par bits (le bit 1 correspondant ` une valeur vraie. Quelques exemples pour bien comprendre (chaque op´rande est fournie sous forme d´cimale et binaire) : e e 14 14 14 14 14 14 x 1110 1110 1110 1110 1110 1110 y 9 1001 9 1001 9 1001 2 2 0010 0010 Op´ration e x & y x | y x ^ y ~x x << y x >> y R´sultat e 8 1000 15 1111 7 0111 1 0001 56 111000 3 11 2. Dans tous les autres e a cas.5 – Les op´rateurs de manipulation des bits e Les op´rateurs bits ` bits n’op`rent que sur des entiers. e e e 18 . 2.4 – Les op´rateurs logiques e ´valu´e que si celle de gauche est vraie dans le cas de l’op´rateur && (respece e e tivement fausse dans le cas de l’op´rateur ||). Par exemple. Plus de d´tails dans le chapitre 3 d´di´ aux pointeurs. 2. dans l’expression e (i < max) && (f(14) == 1). e Tab.Op´rateur e && || ! Traduction ET logique OU logique NON logique Exemple x && y x || y !x R´sultat e 1 si x et y sont diff´rents de 0 e 1 si x et/ou y sont diff´rents de 0 e 1 si x est ´gal ` 0.1. 0 est renvoy´. e 2.5 Op´rateurs bit ` bit e a Traduction ET bit ` bit a OU bit ` bit a XOR bit ` bit a NON bit ` bit a d´calage ` gauche e a s´calage ` droite e a Exemple x & y x | y x ^ y ~x x << y x >> y R´sultat (pour chaque position e de bit) 1 si les bits de x et y valent 1 1 si le bit de x et/ou de y vaut 1 1 si le bit de x ou de y vaut 1 1 si le bit de x est 0 d´cale chaque bit de x de y positions e vers la gauche d´cale chaque bit de x de y positions e vers la droite Op´rateur e & | ^ ~ << >> Tab. &x renvoie l’adresse m´moire de x et est donc e un pointeur vers x. la fonction f n’est appel´e que si i < max.6 Op´rateurs d’acc`s ` la m´moire e e a e L’op´rande de l’op´rateur d’adresse & doit ˆtre une expression qui d´signe un e e e e objet ou une expression. 0 est consid´r´ ee a ee comme une valeur fausse).1.

7 – Les autres op´rateurs e L’op´rateur sizeof renvoie le nombre de bits requis pour stocker un objet du e type sp´cifi´. & * [ ] ..7. e e e L’op´rateur ?: permet une ecriture plus compacte de l’´valuation conditionnelle e e if. Op.y) (long)x sizeof(x) x?:y:z x. l’´l´ment e ee d’indice i dans le tableau t le membre x dans la structure ou l’union s le membre x dans la structure ou l’union point´e par p e Tab. -> Traduction Adresse de Indirection El´ment de tableau e Membre d’une structure ou d’une union Membre d’une structure ou d’une union Exemple &x *p t[i] s..1. Ainsi l’expression : (x >= 0) ? x : -x renvoie la valeur absolue de de x. e 2.y R´sultat e Ex´cute la fonction f avec les argue ments x et y la valeur de x avec le type sp´cifi´ e e nombre de bits occup´ par x e si x est diff´rent de 0.. e e 2.2 Les structures de contrˆle o Comme pour la plupart des langages de programmation.else.4. Le r´sultat est une constante de type size_t. Plus de d´tails au §2.x p->x R´sultat e l’adresse m´moire de x e l’objet (ou la fonction) point´e par p e L’´quivalent de *(x+i).6 – Les op´rateurs d’acc`s ` la m´moire e e a e Les op´rateurs d’acc`s aux membres d’une structure ou d’une union seront plus e e amplement d´taill´s dans les §4. () (type) sizeof ? : . 2.3 et §4.2.then. 2. Traduction Appel de fonction cast taille en bits Evaluation conditionnelle s´quencement e Exemple f(x..7 Autres op´rateurs e Il existe un certain nombre d’autres op´rateurs qui sont list´s dans le tableau 2. o 19 .1.Op. le langage C d´finit e un certain nombre de structures de contrˆle (boucles et branchements).4 page 25). alors y sinon z e Evalue x puis y Tab. e e On y retrouve notamment l’appel de fonction et la conversion de type (qui ne s’applique qu’aux types scalaires et qui sera abord´e plus en d´tail dans la e e section 2.

1.3 Instruction while Syntaxe : – while (expression) Instruction Il s’agit d’une boucle : tant que expression est vraie. // ` la variable max. e a Exemple : 20 . e Expression1 Expression2 != 0 ? oui non fin du for Instruction Expression3 Fig.. Exemple : e e e e if (x > y) else max = x. 2. e – Attention aussi au fait que expression doit ˆtre correctement parenth´s´e. expression3 ) Instruction expression1 et expression3 peuvent ˆtre n’importe quelle expression.2. if ( expression ) Instruction1 2. e ee – la partie ’then’ n’est pas introduite par le mot-cl´ then comme dans certains e langages.i).2. i < MAX . //compteur for (i=0. 2. Le d´roulement o e e de cette instruction est illustr´ par l’organigramme de la figure 2. expression2 e est une expression de contrˆle et doit donc ˆtre de type scalaire. Instruction est ex´cut´e. } 2.. sinon Instruction2 est ex´cut´e (si elle existe).MAX=14.2.2.1 – Organigramme de l’instruction for Un exemple pour bien comprendre : int i.1 Instruction if. i++) { printf("Valeur de i : %i\n". a Remarque : – l’exemple pr´c´dent aurait pu s’´crire plus simplement (mais moins lisiblee e e ment) : (x > y)?(max = x):(max = y).else Syntaxe : 1. if ( expression ) Instruction1 else Instruction2 La valeur de expression est ´valu´e et si elle est diff´rente de 0.2 Instruction for Syntaxe : – for (expression1 .2. // Assigne la plus grande valeur entre x et y max = y. expression2 . Instruction1 est e e e ex´cut´e. ou mˆme max = (x>y)? x : y. e e conform´ment ` l’organigramme de la figure 2.

} while (i < MAX). } 2.. i++. } 21 . comme dans l’organigramme de o e e la figure 2.while Syntaxe : do instruction while (expression) . while (i < MAX ) { printf("Valeur de i : %i\n".3 – Organigramme de l’instruction do L’exemple suivant imprimera ”Valeur de i : 14” (alors que i ≥ 14) : #include <stdio.while a un fonctionnement globalement similaire ` celui a d’une boucle while mise ` part le fait que le corps de la boucle est ex´cut´ a e e avant que l’expression de contrˆle soit ´valu´e. le corps de la boucle est toujours ex´cut´ au moins une e e fois. 2. Ainsi. 2..3. i++.2 – Organigramme de l’instruction while #define MAX 14 int i=0.. do { printf("Valeur de i : %i\n"..h> #define MAX 14 int main() { int i=MAX. return 0.4 Instruction do. Instruction Expression != 0 ? oui non fin du do Fig.i).i).Expression != 0 ? oui non fin du while Instruction Fig. l’instruction do.2.

2..4 page 12). e ni break.. Exemple : enum {FALSE. 3. on peut distinguer 3 types d’utiee lisations possibles : 1. Sa syntaxe est la suivante : e e e switch (expression) { case constante1 : liste d′ instructions1 break . C’est pourquoi on utilisera souvent dans les exemples qui suivent des enum´rations (voir §4. break. default : liste d′ instructions } 1. case constanten : liste d′ instructionsn break . } } 2.. s’il n’existe aucune constantei dont la valeur soit ´gale ` celle de expression. A la premi`re constantei dont la valeur est ´gale ` expression. void print_boolean(int bool) { switch (bool) { case FALSE: printf("faux"). pour chaque alternative case. ATTENTION : expression est n´cessairement une valeur enti`re (voir le tae e bleau 1. si on veut compter le nombre de caract`res qui e sont des chiffres et ceux qui ne le sont pas.. . puis le r´sulat est compar´ avec constante1 . break.2.1 page 41). expression est ´valu´e.2. on peut avoir une ou plusieurs alternatives ne poss´dant ni liste d′ instructions. on peut utiliser le switch suivant : switch (c) { case ’0’: case ’1’: case ’2’: case ’3’: case ’4’: case ’5’: case ’6’: case ’7’: case ’8’: 4 l’instruction break est optionnelle 22 . case TRUE: printf("vrai"). la (ou les4 ) e e a ′ instructions correspondante(s) est ex´cut´e jusqu’` la rencontre e e a liste d d’une instruction break. e a on ex´cute la liste d′ instructions de l’alternative default si celle-ci existe. e sinon rien n’est fait. e Compte tenu du nombre d’´l´ments optionnels. on trouve un break. constante2 e e e e etc. Par exemple.5 Instruction switch Cette instruction est un if g´n´ralis´. TRUE}. default: printf("Erreur interne"). La rencontre d’un break termine l’instruction swith.

2. } } L’instruction break peut ˆtre ´tendue ` d’autres cas de branchements (ou de e e a sauts) comme on le verra au §2. l’instruction goto et la d´claration de l’etiquette doivent ˆtre e e contenu au sein de la mˆme fonction. . La directive goto permet de brancher directement ` n’importe quel endroit de la a e e e fonction courante identifi´e par une ´tiquette.. case POSSIBLE: printf("possible")... ) if ( erreur ) goto TRAITEMENT_ERREUR. vous pouvez vous en passer alors n’en abusez pas ! – Pour une saut en dehors d’une mˆme fonction..."). e – N’utiliser cette instruction que lorsque vous ne pouvez pas faire autrement. On a vu le rˆle de l’instruction break au sein de la directive de branchement o multiple switch.2. Le plus souvent. Les paragraphes qui suivent abordent les branchement non conditionnels (par opposition aux branchements conditionnels correspondant aux instructions if..7 Instruction break Syntaxe : break. void print_cas(int cas) { switch (cas) { case IMPOSSIBLE: printf("im"). 2.. enfin on peut ne pas avoir de break comme dans l’exemple suivant : enum {POSSIBLE. } 3.else et switch) qui permettent de naviguer au sein des fonctions du programme. Une ´tiquette est un identificateur suivi du signe ”:”.6 Instruction goto Syntaxe : goto etiquette . TRAITEMENT_ERREUR: // le traitement d’erreur est effectu´ ici e printf("Erreur: .. default: nb_non_chiffres++.2..case ’9’: nb_chiffres++.7... break.. On peut l’utiliser plus g´n´ralement au sein de n’importe e e 23 . on pourra utiliser les fonctions e setjmp et longjmp.. Remarque : – Encore une fois. Exemple : for ( . . 2. IMPOSSIBLE}. ) for ( .

Elle permet de passer directement ` l’it´ration a e suivante. break fait sortir de la boucle la plus interne e 2.. } // on reprend ici apr`s le break e En cas de boucles imbriqu´es.3 La r´cursivit´ e e Comme pour beaucoup de langages. La notion de r´currence est a e e largement pr´sente dans le domaine math´matique. la r´cursivit´ est possible en C.while) pour interrompre son d´roulement et passer directement ` la premi`re instruction qui suit la boucle. while ou do. } 2. e e ee c’est-`-dire de se rappeler elle-mˆme plusieurs fois. notamment dans la d´finie e e tion des suites.8 Instruction continue Syntaxe : continue .. on pourrait calculer explicitement la valeur de un en fonction de n (ici.. u on peut montrer que ∀n ∈ N. // Sortie de la boucle . Exemple : for (i = -10.quelle boucle (instructions for. i++) { if (i == 0) continue. i < 10. .. . if (command == ESC) break. Bien sˆ r..2. Qu’este e ce que la r´cursivit´ ? C’est la propri´t´ qu’a une fonction de s’auto-appeler. e a e Exemple : while (1) { .h> /*********************************************************** * Exemple de fonction traduisant la d´finition de la suite: e * u_0 = 1 * u_n = 2*u_{n-1} + 1 si n >= 1 24 .. Exemple : u0 = 1 un = 2un−1 + 1 ∀n≥1 Les premiers termes cons´cutifs de cette suite sont donc : e u0 = 1 u1 = 2u0 + 1 = 3 u2 = 2u1 + 1 = 7 . un = 2n+1 − 1) mais on peut utiliser directement la d´finition de la fonction pour la programmation du calcul correspondant : e #include <stdio. L’instruction continue ne peut ˆtre utilis´e que dans le corps d’une boucle e e (instruction for.. // passer ` 1 sans ex´cuter la suite a e .. while ou do).

// float } int main() { int i = 14.* Contrainte: n>=0 ************************************************************/ int ma_suite(unsigned int n) { if (n == 0) return 1.ma_suite(n)). la conversion e peut ˆtre soit implicite. printf("valeur de la suite : %u\n". c’est ` dire effectu´e par le compilateur (par exemple. Elle peut ˆtre ´galement explicite grˆce ` l’op´rateur de cast (transtypage en e e a a e fran¸ais). une valeur d’un certain type est utilis´e dans un contexte qui en requiert e un autre : – passage de param`tre : le param`tre effectif n’a pas le type du param`tre e e e formel (c’´tait le cas dans l’exemple pr´c´dent) . //appel r´cursif e } /*** Fonction main: point d’entr´e de l’ex´cution ***/ e e int main() { unsigned int n.1 Les situations de la conversion de type En C. Les conversions ne peuvent ˆtre effectu´es que sur les types e e scalaires (c’est ` dire les types arithm´tiques et les pointeurs). // appel de f avec conversion de i vers // le type float } 2. les situations o` se produisent les conversions sont les suivantes : u 1. Exemple : e void f(float i. e a e si i est une variable de type float et j de type int. .. scanf("%u". f( (float)i.. alors l’expression i+j est automatiquement de type float). //u_0 = 1 else return 2*ma_suite(n-1) + 1. e e e 25 .0. Enfin. float j = 2. a e Une conversion de type conserve toujours la valeur originale dans la mesure ou le nouveau type est capable de la repr´senter.4 Les conversions de types Une conversion de type renvoie la valeur d’une expression (de type A) dans un nouveau type B.4. Ainsi. Il est de bon goˆ t et tr`s important (pour la clart´ du programme) c u e e d’utiliser l’op´rateur de cast d`s qu’une conversion de type est requise. float j) { // fonction avec 2 param`tres de type e . un nombre flottant peut e ˆtre arrondi dans une conversion de double vers float.. Cela e e ´vite aussi les warnings du compilateur.&n). return 0. } 2. j). puts("Entrer la valeur de n : ")..

Ensuite. op´rateur de conversion : le programmeur demande explicitement une e conversion. e La r`gles qui s’applique est la suivante : e Dans le cas d’op´randes enti`res. unsigned char. abord´es au §2. a e – valeur rendue par une fonction : l’op´rande de return n’a pas le type e indiqu´ dans la d´claration de la fonction. on les convertit dans le type le plus e haut dans la hi´rarchie expos´e dans la figure 2.4. un op´rateur a des op´randes de types diff´rents.4. ainsi e e e e qu’aux op´rateurs binaires de d´calage << et >> et dans les conversions arithe e m´tiques habituelles. et contrairement aux deux cas pr´c´dents. 2. except´s e e a e e e les op´rateurs de d´calage << et >> et les seconde et troisi`me op´randes de e e e e l’op´rateur ?:.2 La r`gle de ”promotion des entiers” e Cette r`gle est appliqu´e aux op´randes des op´rateurs unaires + et -. short . a Ainsi. si les op´e e randes sont toujours de types diff´rents. toute op´rande de type char. ce qui permet de se d´barrasser des petits entiers. Elle a pour but d’amener les ”petits e e entiers” ` la taille des int. e e long double double float unsigned long long long long unsigned long long unsigned int int Fig.4 – Hi´rarchie des conversions arithm´tiques habituelles e e 26 . e e 2. e 2. e e 2. on applique d´j` la r`gle de promotion des e e ea e entiers. c’est le compie e lateur qui effectue la conversion selon des r`gles soigneusement d´finies qui sont e e d´taill´es dans les deux paragraphes qui suivent.3. e e e Dans ce dernier cas.4.4. 3.– affectation : la valeur ` affecter n’a pas le mˆme type que la variable . unsigned short e ou champs de bits sur les op´rateurs pr´c´dent est automatiquement convere e e tie en int (ou en unsigned int si le type int ne permet pas de repr´senter e l’op´rande).3 Les conversions arithm´tiques habituelles e Cette r`gle est appliqu´e ` tous les op´rateurs arithm´tiques binaires.

. e e 27 . e CQFD.4. les conversions fonctionnent de fa¸on satisfaisante vis ` vis du proe e c a grammeur. } imprimera le message. if (i < -1) printf("Cha ch’est louche\n").. Cha ch’est louche. else printf("On est super content!\n").h> int main () { unsigned int i = 0. notamment printf et scanf. laissant croire que 0 < −1. Sur certains compilateurs. il suffit d’utiliser l’op´rateur de conversion e dans le test qui devient : if ( (int) i < -1) Attention car on peut aussi utiliser des unsigned int sans s’en rendre compte ! Ainsi : #include <stdio. qui sont respece e e tivement le clavier et l’´cran.. e 2.. e 2. les int n’ont pas une longueur n´gative. e e Consid´rer ainsi le programme suivant : e #include <stdio. D’apr`s la figure 2.. bien noter que sizeof renvoit une valeur de type entier non sign´.h) renvoie un ´l´ment de type size_t.. return 0.h (voir aussi le §9.18 et le chapitre 6) utilis´es avec les unit´s classiques d’entr´es-sorties.h> n’est pas n´e e cessaire pour utiliser les fonctions pr´sent´es ici. Pour que tout revienne dans l’ordre.4. un e ee entier non sign´. Cependant.4 Les surprises de la conversion de type En g´n´ral. else printf("On est super content!\n"). Pourtant.h> int main () { if ( sizeof(int) < -1) printf("cha ch’est louche\n"). } Celui ci imprimera le message Cha ch’est louche.2. L’explication est la suivante : l’op´rateur < poss`de une op´rande de type e e e unsigned int (i) et l’autre de type int (la constante -1). return 0. Il se trouve simplement qui dans la d´claration de la fonce e tion sizeof (d´finie dans stddef. Ne jamais m´langer entiers sign´s et non sign´s dans les comparaisons e e e (l’option -Wall du compilateur le signale normalement) ! Utiliser l’op´rae teur de conversion pour amener les op´randes de la comparaison dans le e mˆme type.h par la directive au pr´processeur #include <stdio. il existe une situation permettant d’obtenir des r´sule tats surprenants : lors de la comparaison entre des entiers sign´s et non sign´s. e Conclusion : 1.. l’appel ` la librairie e a stdio..5 Principales fonctions d’entr´es-sorties standard e Il s’agit des fonctions de la librairie standard stdio. e cette derni`re est convertie en unsigned int et le compilateur r´alise la compae e raison non sign´e entre 0 et 4294967295 (puisque -1 = 0xFFFFFFFF = 4294967295).

o` var est de type char. printf("Entrer un caract`re:"). } 2. e Exemple : #include <stdio. Syntaxe : Cette fonction affiche. La e syntaxe d’utilisation est la suivante : putchar(var). la chaˆ de caract`res ch puis positionne ıne e le curseur en d´but de ligne suivante. sur stdout. puts(toto).2 La fonction putchar La fonction putchar permet l’affichage d’un seul caract`re sur l’´cran de l’ore e dinateur. return 0. e e return 0. La syntaxe d’utilisation de getchar est la suivante : var=getchar().5. } 28 .h> int main() { char c. printf("Entrer un caract`re:"). Notez que var doit ˆtre de type char.3 La fonction puts puts(ch). putchar(c).h> int main() { char c. Exemple : u #include <stdio. return 0. Exemple : e #include <stdio.c). puts retourne EOF en cas d’erreur.5.h> int main() { char * toto = "on est super content!". e c = getchar(). printf("Le caract`re entr´ est %c\n". } A noter enfin que cette fonction est strictement ´quivalente ` getc(stdin) e a (Cette fonction sera abord´e au chapitre 6). putchar constitue alors la fonction compl´mentaire de getchar.5. e c = getchar().2. e 2.1 La fonction getchar La fonction getchar permet la r´cup´ration d’un seul caract`re ` partir du e e e a clavier.

ı o La chaˆ de contrˆle contient le texte ` afficher et les sp´cifications de format ıne o a e correspondant ` chaque expression de la liste. [flag] fournit des options de cadrage (par d´faut. En donnant le signe * comme largeur.14. expressioni foure e nira la largeur (et expressioni+1 la variable ` afficher) a Exemple : printf("%*f". double pour les r´els). e e a ce champ indique le nombre minimal de chiffres d´sir´s (avec l’ajout de 0 e e sinon).var). 2. Pour ˆtre plus pr´cis. 3.4 La fonction d’´criture ` l’´cran formatt´e printf e a e e La fonction printf est une fonction d’impression format´e. expression1 . Elles sont introduites e a par le caract`re %. une sp´cification de i-`me format est de la forme : e e e e %[flag][largeur][.pr´cision][modificateur]type e Les ´l´ments entre crochet sont facultatifs. e e e Voici plusieurs exemples didactiques : printf("|%d|\n". |14| |-14| |+14| 29 . e e a Valeur par d´faut : 6.5. . printf("|%+d|\n". il est n´anmoins e e ae e ´crit en totalit´. Sa syntaxe est la e suivante : printf("cha^ne de contr^le". . Le i-`me format de la chaˆ de contrˆle sera remplac´ par e e ıne o e la valeur effective de expressioni . on peut ´crire e e e .* pour que expressioni+1 d´signe la pr´cision ` appliquer. [largeur] est le nombre minimal de caract`res ` ´crire (des blancs sont e ae ajout´s si n´cessaire). pour les r´els. ou encore e L (long double pour les r´els). elle indique la longueur maximale ıne imprim´e (tronqu´e si trop longue). Comme pour la largeur. l (long pour les entiers. Les sp´cifications de format ont a e pour but d’annoncer le format des donn´es ` visualiser. Les diff´rents ´l´ments de cette sp´ee e ee e cification sont les suivants : 1. type pr´cise l’interpr´tation ` donner ` expressioni . Si le texte ` ´crire est plus long.2. e 4. .pr´cision] d´finit. ` la place du a signe Il existe d’autres valeurs possibles mais qui sont moins utiles. expressioni est justifi´ e e a ` droite) et peut prendre les valeurs suivantes : expressioni sera justifi´ ` gauche (ajout de blancs si n´cessaire) ea e + affichage du signe (+ ou -) avant la valeur num´rique e espace impression d’un espace devant un nombre positif. . e 5. [. le nombre de chiffres apr`s la virgule e e e e (ce nombre doit ˆtre inf´rieur ` la largeur).14). ce qui signifie que e les donn´es sont converties selon le format particulier choisi. [modificateur] modifie l’interpr´tation de expressioni et peut valoir h e (pour short).14). Les valeurs possibles e e a a sont d´taill´es dans la table 2. alors que pour une chaˆ (%s).-14).8 e e printf retourne le nombre de caract`res ´crits. printf("|%d|\n". expressionn ). ou EOF en cas de probl`me. Dans le cas de nombres entiers.

| 123456.6d|\n".20f|\n".234567890123456789e5).14).14).-14).4e|\n".234568e+05| printf("|%. |1.1.8 – Les diff´rents formats de la fonction printf e printf("|%+d|\n".4g|\n".1. repr´sentation la plus courte parmi %lf et %le e e caract`re e chaˆ de caract`res ıne e Tab.14).10. |1.6d|\n".234567890123456789e5).1.234567890123456789e5).234567890123456789e5).-14).14). | 1. |0x56ab| printf("|%#X|\n". |0X56AB| ======================== printf("|%o|\n". |1.0x56ab).14). | 14| printf("|%10.234567890123456789e5).234567890123456789e5).4g|\n".1.0x56ab).23456789012345674564e+05| printf("|%20.234567890123456789e5).1. |1.1. |-14| printf("|% d|\n".14).234567890123456789e5).6. |000014| printf("|%*.4f|\n".7890| printf("|%.14).234567890123456789e-5).235e-05| printf("|%. |123456.1.4e|\n".*d|\n". | 14| printf("|% d|\n". | 000014| printf("|%*. |123456.14). |123456.7890| ======================== printf("|%e|\n".235e+05| 30 .789012| printf("|%. repr´sentation la plus courte parmi %f et %e e e d´cimale. |56AB| printf("|%#x|\n". |-14| printf("|%x|\n". 2. | 000014| ======================== printf("|%f|\n". |16| printf("|%#o|\n".0x56ab).6d|\n".2346e+05| ======================== printf("|%. |016| ======================== printf("|%10d|\n".0x56ab).1. |56ab| printf("|%X|\n".20e|\n".1.2346e+05| printf("|%. |1.234567890123456789e5).format %d %ld %u %lu %o %lo %x %lx %f %lf %e %le %g %lg %c %s conversion en int long int unsigned int unsigned long unsigned int unsigned long unsigned int unsigned long double long double double long double double long double unsigned char char* ´criture e d´cimale sign´e e e d´cimale sign´e e e d´cimale non sign´e e e d´cimale non sign´e e e octale non sign´e e octale non sign´e e hexad´cimale non sign´e e e hexad´cimale non sign´e e e d´cimale virgule fixe e d´cimale virgule fixe e d´cimale notation exponentielle e d´cimale notation exponentielle e d´cimale.4f|\n". | 000014| printf("|%.10.78901234567456413060| printf("|%20.1.

Chaˆ de caract`res (jusqu’` la lecture d’un espace) ıne e a entier non sign´ e entier hexadecimal Argument requis char * int * float * o s u x int * char * unsigned int * int * Exemple : #include <stdio. L : long double sp´cifie le type de donn´e lue et comment il faut le lire.h> int main() { 31 . |0. les conversions de format sont sp´cifi´es par un caract`re pr´c´d´ e e e e e e du signe %.4g|\n". La syntaxe est la suivante : e e scanf("cha^ne de contr^le".12e4 entier en notation octale.E. dans le e e e format sp´cifi´. |123456. scanf retourne le nombre de valeurs effectivement lues et m´moris´es (en ommettant les %*). . Les donn´es ` entrer au clavier doivent ˆtre s´par´es par des blancs ou des e a e e e <RETURN> sauf s’il s’agit de caract`res. arg1 .5. les sp´cifications de format ont la forme suivante : e e e %[*][larg][modif]type * larg modif la valeur sera lue mais ne sera pas stock´e e pr´cise le nombre maximal de caract`res ` lire e e a sp´cifie une taille diff´rente pour les donn´es point´es par l’argument : e e e e h : short int l : long int (pour les entier) ou double (pour les nombres flottants).1.g. f. Les formats valides pour la fonction scanf diff`rent l´g`rement de e e e ceux de la fonction printf. . Comme e pour printf. ı o La chaˆ de contrˆle indique le format dans lequel les donn´es lues sont converıne o e ties.1. . argn ). Elle ne contient pas d’autres caract`res (notamment pas de \n).G Description caract`re simple (espace inclu) e entier : nombre optionnellement pr´c´d´ par un signe e e e Floating point : nombre d´cimal pr´c´d´ eventuellee e e e ment d’un signe et suivi ´ventuellement du caract`re e e e ou E et d’un nombre d´cimal.printf("|%. Exemple :-732. Ces donn´es sont stock´es aux adresses sp´cifi´es par les argue e e e e e ments de la fonction scanf (on utilise donc l’op´rateur d’adressage & pour les e variables scalaires). e e type La plupart des types de scanf sont les mˆmes que pour printf : e Type c d e.79| 2. e Pour ˆtre plus pr´cis.8g|\n".001235| printf("|%. .103 e ou 7.234567890123456789e-3).5 La fonction de saisie scanf La fonction scanf permet de r´cup´rer les donn´es saisies au clavier.234567890123456789e5).

int i. e 32 . printf("i = %d\n". } Les autres fonctions d’entr´es/sorties (permettant notamment la manipulation e des fichiers) seront abord´es dans le chapitre 6. printf("entrez un entier sous forme hexadecimale i = ").i).&i). return 0. scanf("%x".

l’ordinateur r´serve un e e espace m´moire (de sizeof(T) octets) pour y stocker les valeurs de var. on pourra se contenter e a ee e 33 MEMOIRE CENTRALE 0x5E04 . e Pour retrouver cette variable. s’il s’agit d’une variable qui recouvre plusieurs u e octets contigus. Un num´ro de tiroir correspond ` une adresse.1.). lorsqu’on d´clare une variable var de type T.Chapitre 3 Les pointeurs Toute variable manipul´e dans un programme est stock´e quelque part en m´e e e moire centrale. Adresse 0x5E00 32 bits p (un pointeur vers une zone mémoire) adresses croissantes 0x5E08 0x5E0C 0x5E10 0x5E14 0x5E18 0x5E1C 0x5E20 Fig. Cette m´moire est constitu´e d’octets qui sont identifi´s de e e e mani`re univoque par un num´ro qu’on appelle adresse comme l’illustre la fie e gure 3. au lieu de passer en param`tre ` une fonction un ´l´ment tr`s grand (en taille)... Les pointeurs pr´sentent de nombreux avantages : e – Ils permettent de manipuler de fa¸on simple des donn´es de taille importante c e (comme les tableaux. Ainsi.1 – Illustration de l’adressage de la m´moire centrale e Par analogie. Un pointeur est une variable de type adresse. 3. il suffit donc de connaˆ ıtre l’adresse du premier octet o` elle est stock´e (ou. l’adresse du premier de ces octets). e e e a Ainsi. on peut voir la m´moire centrale comme une armoire constitu´e e e de tiroirs num´rot´s. les structures etc.

On utilise pour cela le type void *. nous avons besoin : – d’un op´rateur ’adresse de’ & pour obtenir l’adresse d’une variable. o` type est le type de l’objet point´. puisque ce type varie d’une invocation ` l’autre de la fonction. e 3.5) e – Il est possible de cr´er des structures chaˆ ees (on dit aussi structures autoe ın´ r´f´r´es) qui sont utilis´es pour d´finir des listes chain´es. c’est ` dire capable de pointer e e a vers n’importe quel type d’objet. Cette e e distinction est indispensable ` l’interpr´tation (en fait la taille) de la valeur a e point´e. e a Par exemple.2 Op´rateurs de manipulation des pointeurs e Lors du travail avec des pointeurs. il sera possible de stocker des ´l´ments de tailles diverses (comme ee des chaˆ ınes de caract`res : voir §4. De telles listes sont eee e e e beaucoup utilis´es en programmation (le nombre d’´l´ments de cette liste e ee peut ´voluer dynamiquement. le type d’un pointeur d´pend du type de l’objet point´.2. e On d´clare un pointeur par l’instruction : e type *nom-du-pointeur .3. e e 3. Cette notion sera abord´e en d´tail au §4. la fonction malloc de la biblioth`que standard est d´finie de la e e mani`re suivante : void *malloc(size_t size).2. On gagne ´videmment alors en ee e efficacit´ dans l’ex´cution du programme. Sans un tel type. La syntaxe est la e e a suivante : 34 . Exemple : u e int *pi. e – d’un op´rateur ’contenu de’ * pour acc´der au contenu d’une adresse.1 L’op´rateur ’adresse de’ & e L’op´rateur & permet d’acc´der ` l’adresse d’une variable.de lui fournir un pointeur vers cet ´l´ment. e e 3. il ne serait pas possible par exemple d’indiquer le type d’objet rendu par les fonctions d’allocation de m´moire qui rendent un pointeur vers e l’objet allou´.. ce qui permet une utilisation plus souple que e celle des tableaux).. // pc pointeur vers un char A noter aussi l’existence de pointeurs g´n´riques.5. // pi est un pointeur vers un int short int *psi. e e – Comme on le verra au chapitre 4. les tableaux ne permettent de stocker qu’un nombre fix´ d’´l´ments de mˆme type. // psi est un pointeur vers un short int char *pc. Si les composantes du tableau sont des e ee e pointeurs. chaque pointeur est limit´ ` un type de donn´e.1 D´claration d’un pointeur e En C. En effet. mˆme si la e a e e valeur d’un pointeur (une adresse) est toujours un entier (ou ´ventuellement e un entier long).

//´tape (2): variable enti`re initialis´e a 14 e e e p=&i. (3) p = &i. (2) int i = 14. p = &i. 3. //p contient l’adresse de i (0x352C) printf("*p = %d \n". Il ne peut pas e a a ˆtre appliqu´ ` des constantes ou des expressions. Ox1A30 p Ox1A30 p Ox1A30 Ox352C p (déclaration d’un pointeur p vers un entier) Ox352C 14 i Ox352C 14 i (déclaration et initialisation d’une variable entière i) (affectation de l’adresse de i à p) Fig.&nom-variable Cette adresse peut alors ˆtre utilis´e pour initialiser la valeur d’un pointeur. e ea 3. //affiche "*p = 14" } Pour r´sumer. La syntaxe est la suivante : e eee *nom-pointeur Ainsi. apr`s les instructions pr´c´dentes : e e e e – i d´signe le contenu de i (soit 14) e – &i d´signe l’adresse de i (soit 0x352C) e – p d´signe l’adresse de i (soit 0x352C) e – *p d´signe le contenu de i (soit 14) e En outre : 35 .2. on d´finit un pointeur p qui pointe vers un entier i : e int * p. c’est ` dire ` des variables et des tableaux. //´tape (3): p pointe vers i e Les diff´rentes ´tapes de ce sc´nario sont illustr´es dans la figure 3. e e Dans l’exemple suivant.*p).2 L’op´rateur ’contenu de’ : * e L’op´rateur unaire d’indirection * permet d’acc´der directement ` la valeur de e e a l’objet point´ (on dit qu’on d´r´f´rence un pointeur). *p d´signe la valeur de i. si p est un pointeur vers un entier i. e e e e (1) int * p. //´tape (1): pointeur vers un entier non initialis´ e e int i = 14. int *p. Exemple : e int main() { int i = 14.2.2 – Illustration m´moire d’une affectation par l’op´rateur & e e L’op´rateur & peut seulement ˆtre appliqu´ ` des objets qui se trouvent dans e e ea la m´moire interne.

Que va t il se passer ? 36 . *p2. lorsque l’on d´clare un pointeur p sur un objet de type T. e Comme toute variable. e e 3. Et *i/*j ? 3.– &p d´signe l’adresse de p (soit 0x1A30) e – *i est en g´n´ral ill´gal (puisque i n’est pas un pointeur mais il peut arriver e e e que la valeur *i ait un sens) Petites devinettes (Merci Bernard Cassagne [Cas98]) : Soient i et j deux pointeurs vers des entiers. *p2. Exemple : int * p = NULL. reprenons l’exemple ee pr´c´dent dans lequel on remplace l’affectation p2 = p1 par *p2 = *p1 : e e int * p1. affectation de p ` la valeur NULL : on peut dire qu’un pointeur ne pointe a sur rien en lui affectant la valeur NULL (cette valeur est d´finie dans le e fichier stddef. Cette op´ration consistant ` r´server un espace-m´moire e a e e pour stocker l’objet point´ s’appelle une allocation dynamique et sera e abord´e plus en d´tail au §3. affectation directe de *p (la zone m´moire point´e par p). il faut d’abord r´server ` *p un espace-m´moire de taille ad´quate (celui du type e a e e point´ par p. Exemple : int *p1. 1. Pour cela.5.h). Si la variable est un poina teur. on peut faire l’affectation directement. soit 0x352C a p2 = p1.3 Initialisation d’un pointeur Par d´faut. la case m´moire qu’il occupe contient une e certaine valeur qui risque de le faire pointer vers une zone hasardeuse de la m´moire. A quoi correspond l’expression *i**j ? 2. En effet. e e Pour bien montrer l’int´rˆt de l’initialisation de tout pointeur.//d´claration de 2 pointeurs vers des entiers e int i = 14. int i = 14. L’adresse de cet espace-m´moire sera e e la valeur de p. sinon on doit utiliser l’op´e rateur &. //affectation de p2 ` p1: a //p2 contient aussi l’adresse de i 2. un pointeur doit ˆtre initialis´ ! e e Cette initialisation peut s’effectuer de trois fa¸ons : c 1. *p2 = *p1. on ne e e sait pas sur quoi il pointe. affectation ` l’adresse d’une autre variable de p. //affectation ` l’adresse de i de p1 . //supposons que i se trouve ` l’adresse 0x352C a p1 = &i. p1 = &i. soit sizeof(T) octets).

– p1 est bien initialis´ et pointe sur i (*p1=14) e – mais p2 n’a pas ´t´ initialis´ : *p2 d´signe une adresse m´moire a priori inee e e e connue. L’instruction *p2 = *p1 force l’´criture de la valeur *p1=14 dans e la case m´moire point´e par p2 ce qui pourrait avoir des cons´quences d´e e e e sastreuses ! Cette ecriture est en g´n´ral sanctionn´ par le message d’erreur e e e ”Segmentation fault” ` l’ex´cution. a e Il faut quand mˆme noter que certain compilateurs ont la bonne id´e d’initialiser e e automatiquement ` la valeur NULL un pointeur non affect´. Mais cela ne doit a e pas empˆcher de prendre la bonne habitude d’initialiser tout pointeur. e

3.4

Arithm´tique des pointeurs e

La valeur d’un pointeur ´tant un entier, on peut lui appliquer un certain nombre e d’op´rateurs arithm´tiques classiques. Les seules op´rations arithm´tiques vae e e e lides sur les pointeurs sont : – l’addition d’un entier ` un pointeur −→ p + i a Le r´sultat est un pointeur de mˆme type que le pointeur de d´part. e e e – la soustraction d’un entier ` un pointeur −→ p – i a Le r´sultat est un pointeur de mˆme type que le pointeur de d´part. e e e – la diff´rence de deux pointeurs −→ p1 – p2 e Il faut absolument que les deux pointeurs pointent vers des objets de mˆme e type T. Le r´sultat est un entier dont la valeur est ´gale ` (p1 - p2)/sizeof(T). e e a e Attention : la somme de deux pointeurs n’est pas autoris´e ! Ainsi, soit i un entier et p un pointeur sur un objet de type T (donc d´clar´ e e par l’instruction : T *p ;). Alors p+i (respectivement p-i) d´signe un poine teur sur un objet de type T. Sa valeur est ´gale ` celle de p incr´ment´e e a e e (respectivement d´cr´ment´e) de i*sizeof(T). Exemple : e e e
#include <stdio.h> int main() { int i = 2; int *p1, *p2; p1 = &i; p2 = p1 + 1; printf("Adresse p1 = Ox%lx \t Adresse p2 = 0x%lx\n", (unsigned long)p1, (unsigned long)p2); printf("Diff´rence des adresses: %lu \t sizeof(int)=%u\n", e (unsigned long)p2-(unsigned long)p1, sizeof(int)); printf("MAIS p2-p1 = %i !\n",p2-p1); return 0; }

Ce programme renverra : Adresse p1 = Oxbffffb24 Diff´rence des adresses: 4 e MAIS p2-p1 = 1 ! Adresse p2 = 0xbffffb28 sizeof(int)=4

37

On peut faire la mˆme exp´rience avec le type double (au lieu du type int). e e On obtiendrait alors : Adresse p1 = Oxbffffb20 Diff´rence des adresses: 8 e MAIS p2-p1 = 1 ! Adresse p2 = 0xbffffb28 sizeof(double)=8

A noter qu’on peut ´galement utiliser les op´rateurs ++ et -- avec des poine e teurs. En g´n´ral, on les utilise pour r´aliser des parcours de tableaux et plus e e e particuli`rement dans les chaˆ e ınes de caract`res. Exemple (comme on le verra e au §4.2.1, toute chaˆ se termine par un caract`re null, le caract`re ’\0’) : ıne e e
#include <stdio.h> int main() { char * mess = "On est super content!"; char *p; for (p = &mess[0]; *p != ’\0’; p++) { printf("Adresse: %ld | Contenu: %c\n",(long)p,*p); } // Autre m´thode classique, avec while e p = mess; // ´quivalent de p = &mess[0] dans ce cas e puts("========================================"); while (*p != ’\0’) { printf("Adresse: %ld | Contenu: %c\n",(long)p,*p); p++; } return 0; }

Les chaˆ ınes de caract`res seront plus amplement d´taill´es au §4.2.1 page 43. e e e A noter que les op´rateurs de comparaison sont ´galement applicables aux poine e teurs1 .

3.5

Allocation dynamique de m´moire e

Nous avons vu que l’utilisation de pointeurs permet de m´moriser ´conomiquee e ment des donn´es de diff´rentes grandeurs (puisqu’on se contente de m´moriser e e e l’adresse de ces donn´es). Pour permettre une utilisation efficace de la m´moire, e e il est primordial de disposer de moyens pour r´server et lib´rer de la m´moire e e e dynamiquement au fur et ` mesure de l’ex´cution. C’est ce qu’on appelle un a e allocation dynamique de la m´moire. Les fonctions pour la gestion dynamique e de la m´moire sont d´clar´es dans le fichier stdlib.h (` inclure). e e e a L’op´ration consistant ` r´server un espace-m´moire est r´alis´ par la fonction e a e e e e malloc. Sa syntaxe est : malloc(nb octets) Cette fonction retourne un pointeur de type char * pointant vers une zone m´moire de nb octets octets. Pour initialiser des pointeurs vers des objets qui e
1

` condition bien sˆ r de comparer des pointeurs qui pointent vers des objets de mˆme type. a u e

38

ne sont pas de type char, il faut convertir le type de la sortie de la fonction malloc ` l’aide d’un cast (d´j` ´tudi´ au §2.4). a eae e A noter enfin qu’en pratique, on utilise la fonction sizeof() pour d´terminer e e la valeur nb octets. Ainsi, pour initialiser un pointeur vers un entier, on ´crit :
#include <stdlib.h> int main() { int *p; p = (int*)malloc(sizeof(int)); // allocation dynamique *p = 14; }

Il est primordial de bien comprendre qu’avant l’allocation dynamique (et plus g´n´ralement avant toute initialisation de p), *p n’a aucun sens ! e e En particulier, toute manipulation de la variable *p g´n´rerait en g´n´ral une e e e e violation m´moire, d´tectable ` l’ex´cution par le message d’erreur Segmentation e e a e fault. La fonction malloc permet ´galement d’allouer un espace pour plusieurs objets e contigus en m´moire. On peut ´crire par exemple e e
#include <stdlib.h> #include <stdio.h> int main() { int *p; p = (int*)malloc(2 * sizeof(int)); //allocation pour 2 int *p = 14; *(p + 1) = 10; printf("p = Ox%lx \t *p = %i \t p+1 = 0x%lx \t *(p+1)=%i\n", (unsigned long)p, *p, (unsigned long)(p+1), *(p+1)); return 0; }

L’appel ` la fonction malloc a ainsi permis de r´server 8 octets en m´moire a e e (qui permettent de stocker 2 objets de type int) et d’affecter ` p l’adresse de a cette zone m´moire. Le programme affiche : e p = Ox8049718 *p = 14 p+1 = 0x804971c *(p+1)=10. La fonction calloc a le mˆme rˆle que la fonction malloc mais elle permet e o a e de r´server nb-objets objets de nb octets octets et de les initialiser ` z´ro. Sa e syntaxe est : calloc(nb-objets,nb octets) Ainsi, si p est de type int*, l’instruction p = (int*)calloc(N,sizeof(int)); est s´mantiquement ´quivalente ` e e a p = (int*)malloc(N * sizeof(int)); for (i = 0; i < N; i++) *(p + i) = 0; L’emploi de calloc est simplement plus pratique et plus rapide. 39

Cependant. A toute instruction de type malloc ou calloc doit ˆtre associ´e une instruction e e de type free. L’exemple des listes chaˆ ees (d´taill´ au §4.3. alors elle l’est automatiquement e ee a a ` la fin du programme. Ceci se fait ` l’aide de e e a l’instruction free qui a pour syntaxe : free(nom-du-pointeur ).5) est tout ın´ e e a ` fait adapt´ : si la m´moire n’est pas lib´r´e ` chaque suppression d’un e e ee a ´l´ment de la liste. – Si la m´moire n’est pas lib´r´e ` l’aide free. il faut lib´rer ce bloc de m´moire. e ee e – free ne change pas le contenu du pointeur.3. on aboutira rapidement ` une violation de m´moire et au ee a e traditionnel message d’erreur ”Segmentation fault”. 40 .6 Lib´ration dynamique avec la fonction free e Lorsque l’on n’a plus besoin de l’espace-m´moire allou´ dynamiquement par e e malloc ou calloc (c’est-`-dire quand on n’utilise plus le pointeur p initialis´ a e par ces fontions). cela ne doit pas dispenser de l’utilisation de cette fonction. Attention : – La fonction free peut aboutir ` un d´sastre si on essaie de lib´rer de la a e e m´moire qui n’a pas ´t´ allou´e par malloc ou calloc. Cette instruction lib`re le bloc de m´moire d´sign´ par nom-du-pointeur mais e e e e n’a pas d’effet si le pointeur a la valeur NULL.

qui permettent de repr´senter des e e e e ensembles de donn´es organis´es. Les valeurs e e e e possibles constante1 . . . flottants). En r´alit´.. } On peut modifier le codage par d´faut des valeurs de la liste lors de la d´claration e e du type ´num´r´.1 Les ´num´rations e e Les ´num´rations permettent de d´finir un type pour des variables qui ne sont e e e affect´es qu’a un nombre fini de valeurs. a #include <stdio..b1). .constanten } . le type enum boolean associe l’entier 0 ` la valeur a FALSE et l’entier 1 ` la valeur TRUE. constante2 . stock´s en m´moire ee e e e a ` des adresses contigu¨s. //declaration printf("b = %d\n". entiers. //d´finition de l’enum´ration boolean e e int main () { enum boolean b1 = TRUE. TRUE}.constanten sont cod´es par des entiers de e 0 ` n-1. on peut cr´er e e e e de nouveaux types. . TRUE = 23}. e Un objet de type ´num´ration est d´fini par le mot-clef enum et un identificateur e e e de mod`le.2 Les tableaux Un tableau est un ensemble fini d’´l´ments de mˆme type. a Dans l’exemple suivant. .Chapitre 4 Les types d´riv´s e e A partir des types pr´d´finis en C (caract`res. constante2 . les objets de type enum sont repr´sent´s comme des int. e 41 . return 0. appel´s types d´riv´s. . e e 4. par exemple : e ee enum boolean {FALSE = 12. suivi de la liste des valeurs que peut prendre cet objet : e enum modele {constante1 . 4.h> enum boolean {FALSE.

Cette d´claration alloue e donc en m´moire pour l’objet tab un espace de 10*4 octets cons´cutifs.1 Initialisation d’un tableau On peut initialiser un tableau lors de sa d´claration par une liste de constantes e de la fa¸on suivante : c type nom-du-tableau[N] = {constante1 . 3. 81}. . les index des el´ments d’un tableau vont de 0 ` nombre-´l´ments-1 e a ee 3. Notamment. int i. . On acc`de ` un ´l´ment du tableau en lui appliquant l’op´rateur [].tab[i]). un tableau ne peut pas figurer e a ` gauche d’un op´rateur d’affectation : on ne peut pas ´crire ”tab1 = tab2. 18. la taille d’un tableau DOIT ˆtre connue statiquement par le compilateur. Par exemple : #define N 5 int t[N] = {14. ee 4. . constanteN } . Par exemple. for (i = 0. //noter l’absence de ". le programme suivant imprime les ´l´ments du tableau tab : ee #define N 10 int main() { int tab[N].. i < N. ee e on dit encore que ”t est un vecteur de dimension 10”.. e a ee e 2. 27. e e Pour plus de clart´.i. par exemple : ee e #define TAILLE 20 int t[TAILLE]." //t tableau de TAILLE int Voici les points importants ` retenir : a 1.La d´claration d’un tableau a une dimension se fait de la fa¸on suivante : e ` c type nom-du-tableau[nombre-´l´ments]. En faisant le rapprochement avec les math´matiques. . e e Il faut effectuer l’affectation pour chacun des ´l´ments du tableau.. . i++) printf("tab[%d] = %d\n". ce qui implique en particulier qu’aucune op´ration gloe bale n’est autoris´e sur un tableau.2. 42 . la d´claration int tab[10]. e Impossible donc d’´crire int t[n] ou n est une variable.. il est recommand´ de donner un nom ` la constante nombree e a ´l´ments par une directive au pr´processeur (voir chapitre 7). On l’appelle aussi la dimension ee (ou taille) du tableau. indique que tab est un tableau de e 10 ´l´ments de type int. e e o` nombre-´l´ments est une expression constante enti`re positive correspondant u ee e au nombre maximal d’´l´ment dans le tableau.. int main() { int i. Chaque ´l´ment du tableau est une composante de celuiee ci. a ee Ce pointeur est constant. e Ainsi.”. } Un tableau correspond en fait ` un pointeur vers le premier ´l´ment du tableau.constante2 .

3E4}. alors le e compilateur r´serve automatiquement le nombre d’octets n´cessaires. Exemple : e e int A[] = {1. ee a e a e R´servation automatique e Si la dimension n’est pas indiqu´e explicitement lors de l’initialisation. //r´servation de e //4*sizeof(float) octets Cas particulier des tableaux de caract`res e La repr´sentation interne d’une chaˆ de caract`res est termin´e par le symbole e ıne e e nul ’\0’. -1. Dans ce cas. //r´servation de 5*sizeof(int) octets e float B[] = {-1. 3. } La figure 4.t[i]). 4. 3. Ainsi... tandis que les autres seront initalis´es ` z´ro. 2. Adresse 0x5E00 32 bits t adresses croissantes 0x5E04 0x5E08 0x5E0C 0x5E10 0x5E14 0x5E18 0x5E1C 0x5E20 (pointeur vers le premier élément du tableau) 14 27 3 18 81 t[0] t[1] t[2] t[3] t[4] t[i]=*(t+i) Fig. i++) printf("t[%d] = %d\n".3. e e 43 . les premiers ´l´ments seront initialis´s avec les valeurs ee ee e indiqu´es. Exemple : e e a e #define N 10 int t[N] = {14. il faut pr´voir n + 1 octets. alors le tableau t r´servera a e N ∗ 4 = 5 ∗ 4 = 20 octets en m´moire.1 montre ce qu’il se passe en pratique au niveau de la m´moire e lors de l’initialisation du tableau t.3.i.81}. 4. les autres ´l´ments (d’indices 2 ` 9) seront initialis´s ` z´ro.for (i = 0. pour un texte de n caract`res.1 – Expression d’un tableau en m´moire physique e On peut donner moins de valeurs d’initialisations que le tableau ne comporte d’´l´ments.27}. 87e-5. 5}. e int t[5]={14. i < N. Les ´l´ments d’indice 0 et 1 seront initialis´s respectivement avec les valeurs 14 ee e et 27.05.27. . En supposant qu’une variable du type int occupe 4 octets (c’est ` dire : sizeof(int)=4).18.

A la d´claration. ıne. ’b’. Mais dans ce cas. un tableau multidimensionnel est consid´r´ comme un tableau dont les ee ´l´ments sont eux-mˆme des tableaux.2 Tableaux multidimensionnels En C. – enfin. e Cela devient ´videmment tr`s vite lourd. //avec ’\0’ printf("t1 (de taille %i)=%s\n". toute la zone m´moire est affich´e jusqu’` ce que le carat`re e e a e nul soit rencontr´. En faisant le rapprochement avec les math´matiques. le compilateur allouera une zone m´moire permettant de e e stocker de mani`re contig¨ e 10 tableaux de 20 entiers. e – Il est ´galement possible de donner une taille ´gale au nombre de caract`res e e e de la chaˆ Dans le cas.sizeof(t2)/sizeof(char). soit 200 entiers . pour t1. attention aux surprises ! Consid´rer l’exemple suivant : e #include <stdio. //sans ’\0’ char t2[]="luxembourg".2. on peut ne pas indiquer la taille et le compilateur comptera le nombre de caract`res de la chaˆ litt´rale pour dimensionner correctement le tableau e ıne e (sans oublier le caract`re null ’\0’).h> int main() { char t1[10]="luxembourg".– un tableau de caract`res peut ˆtre initialis´ comme une liste de constantes e e e caract`res. e u 44 .t2).sizeof(t1)/sizeof(char). Exemple : e char ch[]="ch aura 22 caract`res". Exemple : char ch[3] = {’a’. e e – On peut aussi initialiser un tableau de caract`re par une chaˆ litt´rale : e ıne e char ch[8]="exemple". La figure suivante montre le contenu de la m´moire e a ` la suite de cette initialisation. le null en fin de chaˆ Exemple : char ville[10]="luxembourg". } Ce programme renverra : t1 (de taille 10)=luxembourgߨP˚ @˘d@ ^ßXA^ßÆ-@ u A a u u t2 (de taille 11)=luxembourg En effet. e 4.t1). ’c’}. Un tableau ` deux dimensions se d´clare ee e a e donc de la mani`re suivante : e int mat[10][20]. printf("t2 (de taille %i)=%s\n". return 0. ch ’e’ Adresses: 1E04 ’x’ 1E05 ’e’ 1E06 ’m’ 1E07 ’p’ 1E08 ’l’ 1E09 ’e’ 1E0A ’\0’ 1E0B – on peut d´clarer une taille sup´rieure ` celle de la chaˆ litt´rale : e e a ıne e char ch[100]="exemple". Les mˆmes consid´rations que celles e e que nous avons d´velopp´ sur les tableaux unidimensionnels s’appliquent : e e 1. on peut dire que mat est e une matrice de 10 lignes et de 20 colonnes. le compilateur comprend qu’il ne faut pas rajouter ıne.

i++) { for (j = 0. 200}}. toute r´f´rence ult´rieure a mat sera convertie en l’adresse de sa premi`re ee e ` e ligne. {0.1). j. } On vient de d´finir la matrice e  1 2 tab =  14 15  100 200  La figure suivante montre le contenu de la m´moire ` la suite de cette initialie a sation. j < C. r´servation de 2*3*2 = 12 octets et e mat = 1 0 1 0 1 0 4. avec le type pointeur vers un tableau de 20 int. for (i = 0 . On acc`de ` un ´l´ment du tableau par l’expression mat[i][j].tab[i][j]). {14.j. 0}}. 0. 2}.i. tab 1 Adresses: 1E04 1E08 2 1E0C 14 1E10 15 1E14 100 1E18 200 1E1C 1E20 On comprend bien avec cette figure que si l’on souhaite utiliser la r´servation e automatique.3 Passage de tableau en param`tre e Puisqu’un identificateur de type tableau correspond ` l’adresse du premier ´l´a ee ment du tableau (voir figure 4.2. 1}. int main() { int i. on utilise a a une liste dont chaque ´l´ment est une liste de constantes : ee #include <stdio. } return 0. i < L. 1. e a ee Pour initialiser un tableau ` plusieurs dimensions ` la compilation. {100. 15}. j++) printf("tab[%d][%d]=%d\n". il faudra quand mˆme sp´cifier toutes les dimensions sauf la pree e mi`re (le nombre de lignes dans le cas d’un tableau bi-dimentionnel. c’est cette adresse qui est pass´e en param`tre e e formel. e short int mat[][3] = {{1.h> #define L 3 //nombre de lignes #define C 2 //nombre de colonnes short tab[L][C] = {{1.2. Consid´rons par exemple une fonction print_tab qui affiche le contenu e d’un tableau d’entiers : 45 .

} #define TAILLE 4 int main() { int t[TAILLE] = {1. for (i=0. ee e dans la proc´dure print_tab.. 3. l’´criture int tab[] est strictement ´quivae e lente ` int * tab (mais on pr´ferera la premi`re ´criture pour des raisons de a e e e lisibilit´). i < nb_elem. } #define TAILLE 4 int main() { int t[TAILLE] = {1. //compteur for (i=0.#include <stdio. //cette variable ne pourra plus etre modifi´e ^ e On voit tout de suite l’int´rˆt de const pour les param`tres de fonction. TAILLE). e e 1 46 . la m´moire n’est pas prot´g´e en ´criture et seule l’utilisation directe de la e e e e variable constante empˆche l’´criture. TAILLE). Il est toujours possible d’´crire ` l’adresse de la variable e e e a par des moyens d´tourn´s. i++) tab[i]++. i < nb_elem.tab[i]). int nb_elem) { int i. } Interdire la modification des ´l´ments d’un tableau pass´ en paraee e m`tre e On utilise pour cela le mot-cl´ const qui sp´cifie que la variable associ´e ne e e e peut pas ˆtre modifi´e1 .h> void print_tab(int tab[].i. Exemple : e e const int i = 14. 4}. On peut ´crire par exemple : e #include <stdio. Ainsi. i++) printf("tab[%i]=%i\n".. } Dans le prototype d’une fonction.h> /* Affiche le contenu du tableau d’entiers tab ayant nb_elem composantes */ void print_tab(int tab[]. return 0. TAILLE). e Modification des ´l´ments d’un tableau pass´ en param`tre ee e e Puisque finalement on passe un pointeur en param`tre. print_tab(t. on peut exprimer le fait que cette proc´dure ne e e doit pas modifier le contenu du tableau pass´ en param`tre. print_tab(t. int nb_elem). En pratique. 4}. 2.} /* incr´mente chaque composantes d tableau */ e void incr_tab(int tab[]. incr_tab(t. int nb_elem) { int i. Le prototype de la e e proc´dure deviendra pour cela : e void print_tab(const int tab[]. int nb_elem) {. on peut modifier au e besoin le contenu du tableau. 2. 3. return 0.

*p). comme dans l’exemple ea ee suivant : #define N 5 int tab[5] = {1. e ee Ces op´rations deviennent possibles d`s que l’on manipule des pointeurs allou´s e e e dynamiquement. p = tab.h> int main() { 2 En fait. 6. int *p. ee Autrement dit. } Comme on acc`de ` l’´l´ment d’indice i du tableau tab grˆce ` l’op´rateur e a ee a a e d’indexation [] par l’expression tab[i].4. pour cr´er un tableau d’entiers ` n ´l´ments o` n est e a ee u une variable du programme. on peut le faire depuis la norme C99 mais on ignorera cette possibilit´ ici.4 Relation entre tableaux et pointeurs Pointeurs et tableaux ` une dimension a On rappelle que tout tableau en C est en fait un pointeur constant. tab a pour valeur &tab[0]. 2. En outre. on a les ´quivalences suivantes : e e tab + 1 ⇐⇒ &(tab[1]) (adresse du 2i`me ´l´ment) e ee *(tab + 1) ⇐⇒ tab[1] (valeur du 2i`me ´l´ment) e ee *tab ⇐⇒ tab[0] (valeur du 1er ´l´ment) ee *(tab + k) ⇐⇒ tab[k] (valeur du (k+1)`me ´l´ment) e ee tab + k ⇐⇒ &(tab[k]) (adresse du (k+1)`me ´l´ment) e ee Pointeurs et tableaux se manipulent donc exactement de mˆme mani`re. 7}. p++. i < N. Ainsi. Attene e tion cependant puisqu’aucun contrˆle n’est effectu´ pour s’assurer que l’´l´ment o e ee du tableau adress´ existe effectivement. la manipulation de tableaux e poss`de certains inconv´nients par rapport ` la manipulation des pointeurs dˆ s e e a u au fait qu’un tableau est un pointeur constant : – On ne peut pas cr´er de tableaux dont la taille est une variable du proe gramme2 . d´finit un tableau de 10 valeurs enti`res dont e e e les indices varient entre les valeurs 0 et 9 et tab est un pointeur constant (non modifiable) dont la valeur est l’adresse du premier ´l´ment du tableau. On peut donc utiliser un pointeur initialis´ ` tab pour parcourir les ´l´ments du tableau.2. – on ne peut pas cr´er de tableaux bidimensionnels dont les lignes n’ont pas e toutes le mˆme nombre d’´l´ments. 0. e 47 . Ainsi. int main() { int i. on ´crit : e #include <stdlib. on peut d´duire la relation entre cet e op´rateur d’indexation et l’op´rateur * : e e tab[i] = *(tab + i) Pour r´sumer. la d´claration int tab[10]. for (i = 0. } return 0. i++) { printf(" %d \n".

Avec cette ´criture. on ee e a remplace l’allocation dynamique avec malloc par : tab = (int*)calloc(n. free(tab). // pointeur vers la matrice tab = (int**)malloc(k * sizeof(int*)). .. exacteee e e ment comme pour les tableaux. int *tab. Les ´l´ments de tab sont manipul´s avec l’op´rateur d’indexation []. i++) tab[i] = (int*)malloc(n * sizeof(int)). . En particulier. par e e exemple). int **tab.. sizeof(int)). L’exemple suivant illustre la d´claration d’un tableau ` deux dimensions (une e a matrice de k lignes et n colonnes ` coefficients entiers) sous forme d’un tableau a de pointeurs : int main() { int k=14. On d´clare un pointeur qui pointe sur un e e objet de type type * (deux dimensions) de la mˆme mani`re qu’un pointeur. soit par une allocation dynamique.. on retiendra que les deux principales diff´rence entre un tableau e et un pointeur sont les suivantes : – un pointeur doit toujours ˆtre initialis´. e e c’est-`-dire par la d´claration : a e type **nom-du-pointeur . Chaque ´l´ment du tableau est alors luiee mˆme un pointeur sur un tableau. un tableau ne pas ˆtre utilis´ direce e e tement dans une expression arithm´tique (on ne peut pas ´crire tab++ . ee a – soit comme un tableau de pointeurs.int n. for (i = 0. a – un tableau est un pointeur constant ne peut donc pas figurer a gauche d’un ` op´rateur d’affectation. On pourra aussi utiliser la fonction memset.. Pour conclure. c’est ` dire &tab[0][0]. i < k. 48 . e e soit par affectation ` une expression de type adresse. par exemple p = &i . Pointeurs et tableaux ` plusieurs dimensions a Un tableau ` deux dimensions peut ˆtre vu de deux fa¸ons : a e c – soit comme un pointeur sur une zone m´moire de taille le produit des deux e dimensions. tab a une valeur constante ´gale ` l’adresse du premier e e a ´l´ment du tableau. De mˆme un pointeur qui pointe sur un objet de type type ** (´quivalent ` e e a un tableau ` 3 dimensions) se d´clare par a e type ***nom-du-pointeur . } Si on veut en plus que tous les ´l´ments du tableau tab soient initialis´s ` 0. n=5. tab = (int*)malloc(n * sizeof(int)). comme c’est le cas dans l’´criture : e int tab[L][C].

return 0. Il est impose sible d’´crire par exemple : e int * tab[] = {{1}. i++) free(tab[i]).jour[i]). free(tab).. 4. e Une boucle d’impression des valeurs du tableau jour pourra s’´crire : e #define NB_JOUR 7 int i. {1. "samedi".. i++) printf("%s\n".3}}. {1.5 page 38) L’un des avantages des pointeurs de pointeurs sur les tableaux multi-dimensionn´s e est que cela permet de choisir par exemple des tailles diff´rentes pour chacune e des lignes tab[i]. "dimanche"}. } La premi`re allocation dynamique r´serve pour l’objet point´ par tab l’espacee e e m´moire correspondant ` k pointeurs sur des entiers. Les allocations dynamiques suivantes r´servent e pour chaque pointeur tab[i] l’espace-m´moire n´cessaire pour stocker n entiers. for (i = 0. i < NB_JOUR. for (i=0 . "mercredi". "vendredi". e e Si on d´sire en plus que tous les ´l´ments du tableau soient initialis´s ` 0.2 – Illustration de la d´claration d’un tableau de chaˆ e ınes de caract`res e A noter que cette ´criture directe n’est possible qu’avec les char. L’allure du tableau jour est illustr´ dans la figure 4. il e ee e a suffit d’utiliser la fonction calloc au lieu de malloc (voir §3. i < k. 49 . Ces k pointeurs correse a pondent aux lignes de la matrice. e jour ’l’ ’m’ ’m’ ’j’ ’v’ ’s’ ’d’ ’u’ ’a’ ’e’ ’e’ ’e’ ’a’ ’i’ ’n’ ’r’ ’r’ ’u’ ’n’ ’m’ ’m’ ’d’ ’d’ ’c’ ’d’ ’d’ ’e’ ’a’ ’i’ ’i’ ’r’ ’i’ ’r’ ’d’ ’n’ ’\0’ ’\0’ ’e’ ’\0’ ’e’ ’i’ ’c’ ’d’ ’\0’ ’h’ ’e’ ’\0’ ’i’ ’\0’ ’d’ ’i’ ’\0’ Fig.2.2. 4.5 Cas des tableaux de chaˆ ınes de caract`res e On peut initialiser un tableau de ce type par : char * jour[] = {"lundi"..2. (on a ici un tableau de pointeurs vers des tableaux d’entiers de taille diff´rentes).2}.. "jeudi". "mardi".

. e 3. . char * argv[]) {. param_n.} Voici un exemple d’utilisation de ces param`tres : e #include <stdio.4.5 par d´faut). e 4. } En compilant ce programme par la commande gcc -Wall -g3 toto. . de type int e (14 par d´faut) . printf("Nom du programme: %s\n".. une chaˆ e ıne de caract`res poss´dant au plus 256 caract`res (”On est super content !” e e e par d´faut). i < argc ./a. Supposons par exemple vouloir compiler un programme prog au format d’appel suivant (les ´l´ments entre crochets sont optionee nels) : prog [-i val_i] [-f val_f] [-s string] [-k key] [-h] file sachant que : 1.6 Gestion des arguments de la ligne de commande Les tableaux de pointeurs vers des chaˆ ınes de caract`res sont une structure de e donn´e tr`s importante puisqu’elle est utilis´e dans la transmission de parae e e m`tres lors de l’ex´cutution d’un programme. param_2.out Param`tre 1: nif e Param`tre 2: naf e Param`tre 3: nouf e La librairie getopt. cr´e un tableau de pointeurs vers ces chaˆ ıne e e ınes et lance la proc´dure main en lui passant deux param`tres : e e – un entier contenant la taille du tableau (appel´ classiquement argc) . e 50 .2. l’interpr´teur collecte tous ces mots sous forme e de chaˆ de caract`res. de type e float (1.out nif naf nouf renvoit : Nom du programme: . la fonction main doit ee ˆtre d´finie de la fa¸on suivante : e e c int main(int argc. l’appel . e 2. for (i=1 .argv[i]). Pour que le programmeur puisse exploiter ces ´l´ments. de type unsigned long (0x90ABCDEF par d´faut). e – le tableau des pointeurs vers les chaˆ ınes (traditionnellement argv). e return 0. argv[0]). char * argv[]) { int i. l’option -k permet de sp´cifier au format hexad´cimal la valeur de la e e variable key. i++) printf("Param`tre %i: %s\n"./a. l’option -s permet de sp´cifier la valeur de la variable string. .i. e e Lorsqu’un utilisateur lance l’ex´cution du programme prog avec les param`tres e e param_1. l’option -i permet de sp´cifier la valeur de la variable val_i.c. . l’option -f permet de sp´cifier la valeur de la variable val_f.h> int main(int argc.h permet ´galement de g´rer plus simplement les arguments e e de la ligne de commande.

}. . Cet exemple est trait´ avec <getopt. type_n membren . not´ ”. 6. Le i-`me membre de objet est d´sign´ par e e e e l’expression : identificateur-objet.. Chaque e e e ´l´ment de la structure. est d´sign´ par un identifiee e e e cateur. file est un param`tre obligatoire du programme qui sp´cifie la valeur de e e la chaˆ de caract`res input_file. 2..ps 51 . type_2 membre2 . l’option -h affiche l’aide (auteur. type_2 membre2 . Contrairement aux tableaux. Ce m´canisme pere met de grouper un certain nombre de variables de types diff´rents au sein d’une e mˆme entit´. e 4.. ıne e Pour g´rer ces options.”. les diff´rents ´l´ments d’une struce e e ee ture n’occupent pas n´cessairement des zones contigu¨s en m´moire. } identificateur-objet . on utilise la syntaxe : struct nom structure identificateur-objet . 3. L’utilisation pratique d’une structure se d´roule de la fa¸on suivante : e c 1.stillhq. Le mod`le g´n´ral de e e e e e cette d´claration est le suivant : e struct nom structure { type_1 membre1 . Pour d´clarer un objet de type structure correspondant au mod`le pr´c´e e e e dent.. e 3 informations : man 3 getopt ou plus simplement le tutorial disponible ` l’adresse : a http://www. appel´ membre ou champ.h> en annexe A. On commence par d´clarer la structure elle-mˆme.com/extracted/howto-getopt/output.3 Les structures e Une structure est une suite finie d’objets de types diff´rents. Ou bien.membrei On peut effectuer sur le i-`me membre de la structure toutes les op´rations e e valides sur des donn´es de type type_i.1 page 106.h>3 qui permet de g´rer facilement les options e de la ligne de commande. type_n membren . on peut soit adapter l’exemple de code pr´c´dent et e e e traiter individuellement les ´l´ments du tableau argv ainsi que les cas d’erreurs. si le mod`le n’a pas encore ´t´ d´clar´ au pr´alable : e ee e e e struct nom structure { type_1 membre1 . but du programme et usage) et sort du programme. On acc`de aux diff´rents membres d’une structure grˆce ` l’op´rateur e e a a e membre de structure.5. ee soit utiliser la librairie <getopt. .

A noter qu’a la diff´rence des tableaux. #include <math. le programme suivant d´finit la structure complexe..Ainsi.3. //partie r´elle e double imaginaire.2 Comparaison de structures Aucune comparaison n’est possible sur les structures (en particulier. Dans le contexte pr´c´dent. // ne pas oublier le ".nom 52 .imaginaire * z.z. On ´crit par exemple : e struct complexe i = {0. //tableau de 100 personnes Pour r´f´rencer le nom de la personne qui a l’index i. compos´e de deux e e champs de type double et calcule la norme d’un nombre complexe. on ne peut appliquer les op´rateurs == ou !=).. . z2.." int main() { struct complexe z. }.imaginaire.1 Initialisation et affectation d’une structure Les r`gles d’initialisation d’une structure lors de sa d´claration sont les mˆmes e e e que pour les tableaux. } 4.reelle + z. //partie imaginaire }. on ´crira : ee e t[i]. . on peut appliquer l’op´rateur d’affece e tation aux structures. z2 = z1. e 4. on peut ´crire : e e e int main() { struct complexe z1. printf("norme de (%f + i %f) = %f \n".3.3 Tableau de structures On d´clare un tableau dont les composantes sont des structures de la mˆme e e fa¸on qu’on d´clarerait un tableau d’´l´ments de type simple..h> struct complexe { double reelle. Exemple : c e ee struct personne { char nom[20]..z.reelle * z. norme = sqrt(z. struct personne t[100].norme). } 4. char prenom[20].}. .imaginaire).3..reelle. //d´claration d’un objet de type struct complexe e double norme. 1. .

Cette repr´sentation permet en particulier de e e construire des listes chaˆn´es. Ainsi. Consid´rons par exemple la structure toto poss´dant 2 champs : e e 1. La liste est alors d´finie comme un pointeur sur e e le premier ´l´ment de la chaˆ ee ıne. il est possible de repr´senter une liste d’´l´ments de mˆme type par un e ee e tableau (ou un pointeur). synonyme du type pointeur e sur une struct toto (voir §4. on utilise la fonction suivante : e 4 et non *p. Le dernier ´l´ment pointe sur le pointeur ee ee NULL (d´fini dans stddef. Toutefois. // pers = variable de type struct personne struct personne * p. Cette repr´sentation est illustr´e dans la figure 4. int main() { struct personne pers.3. on peut d´finir le type liste. on utilise une repr´sentation chaˆ ee : l’´l´ment de base de la chaˆ e e ın´ ee ıne est une structure appel´e cellule qui contient la valeur d’un ´l´ment de la liste e ee et un pointeur sur l’´l´ment suivant.5 Structures auto-r´f´r´es ee e Il s’agit de cas particulier de structures dont un des membres pointe vers une structure du mˆme type.6). – il faudrait ´crire normalement (*p).h). // p = pointeur vers une struct personne p = &pers. un champ data de type int . pour ins´rer un ´l´ment ee a e ee en tˆte de liste.3. // p pointe maintenant vers pers } Pour acc`der aux membres de la structure point´e : e e 4.. un champ next de type pointeur vers une struct toto.nom e – mais on utilise en pratique l’´criture simplifi´e p->nom.4.nom) ce qui n’a de sens que si le champ nom est un pointeur ! 53 . on d´clarera une variable de e e e type pointeur vers cette structure de la mani`re suivante : e struct personne *p. On peut affecter ` ce pointeur des adresses sur des struct personne.4 Pointeur vers une structure En reprenant la structure personne pr´c´dentes. 2. Grˆce au a mot-clef typedef. e e 4..3. La liste est alors un objet de type pointeur sur une struct toto. Pour r´soudre ce proee e bl`me. dite contigu¨. cette repr´sentation. On peut alors d´finir un objet liste l qu’il e convient d’initialiser ` NULL. Exemple : a struct personne {. ı e En effet. impose e e que la taille maximale de la liste soit connue a priori (on a besoin du nombre d’´l´ments du tableau lors de l’allocation dynamique). a e e Un des avantages de la repr´sentation chaˆ ee est qu’il est tr`s facile d’ins´rer e ın´ e e un ´l´ment ` un endroit quelconque de la liste.nom qui sera interpr´t´ (compte tenu des priorit´s entre op´rateurs) par ee e e *(p.}.

L->next = Q.l struct toto data 14 next #include <stddef. liste Q) { liste L. // maintenant Q est forcement non vide ==> champ tmp->next existe // recherche de l’endroit ou ins´rer e while (tmp->next != NULL) tmp = tmp->next. //ajout d’éléments . } De fa¸on g´n´ral.. // allocation de la zone // m´moire nec´ssaire e e L->data = element. mise ` jour des champs des cellules voisines. e e e sizeof(struct toto) et non sizeof(struct toto *)) (b) de convertir le r´sultat du malloc vers le bon type (pointeur vers la e structure). struct toto *next. L = (liste)malloc(sizeof(struct toto)). 4.. typedef struct toto *liste. liste l = NULL. // allocation de la zone // m´moire nec´ssaire e e L->data = element. return L. // d´placement jusqu’au e // dernier ´l´ment de la liste e e 54 . if (Q == NULL) return L. l’ajout d’un nouvel ´l´ment dans une liste chaˆ ee s’effectue c e e ee ın´ en trois ´tapes : e 1. // creation de la nouvelle cellule L = (liste)malloc(sizeof(struct toto)). 3. recherche de l’endroit o` la nouvelle cellule devra ˆtre ins´r´e . liste Q) { liste L. struct toto data 10 next NULL Fig. }.3 – Representation d’une liste chaˆ ee ın´ liste insere(int element. a Supposons par exemple que l’on souhaite ins´rer un ´l´ment en queue de liste : e ee liste insereInTail(int element. tmp=Q. on prendra garde : e (a) de r´server le bon nombre d’octets (en reprenant l’exemple pr´c´dent. e a Au niveau de l’allocation en m´moire.h> struct toto { int data. L->next = NULL. cr´ation de la nouvelle cellule (par malloc) et mise ` jour de ses champs. u e ee 2.

a 3. lib´ration de la cellule avec free. de fa¸on similaire e e c a ` l’ajout. a 2. une seule: tmp) a tmp->next = L. a 4. e Un exemple plus complet de l’utilisation des listes chaˆ ees est founi en anın´ nexe A. free(L). //lib´ration de l’espace allou´ pour une cellule e e } return suivant. } Il est primordial d’utiliser free dans le cas des listes chaˆ ees. int numero. Recherche de la cellule ` supprimer . Une union d´signe un ensemble de variables e e de types diff´rents susceptibles d’occuper alternativement une mˆme zone m´e e e moire. } Pour supprimer le premier ´l´ment de la liste par exemple. e e Dans l’exemple suivant.2 page 110.4. la variable hier de type union jour peut ˆtre soit un e caract`re.h> union jour { char lettre. if (L != NULL) { // pour etre s^r que L->next existe u suivant= L->next. mise ` jour des champs des cellules voisines . soit un entier (mais pas les deux ` la fois) : e a #include <stdio.// mise ` jour des cellules voisines (ici. Une union permet donc de d´finir un objet comme pouvant ˆtre d’un e e type au choix parmi un ensemble fini de types. return Q. Si les membres d’une union sont de longueurs diff´rentes. Sinon. 55 . Ainsi.1 D´claration d’une union e La d´claration et la d´finition d’une union ne diff`rent de celles d’une structure e e e que par l’utilisation du mot-cl´ union (qui remplace le mot-cl´ struct). la place r´serv´e en m´moire pour la repr´senter e e e e e correspond ` la taille du membre le plus grand. la suppression d’une cellule dans une liste chaˆ ee s’effectue en trois ın´ ´tapes : e 1. il est important de ee lib´rer l’espace m´moire allou´ : e e e liste supprime_tete(liste L) { liste suivant = L. 4.4 Les unions Il est parfois n´cessaire de manipuler des variables auxquelles on d´sire affecter e e des valeurs de types diff´rents. la m´ın´ e moire risque d’ˆtre rapidement satur´e (par exemple lors d’ajouts et de supprese e sions successives qui ne lib`rent pas l’espace allou´).

} day. demain.lettre). ex2.4 page 12. Pour ˆtre utilisable.lettre = ’J’. /* Affiche le contenu de la variable d.indicateur = CARAC. demain. hier. On aura compris qu’on acc`de aux ´l´ments d’une union avec le mˆme op´rateur e ee e e de s´lection (. et ’day’ de type union de char et de int */ struct jour { enum etat indicateur. printf("demain = %d\n". else printf("Erreur!\n"). ex2.day.d. ou ->) que celui utilis´ dans les structures. char * name){ if (d. } Si on se r´f`re au tableau 1.demain.indicateur == ENTIER) printf("%s = %i\n".numero + 2) % 7. //indique ce qui est dans j union { char lettre. e e 4. }. ENTIER}.numero). la zone m´moire allou´e pour une variable ee e e de type union jour sera de sizeof(int) (2 ou 4 octets).lettre = ’j’.2 Utilisation pratique des unions Lorsqu’il manipule des unions.h> enum etat {CARAC. 56 .numero = (hier.lettre).numero = 4. //jeudi printf("hier = %c\n". nomm´e name */ e void print_jour(struct jour d.day. int numero. else if (d. une union et son indicateur d’´tat sont g´n´ralement englob´s ` l’int´rieur d’une e e e e a e structure. ex1. En pratique.numero = 4. return 0. } int main() { struct jour ex1.indicateur == CARAC) printf("%s = %c\n".d. /* struct jour a deux membres ’indicateur’ de type int. //d´claration e //utilisation ex1.}.numero). une union doit donc toujours ˆtre associ´e ` une variable e e e a dont le but sera d’indiquer le membre de l’union qui est valide.hier. le programmeur n’a malheureusement aucun moyen de savoir ` un instant donn´ quel est le membre de l’union qui poss`de a e e une valeur.day. Exemple : #include <stdio.name.name. hier. "ex1"). //jeudi print_jour(ex1. int main() { union jour hier.day.indicateur = ENTIER.4. ex2.

privilege : 6. On utilise typiquement les champs de bits en programmation syst`me.. A noter e que l’ordre dans lequel les champs sont plac´s ` l’int´rieur de ce mot d´pend e a e e de l’impl´mentation. //initialisation de ex1 ` jeudi a 4..print_jour(ex2. return 0. } L’ex´cution de ce programme renverra : e ex1 = j ex2 = 4 4.lettre #define N day. privilege sur 6 bits etc.” qui suit sa d´claration : e e type [membre] : nb_bits . on aura noter que l’acc`s aux membres de l’union est relativement e e e lourd (ex : d. On voit que le C accepte que l’on ne donne pas de nom e 57 .. On peut all´ger e e cette ´criture en utilisant les facilit´s du pr´processeur (voir le chapitre 7) : e e e #define L day. pour e manipuler des registres particuliers de la machine etc.numero pour acc´der au membre numero).day.4.L = ’j’. : 6. Cela se fait en e pr´cisant le nombre de bits du champ avant le ”.4 – Exemple d’affectation de bits dans un registre Ce registre peut se d´crire de la mani`re suivante : e e struct registre { unsigned int signed int unsigned int unsigned int }. 4. Par exemple.numero .3 Une m´thode pour all´ger l’acc`s aux membres e e e Quand une union est dans une structure (comme c’est la cas dans l’exemple pr´c´dent).5 Les champs de bits Il est possible en C de sp´cifier la longueur des champs d’une structure au bit e pr`s si ce champ est de type entier (int ou unsigned int). /* inutilis´ */ e ov : 1. ex1. imaginons le registre suivant : ov Position: 15 14 13 (inutilisé) 12 11 10 9 8 7 privilege 6 5 4 3 2 masque 1 0 Fig. masque : 3.. "ex2"). Le champ masque sera cod´ sur 3 bits...indicateur = CARAC. ex1.

l’identificateur UCHAR peut ˆtre utilis´ comme une abe e br´viation pour le type unsigned char.aux champs de bits qui ne sont pas utilis´s. y } POINT. POINT point. Aussi. 58 . c2. Le champ ov de la structure ne e peut prendre que les valeurs 0 ou 1. Exemple : typedef unsigned char UCHAR. si r est un objet de type struct registre. typedef POINT *P_POINT. P_POINT pPoint. on peut affecter un nouvel identificateur e e a ` un type ` l’aide du mot-cl´ typedef : a e typedef type synonyme .ov += 2 . typedef struct { double x. – un champ de bits n’a pas d’adresse . l’identificateur POINT peut ˆtre utilis´ e e e pour sp´cifier le type de structure associ´ et P_POINT permet de caract´riser un e e e pointeur vers un POINT : UCHAR c1. on ne peut donc pas lui appliquer l’op´e rateur &. tab[100]. l’op´ration r. e Voici les quelques contraintes ` respecter : a – La taille d’un champ de bits doit ˆtre inf´rieure au nombre de bits d’un entier e e (long). 4. ne modifie pas la valeur du champ.6 D´finition de synonymes de types avec typedef e Pour all´ger l’´criture des programmes. //pointeur vers un POINT A partir de ce moment.

Dans ce cas. le format g´n´ral d’une d´finition de fonction est de e e e la forme : type_resultat nom fonction (type1 arg1 .4. par exemple avec celle pr´d´eae e e e finies dans des biblioth`ques standards (comme printf de <stdio. – type1 arg1 .) ou encore la fonction d’entr´e main. typen argn ) { <d´claration de variables locales > e <liste d’instructions > } Dans cette d´finition de fonction.1 D´claration et d´finition d’une fonction e e Comme on l’a vu au §1. . .2)..h>. e – nom fonction est le nom qui identifie la fonction.Chapitre 5 Retour sur les fonctions La structuration de programmes en sous-programmes se fait en C ` l’aide de a fonctions. ..5.3. L’utilisation de fonctions a d´j` ´t´ vu. . . e 5. Ces arguments sont appel´s param`tres formels. malloc e de <stdlib. e On peut se contenter de d´clarer le prototype d’une fonction (sans le corps) : e type_resultat nom fonction (type1 arg1 . on peut (doit ?) en C d´couper un programme en plusieurs fonctions. la d´finition de la fonction (avec le corps des instructions qui la e 59 . on rappelle que : e – type_resultat correspond au type du r´sultat de la fonction. Il existe une unique fonction e obligatoire : la fonction principale appel´e main. . . Lorsque les listes e chaˆ ees ont ´t´ abord´es au §4. typen argn ) . par opposition e e aux param`tres effectifs qui sont les param`tres avec lesquels la fonction est e e effectivement appel´e (voir §5. ee On l’aura compris : comme dans la plupart des langages. . .h> etc. on a ´galement d´fini des fonctions d’inserın´ ee e e e tion et de suppression d’´l´ments. . typen argn d´finit les types et les noms des param`tres de e e la fonction.

il est donc suivi d’un pointvirgule ! Quelques remarques compl´mentaires : e 1. Si une fonction ne fournit pas de r´sultat. le prototype n’est donc pas suivi a e du corps de la fonction (contenant les instructions ` ex´cuter). – un pointeur (voir chapitre 3).} 3. Il est interdit de d´finir des fonctions ` l’int´rieur d’une autre fonction e a e (comme en Pascal). e – une structure (voir §4. des chaˆ e ınes de caract`res ou des fonctions. int n) { 1 mais cette d´finition doit respecter la d´claration du prototype : n’h´siter pas ` utiliser le e e e a copier/coller du prototype ! 60 . int b) { return(a*b). e Ex : int main(){. Si une fonction n’a pas de param`tres.compose) peut ˆtre d´clar´e plus loin dans le programme1 . La valeur de expression correspond ` la valeur que retourne la fonction et doit a donc ˆtre de type type_resultat. – void (voir ci-apr`s). Le retour ıtre au programme appelant sera alors provoqu´ par le premier return rencontr´ e e lors de l’ex´cution (voir la fonction puissance de l’exemple qui suit). e e e Dans le cas o` type_resultat est diff´rent du type void.. e e e Contrairement ` la d´finition de la fonction. } /*** Renvoit la valeur de a^n (version na¨ve) ***/ ı int puissance (int a. l’ordre des d´finitions de fonctions dans le texte du proe gramme ne joue pas de rˆle. a e ATTENTION ! Le prototype est une instruction. on peut d´clarer la liste des parae e m`tres comme (void) ou simplement comme ().. dite instruction de retour a la fonction appelante. Une fonction peut fournir comme r´sultat : e – un type arithm´tique. 5. En principe.} // equivalent a ’int main(void){. le corps de la foncu e tion doit obligatoirement comporter une instruction return. – une union (voir §4. mais il est cependant possible de renvoyer e un pointeur sur le premier ´l´ment d’un tableau ou d’une chaˆ de caee ıne ract`res. e Ex : void print_liste(liste L){. e 2. e Une fonction ne peut pas fournir comme r´sultat des tableaux.. Ex : e /***Renvoit le produit de 2 entiers ***/ int produit (int a. il faut indiquer void comme e type du r´sultat. e Plusieurs instructions return peuvent apparaˆ dans une fonction.. La syntaxe de cette instruction est : ` return expression ..4).3).. mais chaque fonction doit ˆtre d´clar´e ou o e e e d´finie avant d’ˆtre appel´e.}’ 4.

Les param`tres effectifs e e e e 2. l’ordre d’´valuation des param`tres effectifs n’est pas assur´ et d´pend e e e e du compilateur. Par d´faut. Les variables temporaires se voient allouer un emplacement en m´moire de e fa¸on dynamique lors de l’ex´cution du programme. est rarement utilis´ puisqu’il ne s’applique e qu’aux variables temporaires qui sont automatiques par d´faut. la variable est dite automatique. . . de faire figurer des op´rateurs d’incr´mentation ou de d´cr´mentation e e e e e (++ ou --) dans les expressions d´finissant les param`tres effectifs. . La partie de la m´moire contenant les variables permanentes e est appel´e segment de donn´es. . pour une fonction ` plusieurs parae e a m`tres. a e 5. e Par d´faut. . les variables permanentes e e e sont initialis´es ` z´ro par le compilateur. les variables temporaires sont situ´es dans la partie de la m´moire e e e appel´e segment de pile. les identificateurs des arguments de la fonction (arg1 ..if (n == 0) return 1. . //noter l’absence du ’else’ } Enfin.argn ) n’ont e d’importance qu’` l’int´rieur de celle-ci. . e e 5. elles n’ont pas toutes la mˆme dur´e de vie. En particulier. Dans ce cas. 2. Cet emplacement est allou´ une fois pour toutes lors de la e compilation. n-1). il faut noter que les variables ´ventuellement d´clar´es au sein d’une fonce e e tion seront locales ` celle-ci et n’auront de sens que dans le corps de la fonction. return a*puissance(a. e 61 . Elles ne sont pas inic e tialis´es par d´faut.3 Dur´e de vie des identificateurs e Les variables manipul´es dans un programme C ne sont pas toutes trait´es de e e la mˆme mani`re. e 1.2 Appel d’une fonction L’appel d’une fonction se fait par l’expression : nom fonction ( arg1 . Elles sont caract´ris´es par le e a e e e mot-clef static. Il est donc d´conseill´. e 2 La virgule qui s´pare deux param`tres effectifs est un simple signe de ponctuation . Les variables permanentes (ou statiques) Une variable permanente occupe un emplacement en m´moire qui reste le mˆme durant toute l’ex´cution e e e du programme. e e e e On distingue deux cat´gories de variables. a De mˆme.argn ) L’ordre et le type des param`tres effectifs de la fonction doivent concorder avec e ceux donn´s dans la d´finition de l’en-tˆte de la fonction. Leur emplacement en m´moire est lib´r´ par exemple e e e ee a ` la fin de l’ex´cution d’une fonction secondaire. il ne e e s’agit pas de l’op´rateur virgule. Le sp´cie e ficateur de type correspondant. peuvent ˆtre des expressions e De plus. auto.

sa port´e se limite ` l’int´rieur du bloc dans lequel elle est d´clar´e.Une variable temporaire peut ´galement ˆtre plac´e dans un registre de la mae e e chine. c’est-`-dire ` la portion du e e a e a a programme dans laquelle elles sont d´finies. e a e e e On parle de variable locale. 62 .h> int STATUS. On peut demander au compilateur de rane ger une variable tr`s utilis´e dans un registre.4 Port´e des variables e Selon l’endroit o` on d´clare une variable. Un registre est une zone m´moire sur laquelle sont effectu´es les op´rae e e tions machine.. e e au d´but du fichier. if (STATUS>0) STATUS--. Il est donc beaucoup plus rapide d’acc´der a un registre qu’` e ` a toute autre partie de la m´moire. La dur´e de vie des variables est li´e ` leur port´e.. celle-ci pourra ˆtre accessible (visible) u e e partout dans le code ou seulement dans une partie de celui-ci (` l’int´rieur d’une a e fonction typiquement).4).1. cette requˆte ne sera satisfaite que s’il e e e reste des registres disponibles. on parle de la port´e (ou visibilit´) de la variable. Grˆce aux performances des opee a timiseurs de code int´gr´s au compilateur (cf. ` l’aide de l’attribut de type e e a register. elle est accessible de partout dans le code (n’importe quelle fonction du programme peut faire appel ` cette a variable). Cette technique permettant d’acc´l´rer les proee grammes a aujourd’hui perdu tout son int´rˆt.4. On parle alors de variable globale. Lorsque l’on d´clare une variable ` l’int´rieur d’un bloc d’instructions (entre des e a e accolades). En g´n´ral. e e Lorsqu’une variable est d´clar´e dans le code mˆme.. e e il est maintenant plus efficace de compiler un programme avec une option d’optimisation que de placer certaines variables dans des registres.. mais locales ` main ! a Exemple : La variable STATUS est d´clar´e globalement pour pouvoir ˆtre utilis´e dans les e e e e proc´dures A et B. 5. void A(. c’est-`-dire ` l’ext´rieur e e e a a e de toute fonction ou de tout bloc d’instruction. e e e e Elles sont syst´matiquement permanentes. e #include <stdio. e Remarque : Les variables d´clar´es au d´but de la fonction principale main ne e e e sont pas des variables globales. Le nombre de registres ´tant limit´. e 5. options -O de gcc : voir §1.){ . les variables globales sont d´clae e e r´es imm´diatement derri`re les instructions #include au d´but du programme.1 Variables globales Un variable globale est donc une variable d´clar´e en dehors de toute fonction. ` l’ext´rieur de toutes les fonctions et sont disponibles ` e a e a toutes les fonctions du programme.

printf("Bonjour %s !\n".) { . STATUS++. } Conseils : – Les variables globales sont ` utiliser avec pr´caution. 5. //d´claration d’une variable globale e int fonction(int A) { double X. //variable locale qui cache la variable X globale ... nom). } void B(.. (voir section suivante) e – Je vous conseille donc d’´crire nos programmes aussi ’localement’ e que possible. . – Il faut faire attention ` ne pas cacher involontairement des variables globales a par des variables locales du mˆme nom.. scanf("%s". Exemple : e int X. e a Exemple : la variable nom est d´finie localement dans le bloc ext´rieur de la e e fonction hello. puisqu’elles cr´ent des a e e liens invisibles entre les fonctions. On dit que ce sont des variables locales ` ce bloc.. e e 3 depuis la norme C99 en fait 63 . il est d´conseill´ de le faire et toutes les e e d´clarations locales devront se faire au d´but des fonctions. printf("Introduisez votre nom : "). La modularit´ d’un programme peut en e souffrir et le programmeur risque de perdre la vue d’ensemble.. . } Attention ! Une variable d´clar´e a l’int´rieur d’un bloc cache toutes les vae e ` e riables du mˆme nom des blocs qui l’entourent.2 Variables locales Les variables d´clar´es dans un bloc d’instructions sont uniquement visibles ` e e a l’int´rieur de ce bloc.else .4.. } Remarque : bien qu’il soit possible en C3 de d´clarer des variables ` l’int´rieur e a e d’une boucle ou d’un bloc conditionnel. Ainsi.. aucune autre fonction n’a acc`s ` la variable nom : e a void hello() { char nom[20]...&nom)...

1 G´n´ralit´s e e e En ce qui concerne le passage de param`tres ` une proc´dure. e a Exemple : Le prototype de la fonction pow (biblioth`que <math.b. e Tous les d´tails de la r´cursivit´ ont ´t´ fournis au §2. prodmat a besoin des valeurs des matrices a et b.5 R´cursivit´ e e Les d´finitions par r´currence sont assez courantes en math´matiques.. double y). on peut consid´rer la fameuse suite de Fibonacci : e   u0 = 0  u =1  1  un = un−1 + un−2 ∀ n ≥ 2 Le fonctions d´finies en C ont la propri´t´ de r´cursivit´.6. Une telle fa¸on de passer ee c un param`tre s’appelle du passage par adresse. 64 .3 page 24. A = pow (B.c) o` l’on veut qu’en fin d’ex´cution. Les param`tres e e sont automatiquement convertis dans les types de la d´claration avant d’ˆtre e e pass´s ` la fonction. c e – soit il d´sire passer une r´f´rence ` une variable. et d’une r´f´rence vers la matrice c. la matrice c soit ´gale u e e au produit matriciel des matrices a et b. e e e ee 5. 5. Au cours des instructions. C’est ce dont on a e besoin quand on ´crit une proc´dure r´alisant le produit de deux matrices e e e prodmat(a. Par e e e exemple. c’est ` dire la capacit´ e ee e e a e de s’appeler elle-mˆme. Une e e telle fa¸on de passer un param`tre s’appelle du passage par valeur . e Conversion automatique Lors d’un appel.5. 2). de mani`re ` permettre e ee a e a a ` la proc´dure de modifier la valeur de cette variable.. int A. Elles e ıtes acceptent les donn´es de l’ext´rieur et d´terminent les actions et le r´sultat de e e e e la fonction. B. .6 Passage de param`tres ` une fonction e a Les param`tres ou arguments sont les ’boˆ aux lettres’ d’une fonction.h>) est d´clar´ e e e comme suit : double pow(double x. le programmeur e a e a deux besoins fondamentaux : – soit il d´sire passer une valeur qui sera exploit´e par l’algorithme de la proe e c´dure (c’est ce dont on a besoin quand on ´crit par exemple sin(x)). le nombre et l’ordre des param`tres doivent n´cessairement e e correspondre aux indications de la d´claration de la fonction.

ETOILES(compteur). e e A l’int´rieur de la fonction.2 Passage des param`tres par valeur e En C. } L’appel de ce programme renvoit : ************** Valeur de compteur: 14 la valeur de compteur n’a donc pas ´t´ modifi´ par l’appel de la fonction ee e ETOILES. On peut donc changer les valeurs des param`tres e e sans influencer les valeurs originales dans les fonctions appelantes. e e a 5. printf("Valeur de compteur: %i\n".6. N--.On assiste ` trois conversions automatiques : avant d’ˆtre transmis ` la fonction. } printf("\n"). la valeur 2 est convertie en 2.6. 65 . le passage des param`tres se fait toujours par valeur. Exemple : La fonction ETOILES dessine une ligne de N ´toiles.compteur). return 0. le r´sultat de la fonction doit ˆtre converti en int avant e e d’ˆtre affect´e ` A. Comme pow est du type double. autrement dit les e fonctions n’obtiennent que les valeurs de leurs param`tres et n’ont pas d’acc`s e e aux variables elles-mˆmes. } int main() { int compteur=14. et cette r`gle e e e ne souffre aucune exception. e Les param`tres d’une fonction sont ` consid´rer comme des variables locales e a e qui sont initialis´es automatiquement par les valeurs indiqu´es lors d’un appel. Le param`tre N est modifi´ e e e a ` l’int´rieur de la fonction mais pas ` l’ext´rieur : e a e #include <stdio.h> void ETOILES(int N) { while (N>0) { printf("*"). Cela pose le probl`me de r´aliser un passage de e e param`tre par adresse lorsque le programmeur en a besoin. 5. tout param`tre est pass´ par valeur. e Pour changer la valeur d’une variable de la fonction appelante. a e a la valeur de B est convertie en double .0 .3 Passage des param`tres par adresse e Comme on vient de le voir. on proc`de e comme suit : – la fonction appelante doit fournir l’adresse de la variable .

tab[i] = return. <type> <nom>[] ou simplement par un pointeur sur le type des ´l´ments du tableau : ee <type> *<nom> On pr´f`rera la premi`re ´criture car il n’est pas possible dans la seconde de ee e e savoir si le programmeur a voulu passer en param`tre un pointeur vers <type> e (c’est ` dire un pointeur vers un seul <type>). e e e On peut alors atteindre la variable ` l’aide du pointeur. e e Le param`tre c ne peut ´videmment pas ˆtre pass´ par valeur. } int main() { int i=10. int *c) { /* c rep`re l’entier o` on veut mettre le r´sultat e u e */ *c = a + b.h> /* Initialise void init_tab int i. i++) i. ee En g´n´ral.k. on fournit l’adresse du premier ´l´ment du tableau.6. on peut d´clarer un tableau par le e e nom suivi de crochets. a Exemple : Supposons qu’on d´sire ´crire une proc´dure add. } int main() { 4 les ´l´ments d’un tableau */ e e (int tab[]. int b.4 Passage de tableau en param`tre e Comme il est impossible de passer ’la valeur’ de tout un tableau ` une fonction. puisqu’on d´sire e e e e e modifier la valeur du param`tre effectif correspondant. qui est donn´e e e ee e par le nom du tableau. /* on passe les valeurs de i et j comme premiers param`tres */ e /* on passe l’adresse de k comme troisi`me param`tre e e */ add(i. b et c. On d´sire que le r´sultat de l’ex´cution de add soit d’affecter e e e e au param`tre c la somme des valeurs des deux premiers param`tres. int n){ i < n. a on fournit l’adresse d’un ´l´ment du tableau4 . ou au contraire si il a voulu a passer un tableau. admettant trois pae e e ram`tres a. } 5. a Exemple : #include <stdlib.j. Rappelons qu’un tableau est un pointeur constant (sur le premier ´l´ment du tableau) ee 66 .&k). c’est ` dire un pointeur vers une zone de n <type>.– la fonction appel´e doit d´clarer le param`tre comme pointeur. Il faut donc programmer e add de la mani`re suivante : e void add(int a. Dans la liste des param`tres d’une fonction.j=14. for (i = 0.

et a pour but de rep´rer le param`tre effectif e e courant. int *tab. La macro va_end admet un seul param`tre : la variable ap.” e e e e (3 points ` la suite).. et dans ce cas la taille peut apparaˆ dans le type e ıtre du param`tre effectif : e #define NB_ELEM 10 void init_tab (int tab[NB_ELEM]){{ . } Remarque : Quand une fonction admet un param`tre de type tableau. soit les diff´rents tableaux qui lui sont pass´s en param`tre effectif ont e e e tous la mˆme taille. n = 5. et dans ce cas la taille doit ˆtre un param`tre suppl´e e e e mentaire de la fonction. e e Dans le corps de la fonction on ne dispose pas de nom pour d´signer les parae m`tres. ` passer en param`tre aux autres macros. comme dans l’exemple pr´c´dent .h> : e e e a va list permet de d´clarer une variable opaque au programmeur. il y ` e a deux cas possibles : 1.” apparaissant en derni`re position dans la liste e e de d´claration des param`tres formels. soit les diff´rents tableaux qui lui sont pass´s en param`tre effectif ont des e e e tailles diff´rentes. e e 2.. Cette variable s’appelle traditionnellement e ap (pour argument pointer). e e e e va end doit ˆtre appel´e apr`s toutes les utilisations de va_arg. e Rien n’est pr´vu pour communiquer ` la fonction le nombre et le type des pae a ram`tres effectivement pass´s : c’est un probl`me ` la charge du programmeur.. e e va start doit ˆtre appel´e avant toute utilisation de va_arg.7 Fonction ` nombre variable de param`tres a e Il est possible de d´clarer une fonction comme ayant un nombre variable de e param`tres en ”d´clarant” les param`tres optionnels par l’unit´ lexicale ”.int i. tab = (int*)malloc(n * sizeof(int)).n).. } 5.. puis chaque nouvel appel ` va_arg d´livre le parae a e m`tre suivant. e e e a 67 . La macro va_arg admet deux param`tres : la variable ap e e et le type du param`tre courant. init(tab. L’acc`s ` ceux-ci ne peut se faire qu’en utilisant les macros suivantes e e a d´finies dans la biblioth`que standard <stdarg. les param`tres obligatoires apparaissant en e e premier et l’unit´ lexicale ”. La macro va_start a deux param`tres : la variable ap et le nom du dernier param`tre oblie e gatoire de la fonction. e e a e va arg d´livre le param`tre effectif courant : le premier appel ` va_arg d´livre le premier param`tre.. Une fonction peut avoir ` la fois des param`tres obligaa a e toires et des param`tres optionnels.

unsigned long)). va_start(args. break. /** * Fonction de debuggage */ int trace (int level. case ’s’: printf("%s". on associera ` l’affichage d’infora mations de debuggage pr´cises un niveau level ´lev´ tandis que les affichages e e e de base auront un param`tre level faible. char *)).) { va_list args.//on va a la ligne! return 0. long)). va_arg(args. L’affichage ne s’effectue que si le niveau sp´cifi´ ` l’appel (par le parametre level) est inf´rieur ` une e ea e a variable globale DEBUG_LEVEL.. chaine).//initialisation de la structure va_list a partir //d’un parametre d’appel de la fonction if (level <= DEBUG_LEVEL) { while (chaine[length]!=0) { //traitement des argument %. break. printf("\n"). } } Une utilisation classique d’une telle fonction. } length++. Ensuite.. va_arg(args. } } else { //on a une chaine classique donc on l’affiche printf("%c". va_arg(args. break.. case ’X’: printf("%X". unsigned long)). long)). case ’i’: printf("%i". break.. c’est l’utilisateur qui en e 68 . va_arg(args. . } //on termine correctement l’utilisation d’une structure va_list va_end(args). if (chaine[length] == ’%’) { length++. chaine[length]). case ’x’: printf("%x".Exemple Voici la d´finition d’une fonction de d´buggage sur le mod`le de e e e printf admettant un nombre variable d’arguments.. va_arg(args.//les arguments du println int length = 0. #define DEBUG_LEVEL . char * chaine. break..//on passe au caractere suivant switch (chaine[length]) { case ’d’: printf("%d".

on aura tous les d´tails possibles. consid´rons une fonction operateur_binaire prenant pour param`tres e e deux entiers a et b..faisant varier la valeur de DEBUG_LEVEL modifie la quantit´ et la pr´cision des e e informations affich´es ` l’ex´cution.i-1. e 5.. trace(1. Le prototype de cette fonction sera : int operateur_binaire(int a. "Decalage de tab[%i] en position %i".i).type_n). " Debut decalage des elements"). " Insertion de la valeur %i en position %i". tab[i] = tab[i-1]. trace(2. val=%i". " Fin decalage des elements"). on aura en plus l’affichage des ´tapes dans l’algorithme e utilis´ pour la fonction .val. e – si DEBUG_LEVEL = 2. trace(1. e – si DEBUG_LEVEL > 2.val). } trace(2. Par exemple... Cela permet de savoir rapidement en cas de bug quelle fonce tion pose probl`me . int n. "Debut de insertion .type_n arg_n). Cette proc´dure permet en particulier d’utiliser une mˆme fonction pour e e diff´rents usages. on utilisera comme troisi`me e param`tre effectif l’identificateur de la fonction utilis´e. e e Un pointeur sur une fonction correspond ` l’adresse du d´but du code de la a e fonction. trace(2.. Pour cela. i--. } A l’appel de cette fonction. Un pointeur p_fonction sur une fonction ayant pour prototype type fonction(type_1 arg_1. int)) Pour appeler la fonction operateur_binaire. on aura le comportement suivant : – si DEBUG_LEVEL = O : pas d’affichage . on utilise un m´canisme de pointeur. int val) { int i=n.. – si DEBUG_LEVEL = 1 : l’entr´e et la sortie de la fonction sera visible par un e message affich´.8 Pointeurs sur une fonction Il est parfois utile de passer une fonction comme param`tre d’une autre fonce tion. while( i!=0 && val < tab[i-1]) { trace(3.i) tab[i] = val. On suppose que operateur_binaire renvoit l’entier e f(a. Ainsi. int b. si somme e e est la fonction 69 . ainsi qu’une fonction f de type int qui prend elle-mˆme e deux entiers en param`tres. "Fin de insertion")..n..b). se d´clare par : e type (*p_fonction)(type_1.param n=%i. int (*f)(int. Exemple : e a e /** * Insertion d’une valeur val dans un tableau tab de taille n * contenant n-1 premiers elements tries */ void insertion(int tab[].

Ces deux fonctions sont d´finies dans la librairie standard e <stdlib.h>. e e – une valeur strictement n´gative (resp. e e e e La fonction qsort est param´tr´e par la fonction de comparaison compar de prototype : e e int compar(void *a. int b. const void *)). on ´crit (*f)(a. void *b) . e e a e 5 70 . Les deux param`tres a et b de la fonction compar sont des pointeurs g´n´riques de type void *. } on appelle la fonction operateur_binaire pour la fonction somme par l’expression : operateur_binaire(a.b)). int)) { return((*f)(a. Ainsi. e e Pour appeler la fonction pass´e en param`tre dans le corps de la fonction e e operateur_binaire. Le param`tre ee e taille_elements donne la taille des ´l´ments du tableau. Cette gymnastique sur les pointeurs de fonction n’est pas forc´ment triviale e mais reste utile dans certains cas. positive) si l’objet point´ par a est strictement e e inf´rieur (resp.b. cette fonction pourrait s’´crire : e e int operateur_binaire(int a.h>. e e e e e e Elle permet de trier les nb_elements premiers ´l´ments du tableau tab. int b) { return a+b. int(*compar)(const void *. size_t taille_elements. Le prototype de la fonction de tri5 (algorithme quicksort) est ainsi : void qsort(void *tab. Le type size_t utilis´ ici est un ee e type pr´d´fini dans <stddef. e e e Ils correspondent ` des adresses d’objets dont le type n’est pas d´termin´. size_t nb_elements. Cette fonction de a e e comparaison retourne – un entier qui vaut 0 si les deux objets point´s par a et b sont ´gaux . int (*f)(int.int somme(int a.somme) A noter que la notation &somme n’est pas utilis´e comme param`tre effectif. en particulier dans la d´finition de fonction e ”g´n´rique” comme la fonction qsort ´voqu´e pr´c´demment. } Les pointeurs sur les fonctions sont notamment utilis´s dans la fonction de tri e des ´l´ments d’un tableau qsort et dans la recherche d’un ´l´ment dans un ee ee tableau bsearch. Il correspond au type du r´sultat de l’´valuation de sizeof. sup´rieur) ` celui point´ par b. b).

e Avant de lire ou d’´crire dans un fichier. e Pour des raisons d’efficacit´. a Cette fonction.. les acc`s ` un fichier se font par l’interm´diaire e e a e d’une m´moire-tampon (on parle de buffer).... la position de la tˆte de lecture. de type FILE * ouvre un fichier et lui associe un flot de donn´es.. un programme a besoin d’un certain nombre d’informations : l’adresse de l’endroit de la m´moire-tampon o` se trouve le e u fichier. Apr`s les e e e e traitements.1 6. on annule la liaison entre le fichier et le flot de donn´es grˆce ` la e a a fonction fclose. Cette fonction prend comme argument le nom du fichier. il est n´cessaire avant tout acc`s d’ouvrir e e a e e le fichier ` l’aide de la fonction fopen.)."mode") La valeur retourn´e par fopen est e – soit un flot de donn´es . e – soit NULL si l’ex´cution de cette fonction ne se d´roule pas normalement. le C offre la possibilit´ de lire et d’´crire des e e donn´es dans un fichier.h>. e Sa syntaxe est : fopen("nom-de-fichier". dont le type. Ces informations sont rassembl´es dans une structure.1.Chapitre 6 Gestion des fichiers Comme beaucoup de langages. 6.. qui sera ensuite utilis´ lors de l’´criture ou de la lecture. e e Je vous conseille donc de toujours tester si la valeur renvoy´e par la fonction e fopen est ´gale ` NULL afin de d´tecter les erreurs d’ouverture de fichier (lecture e a e d’un fichier inexistant. on notifie son acc`s par la commande e e fopen. Un objet de type FILE * est appel´ flot de e e donn´es (stream en anglais). ce qui permet de r´duire le nombre e e d’acc`s aux p´riph´riques (disque. 71 . d´fini avec e le syst`me d’exploitation le mode d’ouverture du fichier et initialise un flot de e donn´es.1 Ouverture et fermeture de fichiers Ouverture de fichiers : la fonction fopen Lorsqu’on d´sire acc´der ` un fichier. e e FILE *. e e e Pour pouvoir manipuler un fichier.). le mode d’acc`s au fichier (lecture ou e e ´criture). est d´fini dans <stdio.

le clavier) . ee Les diff´rents modes d’acc`s sont list´s dans le tableau 6. son e e ee ea ancien contenu est conserv´.h e e qui correspondent aux trois fichiers : – stdin (standard input) : flot d’entr´e (par d´faut. e – Si un fichier est ouvert en mode ”´criture ` la fin”. si le fichier n’existe pas. sinon c’est une erreur. le fichier peut ne pas exister. Les modes de communication standard Quand un programme est lanc´ par le syst`me. Comme pour le cas pr´c´dent. leurs ´critures ne s’´craseront pas e a e e mutuellement. et si le fichier existait d´j`.Le premier argument de fopen fournit donc le nom du fichier.1. Dans ce cas. e e e ee On distingue – les fichiers textes. est une chaˆ de caract`res qui sp´cifie le mode d’acc`s au fichier. standard output et standard error. il sera cr´´. 6. mode. e e 72 . r´sultat de e e l’ouverture d’un fichier en ´criture ` la fin. Le second argument. – Si le mode contient la lettre w. le fichier doit exister.) seront interpr´t´s en tant que tels lors de la lecture et de l’´criture . e e e Cela signifie que si plusieurs processus partagent le mˆme FILE*. celui-ci ouvre trois fichiers core e respondant aux trois modes de communication standard : standard input. pour lesquels les caract`res de contrˆle (retour ` la ligne e o a .. si le fichier existe d´j`. toutes les ´critures se font ` e a e a l’endroit qui est ´tait la fin du fichier lors de l’ex´cution de l’ordre d’´criture.. ıne e e e Les sp´cificateurs de mode d’acc`s diff`rent suivant le type de fichier consid´r´. le fichier peut ne pas exister. pour lesquels les caract`res de contrˆle se sont pas intere o pr´t´s.1 – Les modes d’acc`s aux fichiers dans la fonction fopen e Conditions particuli`res et cas d’erreur e – Si le mode contient la lettre r. il est cr´´ . Il y a trois constantes pr´d´finies dans stdio. ee ea – Si le mode contient la lettre a. ee e – les fichiers binaires. e e e "r" "w" "a" "rb" "wb" "ab" "r+" "w+" "a+" "r+b" "w+b" "a+b" ouverture ouverture ouverture ouverture ouverture ouverture ouverture ouverture ouverture ouverture ouverture ouverture d’un d’un d’un d’un d’un d’un d’un d’un d’un d’un d’un d’un fichier fichier fichier fichier fichier fichier fichier fichier fichier fichier fichier fichier texte en lecture texte en ´criture e texte en ´criture ` la fin e a binaire en lecture binaire en ´criture e binaire en ´criture ` la fin e a texte en lecture/´criture e texte en lecture/´criture e texte en lecture/´criture ` la fin e a binaire en lecture/´criture e binaire en lecture/´criture e binaire en lecture/´criture ` la fin e a Tab. son ancien contenu est perdu.

4 page 29). .2. . e exit(1). argn )."cha^ne de contr^le".5 page 31). permet de lire des a donn´es dans un fichier..5. ı o o` flot est le flot de donn´es retourn´ par fopen. ee ea Sa syntaxe est : fclose(flot) o` flot est le flot de type FILE* retourn´ par la fonction fopen correspondante.1. 6."r")) == NULL) { fprintf(stderr."cha^ne de contr^le". ."Impossible d’ouvrir le fichier donn´es en lecture\n"). e l’´cran). arg1 .h> FILE *fp.2 La fonction de saisie en fichier fscanf La fonction fscanf.5. e e – stderr (standard error) : flot d’affichage des messages d’erreur (par d´faut.txt".) ⇐⇒ fprintf(stdout. . u e La fonction fclose retourne 0 si l’op´ration s’est d´roul´e normalement et EOF e e e si il y a eu une erreur. Sa syntaxe est semblable ` celle de scanf : e a fscanf(flot..2. l’´cran) .. analogue ` scanf (voir §2.2 Fermeture de fichiers : la fonction fclose Elle permet de fermer le flot qui a ´t´ associ´ ` un fichier par la fonction fopen. expression1 .1 Les entr´es-sorties format´es e e La fonction d’´criture en fichier fprintf e La fonction fprintf. } 6. ı o o` flot est le flot de donn´es retourn´ par la fonction fopen... . Les sp´cifications de format u e e e sont ici les mˆmes que celles de la fonction scanf. permet d’´crire a e des donn´es dans un flot. Sa syntaxe est : e fprintf(flot.) 6. . Les sp´cifications u e e e de format utilis´es pour la fonction fprintf sont les mˆmes que pour printf e e puisque : printf(. e Utilisation typique de fopen #include <stdio..2 6.– stdout (standard output) : flot de sortie (par d´faut. if ((fp = fopen("donnees. expressionn ). e 73 . analogue ` printf (voir §2.. . . . e Il est fortement conseill´ d’afficher syst´matiquement les messages d’erreur sur e e stderr afin que ces messages apparaissent ` l’´cran mˆme lorsque la sortie a e e standard est redirig´e.

int c. return(EXIT_FAILURE). int putc(int caractere.1 et §2. return(EXIT_FAILURE). "\nErreur: Impossible de lire %s\n".2). u e La fonction fputc ´crit un caract`re dans le flot de donn´es : e e e int fputc(int caractere.5. 6. le programme suivant lit le contenu du fichier texte entree.1 Impression et lecture de caract`res dans un fie chier Lecture et ´criture par caract`re : fgetc et fputc e e Similaires aux fonctions getchar et putchar (voir §2.h> #define ENTREE "entree. // Ouverture du fichier ENTREE en lecture if ((f_in = fopen(ENTREE.ENTREE).5. les fonctions fgetc et fputc permettent respectivement de lire et d’´crire un caract`re dans e e un fichier. e e Leur syntaxe est similaire ` celle de fgetc et fputc : a int getc(FILE* flot).txt" #define SORTIE "sortie.txt : e e #include <stdio.txt" int main(void) { FILE *f_in.6."w")) == NULL) { fprintf(stderr. } // Ouverture du fichier SORTIE en ecriture if ((f_out = fopen(SORTIE. FILE *flot) Elle retourne l’entier correspondant au caract`re lu (ou la constante EOF en cas e d’erreur). o` flot est le flot de type FILE* retourn´ par la fonction fopen.3."r")) == NULL) { fprintf(stderr. Son prototype est : e int fgetc(FILE* flot). "\nErreur: Impossible d’ecrire dans %s\n". } 74 . La fonction fgetc retourne le caract`re lu dans le fichier et la constante EOF e lorsqu’elle d´tecte la fin du fichier. FILE *flot) Ainsi.h> #include <stdlib.3.2 Lecture et ´criture optimis´es par caract`re : getc et putc e e e Il existe ´galement deux versions optimis´es des fonctions fgetc et fputc qui e e sont impl´ment´es par des macros.SORTIE). et le recopie caract`re par caract`re dans le fichier sortie. Il s’agit respectivement de getc et putc. *f_out.3 6.txt.

970. Cette fonction place le caract`re carac (converti en unsigned char) dans le e flot f.txt" int main(void) { FILE *f_in.// Recopie du contenu de ENTREE dans SORTIE while ((c = fgetc(f_in)) != EOF) fputc(c. si carac est ´gal au dernier caract`re lu dans le flot.ENTREE). putchar(c). // Fermeture des flots de donnees fclose(f_in). elle e e annule le d´placement provoqu´ par la lecture pr´c´dente. return(EXIT_FAILURE).19). ce programme affiche ` a l’´cran 0. int c.f_in). L’exemple suivant e e e permet d’illustrer le comportement de ungetc : #include <stdio. ungetc e e e e peut ˆtre utilis´e avec n’importe quel caract`re (sauf EOF).h> #define ENTREE "entree. } while ((c = fgetc(f_in)) != EOF) { if (c == ’0’) ungetc(’."r")) == NULL) { fprintf(stderr. d´finies dans la librairie <stdlib. e 75 .h> et qui sont les param`tres prie e vil´gi´s de la fonction exit (voir §9. } On notera l’utilisation des constantes EXIT_SUCCESS (valant 0) et EXIT_FAILURE (valant 1). e e 6. } fclose(f_in). if ((f_in = fopen(ENTREE. FILE *f). f_out). Toutefois. "\nErreur: Impossible de lire le fichier %s\n".3.’. return(EXIT_SUCCESS).txt dont le contenu est 097023.3 Relecture d’un caract`re e Il est possible de replacer un caract`re dans un flot au moyen de la fonction e ungetc : int ungetc(int carac.23. fclose(f_out). } Sur le fichier entree. return(EXIT_SUCCESS).h> #include <stdlib. En particulier.

"w")) == NULL) { fprintf(stderr. Leurs prototypes sont : e size_t fread(void *pointeur. Elles sont notamment utiles pour manipuler des donn´es de grande taille ou e ayant un type compos´. FILE *f). o` pointeur est l’adresse du d´but des donn´es ` transf´rer. elles sont plus portables car elles ´crivent ce qu’on leur dit. } fread(tab2. size_t taille.txt. e ee Par exemple. size_t taille.txt" int main(void) { FILE *f_in. f_out).h> #define NB 50 #define F_SORTIE "sortie. tab2 = (int*)malloc(NB * sizeof(int)). taille la taille des u e e a e objets ` transf´rer et nbre leur nombre. NB * sizeof(int). size_t fwrite(void *pointeur. int *tab1. i < NB. /* ecriture du tableau dans F_SORTIE */ if ((f_out = fopen(F_SORTIE. return(EXIT_FAILURE).3. f_in). puis lit ce fichier avec fread et imprime les ´l´ments du tableau. for (i = 0 . fclose(f_out). /* lecture dans F_SORTIE */ if ((f_in = fopen(F_SORTIE. La fonction fread lit les donn´es sur le flot f et la fonction fwrite les ´crit.6. "r")) == NULL) { fprintf(stderr. Elles sont ainsi plus efficaces que les fonctions d’entr´ee e sortie standard. e e Ces deux fonctions retournent le nombre de donn´es transf´r´es. correspond au type du r´sultat de l’´valuation de sizeof e e (voir §2.F_SORTIE).h>. } fwrite(tab1.1. int i. 1. 76 . FILE *f). d´fini a e e dans <stddef.4).F_SORTIE).h> #include <stdlib. *f_out. ee #include <stdio. // allocation memoire des tableaux tab1 = (int*)malloc(NB * sizeof(int)).4 Les entr´es-sorties binaires : fread et fwrite e Les fonctions d’entr´es-sorties binaires permettent de transf´rer des donn´es e e e dans un fichier sans transcodage.4. i++) tab1[i] = i. size_t nbre. En ce sens.7 et §2. return(EXIT_FAILURE). 1. NB * sizeof(int). "\nImpossible de lire dans %s\n". size_t nbre. "\nImpossible d’ecrire dans %s\n". *tab2. Rappelons que le type size_t. le programme suivant ´crit un tableau d’entiers (contenant les 50 e premiers entiers) avec fwrite dans le fichier sortie.

h> #include <stdlib. e e a 0. printf("\n"). SEEK_END (´gale ` 2) : fin du fichier. e 6. c’est-`-dire que e e a a l’on peut se positionner ` n’importe quel endroit du fichier. int *tab.5 Positionnement dans un fichier : fseek. SEEK_SET) . compt´ en nombre d’octets. rewind et ftell Les diff´rentes fonctions d’entr´es-sorties permettent d’acc´der ` un fichier en e e e a mode s´quentiel : les donn´es du fichier sont lues ou ´crites les unes ` la suite e e e a des autres. La variable deplacement d´termine la nouvelle position dans le fichier. return(EXIT_SUCCESS). permet de se positionner au d´but du fichier. } Les ´l´ments du tableau sont bien affich´s ` l’´cran.fclose(f_in). // Initialisation du tableau 77 .3. for (i = 0 . retourne la position courante dans le fichier (en nombre d’octets depuis l’origine). L’exemple suivant illustre l’utilisation des fonctions pr´c´dentes : e e #include <stdio. long deplacement. int i. e a e 2. Elle est ´quivalente ` fseek(flot. SEEK_SET (´gale ` 0) : d´but du fichier . La fonction long ftell(FILE *flot). SEEK_CUR (´gale ` 1) : position courante .tab2[i]). e a 3.h> #define NB 50 #define F_SORTIE "sortie. Il s’agit e d’un d´placement relatif par rapport ` origine. int origine). on constate que ee e a e le contenu du fichier sortie n’est pas encod´. La fonction fseek a permet de se positionner ` un endroit pr´cis et a pour prototype : a e int fseek(FILE *flot. *f_out. i < NB.txt" int main(void) { FILE *f_in. i++) printf("%d\t". e a La fonction int rewind(FILE *flot). e a e La variable origine peut prendre trois valeurs : 1. Il est ´galement possible d’acc´der ` un fichier en mode direct. Par contre.

} /* on se positionne a la fin du fichier */ fseek(f_in. f_in). /* lecture dans F_SORTIE */ if ((f_in = fopen(F_SORTIE. printf("\t i = %d\n". ftell(f_in)). printf("\n position %ld". /* deplacement de 5 int en avant */ fseek(f_in. } L’ex´cution de ce programme affiche ` l’´cran : e a e position position position position 200 160 0 24 i = 40 i = 0 i = 6 On constate en particulier que l’emploi de la fonction fread provoque un d´plae cement correspondant ` la taille de l’objet lu ` partir de la position courante. i++) tab[i] = i. /* deplacement de 10 int en arriere */ fseek(f_in. sizeof(i). "w")) == NULL){ fprintf(stderr. SEEK_END). fread(&i. printf("\t i = %d". "r")) == NULL) { fprintf(stderr. i). fread(&i. i).tab = (int*)malloc(NB * sizeof(int)). sizeof(i).F_SORTIE).F_SORTIE). a a 78 . "\nImpossible de lire dans %s\n". 1. printf("\n position %ld". printf("\n position %ld". fread(&i. NB * sizeof(int). fclose(f_out). ftell(f_in)). 1. ftell(f_in)). "\nImpossible d’ecrire dans %s\n". return(EXIT_FAILURE). printf("\n position %ld". SEEK_CUR). /* ecriture du tableau dans F_SORTIE */ if ((f_out = fopen(F_SORTIE. f_in). return(EXIT_FAILURE). SEEK_END). /* retour au debut du fichier */ rewind(f_in). 1. 1. printf("\t i = %d". 5 * sizeof(int). sizeof(i). return(EXIT_SUCCESS). fclose(f_in). for (i = 0 . f_in). -10 * sizeof(int). i). } fwrite(tab. i < NB. ftell(f_in)). f_out). 0.

)...) ou n’importe quel autre fichier.4 page 7. – la d´finition de constantes symboliques et de macros (#define).. e e 79 . 7. On peut sp´cifier d’autres r´pertoires ` l’aide de l’option -I du compie e a lateur (voir chapitre 8). Il effectue des modifications textuelles sur le fichier source ` para tir de directives.h.. La premi`re syntaxe est g´n´ralement utilis´e pour les fichiers en-tˆte de la e e e e e librairie standard. Il s’agit ici eaee de d´tailler l’ensemble des directives du pr´processeur. La directive #include poss`de deux e syntaxes voisines : #include <nom-de-fichier> recherche le fichier mentionn´ dans un ou plusieurs r´pertoires syst`mes d´finis e e e e par l’impl´mentation (typiquement /usr/include/) : e #include "nom-de-fichier" recherche le fichier dans le r´pertoire courant (celui o` se trouve le fichier e u source). Ce dernier peut ˆtre un fichier en-tˆte de la librairie standard (stdio. L’utilisation de ces directives a d´j` ´t´ introduite au §1.2 La directive #define La directive #define permet de d´finir des constantes symboliques (on parle e aussi de macros sans param`tres) ou des macros avec param`tre. e – la compilation conditionnelle (#if.h.. ont pour but : e – l’incorporation de fichiers source (#include).1 La directive #include Elle permet d’incorporer dans le fichier source le texte figurant dans un autre fichier. #ifdef. tandis que la seconde est plutˆt destin´e aux fichiers cr´´s o e ee par l’utilisateur.Chapitre 7 Les directives du pr´processeur e Le pr´processeur est un programme ex´cut´ lors de la premi`re phase de la e e e e compilation. introduites par le e e caract`re #. e e 7. Les diff´rentes directives au pr´processeur.. e e math.

Ex : #define NB_LIGNES 24 #define NB_COLONNES 80 #define TAILLE_MATRICE NB_LIGNES * NB_COLONNES D´finition de constantes symboliques ` l’invocation du compilateur e a Certains compilateurs permettent de d´finir des constantes symboliques ` l’ine a vocation du compilateur. un nom bien choisi permet d’expliciter la s´mantique de la constante. Les avantages ` toujours donner un nom aux constantes sont les a suivants : 1. Ex : e #define NB_COLONNES 100. un exemple pour gcc : la compilation du fichier prog.1 D´finition de constantes symboliques e Bien que cela a d´j` ´t´ vu pr´c´demment. rappelons que lorsque le pr´proceseaee e e e seur lit une ligne du type : #define nom reste-de-la-ligne il remplace dans toute la suite du source toute nouvelle occurrence de nom par reste-de-la-ligne. 3. ce qui facilite la modie a fication du programme quand on veut changer la valeur de la constante (cas de la taille d’un tableau. 7.c en d´finise sant la constante symbolique NB_LIGNES de valeur 14 : gcc -c -DNB_LIGNES=24 prog. 2. Il est alors possible d’´crire un programme utilisant e une macro qui n’est nulle part d´finie dans le source.2. on peut expliciter facilement les relations entre constantes. e Ceci est tr`s pratique pour que certaines constantes critiques d’un programme e aient une valeur qui soit attribu´e ` l’ext´rieur du programme (comme lors e a e d’une une phase de configuration par exemple).2.2 Les macros avec param`tres e Une macro avec param`tres se d´finit de la mani`re suivante : e e e #define nom(liste-de-param`tres) corps-de-la-macro e 80 .1. Il n’y a aucune contrainte quand ` ce qui peut se trouver a dans reste-de-la-ligne : on pourra ainsi ´crire e #define BEGIN { #define END } L’utilit´ principale des macros sans param`tre est de donner un nom parlant ` e e a une constante. r´capitul´es e e e e e dans le tableau 7.c Constantes symboliques pr´d´finies e e Il y a un certain nombre de macros pr´d´finies par le pr´processeur. la constante chiffr´e se trouve ` un seul endroit. par exemple). Ci-dessous.7.

1 – Les constantes symboliques pr´d´finies e e Type entier chaˆ ıne chaˆ ıne chaˆ ıne entier o` liste-de-param`tres est une liste d’identificateurs s´par´s par des virgules. CARRE(x++) aura pour expansion (x++) * (x++).Nom __LINE__ __FILE__ __DATE__ __TIME__ __STDC__ Valeur de la macro num´ro de la ligne courante du programme source e nom du fichier source en cours de compilation la date de la compilation l’heure de la compilation 1 si le compilateur est ISO. le choix ´tant e ee e e bas´ sur un test ex´cut´ ` la compilation. 0 sinon Tab. 7. c’est une macro avec e e param`tres. L’op´rateur d’incr´mentation sera donc appliqu´ deux fois au lieu d’une. ou d’introduire dans le e a e programme des instructions de d´buggage.b) (a > b ? a : b) le processeur remplacera dans la suite du code toutes les occurences du type MAX(x. avec la directive #define MAX(a. L’erreur classique e ´tant d’´crire par erreur : e e #define CARRE (a) a * a la chaˆ de caract`res CARRE(2) sera alors remplac´e par : (a) a * a (2) ıne e e Il faut toujours garder ` l’esprit que le pr´processeur n’effectue que des rema e placements de chaˆ ınes de caract`res. Par exemple.y) o` x et y sont des symboles quelconques par (x > y ? x : y) u Une macro a donc une syntaxe similaire ` celle d’une fonction. Par exemple. il faut mieux s’en passer. !CARRE(x) sera remplac´ par ! x * x et non e e par !(x * x). sinon c’est une constante symbolique. il est conseill´ de toujours e e mettre entre parenth`ses le corps de la macro et les param`tres formels qui y e e sont utilis´s. De mˆme. u e e e Par exemple. Enfin. e e e En conclusion. e e e La distinction entre une d´finition de constante symbolique et celle d’une macro e avec param`tres se fait sur le caract`re qui suit imm´diatement le nom de e e e la macro : si ce caract`re est une parenth`se ouvrante. En particulier. mais son emploi a permet en g´n´ral d’obtenir de meilleures performances en temps d’ex´cution. Il ne faut donc jamais mettre e d’espace entre le nom de la macro et la parenth`se ouvrante. il faut ˆtre attentif aux ´ventuels effets de bord que peut entraˆ e e ıner l’usage de macros. si l’on ´crit sans parenth`ses : e e e #define CARRE(a) a * a le pr´processeur remplacera CARRE(a + b) par a + b * a + b et non par e (a + b) * (a + b).3 La compilation conditionnelle La compilation conditionnelle a pour but d’incorporer ou d’exclure des parties du code source dans le texte qui sera g´n´r´ par le pr´processeur. les macros avec param`tres sont ` utiliser avec pr´caution et en e a e cas de doutes. 7. e 81 . Elle permet d’adapter le programme e e ea au mat´riel ou ` l’environnement sur lequel il s’ex´cute.

une seule partie-du-programme-i sera compil´e : celle e qui correspond ` la premi`re condition-i non nulle. Chaque condition-i doit ˆtre une expression constante. ou bien la partie-du-programme-else a e si toutes les conditions sont nulles. alors e u partie-du-programme-1 sera compil´e et partie-du-programme-2 sera ignoe r´e.2 Condition li´e ` l’existence d’un symbole e a Sa syntaxe est la suivante : #ifdef symbole partie-du-programme-1 #else condition-2 partie-du-programme-2 #endif Si symbole est d´fini au moment o` l’on rencontre la directive #ifdef.3. e Lors de la compilation...1 Condition li´e ` la valeur d’une expression e a Sa syntaxe la plus g´n´rale est : e e #if condition-1 partie-du-programme-1 #elif condition-2 partie-du-programme-2 . #elif PROCESSEUR == PC taille_long = 32. La e e directive #else est comme pr´c´demment facultative.3. Ce type de directive est e e utile pour rajouter des instructions destin´es au d´buggage du programme : e e 82 . on peut ´crire : e #define PROCESSEUR ALPHA . c’est partie-du-programme-2 qui sera compil´e. #elif condition-n partie-du-programme-n #else partie-du-programme-else #endif Le nombre de #elif est quelconque et le #else est facultatif.. Dans le cas contraire.Les directives de compilation conditionnelle se r´partissent en deux cat´gories. #if PROCESSEUR == ALPHA taille_long = 64. #endif 7.. e e suivant le type de la condition invoqu´e qui est test´e : e e – la valeur d’une expression – l’existence ou l’inexistence de symboles 7. Par exemple.

h> #if INT_MAX < 1000000 #error "Entiers trop petits sur cette machine" #endif 1 A noter aussi que comme on l’a vu pr´cedemment. L’int´rˆt e e ee de cet op´rateur est de permettre d’´crire des tests portant sur la d´finition de e e e plusieurs macros. on peut tester la non-existence d’un symbole ` l’aide de la c a directive #ifndef : #ifndef symbole partie-du-programme-1 #else condition-2 partie-du-programme-2 #endif On rencontre beaucoup cette directive dans le cadre de la compilation modulaire (voir chapitre 8) puisqu’elle permet de s’assurer que les d´finitions d’instructions e dans un fichier d’en-tˆte ne seront effectu´es qu’une seule fois.#define DEBUG . e 83 . qui permet de d´finir ce symbole..3. i++) printf("%d\n". e e 7. i < N. Voici un exemple o` on teste que la taille des entiers est suffisante : u #include <limits..3.3 L’op´rateur defined e L’op´rateur defined est un op´rateur sp´cial : il ne peut ˆtre utilis´ que dans e e e e e le contexte d’une commande #if ou #elif. #ifdef DEBUG for (i = 0. on peut remplacer cette derni`re e e directive par l’option de compilation -DDEBUG. alors que #ifdef ne peut en tester qu’une : #if defined(SOLARIS) || defined(SYSV) 7.4 La commande #error La commande #error a la syntaxe suivante : #error chaine La rencontre de cette commande provoquera l’´mission d’un message d’erreur e comprenant la chaine. Cette commande a pour utilit´ de capturer ` la compie a lation des conditions qui font que le programme ne peut pas s’ex´cuter sur cette e plate-forme. et la valeur 0 sinon.. Il peut ˆtre utilis´ sous l’une des e e deux formes suivantes : – defined nom – defined ( nom ) Il d´livre la valeur 1 si nom est une macro d´finie. #endif /* DEBUG */ Il suffit alors de supprimer la directive #define DEBUG pour que les instructions li´es au debuggage ne soient pas compil´es1 .i). e e De fa¸on similaire.

Sa syntaxe e e est la suivante : #pragma commande 84 .3. une directive de pr´traitement.5 La commande #pragma La directive #pragma est li´e ` une impl´mentation sp´cifique et permet de d´e a e e e finir. pour un compilateur donn´.7.

c contiendra la d´finition de la fonction main). que l’on compile s´paremment. e e e 1. Ces prine cipes s’appliquent en fait dans le cas g´n´ral du g´nie logiciel. file_management. 8. Sauf cas tr`s particuliers.c fournira une e e biblioth`que de fonctions g´rant les fichiers dans le contexte du projet effectu´ e e e et main.14 * rayon. tab_management. il est n´cessaire de fractionner le e e e programme en plusieurs fichiers sources. e a a L’id´e est alors de regrouper dans un mˆme fichier les instructions impl´mentant e e e des fonctionnalit´s similaires (par exemple.1 Principes ´l´mentaires ee Trois principes essentiels doivent guider l’´criture d’un programme C. e Ces r`gles d’´criture ont pour objectifs de rendre un programme lisible. r´utilisable mais surtout facile ` maintenir et ` modifier. La factorisation du code Le but est d’´viter les duplications de code. Des instructions comme : fopen("mon_fichier". il est indispensable de se fixer un certain nombre de r`gles d’´criture.Chapitre 8 La programmation modulaire D`s que l’on ´crit un programme de taille importante ou destin´ ` ˆtre utilis´ e e eae e et maintenu par d’autres personnes. En particulier. La programmation e modulaire consiste alors ` op´rer les manipulations n´cessaires permettant de a e e lier ces fichiers. perimetre = 2 * 3. "r"). sont ` proscrire (il faudrait d´finir des constantes fournissant le nom du a e fichier ou la valeur de π). les constantes doivent e ˆtre d´finies comme des constantes symboliques au moyen de la directive e e #define. L’abstraction des constantes litt´rales e L’utilisation explicite de constantes litt´rales dans le corps d’une fonce tion rend les modifications et la maintenance difficiles. La pr´sence d’une mˆme pore e e tion de code ` plusieurs endroits du programme est un obstacle ` d’´vena a e 85 . pore e table.c contiendra une e biblioth`que de fonctions g´rant les tableaux. 2.

c : e /*********************************************/ /* fichier: main.c */ /* saisit 2 entiers et affiche leur produit */ /*********************************************/ #include <stdlib. } /********************************************************/ 1 Il s’agit ´videmment d’un exemple tr`s simpliste ! e e 86 .h que l’on inclut dans le fichier contenant le programme principal ` l’aide de la directive #include. (b) un fichier source ayant l’extension . il a ´t´ choisi de ee d´finir un module arithm´tique qui fournit la fonction effectuant le proe e duit1 . cette r`gle permet de r´utiliser facilement e e une partie du code pour d’autres applications. Il ne faut pas craindre de e e d´finir une multitude de fonctions de petite taille. La fragmentation du code Pour des raisons de lisibilit´. return EXIT_SUCCESS.c). c. chaque module impl´mentant e e des fonctions sur un th`me similaire et qui se traduiront physiquement e par deux fichiers : (a) un fichier en-tˆte (on dit aussi de header) ayant l’extension .h> #include "arithmetique. // appel de la fonction d´finie dans arithmetique. e e Une possibilit´ est de placer une partie du code dans un fichier en-tˆte e e (on dit aussi de header) ayant l’extension . Ce fichier e e e inclut ´videmment le fichier en-tˆte par le biais de la directive #include. En plus. b. Le programme est donc compos´ de trois fichiers : e e les 2 fichiers du module et le fichier principale contenant la fonction main appel´ ici main.h e e e et arithm´tique.c.&b).b). a L’exemple suivant illustre ce principe sur un programme qui saisit deux entiers au clavier et affiche leur produit. scanf("%d".tuelles modifications. il est pertinent de d´couper un programme e e en plusieurs fichiers. Ce module est donc impl´ment´ dans les fichiers arithm´tique. Les fonctions doivent donc ˆtre syst´matiquement e e utilis´es pour ´viter la duplication de code.h" // Le module d’arithmetique int main(void) { int a.h e printf("\nle produit vaut %d\n". Sur cet exemple.h> #include <stdio.c contenant non seulement le corps des fonctions d´clar´es dans le fichier en-tˆte mais ´galement e e e e celui des fonctions interm´diaires ´ventuellement utilis´es. scanf("%d".&a). On s´pare alors le programme en modules. c = produit(a.h et e contenant le prototype des fonctions principales impl´ment´es dans e e ce module. e 3.

2). #endif Je vous recommande de toujours appliquer cette derni`re r`gle d`s e e e lors que vous cr´ez un fichier de header ! e 87 . tr`s importante.3.o:toto. consiste ` ´viter les erreurs de double e e e a e d´finition de fonctions qui se traduise ` l’´dition de lien par le message suivant e a e (avec gcc) : toto. /*****************************************************/ /* fichier: arithmetique.c */ /* Corps des fonctions d´finies dans arithmetique. on utilise la direcee e ea tive #ifndef (voir 7.’ toto. le fichier arithmetique.2 Eviter les erreurs d’inclusions multiples Une derni`re r`gle. int b). la m´thode consiste ` d´finir une constante ` la premi`re ine e a e a e clusion du fichier en-tˆte et d’ignorer le contenu de celui-ci si la constante a d´j` e ea ´t´ d´finie (donc si on a d´j` fait une inclusion).c multiple definition of ’. } Remarquer que c’est exactement la proc´dure utilis´e pour les fonctions de e e la librairie standard : les fichiers en-tˆte .h de la librairie standard sont e constitu´s de d´clarations de fonctions et de d´finitions de constantes e e e symboliques (la seule information importante pour un utilisateur) et le corps des fonctions en lui-mˆme est s´par´./* fichier: arithmetique. int b) { return(a * b). seul le produit est impl´ment´ e e */ /********************************************************/ int produit(int a.h" int produit(int a. e e e 8.h */ e /*****************************************************/ #include "arithmetique.o: first defined here Cette erreur se produit en g´n´ral lors de l’inclusion multiple d’un mˆme fichier e e e en-tˆte.h En appliquant cette r`gle.h */ /* G`re les op´rations arithm´tiques sur les entiers e e e */ /* (Ici.h */ /* G`re les op´rations arithm´tiques sur les entiers e e e */ /* (Ici. int b). Il est recommand´ d’appeler la constante __TOTO_H e pour le fichier toto.. Pour cela. seul le produit est impl´ment´ e e */ /********************************************************/ #ifndef __ARITHMETIQUE_H #define __ARITHMETIQUE_H int produit(int a.. e Pour ´viter cela.h de e l’exemple pr´c´dent devient : e e /********************************************************/ /* fichier: arithmetique.

-c module_2.c contenant le corps des fonctions impl´ment´es dans e e ce module. . (ici.h est inclus dans le fichier module.o module_2..c (via la directive #include) et dans tous les autres fichiers qui font appel aux fonctions export´es par ce module. Ce fichier se compose : – de variables globales qui ne sont utilis´es que dans le fichier module.. Une passe d’´dition de lien entre ces fichiers objets en ensuite n´cessaire pour e e g´n´rer l’ex´cutable final toto. – d’´ventuelles fonctions locales ` module. .4 R´sum´ des r`gles de programmation modulaire e e e 1. c’est ` dire le e e a prototype des fonctions du module qui sont export´es. e – du corps des fonctions d’interface dont la d´claration se trouve dans e module.h qui d´finit son interface. Chaque module est compil´ pour g´n´rer l’ex´cutable final (voir 8. Chaque module se traduit en 2 fichiers sources : – un fichier en-tˆte module. D´couper le programme en modules impl´mentant des fonctions similaires. le r´pertoire coue u e e e rant.c . e e L’option -I de gcc permet d’ajouter un r´pertoire en premi`re position de la e e liste des r´pertoires o` sont cherch´s les fichiers en-tˆte. Le fichier module. e e e ce fichier se compose : – des d´clarations des fonctions d’interface (celles qui sont export´es et e e donc utilis´es dans d’autres fichiers sources) e – d’´ventuelles d´finitions de constantes symboliques et de macros. Plus pr´cis´ment. La m´thode consiste ` g´n´rer un fichier objet par module (option -c de gcc) : e a e e gcc -O3 -Wall -I.exe : e e e gcc -o toto.o.c Ces commandes g´n`rent n fichiers objets module_i.o . e 4.exe module_1. La compilation avec gcc comportera alors l’option -IInclude. . Cette option est utile lorsque./ : l’option -I est donc en fait facultative dans ce cas). e e – un fichier module. e e compilation conditionnelle). encore faut-il les compiler pour obtenir un executable. e e – d’´ventuelles directives au pr´processeur (inclusion d’autres fichiers.c gcc -O3 -Wall -I.8. Ce fichier doit respecter ´galement les conventions d’´critures propos´es e e e au §8. module_n. -c module_1. e a 3.3). -c module_n.2 pour ´viter les erreurs de d´finitions multiples.c . un r´pertoire Include est cr´´ pour contenir tous les fichiers en-tˆte e ee e du programme.o 8. gcc -O3 -Wall -I.3 La compilation s´par´e e e Ce n’est pas le tout de bien fragmenter son code en plusieurs fichiers.c. e e 2. pour des raisons de lisibilit´ dans l’architecture des fichiers e sources.h . e e e e 88 .

l’´criture d’un fichier Makefile se rapproche largement de c e e e l’´criture d’un script shell : e – On peut placer des commentaires.5.1 Principe de base L’id´e principale de make est d’effectuer uniquement les ´tapes de compilation e e n´cessaires ` la cr´ation d’un ex´cutable. pour plus de d´tails : man make) e – make -f <nom_fichier> : utiliser le fichier <nom_fichier> ` la place du a traditionnel fichier Makefile. Ces a e e e commandes ne seront ex´cut´es que si le fichier cible n’ex´cute pas ou si l’un e e e des fichiers de d´pendance est plus r´cent que le fichier cible. e Voyons tout de suite les principales options de cette commande (les plus utiles . Par exemple. e e Les lignes suivantes. e e 8. la proc´dure de compilation peut tr`s vite devenir longue et fastie e dieuse. objets et ex´cutables. e e De fa¸on g´n´rale. e e 89 .5 L’outils ’make’ Losrqu’un programme est fragment´ en plusieurs fichiers sources compil´s s´e e e paremment. Ce fichier sp´cifie les d´pendances entre les diff´rents e e e fichiers sources. Une bonne utilisation de make permet de r´duire le temps de compilae tion et ´galement de garantir que celle-ci est effectu´e correctement. – make -C <path> : ex´cuter le fichier Makefile situ´ dans le r´pertoire <path>.Ces r`gles s’ajoutent aux r`gles g´n´rales d’´criture de programmes C abord´es e e e e e e au §1. il suffit de ee e e recompiler ce fichier et d’effectuer l’´dition de liens. e e e 8. e e La commande make recherche par d´faut dans le r´pertoire courant un fichier e e makefile ou Makefile. si un seul fichier source e a e e a ´t´ modifi´ dans un programme compos´ de plusieurs fichiers. de la forme : cible: liste_de_d´pendances e <TAB> commandes_a_effectuer La premi`re ligne sp´cifie un fichier cible (g´n´r´ par commandes_a_effectuer) e e e ee et la liste des fichiers dont il d´pend (s´par´s par des espaces) : il s’agit donc e e e de la liste des ficiers requis pour la g´n´ration de cible. Les autres fichiers sources e n’ont pas besoin d’ˆtre recompil´s. qui doivent commencer par une tabulation (le caract`re e TAB). indiquent les commandes ` ex´cuter pour g´n´rer le fichier cible. qui commencent par # : #Ceci est un commentaire Les commentaires s’´tendent sur une seule ligne (´quivalent du // en C99).5. 8.2 Cr´ation d’un Makefile e Un fichier Makefile est compos´ d’une liste de r`gles de d´pendances entre e e e fichiers.6 page 9. Il est alors extr`mement pratique de l’automatiser ` l’aide de l’utilitaire ’make’ e a d’Unix.

h e produit. e e L’arborescence g´n´ral des fichiers et le graphe des d´pendances entre les fichiers e e e est expos´ dans la figure 8.o gcc -g3 -Wall -o toto main.h gcc -g3 -Wall -Iinclude/ -c main. le contenu de cette variable (machin.h gcc -g3 -Wall -c produit.5.c -o obj/main.c) sont quand ` eux dans le r´pertoire courant et on a e souhaite qu’il en soit de mˆme pour l’ex´cutable final (toto). e Avec une telle arborescence.h gcc -g3 -Wall -c main.h e main.c) est obtenu par l’appel $(SRC) Ainsi.o: produit.gestion d’une arborescence all: toto obj/produit.1.c include/produit.o obj/produit. il suffira ensuite d’ex´cuter e e la commande make pour lancer la compilation du programme toto ! 8.3 Utilisation de macros et de variables On suppose maintenant qu’on souhaite en plus que les fichiers de header soient plac´s dans le r´pertoire include/ et les fichiers objets dans le r´pertoire obj/.o: produit.c gcc -g3 -Wall -c main.o produit.o produit.h gcc -g3 -Wall -Iinclude/ -c produit.c et de produit.c # Le fichier main.c gcc -g3 -Wall -o toto main.c e c Ensuite.o: main.o obj/main.o Moyennant l’´criture de ce fichier de configuration. on peut modifier le Makefile pr´c´dent pour g´rer e e e la compilation du programme toto de la fa¸on suivante : c ## Deuxieme exemple de Makefile . e e e e e – Pour une compilation ”` la main” et en suivant les recommendations du §1. Une variable se d´clare de la fa¸on suivante : SRC = machin.c -o obj/produit.c include/produit.o d´pend de produit. consid´rons l’exemple du programme effectuant le produit de deux entiers e pr´sent´ au §??.c # Generation de l’ex´cutable toto qui d´pend des fichiers objets e e toto: main. a il faudrait ex´cuter les commandes : e gcc -g3 -Wall -c produit.o On l’aura compris : cela peut devenir tr`s vite long et lassant ! e – Un premier fichier Makefile g´rant cette compilation pourrait ˆtre : e e ## Premier exemple de Makefile # il faut generer l’executable toto all: toto # Le fichier produit.o toto: obj/main. e e e Les fichiers sources (.o d´pend de main.o 90 .c produit.c et de produit.o produit.4.c produit.1.o: main.– On peut declarer des variables. On souhaite g´n´rer l’ex´cutable toto.

gestion d’une arborescence # Utilisation de variables EXE = toto OBJ_DIR = obj H_DIR = include OPT = -g3 -Wall all: $(EXE) $(OBJ_DIR)/produit.c -o $(OBJ_DIR)/produit. En particulier.h gcc $(OPT) -Iinclude/ -c produit.c $(H_DIR)/produit.c produit. on continue ` dupliquer un certain nombre d’informations.c -o $(OBJ_DIR)/main.o .ARBORESCENCE projet/ include/ produit.1 – Arborescence et graphe de d´pendances entre les fichiers e gcc -g3 -Wall -o toto obj/main.c toto Makefile .o main.c main.o $(OBJ_DIR)/main. si on change le nom d’un r´pertoire. on obtient une version beaucoup plus simple du Makefile pr´c´dent : e e ## Quatrieme exemple de Makefile .gestion d’une arborescence # Utilisation des variables predefinies EXE = toto OBJ_DIR = obj H_DIR = include 91 .h gcc $(OPT) -Iinclude/ -c main.o $(OBJ_DIR)/produit.h .c main.o gcc $(OPT) -o $(EXE) $(OBJ_DIR)/main. Pour a cela.o . On utilise pour e cela des variables : ## Troisieme exemple de Makefile . 8.o $(EXE): $(OBJ_DIR)/main. e e A partir de ces variables.o On voit qu’un certain nombre d’informations sont redondantes.c Fichiers Sources Fig. dont les les plus utiles e e sont r´sum´es dans le tableau 8.1.c produit. il existe un certain nombre de variables pr´d´finies.o obj/produit.o: produit.o $(OBJ_DIR)/produit.o produit.o Fichiers Objets .c $(H_DIR)/produit. il faut le changer ` plusieurs endroit dans e a le Makefile d’ou l’id´e de centraliser ce type d’informations.h GRAPHE DES DEPENDANCES toto Exécutable obj/ main.o Mais la encore.h produit.o produit.o: main.

$@ $* $< $?

Le fichier cible (par exemple : obj/main.o) Le fichier cible sans suffixe (ex : obj/main) Le premier fichier de la liste des d´pendances (par exemple : main.c) e L’ensemble des fichiers de d´pendances e Tab. 8.1 – Les principales variables pr´d´finies dans un Makefile e e

OPT = -Wall -g3 all: $(EXE) $(OBJ_DIR)/produit.o: produit.c $(H_DIR)/produit.h gcc $(OPT) -I$(H_DIR)/ -c $< -o $@ $(OBJ_DIR)/main.o: main.c $(H_DIR)/produit.h gcc $(OPT) -I$(H_DIR)/ -c $< -o $@ $(EXE): $(OBJ_DIR)/main.o $(OBJ_DIR)/produit.o gcc $(OPT) -o $(EXE) $?

A noter que pour d´terminer facilement les d´pendances entre les diff´rents e e e fichiers, on peut utiliser l’option -MM de gcc. Par exemple, % gcc -MM -Iinclude/ *.c main.o: main.c include/produit.h produit.o: produit.c include/produit.h On rajoute habituellement dans un fichier Makefile une cible appel´e clean e permettant de d´truire tous les fichiers objets et une cible distclean qui supe prime ´galement les ex´cutables g´n´r´s lors de la compilation. e e e ee clean: rm -f $(OBJ_DIR)/*.o distclean: clean rm -f $(EXE) La commande make clean permet donc de ”nettoyer” le r´pertoire courant. Noe tons que l’on utilise ici la commande rm avec l’option -f qui ´vite l’apparition e d’un message d’erreur si le fichier ` d´truire n’existe pas. a e Il y aurait encore beaucoup de choses ` dire sur les Makefile mais cela d´passe a e largement le cadre de ce polycopi´. La meilleur documentation ` ce sujet se e a trouve ` l’URL http://www.gnu.org/software/make/manual/make.html. a Pour conclure, on notera que tous les exemples de Makefile propos´s doivent e ˆtre modifi´s d`s lors qu’un nouveau fichier est ajout´ au projet. Un exemple e e e e de Makefile g´n´rique (dans le sens o` il n’a pas besoin d’ˆtre modifi´ lors de e e u e e l’ajout de nouveau fichiers) est fourni an annexe B page 112. Pour r´utiliser ce e Makefile dans n’importe quel projet, il suffit d’adapter les variables $(INCL), $(DIR_OBJ) et $(EXE). Pour plus d’informations, il suffit de taper make help. 92

Chapitre 9

La biblioth`que standard e
Ce chapitre est un aide-m´moire qui donne la liste exhaustive de toutes les e fonctions de la biblioth`que standard, accompagn´es la plupart du temps de e e leurs prototypes. Les fichiers d’en-tˆte de la librairie ANSI sont les suivants (ceux correspone dants ` de nouvelles fonctionnalit´s introduites dans la norme ANSI C99 sont a e indiqu´es par un ast´risque entre parenth`ses (*). e e e assert.h complex.h(*) ctype.h errno.h fenv.h(*) float.h inttypes.h(*) iso646.h(*) limits.h locale.h math.h setjmp.h signal.h stdarg.h stdbool.h(*) stddef.h stdint.h(*) stdio.h stdlib.h string.h tgmath.h(*) time.h wchar.h(*) wctype.h(*)

De mani`re g´n´rale, pour obtenir le maximum d’informations sur une come e e mande, on utilisera la commande man.

9.1

Diagnostics d’erreurs <assert.h>

Cette biblioth`que ne fournit qu’une seule fontion, assert, qui permet de mettre e des assertions dans le source du programme. Son prototype est le suivant : void assert(int expression); ` A l’ex´cution, si le param`tre de assert s’´value ` faux, le programme est e e e a stopp´ sur terminaison anormale. Exemple : e
#include <assert.h> #include <stdio.h> #define TAILLE 512 int calcul(int tab[], int i) { assert(i >= 0 && i < TAILLE); return 2 * tab[i] + 5; }

93

int main() { int tableau[TAILLE]; #ifndef NDEBUG printf("DEBUG: tableau[0] = %d\n", tableau[0]); #endif printf("%d\n", calcul(tableau, 1024)); return 0; }

L’ex´cution de ce programme renverra : e DEBUG: tableau[0] = 180613 a.out: toto.c:7: calcul: Assertion ‘i >= 0 && i < 512’ failed. Abandon

9.2

Gestion des nombres complexes <complex.h>

Cette biblioth`que permet de g´rer les nombres complexes, de la forme e e z = a + i ∗ b, o` a, b ∈ R. On rappelle que√ est la partie r´elle de z, b sa partie u a e imaginaire et i le nombre imaginaire i = −1 (i2 = −1). man complex pour des exemples de code utilisant ces nombres complexes.

9.3

Classification de caract`res et changements de e casse <ctype.h>

Toutes ces fonctions permettent de tester une propri´t´ sur le caract`re pass´ ee e e en param`tre : e
Fonction isalnum isalpha isblank iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit Prototype int isalnum(int c) int isalpha(int c) int isblank(int c) int iscntrl(int c) int isdigit(int c) int isgraph(int c) int islower(int c) int isprint(int c) int ispunct(int c) int isspace(int c) int isupper(int c) int isxdigit(int c) Teste si le param`tre est e une lettre ou un chiffre une lettre un espace ou une tabulation un caract`re de commande e un chiffre d´cimal e un caract`re imprimable ou le blanc e une lettre minuscule un caract`re imprimable (pas le blanc) e un signe de ponctuation un caract`re d’espace blanc e une lettre majuscule un chiffre hexad´cimal e

On dispose ´galement de deux fonctions de conversions majuscules/minuscules : e
Fonction tolower toupper Prototype int tolower(int c) int toupper(int c) Action convertit c en minuscule convertit c en majuscule.

94

int excepts) int fetestexcept(int excepts) Action Efface les exceptions excepts Sauvegarde le statut d’exception sp´cifi´ dans *flagp e e D´clenche l’exception expects e Sauvegarde le statut de l’exception expects dans *flagp renvoie les bits correspondants aux exceptions courantes 95 . Dans ce cas. e e – FE_OVERFLOW : d´passement de capacit´ . la variable e errno indique quel type d’erreur s’est produite.9. 2.h> Cette biblioth`que ne d´finit qu’une variable globale. e e extern int errno. Toutes (` l’exe e a ception de fetestexcept) retournent 0 en cas de succ`s. les modes de contrˆle qui d´terminent les diff´rents comportements de o e e l’arithm´tique en virgule flottante (la m´thode d’arrondi utilis´e par exemple). Cette valeur n’a de sens que si l’appel syst`me e s’est mal pass´ et retourne une erreur (la valeur -1).h> Le standard C99 a introduit via <fenv. e Les fonctions suivantes sont utilis´es pour g´rer ces exceptions. e e La constante FE_ALL_EXCEPT est un OU bit-`-bit de toutes les constantes pr´a e c´dentes.5 Gestion d’un environnement ` virgule flottante a <fenv. e e Fonction feclearexcept fegetexceptflag feraiseexcept fesetexceptflag fetestexcept Prototype int feclearexcept(int excepts) int fegetexceptflag(fexcept_t *flagp. int excepts) int feraiseexcept(int excepts) int fesetexceptflag(fexcept_t *flagp. L’argument excepts e indique l’une des constantes pr´c´dentes. e e e 9. dont la valeur est un num´ro d’erreur affect´ lors d’appels syst`mes (et par e e e certaines fonctions de librairie). e e – FE_UNDERFLOW : sous-d´passement de capacit´.h> la notion d’environnement a vir` gule flottante afin de permettre une repr´sentation plus d´taill´e des conditions e e e d’erreurs dans l’arithm´tique en virgule flottante.5. man errno fournit la liste des valeurs d’erreurs valides. utilis´s dans la gestion des exceptions en virgule flote tante. e e – FE_INVALID : r´sultat ind´fini . les flags de statut. 9.4 Valeur du dernier signal d’erreur <errno. e Deux variables syst`mes sont d´finies dans cet environnement : e e 1. – FE_INEXACT : le r´sultat de l’op´ration n’est pas exact .1 Gestion des exceptions Toute erreur (exception) en virgule flottante est caract´ris´e par une constante e e symbolique : – FE_DIVBYZERO : division par 0 .

6 Intervalle et pr´cision des nombres flottants <float.0 + x != 1.0 le plus petit nombre tel que 1.0 le plus grand nombre du type float le plus grand nombre du type double le plus grand nombre du type long double le plus petit nombre du type float le plus petit nombre du type double le plus petit nombre du type long double 9.9. en e e e particulier : FLT_DIG DBL_DIG LDBL_DIG FLT_EPSILON DBL_EPSILON LDBL_EPSILON FLT_MAX DBL_MAX LDBL_MAX FLT_MIN DBL_MIN LDBL_MIN nombre de chiffres significatifs d’un float nombre de chiffres significatifs d’un double nombre de chiffres significatifs d’un long double le plus petit nombre tel que 1.7 D´finitions de types entiers de taille fix´e <inte e types.h d´finit des constantes symboliques qui caract´risent les types r´els. L’environnement par d´faut est repr´sent´ par la constante FE_DFL_ENV. et efface tous les flags d’exceptions et continue en mode ”sans interruption” recharge l’environnement ` *envp a idem et replace les exceptions d´j` pr´sentes ea e dans *envp 9.5.h supporte quatre m´thodes d’arrondis.2 Gestion des arrondis fenv.h> e float. e 9. e – FE_UPWARD : arrondi vers la valeur sup´rieure la plus proche.5.3 Gestion des environnements en virgule flottante.0 + x != 1.0 + x != 1. e e e Fonction fegetenv feholdexcept fesetenv feupdateenv Prototype int fegetenv(fenv_t *envp) int feholdexcept(fenv_t *envp) int fesetenv(fenv_t *envp) int feupdateenv(fenv_t *envp) Action Sauve l’environnement courant dans *envp idem. e Les fonctions int fegetround() et int fesetround( int rounding_mode) permettent de lire et de changer la m´thode d’arrondi courante.h> La biblioth`que <inttypes.h> d´finit au moins les types suivants : e e 96 . caract´ris´es par les constantes e e e symboliques suivantes : – FE_DOWNWARD : arrondi vers la valeur inf´rieure la plus proche . L’environnement de travail en virgule flottante peut ˆtre manipul´ via les fonce e tions suivantes sous forme d’un seul objet opaque de type fenv_t. e – FE_TONEAREST : arrondi vers la valeur la plus proche .0 le plus petit nombre tel que 1. – FE_TOWARDZERO : partie enti`re .

Ces fonctions sont setlocale et localeconv (faire un man sur ces e fonctions pour plus de d´tails).h> Le fichier <limits.10 Gestion de l’environnement local <locale. le signe repr´sentant l’unit´ moe e e e n´taire etc... e 9. ces types sont d´finis dans stdint.int8_t int16_t int32_t int64_t uint8_t uint16_t uint32_t uint64_t intptr_t uintptr_t type entier sign´ sur 8 bits e type entier sign´ sur 16 bits e type entier sign´ sur 32 bits e type entier sign´ sur 64 bits e type entier non sign´ sur 8 bits e type entier non sign´ sur 16 bits e type entier non sign´ sur 32 bits e type entier non sign´ sur 64 bits e entier sign´ pouvant stocker un pointeur (une adresse) e entier non sign´ pouvant stocker un pointeur (une adresse) e En pratique.9 Intervalle de valeur des types entiers <limits. correspondant au nombre e e de bits du type char. 9.h> Il y a deux fonctions permettant de g´rer les conventions nationales concernant e l’´criture du point d´cimal dans les nombres. e 97 .8 Alias d’op´rateurs logiques et binaires <iso646.h> d´finit ´galement la constanste CHAR_BIT. e e e e Type char signed char short int long long long Minimum CHAR_MIN SCHAR_MIN SHRT_MIN INT_MIN LONG_MIN LLONG_MIN Maximum CHAR_MAX SCHAR_MAX SHRT_MAX INT_MAX LONG_MAX LLONG_MAX Maximum (types non-sign´s) e UCHAR_MAX USHRT_MAX UINT_MAX ULONG_MAX ULLONG_MAX <limits.17).h> d´finit les constantes symboliques qui pr´cisent les valeurs e e maximales et minimales qui peuvent ˆtre repr´sent´es par un type entier donn´.h (voir §9.h> e <iso646.h> d´finit des alias (synonymes) aux op´rateurs binaires et logiques : e e and bitand compl not_eq or_eq xor_eq ⇐⇒ ⇐⇒ ⇐⇒ ⇐⇒ ⇐⇒ ⇐⇒ && & ~ != |= ^= and_eq bitor not or xor ⇐⇒ ⇐⇒ ⇐⇒ ⇐⇒ ⇐⇒ &= | ! || ^ 9.

c -o prog Le r´sultat et les param`tres de toutes ces fonctions sont de type double.12 Branchements non locaux <setjmp. Si e e les param`tres effectifs sont de type float.h> e Pour utiliser les fonctions de cette librairie. il faut en plus de l’inclusion de la librairie par la directive #include <math.3 Fonctions diverses Fonction ceil fabs floor fmod pow sqrt S´mantique e entier le plus proche par les valeurs sup´rieures e valeur absolue entier le plus proche par les valeurs inf´rieures e reste de division puissance racine carr´e e 9. Plus pr´cis´ment.c utilisant la librairie <math.h> se fera par la commande : gcc -g3 -Wall -lm prog.11.2 Fonctions exponentielles et logarithmiques Fonction exp frexp ldexp log log10 modf S´mantique e exponentielle ´tant donn´ x. setjmp saue 98 .11 Les fonctions math´matiques de <math.1 Fonctions trigonom´triques et hyperboliques e Fonction acos asin atan cosh tanh S´mantique e arc cosinus arc sinus arc tangente cosinus hyperbolique tangente hyperbolique Fonction cos sin tan sinh atan2 S´mantique e cosinus sinus tangente sinus hyperbolique arc tangente 9.h> ajouter l’option -lm a l’´dition de e lien. void longjmp(jmp_buf env. 9.11.h> L’instruction goto pr´sent´e au §2. la compilation de prog. Pour r´aliser des branchements ` l’ext´rieur e e e a e d’une proc´dure. int val). e Prototype de ces fonctions : int setjmp(jmp_buf env).11. trouve n et p tels que x = n * 2p e e multiplie un nombre par une puissance enti`re de 2 e logarithme logarithme d´cimal e calcule partie enti`re et d´cimale d’un nombre e e 9.9.2. il faut utiliser setjmp et longjmp. ils seront convertis en double par le e compilateur. les fonctions setjmp et longjmp permettent de g´rer les ere e e reurs et les interruptions rencontr´es dans des routines bas-niveau.6 ne permet de r´aliser des branchements e e e qu’au sein d’une mˆme proc´dure. Ainsi.

handler est un pointeur vers la fonction effectuant le traitement de ce signal. utiliser le man sur ces fonctions.h> et caract´risant ces erreurs : e e Signal SIGABRT SIGFPE SIGILL SIGINT SIGSEGV SIGTERM Signification Fin anormale du programme (ex : utilisation de la fonction abort()) Exception ` la virgule flottante. on dispose des macros va_start. a Instruction ill´gale rencontr´e dans le code machine.23) e e e Taille d’un objet en nombres d’octets (≃ unsigned int en g´n´ral) e e 99 . e 9. 9. e pr´cise la r´ponse du programme au signal e e signum.vegarde le contexte de pile et d’environnement dans env afin de l’utiliser ult´e rieurement avec longjmp.h>. va_arg et va_end. Action Envoie le signal sig au processus ex´cutant.14 Nombre variable de param`tres <stdarg. introduit la notion e de bool´en et d´finit le type bool´en bool et les valeurs associ´es.16 D´finitions standards <stddef. Comme toujours. e e e e 9.15 D´finition du type bool´en <stdbool. Pour plus de d´tails.7 page 67. e e a e Terminer : requˆte pour terminer le programme. e e Interruption par la touche Ctrl-C. 9.h> Lorsqu’un ´v`nement exceptionnel se produit. e Deux fonctions permettent d’interagir avec le m´canisme des signaux : e Fonct. true et false. L’utilisation de ces macros et du type associ´ va_list est abord´ en d´tail dans e e e le §5. sigh_t handler) . sigh_t signal(int signum.h> e e La librairie <stdbool. Violation de Segmentation : acc`s ill´gal ` la m´moire. le man reste la meilleur source d’informations sur ce sujet. ajout´e avec la norme ANSI C99. le syst`me d’exploitation peut e e e envoyer un signal aux processus.13 Manipulation des signaux <signal. Le tableau suivant fournit la liste de quelques constantes symboliques d´finies dans <signal.h> e Cette librairie introduit un ensemble de types pr´d´finis tr`s utiles qui sont e e e list´s dans le tableau suivant : e Type ptrdiff_t wchar_t size_t Description Diff´rence entre deux pointeurs (≃ int en g´n´ral) e e e Employ´ pour les caract`res ´tendus (voir §9. raise signal Prototype int raise(int sig) typedef void (*sigh_t)(int) .h> e Si on d´sire programmer une fonction avec un nombre variable de param`tres e e (comme printf). Ceci peut arriver en cas d’erreur grave (une erreur d’adressage par exemple).

<stddef.h> d´finit ´galement le pointeur NULL, utilis´ lors de l’initialisation des e e e pointeurs (cf chapitre 3) ainsi que la fonction : size_t offsetof(type, member-designator) qui permet de connaˆ le d´calage (en nombre d’octets) entre un champ d’une ıtre e structure (member-designator) et le d´but de cette structure (type). Ainsi, e si on suppose d´finie la structure toto ayant un champ next (voir §4.3.5), le e d´calage du champ data est donn´ par l’expression : e e offsetof (struct toto, data)

9.17

D´finitions de types entiers <stdint.h> e

La libraisie <stdint.h> est un sous ensemble de la librairie <inttypes.h> (voir §9.7) et en constitue donc une version all´g´e, adapt´e aux environnements eme e e barqu´s qui peuvent ne pas supporter les fonctions d’entr´es/sorties formatt´es. e e e

9.18
9.18.1

Entr´es-sorties <stdio.h> e
Manipulation de fichiers
Prototype FILE *fopen(char *path, char *mode) int fclose(FILE *fp) int fflush(FILE *stream) FILE *tmpfile (void) Action (voir chapitre 6) ouverture d’un fichier Fermeture d’un fichier ´criture des buffers en m´moire dans le fichier e e cr´ation d’un fichier temporaire e

Fonction fopen fclose fflush tmpfile

9.18.2
Fonction fprintf fscanf printf scanf sprintf sscanf

Entr´es et sorties format´es e e
Prototype int fprintf(FILE *stream, char *format, ...) int fscanf(FILE *stream, char *format, ...) int printf(char *format, ...) int scanf(char *format, ...) int sprintf(char *s, char *format, ...) int sscanf(char *s, char *format, ...) Action (voir §2.5/chapitre 6) ´criture sur un fichier e lecture depuis un fichier ´criture sur la sortie standard e lecture depuis l’entr´e standard e ´criture dans la chaˆ de caract`res s e ıne e lecture depuis la chaˆ de caract`res s ıne e

9.18.3
Fonction fgetc fputc getc putc getchar putchar fgets fputs gets puts

Impression et lecture de caract`res e
Prototype int fgetc(FILE *stream) int fputc(int c, FILE *stream) int getc(FILE *stream) int putc(int c, FILE *stream) int getchar(void) int putchar(int c) char *fgets(char *s, FILE *stream) int *fputs(char *s, FILE *stream) char *gets(char *s) int *puts(char *s) Action lecture d’un caract`re depuis un fichier e ´criture d’un caract`re sur un fichier e e ´quivalent de fgetc mais optimis´e e e ´quivalent de fputc mais optimis´e e e lecture d’un caract`re depuis stdin e ´criture d’un caract`re sur stdout e e lecture d’une chaˆ de caract`res depuis un fichier ıne e ´criture d’une chaˆ de caract`res sur un fichier e ıne e lecture d’une chaˆ de caract`res sur stdin ıne e ´criture d’une chaˆ de caract`res sur stdout e ıne e

100

9.19
9.19.1

Utilitaires divers <stdlib.h>
Allocation dynamique

Ces fonctions sont d´crites dans le chapitre 3 et au §3.5. e
Fonct. calloc malloc realloc free Prototype void *calloc(size_t n, size_t size) void *malloc(size_t size) void *realloc(void *ptr, size_t size) void free(void *ptr) Action allocation dynamique de n*size octects et initialisation ` z´ro. a e allocation dynamique modifie la taille d’une zone pr´alablee ment allou´e par calloc ou malloc e lib`re une zone m´moire e e

9.19.2

Conversion de chaˆ ınes de caract`res en nombres e

Les fonctions suivantes permettent de convertir une chaˆ de caract`res en un ıne e nombre.
Fonct. atof atoi atol Prototype double atof(char *chaine) int atoi(char *chaine) long atol(char *chaine) Action convertit chaine en un double convertit chaine en un int convertit chaine en un long int

Il semblerait que qu’il faille pr´f´rer maintenant les fonctions strtol, strtoll ee et strtoq.

9.19.3

G´n´ration de nombres pseudo-al´atoires e e e

La fonction rand fournit un nombre entier pseudo-al´atoire dans l’intervalle e [0,RAND_MAX]. RAND_MAX est une constante pr´d´finie au moins ´gale ` 215 − 1. e e e a L’al´a fournit par la fonction rand n’est toutefois pas de tr`s bonne qualit´. e e e La valeur retourn´e par rand d´pend de l’initialisation (germe ou seed en ane e glais) du g´n´rateur. Cette derni`re est ´gale ` 1 par d´faut mais elle peut ˆtre e e e e a e e modifi´e ` l’aide de la fonction srand. e a
Fonct. rand srand Prototype int rand(void) void srand(unsigned int seed) Action fournit un nombre pseudo-al´atoire e modifie la valeur du germe du g´n´e e rateur pseudo-al´atoire e

On utilise souvent la valeur de l’horloge interne de l’ordinateur (obtenue ` l’aide a de la librairie <time.h>, voir §9.22) pour fournir un ”bon”1 germe ` srand. a Exemple :
#include <stdlib.h> #include <time.h> //see also ’man 3 rand’

/* initialize random generator */ void init_rand(){ srand(time(NULL)); }
1

Pas suffisamment bon cependant pour les applications cryptographiques

101

/* return a random number between 1 and m */ unsigned long myRand(unsigned long m){ return 1+(unsigned long)(((double) m)*rand()/(RAND_MAX+1.0)); }

9.19.4

Arithm´tique sur les entiers e

Fonct. abs labs div ldiv

Prototype int abs(int n) long labs(long n) div_t div(int a, int b) ldiv_t ldiv(long a, long b)

Action valeur absolue d’un entier idem mais pour un long int quotient et reste de la division euclidienne de a par b. idem pour les long int

Les structures div_t et ldiv_t disposent de deux champs, quot et rem, qui stockent les r´sultats des fonctions div et ldiv. e

9.19.5

Recherche et tri

Les fonctions qsort (resp. bsearch) permettent de trier un tableau (resp. rechercher un ´l´ment dans un tableau d´j` tri´). Pour leurs syntaxes : voir §5.8. ee ea e

9.19.6

Communication avec l’environnement

Fonct. abort exit system

Prototype void abort(void) void exit(int etat) int system(char *s)

Action terminaison anormale du programme terminaison du programme ; rend le contrˆle au syst`me o e en lui fournissant la valeur etat (0 = fin normal). ex´cution de la commande syst`me d´finie par s. e e e

102

h> et d´finit pour chacune des fonctions e communes ` ces deux librairie une version similaire d´finie sous forme de macro a e pour un type g´n´rique.char *ch2. idem mais ne copie que n caract`res. retourne ch1.char *ch2. a idem mais seuls n caract`res sont copi´s e e compare ch1 et ch2 pour l’ordre lexicographique . e e a – 0 si ch1 et ch2 sont identiques . strrchr. strncat . memmove. – transformer : strxfrm .h> e Prototype char *strcpy(char *ch1. – initialiser : memset . introduite avec la norme ANSI C99. strchr.9. strtok .h> Cette librairie. a e Concernant strcmp. strpbrk. – concat´ner : strcat. strcoll.char c) char *strchr(char *chaine. on notera que le r´sultat de cette fonction est e – une valeur n´gative si ch1 est inf´rieure ` ch2 (pour l’ordre lexicographique) . 9. strcpy. strncmp . strstr. int n) char *strcat(char *ch1. e – comparer : memcmp.char *ch2) char *strncat(char *ch1. est un sur-ensemble des librairies <math.char *ch2) int strcmp(char *ch1. strcpy strncpy strcat strncat strcmp strncmp strchr strrchr strstr strlen Manipulation de chaˆ ınes de caract`res <string. La liste de ces fonctions est donn´e dans le tableau e e e suivant : 103 . La librairie <string. strspn. strcmp. e copie ch2 ` la fin de ch1 . – une valeur positive sinon. / retourne un pointeur sur la derni`re occue rence de c dans ch et NULL si c ∈ ch / retourne un pointeur sur la premi`re occue rence de ch2 dans ch1 et NULL si ch2 ∈ ch1 / retourne la longueur de chaine. e e retourne un pointeur sur la premi`re occue rence de c dans ch et NULL si c ∈ ch. – obtenir un message d’erreur ` partir du num´ro de l’erreur : strerror.char *ch2. int n) char *strchr(char *ch.h> et de <complex.20 Fonct.char c) char *strchr(char *ch1. int n) int strcmp(char *ch1. – rechercher : memchr.char *ch2) char *strcpy(char *ch1. retourne ch1.21 Macros g´n´riques pour les fonctions math´mae e e tiques <tgmath.h> fournit un ensemble de fonctions permettent de g´rer e les chaˆ ınes de caract`res pour : e – copier : memcpy.char *ch2) int strlen(char *chaine) Action copie ch2 dans ch1 . strncpy . – mesurer : strlen . strcspn. idem mais seuls n caract`res sont compar´s.

org/onlinepubs/009695399/basedefs/tgmath. 9.h> Plusieurs fonctions permettent d’obtenir la date et l’heure. e e Dans le cadre du design d’applications multilingues.h> e e et <wctype.<math. 0 heures G.t2 en secondes. Les plus utiles sont list´es dans le tableau suivant : e Fonct. ctime.h> Function cacos() casin() catan() cacosh() casinh() catanh() ccos() csin() ctan() ccosh() csinh() ctanh() cexp() clog() cpow() csqrt() cabs() Type-g´n´rique e e Macro acos() asin() atan() acosh() asinh() atanh() cos() sin() tan() cosh() sinh() tanh() exp() log() pow() sqrt() fabs() Pour un compl´ment d’informations : e http://www. time difftime ctime clock Prototype time_t time(time_t *tp) double difftime(time_t t1.23 Manipulation de caract`res ´tendus <wchar.M. gmtime. asctime. localtime.T. strftime. lesquels correspondent e e g´n´ralement ` des int ou a des long int. 104 .html 9. mktime.h. Les fonctions de manipulation de e e a ` la date et de l’heure sont : clock. e e e renvoit le temps CPU (ms) depuis le dernier clock. difftime. on devra donc g´rer des e caract`res et des chaˆ e ınes au format UNICODE. time_t t2) char *ctime(time_t *tp) clock_t clock(void) Action retourne dans *tp le nombre de secondes ´coul´es e e depuis le 1er janvier 1970. Le temps est repr´sent´ par des objets de type time_t ou clock_t.h> Le type char est cod´ sur 8 bits et le restera parce qu’il d´signe la plus petite e e unit´ de donn´es adressable. e convertit *tp en une chaˆ de caract`res expliciıne e tant la date et l’heure (format pr´d´termin´).h> Fonction acos() asin() atan() acosh() asinh() atanh() cos() sin() tan() cosh() sinh() tanh() exp() log() pow() sqrt() fabs() <complex. retourne la diff´rence t1 .22 Date et heure <time. time.opengroup.

h> et a <ctype.html 105 .opengroup.h>) .freenix.h> (d´clar´es respectivement dans <wchar. e e Pour plus de d´tails : e http://www.h> et <wctype. e e – un ensemble de fonctions similaires ` celles contenues dans <string.A cet effet.fr/unix/linux/HOWTO/Unicode-HOWTO-5.h>). la norme ANSI C99 a introduit : – un type de caract`re cod´ sur 16 bits wchar_t . e e – un ensemble de fonctions de conversion entre les types char * et wchar_t * (d´clar´es dans <stdlib.html ou http://www.org/onlinepubs/007908799/xsh/wchar.h.

fr> * @date Tue Oct 4 2005 * * @brief see command. but du programme et usage) et sort du programme.h> #include <getopt.1 Gestion des arguments de la ligne de commande Le code source suivant traite l’exemple pr´sent´ au §4.h> Exemple trait´: programme ’command’ au format d’appel e suivant (les ´l´ments entre crochets sont optionnels): e e .6 page 50.5 e -s: permet de sp´cifier la valeur de arg_s. une cha^ne de caract`res e ı e poss´dant au plus MAX_SIZE caract`res e e Valeur par d´faut: "On est super content!" e -h: affiche l’aide (auteur. ı e */ /********************************************************************************/ #include <stdio.h> /* for printf */ #include <stdlib. de type float e Valeur par d´faut: 1.Varrette@imag. e e Commande de compilation : gcc -O3 -Wall commande.2.h> //#include "toto.Annexe A Etude de quelques exemples A./command [-i arg_i] [-f arg_f] [-s arg_s] [-k arg_k] [-h] [-V] arg_file Les options de la ligne de commande sont donc: -i: permet de sp´cifier la valeur de arg_i.h> /* for exit */ #include <assert.c * @author Sebastien Varrette <Sebastien.h> /* for assert */ #include <string. -V: affiche la version du programme et sort arg_file est un param`tre obligatoire du programme qui sp´cifie e e la valeur de la cha^ne de caract`res arg_file. de type int e Valeur par d´faut: 14 e -f: permet de sp´cifier la valeur de arg_f.c -o commande /** * @file command.h" 106 .h traitement des arguments de la ligne de commande utilisant la librairie <getopt.

MAX_SIZE). default: printError("Bad Format!".MAX_SIZE).MAX_SIZE).5. including ’\0’ void printHelp(char * command). case ’f’: assert(strlen(optarg) < MAX_SIZE). case ’i’: assert(strlen(optarg) < MAX_SIZE).argv[0]). break.1 #define MAX_SIZE 256 // source version // maximal size of strings. argv. 10). optopt. // copy current arg to opt_f // Minimal check for validity (opt_f isn’t a command line option) if (opt_f[0] == ’-’) printError("Bad Format for arg_f!". // specific to getopt extern int optind.argv[0]). "i:f:s:hV")) != -1) { switch(c) { case ’h’: printHelp(argv[0]). // print error message void printVersion(char * command). // conversion break. char arg_file[MAX_SIZE] = "".#define VERSION 0. opterr. // copy current arg to opt_i // Minimal check for validity (opt_i isn’t a command line option) if (opt_i[0] == ’-’) printError("Bad Format for arg_i!". long arg_i = 14. char c. char * command). arg_i = strtol(opt_i. // copy current arg to arg_s if (arg_s[0] == ’-’) printError("Bad Format for arg_i!". // to deal with options char opt_i[MAX_SIZE] = "". NULL.optarg. double arg_f = 1. //. } 107 . // last arg: base for convertion break. arg_f = strtod(opt_f. return EXIT_SUCCESS.optarg.argv[0]). case ’s’: assert(strlen(optarg) < MAX_SIZE).argv[0]). char opt_f[MAX_SIZE] = "". // check size strncpy(opt_i. argv[argc]=NULL) * @return status of the execution (0: correct execution) */ int main(int argc. // check size strncpy(opt_f. case ’V’: printVersion(argv[0]). NULL).optarg. return EXIT_SUCCESS. char arg_s[MAX_SIZE] = "On est super content!". // id. * @param argc number of arguments of the command line * @param argv multi-array containing the command line arguments * (argv[0] is the name of the program. // print help message void printError(char * error_message. // Management of parameters in command line while ((c = getopt(argc. // check size strncpy(arg_s. // print version /** * Entry point where the program begins its execution. char * argv[]){ // Command line management extern char *optarg.

printf(" Web page : http://www-id.command ).arg_s). // required parameter: arg_file assert(strlen(argv[optind]) < MAX_SIZE).fr>\n" ). printf(" %s [-i arg_i] [-f arg_f] [-s arg_s] arg_file\n".Varrette@imag. If not. printf("\nREPORTING BUGS\n" ).fr>\n" ). printf(" Default value : 14\n" ). //Print values printf("arg_i = %ld\n". printf(" http://www-id.argv[0]).arg_i). } /** * print error message on stderr 108 . printf("\nAUTHOR\n" ).arg_file). printf(" -i arg_i : set arg_i (long)\n" )." ). printf(" -h : print help and exit\n" ). not even for MERCHANTABILITY or FITNESS\n"). printf(" FOR A PARTICULAR PURPOSE.imag.argv[optind]. } /** * Print the help message * @param command used (argv[0]) */ void printHelp(char * command) { printf("NAME\n" ). printf(" -s arg_s : set arg_s (char *).fr/~svarrett/perso.MAX_SIZE). printf("arg_f = %f\n".command ). printf(" %s [-h] [-V]\n". printf(" This is free software.optind > 1 : to many parameters or error not detected ((argc . printf(" %s\n". printf("Valeur de arg_s: %s\n".command ).\n"). printf("Valeur de arg_file: %s\n".arg_f).optind) != 1) printError("Bad Format". printf(" Sebastien Varrette <Sebastien. print error and exit argc . printf(" There is NO warranty. printf("\nCOPYRIGHT\n" ). return EXIT_SUCCESS.5\n" ).imag. printf(" Please report bugs to <Sebastien.html\n" ).optind == 0 : no arg_file argc .} // Now proceed to detect errors and/or set the values // // // if parameter arg_file is required. printf("\nDESCRIPTION\n" ). printf(" -V : print version and exit\n" ). printf(" characters. printf(" Default value : 1.Varrette@imag. a string with at most MAX_SIZE\n"). see the source for copying conditions. printf("\nSEE ALSO\n" ).fr/~svarrett/\n" ). printf(" -f arg_f : set arg_f (float)\n" ). Default value : \"Toto\"\n" ). strncpy(arg_file. printf("\nSYNOPSIS\n" ).

command). char * command) { fprintf(stderr. exit(EXIT_FAILURE). "This is %s version %f\n".error_message). } /** * print version * @param command used (argv[0]) */ void printVersion(char * command) { fprintf(stderr. fprintf(stderr.VERSION). "Use ’%s -h’ for help\n".* @param error_message Error message to print * @param command command used (argv[0]) */ void printError(char * error_message. "[ERROR] %s\n". fprintf(stderr. } 109 . "Please type ’%s -h’ for help\n".command. command).

return Q.3. // deplacement jusqu’au // dernier elt de la liste tmp->next = L. L->next = Q. L = (liste)malloc(sizeof(struct toto)).h> /* D´finition de la structure toto*/ e struct toto { int data.5 page 53. L->next = NULL.A.h> #include <stdio. } /*************************************** * Supprime l’´l´ment en t^te de liste * e e e ***************************************/ liste supprime_tete(liste L) { liste suivant = L. } /*************************************** * Ins`re un ´l´ment en queue de liste * e e e ***************************************/ liste insereInTail(int element.h> #include <stdlib. struct toto *next. // allocation de la zone // m´moire nec´ssaire e e L->data = element. tmp=Q. return L. e e Commande de compilation : gcc -O3 -Wall toto.fr> Un exemple de gestion des listes chain´es e *********************************************************/ #include <stddef.c -o toto /********************************************************* Fichier: toto.Varrette@imag. L = (liste)malloc(sizeof(struct toto)). /************************************** * Ins`re un ´l´ment en t^te de liste * e e e e **************************************/ liste insere(int element.2 Exemple de liste chaˆ ee ın´ Le code source suivant traite l’exemple pr´sent´ au §4. liste Q) { liste L. //type liste synonyme du type pointeur vers une struct toto typedef struct toto *liste. // maintenant tmp=Q est forcement non vide => tmp->next existe while (tmp->next != NULL) tmp = tmp->next. if (Q == NULL) return L. if (L != NULL) { // pour etre s^r que L->next existe u suivant= L->next. // allocation de la zone // m´moire nec´ssaire e e L->data = element. liste Q) { liste L. }.c Auteur: Sebastien Varrette <Sebastien. 110 .

insere(10. return 0. print_liste(L). printf("Impression de la liste:\nL = \t"). printf("Suppression de la liste:\nL = \t"). } /***************************** * Supprime toute la liste L * *****************************/ liste supprime(liste L) { while (L != NULL) L = supprime_tete(L). printf("Suppression de l’element en tete:\nL = \t"). //suppression de la t^te de liste e return L. L = supprime_tete(L).insere(3. print_liste(L). } /************************************ * Affiche le contenu de la liste L * ************************************/ void print_liste(liste L) { liste tmp = L. tmp = tmp->next.tmp->data).insere(2.free(L). print_liste(L). } 111 . L = supprime(L). //lib´ration de l’espace allou´ pour une cellule e e } return suivant.L))). while (tmp != NULL) { //on aurait pu ´crire ’while (tmp)’ e printf("%d \t".(insereInTail(14. } printf("NULL\n"). } /********************************************************/ int main() { liste L. L = insereInTail(15. L = insere(14.NULL)))).

imag.Annexe B Makefile g´n´rique e e #################################################################################### # Makefile (configuration file for GNU make .) all: @echo "No source files available .fr/~svarrett/ # # -------------------------------------------------------------------------------# This is a generic makefile in the sense that it doesn’t require to be # modified when adding/removing new source files."\ "you may modify variables C_EXT and CPP_EXT to reflect your " \ "own programming style)" else 112 . even if not needed # make clean : Remove backup files generated by emacs and vi # make realclean : Remove all generated files # make doc : Generate Doxygen documentation (in Doc/) see www. binary is generated in the current directory # make force : Force the complete re-compilation.$(C_EXT) or *.org/software/make/manual/make.gnu.$(H_EXT) $(INCLUDE_DIR)/*.$(CPP_EXT) .org/software/make/) # # Compilation of files written in C/C++ (version 0.h) and object files (.Varrette@imag.doxygen.gnu.$(C_EXT) *.$(CPP_EXT)) SRC_H = $(wildcard *. C++ and header files C_EXT = c CPP_EXT = cpp H_EXT = h # Source files SRC = $(wildcard *.org # make help : print help message # ############################## Variables Declarations ############################## # Name of the executable to generate --.fr> # Web page : http://www-id.o) will be placed INCLUDE_DIR = Include OBJ_DIR = Obj # File entensions for C.5) # # Author : Sebastien Varrette <Sebastien.I can’t handle the compilation" @echo "Please check the presence and/or extension of source files " @echo "(This makefile is configured to manage *.TO BE ADAPTED --EXE = toto # Directory where header files (.see http://www.$(H_EXT)) ALL_SRC = $(SRC) $(SRC_H) # Check avalibility of source files ifeq ($(SRC). # -------------------------------------------------------------------------------# # Documentation on the Makefile utility may be found at # http://www.html # # Available Commands # -----------------# make : Compile files.

c.doxygen. \ fi # Force re-compilation.$(CPP_EXT) 113 .%.$(C_EXT) $(CXX) $(CXXFLAGS) $< -o $(OBJ_DIR)/$@ %.o)) ABSOBJ = $(OBJ:%.# Object files OBJ = $(patsubst %. -name "*~") # Doxygen stuff DOC_DIR = Doc DOXYGEN = $(shell which doxygen) DOXYGEN_CONF = .$(SRC:.$(CPP_EXT)=.o) # Backup files generated by text editors BACKUP_FILE = $(shell find . binaries and documentation) realclean : clean @echo "Remove object files" rm -f $(ABSOBJ) @echo "Remove generated executable" rm -f $(EXE) @if [ ! -z "$(DOC_DIR)" -a ! -z "$(DOXYGEN)" ].o : %.$(C_EXT).o=$(OBJ_DIR)/%.o : %.conf YES_ATTRIBUTES := JAVADOC_AUTOBRIEF EXTRACT_ALL EXTRACT_PRIVATE EXTRACT_STATIC \ SOURCE_BROWSER GENERATE_MAN # Compilator configuration # Detect if you have a C or a C++ project through file extension ifneq ($(filter %.$(SRC)).Makefile.o. \ rm -rf $(DOC_DIR)/*. even if not required force : touch $(ALL_SRC) $(ABSOBJ) @$(MAKE) # Generate the dependance file $(MAKEDEP_FILE) : $(ALL_SRC) $(CXX) $(SPECIAL_CPP_OPTION) -MM -I$(INCLUDE_DIR) $(SRC) > $@ include $(MAKEDEP_FILE) # Generic description for compilation of object files %.dep ############################### Now starting rules ################################ # Required rule : what’s to be done each time all : $(MAKEDEP_FILE) $(EXE) TAGS # Generate TAGS for emacs TAGS : $(ALL_SRC) etags $< cp TAGS $(INCLUDE_DIR)/ # Clean Options clean : @echo "Remove backup files generated by emacs and vi" rm -f $(BACKUP_FILE) # Clean everything (including object files. then \ echo "Remove documentation (’make doc’ to regenerate it)".) CXX = gcc YES_ATTRIBUTES := $(YES_ATTRIBUTES) OPTIMIZE_OUTPUT_FOR_C else CXX = g++ SPECIAL_CPP_OPTION = -Wno-deprecated endif CXXFLAGS = -g3 -O3 -Wall $(SPECIAL_CPP_OPTION) -I$(INCLUDE_DIR) -c ADD_OPT = #-lntl -lgmp -lm # Optionnal option for the linker # Specifies the list of directories that make should search VPATH = $(INCLUDE_DIR):$(OBJ_DIR) # dependance file used for make rules MAKEDEP_FILE = .

\ cat $(DOXYGEN_CONF) | sed -e "s/^\(OUTPUT_DIRECTORY \+= \+\).generate one with ’doxygen -s -g’ # The following attributes should be modified in the generated file: # .*/\1$(DOC_DIR)/"\ > $(DOXYGEN_CONF). $(INCLUDE_DIR)/" \ > $(DOXYGEN_CONF). $(INCLUDE_DIR)’". INPUT to ’.$(YES_ATTRIBUTES) attributes should be set to YES # . \ mkdir -p . \ echo "Now updating INPUT attribute to ’. then \ echo "$(DOC_DIR)/ does not exist => creating $(DOC_DIR)/".) @echo "Please install Doxygen to use this option!" @echo "(’apt-get install doxygen’ under Debian)" else @if [ ! -d . \ echo "Now generating one using ’$(DOXYGEN) -s -g $(DOXYGEN_CONF)’". binary is generated in the current directory|’ ’| make force | Force the complete re-compilation./$(DOC_DIR)/.OPTIMIZE_OUTPUT_FOR_C should be set to YES if the project in in C # Finally. \ cat $(DOXYGEN_CONF) | sed -e "s/^\(INPUT \+= \+\). \ $(DOXYGEN) -s -g $(DOXYGEN_CONF).*/\1. $(INCLUDE_DIR)’ # .$(CXX) $(CXXFLAGS) $< -o $(OBJ_DIR)/$@ # Generation of the final binary (see $(EXE)) $(EXE) : $(OBJ) $(ALL_SRC) $(CXX) -g -o $@ $(ABSOBJ) $(ADD_OPT) @$(MAKE) help # Help rule help : @echo @echo @echo @echo @echo @echo @echo @echo @echo @echo # Test values test : @echo @echo @echo @echo @echo @echo @echo @echo @echo @echo @echo @echo @echo @echo @echo @echo print help message ’+-----------------------------------------------------------------------------+’ ’| Available Commands |’ ’+----------------+------------------------------------------------------------+’ ’| make | Compile files. \ 114 . \ fi @if [ ! -f $(DOXYGEN_CONF) ]. even if not required |’ ’| make clean | Remove cache backup files generated by emacs and vi |’ ’| make realclean | Remove all generated files (including . then \ echo "I don’t found the configuration file for Doxygen ($(DOXYGEN_CONF))".for debug purpose "INCLUDE_DIR = "OBJ_DIR = "EXE = "SRC = "SRC_H = "ALL_SRC = "OBJ = "BACKUP_FILE = "CXX = "CXXFLAGS = "ADD_OPT = "DOC_DIR = "DOXYGEN = "DOXYGEN_CONF = "YES_ATTRIBUTES "MAKEDEP_FILE = $(INCLUDE_DIR)" $(OBJ_DIR)" $(EXE)" $(SRC)" $(SRC_H)" $(ALL_SRC)" $(OBJ)" $(BACKUP_FILE)" $(CXX)" $(CXXFLAGS)" $(ADD_OPT)" $(DOC_DIR)" $(DOXYGEN)" $(DOXYGEN_CONF)" = $(YES_ATTRIBUTES)" $(MAKEDEP_FILE)" # Documentation generation through doxygen # First check if the $(DOXYGEN) and the $(DOC_DIR) directory exist # Then Check $(DOXYGEN_CONF) availability.doxygen. \ echo "Now updating OUTPUT_DIRECTORY attribute to ’$(DOC_DIR)’".otherwise./$(DOC_DIR) ].OUTPUT_DIRECTORY should be set to ’$(DOC_DIR)’. launch documentation generation doc : ifeq ($(DOXYGEN).o and binary) |’ ’| make doc | Generate documentation using doxygen (see www.org) |’ ’| make help | Print help message |’ ’+----------------+------------------------------------------------------------+’ of variables .

cat $(DOXYGEN_CONF) | sed -e "s/^\($$attr \+= \+\). done.*/\1YES/" > $(DOXYGEN_CONF). do echo "now updating $$attr to YES"./$(DOC_DIR)/html/index.html’ endif endif \ \ \ \ 115 .for attr in $(YES_ATTRIBUTES). \ fi $(DOXYGEN) $(DOXYGEN_CONF) @echo @echo Documentation generated in $(DOC_DIR)/ @echo May be you can try to execute ’mozilla .

2 Erreurs avec les macros Le m´canisme des macros est r´alis´ par le pr´processeur qui r´alise un traitee e e e e ment en amont du compilateur proprement dit. e e e Ce que voulait le programmeur Ce qu’il aurait dˆ ´crire ue Ce qu’il a ´crit e Ce qu’il a obtenu C. rendant ainsi leur d´tection difficile. La d´couverte de telles erreurs ne peut donc se faire que par un examen e e tr`s attentif du source du programe. c’est un nid ` erreurs. e Ce que voulait le programmeur Ce qu’il aurait dˆ ´crire ue Ce qu’il a ´crit e Ce qu’il a obtenu Pourquoi tant de haˆ ? L’affectation est un op´rateur et non pas une ıne e instruction.1 Erreur avec les op´rateurs e Erreur sur une comparaison : : : : Comparer a et b if (a == b) if (a = b) une affectation de b ` a.1. a 116 .1. e C. Ce concept est abord´ en d´tail e e dans le chapitre 7.Annexe C Le bˆtisier e Cette annexe est une collection des bˆtises qu’il faut faire au moins une fois e dans sa vie pour ˆtre vaccin´. La diff´rence e e entre le texte correct et le texte erron´ est souvent seulement d’un seul carace t`re. mais qui reste beaucoup moins fr´quente. a C’est l’inverse de l’erreur pr´c´dente. sans aucun contexte. C.2 Erreur sur l’affectation : : : : Affecter b ` a a a = b a == b La comparaison de a ` b. Comme le traitement des macros est un pur traitement textuel. le r´sultat ´tant la a e e valeur affect´e.1 C. L’id´e est reprise de [Cas98] e e e La caract´ristique de beaucoup de ces erreurs est de ne pas provoquer de mese sage d’erreur du compilateur.

Ceci peut provoquer les erreurs suivantes : 117 .1 Un #define n’est pas une d´claration e la d´finition de la constante MAX initialis´e a 10 e e #define MAX 10 #define MAX 10 .C. e e e sans aucun blanc entre les deux.2. Exemple : e e #define CARRE(a) ((a) * (a)) l’utilisation de CARRE(x++) aura comme traduction ((x++) * (x++)) et l’op´e rateur ++ sera appliqu´ deux fois.4 Erreur avec les effets de bord Le corps d’une macro peut comporter plusieurs occurrences d’un param`tre. Ceci peut amener des r´sultats surprenant . e comparer les deux exemples suivants : D´finition de la macro e #define add(a. e e cet effet de bord sera r´alis´ plusieurs fois.b) (a + b) param`tres e a et b aucun traduction (a + b) (a.3 Erreurs avec l’instruction if L’instruction if ne comporte ni mot-cl´ introducteur de la partie then.3 Erreur sur macro avec param`tres e La distinction entre macro avec param`tres et macro sans param`tre se fait e e sur la pr´sence d’une parenth`se ouvrante juste apr`s le nom de la macro. e C.2. Ce que voulait le programmeur : Ce qu’il aurait dˆ ´crire : ue Ce qu’il a ´crit : e Cette erreur sera g´n´ralement d´tect´e ` la compilation. aura pour traduction x = 10 . malheureusement le e e e e a message d’erreur sera ´mis sur l’utilisation de la macro. Ce que voulait le programmeur : Ce qu’il aurait dˆ ´crire : ue Ce qu’il a ´crit : e Cette erreur peut provoquer ou non une erreur de compilation ` l’utilisation de a la macro : – L’utilisation x = MAX .2.2 Un #define n’est pas une initialisation la d´finition de la constante MAX initialis´e a 10 e e #define MAX 10 #define MAX = 10 . ni tere minateur (pas de fi dans le style des if then else fi). Si e a ` l’utilisation de la macro on r´alise un effet de bord sur le param`tre effectif. et non pas l` o` r´side e a u e l’erreur.2. ` savoir la d´finition de la macro. ce qui est possible : il y a une instruction nulle derri`re l’instruction x = 10 . a e C. ce qui g´n`rera e e un message d’erreur. aura pour expansion int t[10 .b) (a + b) #define add (a. C.b) (a + b) C.] .. . e – L’utilisation int t[MAX] .

.. Les cas les plus gˆnants e e e sont les suivants : – La priorit´ des op´rateurs bit ` bit est inf´rieure ` celle des op´rateurs de e e a e a e comparaison..Ce que voulait le programmeur : Ce qu’il aurait dˆ ´crire : ue Ce qu’il a ´crit : e tester si a > b if ( a > b) a = b. le programmeur oublie la s´quence fermante /*.5 Erreurs avec les priorit´s des op´rateurs e e Les priorit´s des op´rateurs sont parfois surprenantes. e C. a = b. Le probl`me vient aussi du fait de l’existence de l’instruction nulle : e Ce que voulait le programmeur : Ce qu’il aurait dˆ ´crire : ue tester si a > b if ( a > b) { if ( x > y) x = y.. e Le programmeur a ´crit e x << 4 + 0xf il d´sirait e (x << 4) + 0xf il a obtenu x << (4 + 0xf) 118 . La m´thode classique pour enlever (tout en le laissant e dans le source) un ensemble d’instructions est d’utiliser le pr´processeur : e #ifdef NOTDEFINED . #endif C. Le compilateur ”mange” e donc tout le texte jusqu’` la s´quence fermante du prochain commentaire.4 Erreurs avec les commentaires Il y a deux erreurs classiques avec les commentaires : 1. else . ca n’aura pas l’effet escompt´ e e ¸ e par le programmeur.. Les commentaires ne pouvant ˆtre imbriqu´s. Le programmeur a ´crit e x & 0xff == 0xac il d´sirait e (x & 0xff) == 0xac il a obtenu x & (0xff == 0xac) – La priorit´ des op´rateurs de d´calage est inf´rieure ` celle des op´rateurs e e e e a e arithm´tiques. Ce qu’il a ´crit : e On rappelle qu’un else est raccroch´ au premier if.. } else . if ( a > b) if ( x > y) x = y. a e 2. if ( a > b). On veut enlever (en le mettant en commentaire) un gros bloc d’instructions sans prendre garde au fait qu’il comporte des commentaires.

e a a C.2. En effet.8 Erreur avec la compilation s´par´e e e Une erreur classique est d’avoir un tableau d´fini dans une unit´ de compilation : e e int tab[10] .. (voir §2.– La priorit´ de l’op´rateur d’affectation est inf´rieure ` celle des op´rateurs e e e a e de comparaison. avec les fontes utilis´es pour imprimer les sources. defult : return(1). si on utilise par erreur la notation t[i. } /* erreur non d´tect´e e e */ Version diabolique de cette erreur : si la lettre l de default est remplac´e par e le chiffre 1. donc t[i.6. Si a e e une faute de frappe est commise sur cette ´tiquette.2 Erreur sur le default L’alternative ` ex´cuter par d´faut est introduite par l’etiquette default. cette ´valuation est inutile .. l’alternative par d´faut ne e e sera plus reconnue : l’´tiquette sera prise pour une ´tiquette d’instruction sur e e laquelle ne sera fait aucun goto (voir §2.6. } C. Comme l’´valuation de l’op´rande gauche e e e e e e ne r´alise ici aucun effet de bord.j] est e e ´quivalent ` t[j] qui est l’adresse du sous-tableau correspondant ` l’index j. toutes les e e e parenth`ses sont n´cessaire : e e while ((c = getchar()) != EOF) { . Malheureusement. qui verra la e diff´rence entre l et 1 ? e C.1 Erreur avec l’instruction switch Oubli du break Sans l’instruction break.7 Erreur sur les tableaux multidimensionnels La r´f´rence ` un tableau t ` deux dimensions s’´crit t[i][j] et non pas t[i.6). le programme continuera ` l’alternative suivante ce a qui peut poser probl`me. elle pourra ˆtre accept´e par le compilateur.6 C. switch(a) { case 1 : a = b. et d’utiliser comme d´claration de r´f´rence dans une autre unit´ de compilae ee e tion : 119 .2. tr`s souvent utilis´e.j] ee a a e comme dans d’autres langages de programmation. dans cette e e expression la virgule est l’op´rateur qui d´livre comme r´sultat l’op´rande droite e e e e apr`s avoir ´valu´ l’op´rande gauche. e C.5). Dans la s´quence ci-dessous.j] et selon le contexte d’utilisation.

extern int * tab . Rappelons que int tab[] et int *t ne sont ´quivalents que dans le seul cas e de param`tre formel de fonction. e Dans le cas qui nous occupe ici. la d´claration de r´f´rence correcte est : e ee extern int tab[] . 120 .

org/wiki/Programmation_C – [Can03] : http://www-rocq.fr/codes/Anne.lu/Tutoriel_Ansi_C/ (un tr`s bon tutorial en e ligne).fr/commun/bernard.Canteaut/COURS_C/ – [Fab02] : http://www.ltam. voici quelques sites web qui e m’ont servis pour r´aliser ce polycopi´ et/ou qui m’ont parus int´ressants : e e e – [Cas98] : http://www-clips.wikibooks.cassagne/Introduction_ ANSI_C.9 Liens utiles En plus des ouvrages cit´s dans la bibliographie. 121 .C.imag.inria.html – http://fr.

USA. Canteaut/COURS_C/.imag. Ritchie.inria. 2003. [Fab02] [Her01] [KR88] [Pil04] [PKP03] Peter Prinz and Ulla Kirch-Prinz. ”Introduction au Langage C”.fr/ ~matthieu/cours/c-superflu/index. http://www-rocq. 2004. http://www. 1997-1998. Technical rec port. [Ste90] David Stevenson. 1990. Introduction ` la programmation en ANSI-C. ”IEEE Std 754-1985 IEEE Standard for Binary Floating-Point Arithmetic”. C Pocket Reference.fr/commun/\\ bernard. Prentice-Hall. New Jersey. Introduction au Langage C. http://www-clips. INRIA projet CODES.M. Encyclop´die Informatique Libre. Matthieu Herrb. F.Bibliographie [Can03] Anne Canteaut. http://www. 122 . 2nd edition.cassagne/Introduction\_ANSI\_C.laas. http://www. 1988. Tutorial.W. Jean-Fran¸ois Pillou.php3. Tutorial.html. Faber. [Cas98] Bernard Cassagne.lu/Tutoriel_Ansi_ u C/. O’Reilly & Associates.ltam. IEEE Standards Association. Englewood Cliffs. Aoˆ t 2002. Technical a report. ”Programmation en Langage C”. Technical report. e commentcamarche. Guide superflu de programmation en langage C. Technical report. Laboratoire CLIPS UJF/CNRS. The C Programming Language.fr/codes/Anne. CNRS/LAAS. B. 2003. 2001. Dec. Kernighan and D.net/c/cintro.

Sign up to vote on this title
UsefulNot useful