Sunteți pe pagina 1din 91

` LA INTRODUCTION A PROGRAMMATION PRATIQUE DU LANGAGE C

M. Le Gonidec

Table des mati` eres


1 Introduction 1.1 Un peu dhistoire... . . . . . . . . . . . . . . . 1.2 Quest ce quun langage de programmation ? . 1.3 Compilation dun programme . . . . . . . . . 1.4 R esum e du cycle de vie dun programme . . . 2 Les 2.1 2.2 2.3 5 5 9 11 13 15 15 16 19 19 20 21 22 23 24 24 24 24 25 25 25 26 26 27 27 27 27 28 28 29 30 30 31 31 32 33 34

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

2.4 2.5

2.6

2.7

2.8

bases de la programmation en C Les composants du langage C . . . . . . . . . . . . . . . . . . . . . . . . . Anatomie dun programme C monochier . . . . . . . . . . . . . . . . . . Les types de donn ees pr ed enis . . . . . . . . . . . . . . . . . . . . . . . . 2.3.1 Les types caract` eres . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.2 Les types entiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.3 Les types ottants . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.4 Les constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les op erateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5.1 Aectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5.2 Op erateur de conversion de type : transtypage . . . . . . . . . . . 2.5.3 Op erateurs arithm etiques . . . . . . . . . . . . . . . . . . . . . . . 2.5.4 Op erateurs daectation compos ee et op erateurs dincr ementation 2.5.5 Op erateurs de comparaison . . . . . . . . . . . . . . . . . . . . . . 2.5.6 Op erateurs logiques . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5.7 Op erateurs bit ` a bit . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5.8 Op erateur adresse . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5.9 Op erateur daectation conditionnel ternaire . . . . . . . . . . . . Les instructions de branchement . . . . . . . . . . . . . . . . . . . . . . . 2.6.1 Branchement conditionnel : if... else ... . . . . . . . . . . . 2.6.2 Branchement multiple : switch . . . . . . . . . . . . . . . . . . . . Les boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.7.1 La boucle while . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.7.2 La boucle do ... while . . . . . . . . . . . . . . . . . . . . . . . 2.7.3 La boucle for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.7.4 Choisir entre while, do ... while et for . . . . . . . . . . . . . . 2.7.5 Instructions de branchement non conditionnel dans les boucles . . Fonctions dentr ees-sorties . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.8.1 Les fonctions putchar et getchar . . . . . . . . . . . . . . . . . . 2.8.2 Ecriture avec format : printf . . . . . . . . . . . . . . . . . . . . . 2.8.3 Lecture avec format : scanf . . . . . . . . . . . . . . . . . . . . . . 3

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

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

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

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

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

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

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

3 Fonctions et programmes 3.1 Notion de fonction . . . . . . . . . . . . . . . . . . . . . . 3.1.1 D enition dune fonction . . . . . . . . . . . . . . 3.1.2 Prototype dune fonction . . . . . . . . . . . . . . 3.2 La fonction main . . . . . . . . . . . . . . . . . . . . . . . 3.3 Stockage des variables et Visibilit e des identicateurs . . . 3.3.1 Variables permanentes et variables temporaires . . 3.3.2 Variables globales et variables locales . . . . . . . . 3.4 Transmission des arguments par valeurs et par adresses . 3.5 Qualicateurs de type const et volatile . . . . . . . . . 3.6 Directives au pr eprocesseur et compilation conditionnelle 3.6.1 la directive #include . . . . . . . . . . . . . . . . 3.6.2 D enitions de macros : la directive #define . . . 3.6.3 Compilation conditionnelle . . . . . . . . . . . . . 4 Types compos es 4.1 Tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2 Chaines de caract` eres . . . . . . . . . . . . . . . . . . . . 4.3 Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.4 Enum erations . . . . . . . . . . . . . . . . . . . . . . . . . 4.5 Unions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.6 Indentication des types compos es avec typedef . . . . . 4.7 A propos des valeurs des identicateurs de tableaux et des

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

41 41 41 43 44 45 46 46 49 50 51 51 51 53 57 57 59 61 63 64 65 66 67 67 68 69 71 71 72 73 74 75 76 77 77 78 80 80 80 82 83 83 85 89 91

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . chaines de caract` eres

5 Pointeurs 5.1 Adresses et pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1.1 D enition de variable de type pointeur . . . . . . . . . . . . . . . . . . . . . 5.1.2 Op erateur dindirection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1.3 Arithm etique des pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2 Allocation dynamique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2.1 Allouer de la m emoire : les fonctions malloc et calloc . . . . . . . . . . . 5.2.2 Reaecter la m emoire : la fonction realloc . . . . . . . . . . . . . . . . . . 5.2.3 Allocation automatique VS Allocation dynamique . . . . . . . . . . . . . . 5.2.4 Lib erer la m emoire : la fonction free . . . . . . . . . . . . . . . . . . . . . . 5.2.5 Quelques bons reexes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3 Pointeurs, tableaux et chaines de caract` eres . . . . . . . . . . . . . . . . . . . . . . 5.3.1 Tableaux unidimensionnels . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3.2 Tableaux et pointeurs multidimensionnels . . . . . . . . . . . . . . . . . . . 5.3.3 Pointeurs et chaines de caract` eres . . . . . . . . . . . . . . . . . . . . . . . 5.4 Pointeurs et structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.4.1 Pointeur sur une structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.4.2 Structures auto-r ef erenc ees et listes chain ees . . . . . . . . . . . . . . . . . . 5.5 Pointeurs et fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.5.1 Fonctions modiant leur param` etres : Passage darguments par adresses . . 5.5.2 Retour sur la pile, le tas, les segments de donn ees et les segments de code... 6 Quelques conseils... 7 Bibliographie

Chapitre 1

Introduction
1.1 Un peu dhistoire...

Les langages de programmation ont fait leur apparition aux environs de 1950, mais le souci dautomatisation des calculs date de bien avant cela. Ainsi les grecs, les chinois savaient calculer, automatiser des calculs (` a la main, en utilisant par exemple un boulier). En 820, le math ematicien Al Khawarizmi a publi e ` a Bagdad un trait e intitul e La science de l elimination et de la r eduction qui, import e en Europe Occidentale lors des invasions arabes a eu une grande inuence sur le d eveloppement des math ematiques. Si on veut vraiment retourner aux racines, il faut continuer en citant les cartes perfor ees. En 1725, Basile Bouchon inventa le principe des cartes perfor es qui permettent, en 1801, aux m etiers Jacquard de r ealiser des motifs complexes. Si lon consid` ere que la mani` ere particuli` ere de perforer les cartes produit un eet tout aussi particulier, quun trou peut correspondre ` a un 1 en langage binaire, quelles ont et e int egr ees dans une machine consid er ee aujourdhui comme lanc etre de lordinateur alors les cartes perfor ees sont les anc etres des programmes informatiques. Mais la programmation moderne math ematique, commence sans doute en 1840, gr ace ` a Lady Ada Lovelace (1814-1852), qui d enit le principe des it erations successives dans lex ecution dune op eration. En lhonneur dAl Khawarizmi elle a nomm e algorithme le processus logique dex ecution dun programme. Elle est aussi ` a lorigine du Principe des machines ` a calculer. Pour elle, une machine ` a calculer devait comporter : 1. un dispositif permettant dintroduire les donn ees num eriques (cartes perfor ees, roues dent ees...), 2. une m emoire pour conserver les valeurs num eriques entr ees, 3. une unit e de commande qui indique les t aches ` a eectuer, 4. un moulin charg e deectuer les calculs, 5. un dispositif permettant de prendre connaissance des r esultats (imprimante...). Ces principes seront, un si` ecle plus tard, ` a la base des premiers ordinateurs. On a en quelque sorte la m eme d ecomposition : 1. lintroduction des donn ees seectue gr ace ` a un p eriph erique dentr ee (clavier, souris, cam era, micro...) 2. la m emoire servant ` a conserver les donn ees sous di erentes formes : registres (m emoire interne au processeur), m emoire cache, m emoire vive, m emoire de masse... 3. Lunit e de commande (Partie Commande du processeur) indique et coordonne l ex ecution les t aches, 4. le moulin est lunit e arithm etique et logique (ALU) du processeur, 5

CHAPITRE 1. INTRODUCTION

5. la r ecup erations des r esultats seectue via un p eriph erique de sortie ( ecran, enceintes, imprimante...). Le principal probl` eme Cest que lunit e de commande dun ordinateur ne comprend que le binaire ainsi que les op eration logiques de base... En 1854, Boole publie un ouvrage dans lequel il d emontre que tout processus logique peut etre d ecompos e en une suite dop erations logiques (ET, OU, NON) appliqu ees sur deux etats (ZERO-UN, OUI-NON, VRAI-FAUX, OUVERT-FERME). En 1950, linvention du langage assembleur par Maurice V. Wilkes de luniversit e de Cambridge ouvre la voie aux langages dits de haut niveau. Avant, la programmation seectuait directement en binaire. Grace Hopper, une am ericaine mobilis ee comme auxiliaire dans la marine am ericaine, d eveloppe pour Remington Rand le tout premier compilateur, nomm e A0 . Il permet de g en erer un programme binaire ` a partir dun code source. Alors que le langage Fortran (FORmula TRANslator, utilis e avant tout pour des applications num eriques) commence ` a apparaitre vers 1955 notamment chez IBM, Grace Hopper sint eresse aux langages qui utilisent des mots voire des expressions du langage naturel. Apr` es B0 et Flowmatic, elle participe d` es 1959. ` a l elaboration de ce qui deviendra le langage Cobol (Commun Business Oriented Langage, utilis e principalement pour la gestion). Dans le m eme temps (soit vers 1958) John Mc Carthy, math ematicien au MIT qui y a fond e en 1957 le d epartement dIntelligence Articielle, cr ee le langage Lisp (tr` es adapt e pour lintelligence articelle). Avec la n des ann ees 50 sach` eve ce quon nomme aujourdhui l` ere des ordinateurs de premi` ere g en eration qui utilisent principalement les cartes perfor ees. La seconde g en eration, celle qui utilise les transistors va prendre pleinement son essor. Fortran et Cobol sinstallent comme langages principaux : pendant plus 20 ans Cobol sera le langage le plus utilis e au monde. Aujourdhui, il d etient toujours le record du grand nombre de lignes ecrites dans un langage de programmation. Par contre, Lisp reste cantonn e` a la communaut e de lintelligence articielle. Si Algol (ALGOrithmic Language) devient un langage pour la publication dalgorithmes, il sera peu impl ement e. Modi e en Algol W puis en Algol68, il ne parvient pas ` a devenir le langage standard universel quil aurait du etre sans doute parce que trop complet pour l epoque et trop dicile a implanter pour ces ann ` ees 60. Cela dit, LAlgol introduit des concepts qui seront largement utilis es dans les langages qui suivront : notions de bloc et de programmation structur ee, r ecursivit e... Thomas Kurtz et John Kemeny cr eent en 1964 un langage (beaucoup plus accessible que lAlgol) au Dartmouth College pour leurs etudiants. ce langage pour d ebutants se nomme BASIC (Beginners All purpose Symbolic Instruction Code) . Depuis 1968 Niklaus WIRTH d eveloppe Algol. A force de le modier, il nit par mettre au point un successeur dAlgol nomm e le langage PASCAL (en lhonneur de ce cher Blaise...). Ce langage, moins puissant quAlgol, est bien structur e, tr` es lisible, tr es coercitif et se trouve donc bien adapt e a lenseignement de la programmation. ` D` es 1970, Ken Thompson, pensant que le syst` eme dexploitation UNIX ne serait pas complet sans un langage de programmation de haut niveau, cr ee un nouveau langage, le B (en r ef erence au BCPL dont il sinspire). Deux ans plus tard, Dennis Ritchie du Bell Lab dATT reprend ce langage B pour mieux ladapter au PDP/11 sur lequel UNIX vient juste d etre port e. Il fait evoluer le langage et le dote dun vrai compilateur g en erant du code machine PDP/11 : cest la naissance du langage C. En 1970 aussi, le D epartement de la d efense am ericaine trouve quil y a beaucoup trop dassembleur dans les syst` emes embarqu es et aimerait utiliser un bon langage de programmation. En 1975, une groupe d etudes ds penche sur 23 langages d ej` a existants. La conclusion de lanalyse sera quil faudrait un nouveau langage, sans doute bas e ` a la fois sur Pascal, Algol et Pl/1. Apr` es un appel dore et plusieurs s elections, cest nalement en 1979 le projet de Jean Ichbiach, avec l equipe dHoneywell Bull de Marseille qui sera retenu. Ce langage sera nomm e Dod-1 puis Ada. Ce choix par le Pentagone Am ericain comme lunique langage de d eveloppement impos e` a ses services ` a la place de la jungle des centaines de langages et dialectes aurait du en faire le premier langage de d eveloppement au monde 6

1.1. Un peu dhistoire...

mais la dicult e` a maitriser Ada en fera un outsider. Toujours au d ebut des ann ees 1970 (d ecid ement !), Philippe Roussel et Alain Colmerauer dans leur Groupe dIntelligence Articielle de Marseille d eveloppent un langage qui fonctionne de fa con totalement di erente des autres langages : on y programme logiquement cest ` a dire en sp eciant seulement des relations logiques : il se nomme Prolog et devient le langage privil egi e pour lIntelligence Articielle. Le d ebut des ann ees 80 consacre le d eveloppement de la petite informatique et de la microinformatique : on y voit naitre les premiers PC et les premiers Apple (mais Windows nexiste pas encore). Dbase va simposer dans ces ann ees 80 comme LE gestionnaire de bases de donn ees relationnelles lorsque lapproche tableur (Multiplan, Visicalc...) nest pas adapt ee. Pourtant, la r evolution objet est en marche ; elle permet d ecrire de plus gros programmes mieux structur es, plus facilement modiables et plus s urs. En 1983 Bjarn Stroustrup d eveloppe une extension orient ee objet au langage C qui deviendra le langage C++ dans les m emes laboratoires dans lesquels a vu le jour le C de Dennis Ritchie. La naissance de lObjective-C arrive dans le m eme cadre, Brad Cox le met au point au d ebut des ann ees 1980. Le langage est bas e sur un autre, le Smalltalk-80, et est destin e` a etre une couche suppl ementaire au C pour permettre la cr eation et la manipulation dobjets. Le code compil e Objective-C sex ecute dans un environnement dex ecution tr` es l eger (runtime) ecrit en C qui ajoute peu ` a la taille de lapplication. Le premier syst` eme dexploitation ` a utiliser Objective-C fut NeXTStep, de la soci et e NeXT, fond ee par Steve Jobs... et naturellement, Objective-C sera par la suite beaucoup utilis e sur Macintosh, notamment pour les API Cocoa de Mac OS X. Un langage compl` etement objet nomm e Eiel et mis au point par Bertrand Meyer sort en 1986. Il faudra pourtant quelques ann ees avant que les objets deviennents pr epond erants en programmation... Cette m eme ann ee 1986, Larry Wall, programmeur syst` eme mais aussi linguiste, d ecide de parfaire les outils Unix de traitement dinformations texte : le langage Perl vient de naitre. Il reprend des fonctionnalit es du langage C et des langages de scripts. Gr ace au Web et ` a ces scripts (programmes) parfois tr` es courts, Perl devient un outil indispensable pour g erer les chiers-textes notamment lextraction dinformations et la g en eration de rapports. Sa souplesse autorise lemploi de plusieurs mod` eles de programmation : proc edurale, fonctionnelle et orient ee objet. Vers la n des ann ees 80, les langages de commandes et de scripts se d eveloppent pour tous les types dordinateurs. Parall` element, la notion dinterface graphique pour utilisateur (GUI) commence ` a entrer dans les moeurs pour les grands syst` emes ; John Osterout invente ` a lUniversit e de Californie (Berkeley) en 1988 les langages Tcl et Tk pour des d eveloppements rapides : Tcl est la partie scripts de commandes dont Tk produit linterface. Le langage est connu depuis sous le nom de Tcl/Tk. Les math ematiques ne sont pas en reste : d` es la n des ann ees 80, Stephen Wolfram d eveloppe un langage et environnement pour programmer des math ematiques formelles et appliqu ees : Mathematica alors quun autre langage au d ebut sans interface autre que la ligne de commande, commence a emerger : le langage Maple. Enn, m eme sil nest pas vraiment consid er e comme un langage de programmation, le langage HTML (Hypertext Markup Language) est d evelopp e en 1989 par Tim Berners-Lee. Ce sera LE langage du Web. En 1989, cest egalement lann ee o` u le langage C est d enitivement normalis e par lANSI (American National Standards Institute). Ce travail aura dur e 6 ans et sach` eve par la d enition de la norme ANSI C. Les ann ees 90 voient sinstaller un peu partout dans le monde Windows. Cest en 1990 que Microsoft sort son produit Windows 3.0 qui est une version compl etement revue des premiers Microsoft Windows. Un an plus tard, mais sans publicit e, Linux 0.01 est annonc e par un etudiant, Linus Torvald a lUniversit ` e dHelsinki. Linux va se d evelopper tr` es rapidement gr ace ` a Internet et gr ace ` a deux concepts-phare : la disponibilit e du code-source des programmes et la gratuit e (entre autres), suivant 7

CHAPITRE 1. INTRODUCTION

en cela le projet GNU de Richard Stallman, le fameux cr eateur de l editeur emacs. Dans le m eme temps, les laboratoires de Sun etoent Tcl pour en faire un langage de script universel adapt e` a Internet et le portent pour Windows et Macintosh. Tk va devenir une sur-couche de nombreux langages dont Rexx, Perl... En 1991, Guido van Rossum cr ee le langage Python (en r ef erence au Monty Python...). Ce langage est particuli` erement r epandu dans le monde scientique, et poss` ede de nombreuses extensions destin ees aux applications num eriques. Il est ` a la base du projet SAGE, un logiciel open-source de math ematiques sous licence GPL qui combine la puissance de nombreux programmes open-source en une interface commune bas ee sur Python. Le projet SAGE est avant tout destin e` a cr ee une alternative viable libre et open source ` a Magma, Maple, Mathematica et autres Matlab. Le d eveloppement tr` es fort dInternet inuence fortement les concepteurs de langage. En 1995, suite a de nombreuses r ` eunions de comit e du WWW, le langage LiveScript est renomm e en Javascript et est consid er e comme une bonne solution pour g erer dynamiquement les pages Web. Il est aussitot incorpor e dans Netscape 2. Mais il manque toujours un langage complet, objet, capable de dialoguer avec les serveurs Web et les bases de donn ees. Dans la m eme ann ee 95, Java est introduit comme langage de d eveloppement objet multi-OS pour combler ce manque. La gestion des formulaires et des bases de donn ees accessibles par le Web voit apparaitre pratiquement en m eme temps le langage Php, souvent coupl e au langage de base de donn ees Sql notamment dans ses impl ementations MySql et PosgresSql. Parall` element, lutilisation r epandues des logiciels Word et Excel induit progressivement lutilisation de macros pour tirer pleinement prot des possibilit es de ces logiciels : le langage Basic, remani e, rendu objet avec ses fonctions li ees aux documents devient le Visual Basic. Le d ebut du XXI` eme si` ecle na pas et e tr` es riche en nouveaux langages mais plusieurs dentre eux ont acquis une certaine importance. En lan 2000, Manfred vos Thun con coit Joy, Walter Bright d eveloppe le langage D, et Microsoft d evoile C# avec limplication de Anders Hejlsberg, cr eateur du langage Delphi cinq ans auparavant. Les inspirations du C# vont du C au C++ en passant par le Java dont il reprend g en eralement la syntaxe, mais aussi par le Pascal dont il h erite de la surcharge des op erateurs. Outre ociellement avoir et e con cu pour exploiter tout le potentiel de la plateforme .Net de Microsoft, C# a egalement et e d evelopp e pour saranchir de la plateforme Java de Sun, avec qui Microsoft a eu des d em el es ` a ce sujet. Un an plus tard, Microsoft publie Visual Basic.Net. Pas un nouveau langage ` a proprement parler, mais plut ot une evolution, destin ee ` a lint egration du langage dans la plateforme .Net. La m eme ann ee, Xerox PARC d evoile AspectJ, bas e sur Java, apportant ` a ce langage la programmation orient ee aspect. Ce type de programmation permet dam eliorer la s eparation des pr eoccupations lors du d eveloppement. Ces pr eoccupations forment les di erents aspects techniques du logiciel, habituellement fortement d ependants entre eux. La programmation orient ee aspect propose des m ecanismes de s eparation de ces aspects, an de modulariser le code, le d ecomplexier, et le rendre plus simple ` a modier. C# fera des emules. En 2003, luniversit e de Wroclaw publie Nermle qui sinspire tout ` a la fois du langage de Microsoft, de ML et de MetaHaskell. En 2004, Rodrigo de Oliveira d eveloppe Boo qui ajoute une syntaxe inspir ee de Python compatible avec C#. La premi` ere version de Mono, impl ementation libre du Framework .Net de Microsoft, appara tra la m eme ann ee, con cu par Miguel de Icaza, apportant par l` a m eme le C#, devant initialement etre multiplateforme, au monde du libre, et notamment Linux. Microsoft, d ecid ement tr` es productif (faut bien justier les co uts...), d evoilera F# en 2005. Lui aussi inspir e du C#, le langage OCaml (Objective Categorical Abstract Machine Language) est d evelopp e par lINRIA et lEcole Normale Sup erieure et Haskell, il ne sera propos e au grand public quavec la prochaine g en eration de lenvironnement de d eveloppement Microsoft Visual Studio 2010. En 2006, Microsoft - encore - d evoile Windows PowerShell, qui nest pas quun shell mais aussi un 8

1.2. Quest ce quun langage de programmation ?

langage de script destin e` a ladministration de machines Windows. M eme en etant tol erant, cest une p ale copie des shell Linux. Cela dit, elle constitue une evolution de la console DOS lui apportant une grande puissance. Le projet GNOME d evoile Vala en 2007, destin e ` a apporter aux d eveloppeurs sous Linux une syntaxe tr` es proche du C#. Comme d eja plusieurs autres langages, on ne compiler un code source Vala directement en assembleur mais dabord en C, et cest ce code en C qui sera compil e par la suite. On conclura sur le LOLCODE, d evelopp e en 2007 par Adam Lindsay. Langage esot erique sil en est : sa syntaxe est celle de langlais SMS... nous vivons une epoque moderne...

1.2

Quest ce quun langage de programmation ?

Un langage de programmation est, dun point de vue math ematique, un langage formel, cest-` a-dire un ensemble de suites (appell es mots) de symboles choisis dans un ensemble donn e (appel e alphabet) qui v erient certaines contraintes sp eciques au langage (syntaxe). Dans le cas des langages de programmation, on trouve dans lalphabet plusieurs types de symb oles : des mots-cl e. Ex : main, int, if, for ..., des op erateurs. Ex : =, <,& ..., des chires et des lettres permettant didentier des variables ou est constantes, des caract` eres sp eciaux : Ex : accolades, crochets, point-virgule, tiret bas ou blanc soulign e (underscore)... permettant de structurer le tout... Un programme est un mot du langage, cest-` a-dire une suite de symboles v eriants les contraintes dict ees par la syntaxe du langage. Comme le but dun langage est quand m eme de se faire comprendre de la machine, on a plusieurs possibilit es : 1. soit on utilise un langage de bas niveau : on ecrit un programme directement dans le langage machine (ou langage natif) compr ehensible par le processeur, mais rarement intelligible puisque rares sont les personnes qui parlent le binaire courrament. 2. soit on utilise un langage de haut niveau : on ecrit un programme dans un langage inint elligible pour le processeur, qui sera ensuite traduit en langage machine an que le processeur l execute. Ces langages, plus sympathiquepour lhomme, permettent en quelques sorte de d ecrire des taches sans se soucier des d etails sur la mani` ere dont la machine lex ecute. Deux strag egies sont utilis ees pour les langages de haut niveau. La di erence r eside dans la mani` ere dont on traduit le programme, qui est ` a lorigine dans un ou plusieurs chiers texte appel es chiers source ou code source : 1. soit on ecrit un programme dans un langage interpr et e. Le code source sera traduit mot a mot ou instruction par instructions, dans le langage machine propre au processeur par un ` programme auxiliaire : linterpreteur. Lhumain fait une requ ete, cette requ ete est traduite par linterpr eteur, qui d etermine la strat egie d execution, le processeur l execute puis on passe ` a la requ ete suivante. Chaque execution du programme n ecessite lutilisation de linterpr eteur. 2. soit on ecrit un programme dans un langage compil e. Le code source sera traduit (une bonne fois pour toute) dans le langage machine propre au processeur par un programme compilateur. Contrairement aux interpr eteurs, les compilateurs lisent enti` erement le code source avant de le traduire, d etermine la strag egie d execution de toutes les instructions, puis g en` ere un chier binaire executable : le chier produit na plus besoin d etre lanc e par un autre programme, il est autonome. Cela permet de garantir la s ecurit e du code source mais chaque modication n ecessite une recompilation. 9

CHAPITRE 1. INTRODUCTION

Langage ADA BASIC C C++ Cobol Fortran Java MATLAB Mathematica LISP Pascal PHP Prolog Perl

domaine dapplications Programmation temps r eel Programmation ` a but educatif Programmation syst` eme Programmation orient ee objet Gestion Calcul Programmation internet Calcul math ematique Calcul math ematique Intelligence articielle Programmation ` a but educatif D eveloppementde sites Web dynamiques Intelligence articielle traitement cha nes de caract` eres

Compil e/interpr et e compil e interpr et e compil e compil e compil e compil e interm ediaire interpr et e interpr et e interm ediaire compil e interpr et e interpr et e interpr et e

Tab. 1.1 Quelques exemples de langages courants Il existe egalement des langages faisant en quelque sorte partie des deux cat egories ci-dessus : les langages interm ediaires. Un programme ecrit dans de tels langages doit subir une phase de compilation vers un chier non executable qui necessite un interpr eteur. Si chaque langage de programmation a ses propres particularit es, certains pr esentent des similitudes quant ` a la mani` ere de programmer. Il y a des styles, appel es paradigmes : Deux programmes repondant au m eme probl` eme et ecrits dans deux langages di erents qui utilisent le m eme paradigme vont se ressembler, tandis quil seront fondamentalement di erents si les deux langages utilisent des paradygmes di erents. Les langages de programmation permettent souvent dutiliser plusieurs de ces styles, dans ce cas, on parle de langage multiparadigmes. Ci dessous, quelques exemples de paradygmes : Programmation imp erative : Chaque instruction du langage correspond ` a un ensemble dinstruction du langage machine. Les structures de donn ees et les op erations sont bien sur plus complexes quau niveau machine, mais cest le m eme paradygme qui est suivi. Cetait lunique paradygme des premiers langages de programmation et le paradygme du langage C. Programmation orient ee objet : Ce style de programmation est une r eponse au besoin de concevoir des programmes complexes, dont les donn ees et les traitement sont rassembl es au sein de structures (Les objets) qui peuvent etre polymorphes. Programmation fonctionnelle : Lop eration de base des langages fonctionnels nest pas laffectation comme pour les langages imp eratifs, mais l evaluation de fonctions. Les langages fonctionnels emploient des types et des structures de donn ees de haut niveau permettant r ealiser facilement des op erations sur les listes. Un m ecanisme puissant des langages fonctionnels est lusage des fonctions dordre sup erieur, cest-` a-dire qui peut prendre des fonctions comme argument et/ou retourner une fonction comme r esultat. Programmation g en erique : Ce style de programmation utilise des classes dobjets et des fonctions parametr ees. Loutil le plus utile de ce style de programmation est la notion de template (gabarit) pour g erer le polymorphisme. Elle permet de d evelopper des composants param etr es par des types de donn ees, lobjectif principal etant la r eutilisabilit e du d eveloppement. Programmation logique Cette forme de programmaiton d enit des applications ` a laide dun ensemble de faits el ementaires les concernant et de r` egles de logique leur associant des cons equences plus ou moins directes. Ces faits et ces r` egles sont exploit es par un d emonstrateur 10

1.3. Compilation dun programme

de th eor` eme ou moteur dinf erence, en r eaction ` a une question ou requ ete. Programmation par contraintes. Ce type de programmation, ` a rapprocher du pr ec edent, consiste ` a poser un probl` eme sous forme de relations logiques (contraintes) entre plusieurs variables. Un probl` eme formul e de la sorte comporte donc un certain nombre de ces variables, chacune ayant un domaine de valeurs possibles, et un certain nombre de contraintes. Une contrainte peut porter sur une ou plusieurs variables, et restreint les valeurs que peuvent prendre simultan ement ces variables. Trouver une solution ` a un probl` eme de pos e sous cette forme consiste ` a d ecider sil existe ou non une aectation de toutes les variables telle que lensemble des contraintes du probl` eme soient satisfaites. Programmation d eclarative : En programmation d eclarative, on d ecrit le quoi, cest-` a-dire le probl` eme. Par exemple, les pages HTML sont d eclaratives car elles d ecrivent ce que contient une page (texte, titres, paragraphes, etc.) et non comment les acher (positionnement, couleurs, polices de caract` eres, etc.). Alors quen programmation imp erative (par exemple, avec le C ou Java), on d ecrit le comment, cest-` a-dire la solution. Certains langages sont (ou pas) mieux adapt es ` a des paradygmes. Par exemple, le C++ est ` a la fois un langage imp eratif, g en erique et objet ; le Python est un langage orient e objet proposant certaines fonctionnalit e du paradygme fonctionnel ; et le Common Lisp permet ` a la fois de programme dans les styles imperatif, fonctionnel, et objet et le fait quil soit programmable permet egalement de faire de la programmation logique et de la programmation par contraintes. Un autre trait de caract` ere qui peut egalement permettre de distinguer les langages de programmation les uns des autres, cest le typage des donn ees : un langage est dit typ e sil permet dattribuer un type (entier, nombre ottant, chaine de caract` ere, chier...) aux el ements du code source (variables, fonctions...). Le typage peut etre explicite ou implicite selon que le programmeur ou le compilateur les attribue ; fort ou faible, selon la rigidit e des r` egles de passage dun type ` a un autre ; statique ou dynamique selon si lattribution des types se fait ` a la compilation ou pendant l execution. Il existe bien s ur dautres particularit es permettant de cat egoriser les langages de programmation,

1.3

Compilation dun programme

Le C est un langage compil e : Le chier source, ecrit dans un chier texte doit etre traduit dans le langage machine. Le C fait partie des langages faisant appel ` a un compilateur. Un compilateur est un programme qui traduit une instance dun langage source, dans un autre langage, appel e le langage cible, en pr eservant la signication du texte source. Ce principe g en eral d ecrit un grand nombre de programmes di erents et ce que lon entend par signication du texte source d epend du r ole du compilateur. Lorsque lon parle de compilateur, on suppose aussi en g en eral que le langage source est, pour lapplication envisag ee, de plus haut niveau que le langage cible, cest-` a-dire quil pr esente un niveau dabstraction sup erieur. En pratique, un compilateur sert le plus souvent ` a traduire un code source ecrit dans un langage de programmation en un autre langage, habituellement un langage dassemblage ou un langage machine. Un compilateur fonctionne par analyse-synth` ese, cest-` a-dire quau lieu de remplacer chaque construction du langage source par une suite equivalente de constructions du langage cible, il commence par analyser le texte source pour en construire une repr esentation interm ediaire appel ee code objet ` a partir duquel il construit le code executable (instance autonome du langage machine) ou un programme equivalent au code source ecrit dans le langage cible, si celui-ci nest pas le langage machine. Il est donc naturel de s eparer au moins conceptuellement, mais aussi en pratique le compilateur en une partie avant (ou frontale), parfois appel ee souche, qui lit le texte source et produit la 11

CHAPITRE 1. INTRODUCTION

repr esentation interm ediaire, et une partie arri` ere (ou nale), qui parcourt cette repr esentation pour produire le texte cible. Dans un compilateur id eal, la partie avant est ind ependante du langage cible, tandis que la partie arri` ere est ind ependante du langage source. Certains compilateurs eectuent de plus sur la forme interm ediaire des traitements substantiels, que lon peut regrouper en une partie centrale, ind ependante ` a la fois du langage source et de la machine cible. On peut ainsi ecrire des compilateurs pour toute une gamme de langages et darchitectures en partageant la partie centrale, ` a laquelle on attache une partie avant par langage et une partie arri` ere par architecture. Voici les 4 phases de la compilation : 1. Le traitement par le pr eprocesseur : Le chier source est analys e par le pr eprocesseur qui eectue les transformations purement textuelles : le remplacement des chaines de caract` eres, linclusion dautre chiers sources, la suppression des commentaires. 2. La compilation : La compilation proprement dite traduit le code source en assembleur, cesta-dire une suite dinstructions qui utilise des mn ` emoniques : des mots courts correspondants un a un ` a des octets mais qui rendent la lecture possible. Trois phases danalyse sont eectu ees avant la traduction proprement dite : le d ecoupage du programme en lex` emes (analyse lexicale), la v erication de la correction de la syntaxe du programme (analyse synthaxique), lanalyse des structures de donn ees (analyse s emantique), 3. Lassemblage : Cette op eration traduit le code assembleur en chier binaire, compr ehensibles directement par le processeur. Cette etape est en g en eral faite direcement apr` es la compilation, sauf si on sp ecie que lon veut uniquement le code assembleur. Le chier obtenu est appell e chier objet. Il contient egalement les informations n ecessaires ` a l etape suivante. 4. L edition de liens : Un programme est souvent s epar e dans plusieurs chiers sources pour des raisons de clart e mais aussi parce que lon fait souvent appel ` a des biblioth` eques (library, en anglais...) de fonctions standards d ej` a ecrites. Une fois le code source assembl e, il faut donc lier entre eux tous les chier objets cr e es. L edition de ces liens produit alors le chier ex ecutable. Ces di erentes etapes expliquent que les compilateurs fassent toujours lobjet de recherches, particuli` erement pour des questions doptimisation.
code source Pr ecompilateur Compilateur code objet Edition de liens code executable

Fig. 1.1 Sch ema du processus de compilation Voici la marche ` a suivre pour compiler et executer un programme C monochier dans un environnement Unix (terminal ou console sous Linux, Cygwin sous Windows) et avec le compilateur gcc (pour GNU Compiler Collection). On saisit le texte du programme sous un editeur de texte (emacs, gedit...) que lon enregistre avec lextension ".c". Ex : programme.c, fichier.c, machin.c.... Cette extension est n ecessaire car gcc peut compiler des programmes ecrits dans plusieurs langages (C, C++, java...). Lextension lui indique donc quil est en pr esence dun chier source ecrit en C. Au passage, les chiers trait es par le pr eprocesseur ont lextension .i, les chiers assembleurs ont lextension .s et les chier objets lextension .o. On compile ensuite le programme chier.c en lan cant la commande suivante : gcc [options ] programme.c [-lbibliotheque ] Un chier ex ecutable, appel e par d efaut a.out est alors g en er e et lexecution du programme se fera en lancant la commande : 12

1.4. R esum e du cycle de vie dun programme

/a.out Ce nom peut etre modi e en utilisant loption -o. Les eventuelles bibliotheques pr ecompil ees n ecessaires sont d eclar ees sous forme doptions dans la chaine [-lbibliotheque ]. Par exemple, pour lier le programme avec la biblioth` eque de fonctions math ematiques, on sp ecie -lm. Le compilateur recherchera alors (g en eralement dans /usr/lib/) le chier pr e-compil e libm.a (plus g en eralement, si on sp ecie -lbiblio , le compilateur recherchera alors libbiblio.a). On peut egalement (cest m eme pr ef erable...) rajouter des options. Les deux plus usuelles sont : -Wall (W pour warning et all pour all...) qui g en` ere une liste tr` es compl` ete davertissements sur les eventuelles incorrections du programme, qui pourrait provoquer des probl` emes lors de lexecution. -o (pour out ) qui sp ecie le nom du chier executable. Dautres options existent bien sur, comme -O qui permet doptimiser la taille et le temps dexecution du code executable (par d efaut, le compilateur optimise son propre temps dexecution) ou -S qui produit le chier assembleur.... Pour la liste compl` etes des options de gcc, on se r ef` ere au manuel via la commande man gcc. Ainsi, pour compiler un chier source enregistr e dans sous le nom max.c, sera compil e en lan cant par exemple la commande : gcc -Wall max.c -o maximum puis ex ecut e en lan cant la commande : maximum Apr` es compilation, un programme C est execut e sous le contr ole du syst` eme dexploitation de lordinateur. Il communique avec lext erieur par linterm ediaire dunit es dentr ees-sorties (un clavier, un ecran, une imprimante, un disque dur, etc...). On dit quun programme lit des donn ees sur une unit e dentr ee et ecrit des donn ees sur une unit e de sortie.

1.4

R esum e du cycle de vie dun programme

Voici les di erentes etapes de la vie dun programme ecrit en langage compil e: 1. Conception du programme : Analyser lapplication ` a programmer pour mettre en evidences les types de donn ees ` a manipuler et les op erations ` a eectuer, choisir les meilleurs algorithmes pour r ealier ces op erations, d ecider de la mani` ere de pr esenter les r esultats ` a lutilisateur du programme, quel langage est le mieux adapt e ` a aux taches qui doivent etre r ealis ees par le programme, etc ... Cest une etape cruciale qui peut etre faite sans ordinateur sous la main... 2. Ecriture du programme : Si la premi` ere etape a et e faite avec soin, il sagit uniquement de traduire les r esulats de la conception en un programme ecrit dans le langage de programmation choisi, ici le C. 3. Compilation : Cest l etape de traduction vers le langage machine. Il v erie si le programme est lexicalement, synthaxiquement et s emantiquement correct. Si cest le cas, il produit le code ex ecutable, sinon, il signale les erreurs en les localisant le plus pr ecis ement possible dans le texte du programme et vous renvoie donc ` a l etape 2. 4. Ex ecution : Le code executable produit ` a lissu de la compilation peut etre lanc e, cest-` a-dire soumis au syst` eme dexploitation pour etre execut e. Tant que le texte du programme nest pas 13

CHAPITRE 1. INTRODUCTION

modi e, il nest pas n ec essaire de recompiler avant de lancer lexecutable. Il se peut que lexecution d eclenche des erreurs non d etect ees par le compilateur.Notez bien quun programme qui compile sans erreurs nest pas forc ement correct : il se peux quil ne satisfasse pas le cahier des charges de lapplication ` a programmer.... on retourne dans ce cas ` a l etape 1 ou 2...

14

Chapitre 2

Les bases de la programmation en C


2.1 Les composants du langage C

Les mots du langage C peuvent etre class es en 6 groupes el ementaires : les mot-cl es, les constantes, les identicateurs, les chaines de caract` eres, les op erateurs, les signes de ponctuation, auxquels il convient dajouter les commentaires au code source. Le langage C contient 32 mots-cl es (selon la norme ANSI C) qui ne peuvent pas etre utilis es comme itenticateurs : les sp ecicateurs de stockage : auto register static typedef les sp ecicateurs de type : char double enum float int long short signed struct union unsigned void les qualicateurs de type : const volatile les instructions de contr ole : break case continue default do else for goto if switch while les autres : return sizeof Le r ole des identicateurs est de donner un nom ` a une entit e du programme. Un identicateur peut d esigner : le nom dune variable. Une variable nomme une ou plusieurs cases m emoires qui contiendront la derni` ere valeur aect ee ` a la variable. le nom dune fonction, le nom dun type d eni par typedef, struct, union ou enum. Quelques r` egles pour le choix des identiacteurs : il ne doivent pas etre choisis parmi les mots cl es, Un identicateur est une chaine de caract` eres choisis parmi les chires et les lettres NON AC CENTUEES, en tenant compte de leur casse (Majuscule ou minuscule...). On peut aussi utiliser le tiret bas ( ), 15

CHAPITRE 2. LES BASES DE LA PROGRAMMATION EN C

Le premier caract` ere de la chaine ne doit cependant pas etre un chire et il vaut mieux que quil soit le tiret bas car il est souvent employ e pour les variables globales de lenvironnement C, Il se peut que le compilateur tronque les identicateurs au del` a dune certaine longueur (sup erieure en g en eral ` a 31 caract` eres). Cela laisse une marge assez large pour les choisir. Il est en g en eral conseill e de simposer une nomenclature pr ecise. Un commentaire dans le code source d ebute par /* et se termine par */ : /* ceci est un petit commentaire dans un code source */ Notez que lon peut egalement mettre des commentaires courts en n de ligne en utilisant // mais on pr eferera quand m eme lenvironnement /* */. Les commentaires, bien optionnels, sont fondamentaux. Ils permettent une compr ehension plus facile dun code laiss e de cot e pendant des semaines et sont utiles lors de la maintenance des programme. Une expression est une suite de composants el ementaires qui est synthaxiquement correcte. Par exemple : x=1 ou (i>=1)&&(i<11)&&(i%2 !=0) Une instruction est une expression suivie par un point-virgule. Le point virgule signife que lexpression va etre evalu ee. Plusieurs instructions peuvent etre r eunies entre accolades pour former une instruction compos ee ou bloc, synthaxiquement equivalente ` a une instruction. Par exemple : if(x!=0) { z=y/x; t=y%x; } Une instruction compos ee dun sp ecicateur de type et dune liste didentiant s epar es par une virgule est une d eclaration. Par exemple : int a, b; double c; char message[256]; float d=1.5, x ; La derni` ere d eclaration est coupl ee avec une aectation (symb ole =). Toute variable utilis ee en C doit faire lobjet dune d eclaration avant d etre utilis ee.

2.2

Anatomie dun programme C monochier


[directives au pr eprocesseur ] [d eclaration des variables externes ] [fonctions secondaires ] type main (arguments ) { declaration des variables internes instructions } 16

Voici lossature dun programme monochier ecrit en C :

2.2. Anatomie dun programme C monochier

Tout programme C doit contenir au moins une fonction : Cest la fonction main (fonction principale). Lexecution du programme commence par lappel ` a cette fonction. Un programme C manipule des valeurs class ees par types (voir paragraphe suivant). Ici, la fonction main retourne un objet du type type via une instruction return resultat ; o` u resultat doit etre lidenticateur dune variable de type type . Un programme C est compos e dun ensemble de fonctions, qui peuvent sappeller entre elles en se transmettant des valeurs. Comme la fonction main, une fonction secondaire est d ecrite de la mani` ere suivante : type FonctionAuxiliaire (arguments ) { declaration des variables internes instructions return resultat ; } La premi` ere ligne donne le prototype de la fonction fonction. Il sp ecie : le nom de la fonction, le type des parametres qui doivent lui etre fournies. La d eclaration des arguments dune fonctions seectue dans une syntaxe voisine des celle des variable : on mets en argument une suite dobjets de type sp eci es s epar es par des virgules, ex : FonctionAuxiliaire(int a, float b) le type de la valeur quelle retourne. Les prototype des fonctions secondaires doivent etre plac es avant la fonction main (on parle de d eclaration de fonctions). Le corps de la fonction (entre les accolades) est la suite dintructions qui doivent etre accomplies lorsque la fonction est appell ee. Les corps de fonctions peuvent quant a ` eux etre plac es indi eremment avant ou apr` es la fonction principale. On reviendra en d etails sur les fonctions au Chapitre 3. Les directives au pr eprocesseur indiquent quelles biblioth` eques de fonctions pr ed enies on souhaite pouvoir utiliser dans le programme, par exemples les fonctions permettant de lire et d ecrire des donn ees, ou des fonctions math ematiques usuelles. Lorsquon veut faire appel ` a ces fonctions, on doit le sp ecier. Cest le r ole des premi` ere lignes du programme. Le plus souvent, la forme des directives au pr eprocesseur est sous la forme : #include<chemin/vers/la/bibliotheque.h>. Par exemple, pour utiliser les fonctions qui permettent de lire et d ecrire dans les entr ees/sorties standard, on utilise la directive #include<stdio.h> (pour StanDard In and Out), pour utiliser les fonctions math ematiques standards, on utilise la directive #include<math.h>, ou encore, pour utiliser des fonctions traitant de lallocation m emoire, de conversion des chaines de caract` eres en type num eriques ou de tirages al eatoires, on utilisera la directive #include<stdlib.h>. Les directives au pr eprocesseur peuvent egalement d enir des macros. Elles seront d etaill ees au Chapitre 3. Ci-dessous, voici un premier exemple dun programme (assez lourd...) qui calcule le maximum de 5 et 9.

17

CHAPITRE 2. LES BASES DE LA PROGRAMMATION EN C

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

/* Programme calculant le maximum de deux nombres entiers fix es*/ #include<stdio.h> int Max(int x, int y) { if(x>y) return x; else return y; } int main(void) { int m; m=Max(5,9); printf("le maximum de 5 et 9 est %d.\n",m); return 0; }

Ce programme est construit de la mani` ere suivante : La ligne 1 contient un commentaire qui indique ce queectue le programme. Cette ligne est optionnelle, mais indispensable... La Ligne 2 indique que le programme peut faire appel ` a des fonctions de la biblioth` eque standard stdio.h, en loccurrence, le programme contient un appel ` a la fonction printf qui permet dacher des donn ees ` a l ecran. Les lignes 3 ` a 9 constituent la d enition de la fonction Max. La ligne 3 est appel ee en-t ete de la fonction ; elle indique que la fonction a pour nom Max, prend en argument deux entiers (de type int) qui sont aect es aux variables x et y et retourne un nombre entier (type int). Le corps de la fonction Max est constitu e des lignes 4 ` a 9. Ces lignes d etaillent quoi faire si une autre fonction fait appel ` a la fonction Max. Linstruction if, qui commence ligne 5 et nit ligne 9, sappuie sur la comparaison des valeurs des variables x et y. Si la valeur de x est sup erieure ` a celle de y, alors (instruction return de la ligne 6) la valeur x de est retourn ee comme r esultat ` a la fonction qui avait fait appel ` a la fonction Max. Sinon (instruction else de la Ligne 7), Cest la valeur de y qui sera retourn ee. (instruction return de la Ligne 8) Les lignes 10 ` a 16 constituent la d enition de la fonctionmain. Le corps de la fonction est constitu e des Lignes 11 ` a 16. On notera que la fonction ne pred ici aucun argument ( void pour vide...). La ligne 12 contient da d enition dune variable m de type int. A laquelle on aectera le r esultat du calcul du maximum. Linstruction de la ligne 13 appelle la fonction Max pour calculer le maximum des deux nombres qui lui sont transmis en arguments. La valeur retourn ee par la fonction Max sera aect ee ` a la variable m (via lop erateur daectation =). Linstruction de la ligne 14 fait appel ` a la fonction pour acher ` a l ecran la chaine de caract` eres suivante : Le maximum de 5 et 9 est xxx o` u xxx est la valeur de la variable m. 18

2.3. Les types de donn ees pr ed enis

Linstruction de la ligne 15 demande que la valeur 0 soit retourn ee au syst` eme dexploitation pour lui indiquer que le programme na rencontr e aucune erreur de fonctionnement. Une fois compil e, lex ecution du programme se d eroule comme suit : 1. la fonction main est appel ee par le syst` eme dexploitation, 2. la fonction Max est appell ee avec les valeur 5 et 9 (Ligne 13) qui sont aect ees respectivement aux variables x et y de la fonction Max. 3. la condition x>y est test ee (ligne 5) : elle est fausse, linstruction introduite par le mot cl e else est donc execut ee et la valeur 9 est retourn ee ` a la fonction main. 4. la valeur 9 est aect ee ` a la varible m (toujours Ligne 13). 5. la chaine de caract` eres Le maximum de 5 et 9 est 9 est ach ee (Ligne 14). 6. lexecution de linstruction return 0 ; (Ligne 15) rend la main au syst` eme dexploitation en lui indiquant que le programme sest bien d eroul e.

2.3

Les types de donn ees pr ed enis

Le langage C est un langage typ e : toutes les variables et les constantes, ainsi que les valeurs retourn ees par les fonctions sont dun type sp eci e. Le type dun objet sp ecie la fa con dont il est repr esent e en m emoire (quel volume, quel norme de codage...), ainsi que les fonctions qui lui sont applicables. Dans la m emoire dun ordinateur, les donn ees sont stock ees sous forme doctets (s eries de 8 bits). Chaque octet est caract eris e par son adresse, qui est un entier. Deux octets contigus ont des adresses qui di` erent dune unit e. Lorsque lon d enit une variable dun certain type, on aecte ` a son identicateur une zone de m emoire caract eris ee par son adresse et dont la longueur est x ee par le type. Les types de base en C concernent : les caract` eres, les nombres entiers, les nombres ottants (qui approximent les nombres r eels). Les mots-cl es permettant de les d esigner sont parmis les suivants : char int float double short long unsigned Il existe egalement un autre type : void, qui repr esente le vide (on rien). Il nest pas possible de d eclarer une variable de type void mais ce type est utile, notamment pour sp ecier (de mani` ere propre) quune fonction ne prend pas dargument ou quelle ne revoie pas de valeur. Nous verrons cela au Chapitre 3

2.3.1

Les types caract` eres

Le type char (de langlais character) permet de repr esenter les charact` eres de la machine utilis ee (symboles du clavier...). Ce type est un peu particulier parce que sa taille d enit lunit e de calcul pour les quantit es de m emoire (et donc pour les tailles des autres types du langage). Par d enition, la taille du type char, not ee sizeof(char), vaut toujours 1 (on reviendra sur cette fonction au Chapitre 4, section 4.2). Cependant, il faut faire attention : contrairement ` a ce quon pense souvent, un char au sens du C ne vaut pas toujours un octet. Il occupera au minimum 8 bits certes, mais il existe des architectures, relativement sp ecialis ees il est vrai, codant les char sur 9 bits ou 16 19

CHAPITRE 2. LES BASES DE LA PROGRAMMATION EN C

bits, voire plus. Dans une large majorit e des cas, les compilateurs utilisent des char de 8 bits, ` a la fois par simplicit e (les machines modernes fonctionnent g en eralement en 8, 16, 32 ou 64 bits) et pour eviter des probl` emes de portabilit e de code (beaucoup de codes C existants reposent sur lhypoth` ese que les char sont cod es sur 8 bits, et risque de ne pas marcher sur une autre architecture). Par souci de simplication, on utilisera le terme octet la plupart du temps. Type de donn ee char unsigned char Signication Caract` ere Caract` ere non sign e Nb Octets 1 1 Plage de valeurs accept ees -128 ` a 127 0` a 255

Tab. 2.1 Les types caract` eres Les caract` eres sont en fait repr esent es dans lordinateur sous forme dentiers. Les machines utilisent le jeu de caract` eres (en France, typiquement la norme ISO-8859), cod e sur 8 bits dont les 128 premiers charact` eres correspondent au code ASCII, les 128 derniers etant utilis es pour coder les caract` eres sp eciques au caract` eres propres aux di erentes langues (caract` eres accentu es, caract` eres cyrilliques, caract` eres de la langue arabe, etc... selon la norme choisie). La correspondance entre caract` eres imprimables et code ASCII est pr esent ee aux Tableaux 2.7 et 2.8 en n de chapitre. Les caract` eres ASCII inf erieurs ` a 33 ne sont pas achables, il sagit de caract` ere sp eciaux et de caract` eres de formatage (caract` ere non achables ` a proprement parler, qui inuencent la mise en page). Comme un objet de type char peut etre assimilable ` a un entier, tout caract` ere peut etre utilis e dans une expression qui utilise des objets de type entiers. Par exemple, lexpression a+1 pour un objet a de type char a un sens : elle d esigne le caract` ere suivant dans le code ASCII, cest-` a-dire le caract` ere b ou lentier 98.

2.3.2

Les types entiers

Le mot-cl e d esignant une donn ee de type entier est int. On peut faire pr eceder le mot-cl e int par un attribut de pr ecision (short ou long) et/ou dun attribut de repr esentation (signed ou unsigned). Type de donn ee short int unsigned short int int unsigned int long int unsigned long int Signication Entier court Entier court non sign e Entier Entier non sign e Entier long Entier long non sign e Nb Octets 2 2 2 4 2 4 4 4 Plage de valeurs accept ees -32 768 ` a 32 767 0` a 65 535 -32 768 ` a 32 767 -2 147 483 648 ` a 2 147 483 647 0` a 65 535 0` a 4 294 967 295 -2 147 483 648 ` a 2 147 483 647 0` a 4 294 967 295

Tab. 2.2 Les types entiers Selon la machine sur laquelle ont travaille, la taille de la m emoire allou ee ` a un objet de type int peut varier, mais elle est en g en eral de 2 ou 4 octets (selon le processeur) et toujours sup erieure ` a celle alou ee pour un char. De mani` ere g en erale, pour connaitre le nombre doctets n ecessaire pour stocker un objet, on utilise le mot cl e sizeof, qui a pour syntaxe : sizeof(expression ) o` u expression est un type ou un objet. Cette fonction renvoie le nombre doctets necessaire au stockage de expression sous la forme dun entier. 20

2.3. Les types de donn ees pr ed enis

Petite remarque ` a propos de la repr esentation binaire des entiers sign es : En supposant quun objet de type entier soit cod e sur 4 octets, cest-` a-dire 32 bits, le bit de poids fort (le premier) correspond au codage du signe (0 si positif, 1 si strictement n egatif). Pour les nombres positifs, les 31 bits qui suivent correspondent ` a l ecriture binaire du nombre (do` u une borne sup erieure en 231 1), complet ee ` a gauche par des z eros. Pour les nombres n egatifs, les 31 bits qui suivent correspondent ` a la valeur absolue du nombre, repr esent ee par la technique du compl ement ` a 2 : le nombre est ecrit en binaire, compl et e ` a gauche par des z eros, puis les bits sont invers es (les 1 deviennent des 0 et les 0 deviennent des 1) et enn, on rajoute 1 au r esultat. Lattribut unsigned signiant labsence de signe, un unsigned int cod e sur 4 octets peut donc 32 repr esenter un entier entre 0 et 2 1. Petite remarque ` a propos des bool eens : Le langage C (jusqu` a la norme C99) ne fournit pas de type bool een. La valeur enti` ere 0 prend la valeur de v erit e FAUX et toutes les autres valeurs enti` eres prennent la valeur de v erit e VRAI : toute expression utilisant des op erateurs bool eens (voir Paragraphe 2.5), retourne 0 si lexpression est fausse, et retourne quelque chose de non nul si lexpression est vraie. La norme C99 a introduit le type _Bool, qui peut contenir les valeurs 0 et 1. Elle a aussi ajout e len-t ete <stdbool.h>, qui d enit le type bool qui est un raccourci pour _Bool, et les valeurs true et false. Ces nouveaut es du C99 ne sont pas tr` es utilis ees, la mauvaise habitude ayant et e prise dutiliser 0 et di erent de z ero pour les bool eens en C.

2.3.3

Les types ottants

Les types float, double et long double sont utilis es pour repr esenter des nombres ` a virgule ottante, qui correspondent ` a des approximations de nombres r eels ` a des degr es de pr ecision di erents. Type de donn ee float double long double Signication Flottant Flottant double Flottant double long Nb Octets 4 8 10 Plage de valeurs accept ees 38 38 3, 4 10 a 3, 4 10 env. ` 308 1, 7 10 a 1, 7 10308 env. ` 4932 3, 4 10 a 3, 4 104932 env. `

Tab. 2.3 Les types ottants Ces types sont en g en eral stock es en m emoire selon la repr esentation de la virgule ottante normalis ee : s1.x---xB^y---y. Le symbole s repr esente le signe. La plage de chires x---x est appell ee mantisse et la plage de chires y---y est lexposant ; leurs tailles d ependent de la pr ecision choisie. Lentier B repr esente la base (2 en g en eral). Un ottant est repr esent e ainsi : le bit de poids fort donne le signe, lexposant est ensuite repr esent e en binaire puis les bits de poids faible repr esentent la mantisse. Attention : Les nombres ottants peuvent pas repr esenter tous les r eels... Certaines approximations sont faites, puisque la majorit e des nombres r eels ont une expansion binaire innie... Le codage des variables de type float se fait sur 4 octets. Le premier chire repr esente le signe (0 si positif, 1 si n egatif). Les chires suivants sobtiennent comme suit : On ecrit dans un premier temps la valeur qui lon doit repr esenter en binaire : 2N + aN 1 2N 1 + . . . a1 2 + a0 + a1 21 + a2 22 + . . . , Les 8 chires suivants le code du signe repr esentent alors N + 127 (en binaire, eventuellement compl et e par des z eros). 21

CHAPITRE 2. LES BASES DE LA PROGRAMMATION EN C

Les 23 chires restants sont donn es en utilisant la r` egle suivante : aN 1 . . . a1 a0 a1 a2 . . . aN 23 si aN 24 est nul N 1 i le code binaire de N et e` a gauche par des z eros si aN 24 est egal ` a 1. 23 ai 2 + 1 compl Ainsi, lorsquon aecte ` a une variable de type float la valeur d ecimale 0, 1, dont la repr esentation binaire est donn ee par : 2 (0, 1) = 0.0001100110110011001100110011001100 = 24 1.1001101100110011001100 . . . Les 4 octets repr esentant cette valeur en m emoire sont : 0
signe

01111011 1001101100110011001101
exposant mantisse

Un arrondi est eectu e (dernier chire de la mantisse), de sorte que ce nest pas exactement la constante d ecimale 0, 1 qui est repr esent ee en m emoire. Ces arrondis peuvent donner lieu ` a des ecarts entre les r esultats attendus et les r esultats fournis par lordinateur lors de calculs avec des ottants.

2.3.4
2.3.4.1

Les constantes
Constantes num eriques

Une constante enti` ere peut etre repr esent es dans 3 bases di erentes : 10, 8 ou 16. voici les di erentes conventions d ecriture : Les constantes d ecimales (en base 10) utilisent repr esentation usuelle. Ex : 0, 256, -34678956734. Les constantes octales (en base 8) commencent par un z ero. Ex : les repr esentations octales de 0 et 255 sont respectivement 00 et 0377. Les constantes h exad ecimales (en base 16) commencent par un z ero suivi dun x et sont repr esent ees en utilisant les lettres a ` a f pour les entiers de 10 ` a 15. Ex : les repr esentations octales de 13 et 249 sont respectivement 0xd et 0xf9. Par d efaut, une constante enti` ere est stock ee avec le format interne le plus court qui permet de la repr esenter. On peut cependant forcer son format en ajoutant un suxe U pour indiquer que lon d esire un entier non sign e et/ou en ajoutant une suxe L pour indiquer quelle doit etre de type long. De m eme on peut forcer une ` a stocker une constante enti` ere dans le format ottant en lui ajoutant un point en suxe. Les constantes r eelles sont repr esent ees par la notation classique mantisse e exposant . Cest un nombre d ecimal eventuellement sign e qui est par d efaut de type double. On peut forcer son format en ajoutant un suxe F pour indiquer que lon d esire un format float ou en ajoutant une suxe L pour indiquer quelle doit etre de type long double. 2.3.4.2 Constantes caract` eres et chaines de caract` eres

Pour d esigner un caract` ere imprimable, il sut de le mettre entre guillemets simples : A, $. Les seuls caract` eres imprimables que lon ne peut pas repr esenter de cette mani` ere sont lantislash et le guillemet simples que lon designe en utilisant \\ et \. Les doubles guillemets sont repr esent es par \". Quant aux caract` eres non imprimables, on utilise la convention : \code o` u code est le code octal ou le code h exad ecimal pr ec ed e dun x du caract` ere dans la norme ASCII (voir tableau 2.7). Par exemples \11 indique une tabulation horizontale avec son code octal et \x1b indique une caract` ere Escape avec son code h exad ecimal. Pour les caract` eres non imprimables usuels, il y a quand m eme des raccourcis, donn es dans le Tableau 2.4 22

2.4. Expressions

Caract` ere \0 \a \b \f \n \r \t \v \\ \ \"

Correspondance caract` ere null cloche ou bip retour arri` ere (backspace) saut de page saut de ligne retour chariot tabulation horizontale tabulation verticale antislash apostrophe guillemets

Tab. 2.4 Caract` eres sp eciaux Une chaine de caract` eres est une suites de caract` eres, imprimables ou non, entour es de guillemets (doubles). Ex : "ceci est ce quon appelle \"une chaine de caract` eres\"\n" On reviendra en d etails sur les cha nes de caract` eres au Chapitre 4.

2.4

Expressions

Une expression est une suite de symboles form ee ` a partir de constantes litt erales, dindenticateurs de variable et dop erateurs (les op erateurs usuels seront d etaill es au paragraphe suivant). On parle dexpression simple lorquelle ne contient pas dop erateur comme par exemple : x, 12, et on parle dexpression compos ee si elle en contient, comme par exemple : -x, x+12, (x>4)&&(x<10). Une expression est destin ee ` a etre evalu ee. Elle a toujours un type et une valeur. Par exemple, si le programme est dans un etat o` u la variable x de type int est visible et a pour valeur 8, alors : lexpression x est de type int et a pour valeur 8, lexpression 12 est de type int et a pour valeur 12, lexpression -x est de type int et a pour valeur -8, lexpression x+12 est de type int et a pour valeur 20, lexpression (x>4)&&(x<10) est de type int et a pour valeur le bool een VRAI (repr esent e par un entier non nul), Une expression peut eventuellement avoir aussi une adresse m emoire (cest le cas pour les indenticateur de variables) et son evaluation peut produire des eets de bord : modication de l etat de la m emoire, d eclenchement dentr ees-sorties... 23

CHAPITRE 2. LES BASES DE LA PROGRAMMATION EN C

2.5
2.5.1

Les op erateurs
Aectation

Laectation en C est symbolis ee par le signe =. On lutilise comme suit : variable = expression Cet op erateur evalue lexpression expression et aecte le r esultat ` a la variable variable . Cette expression poss` ede une valeur qui est celle du r esultat de expression : lexpression a=2 vaut 2. Cest un op erateur ` a eet de bord puisquil modie la valeur ` a lemplacement en m emoire associ e a la variable variable : une aectation substitue le r ` esultat de lexpression de droite au contenu de la variable. Attention : Laectation eectue une conversion de type implicite : si variable et le r esultat de expression ne sont pas du m eme type, alors le r esultat de expression est convertie dans le type de variable . Si la conversion est impossible, alors un message derreur est renvoy e.

2.5.2

Op erateur de conversion de type : transtypage

Puisque tous les types sont repr esent es sous forme de plages de 0 et de 1, on peut traduire un objet dun certain type dans un autre type. On dit que lon fait une op eration de transtypage (cast en aglais), ou (par mauvais raccourci) que lon caste un objet dun type ` a un autre (francisation litt erale du verbe anglais to cast, que lon peut traduire par d epouiller, mouler ou bien dautres encore...). Le C est un langage peu typ e, dans le sens o` u les r` egles de transtypage sont assez larges (dautres langages ne le permettent pas ou le permettent, mais sous des conditions tr` es strictes). Il peut arriver par exemple que lon veuille travailler sur un type de variable, puis lutiliser sous un autre type. Imaginons que lon travaille par exemple sur une variable en virgule ottante (type float), il se peut que lon veuille supprimer les chires apr` es la virgule, cest-` a-dire convertir un float en int. Cette op eration peut etre r ealis ee de deux mani` eres : soit par conversion implicite : une conversion implicite consiste en une modication du type de donn ee eectu ee automatiquement par le compilateur. Cela signie que lorsque lon va stocker un type de donn ee dans une variable d eclar ee avec un autre type, le compilateur ne retournera pas derreur mais eectuera une conversion implicite de la donn ee avant de laecter ` a la variable. int x; x = 8.324; x contiendra apr` es aectation la valeur 8. soit par conversion explicite : une conversion explicite (appel ee aussi op eration de cast) consiste en une modication du type de donn ee forc ee. Cela signie que lon utilise un op erateur dit de cast pour sp ecier la conversion. Lop erateur de cast est tout simplement le type de donn ee, dans lequel on d esire convertir une variable, entre des parenth` eses pr ec edant la variable. int x; x = (int)8.324; x contiendra apr` es aectation la valeur 8. La conversion explicite est ` a privil egier car elle nest pas ambig ue.

2.5.3

Op erateurs arithm etiques

Les op erateurs arithm etiques classiques sont : lop erateur unaire de changement de signe : -, les quatres op erations usuelles : +, -, *, /, lop erateur modulo : % qui donne le reste de la division enti` ere. 24

2.5. Les op erateurs

Remarque : Notez que la division / est la m eme pour les ottants et les entiers. Par d efaut, si les deux op erandes sont entiers, lop erateur / eectue la division enti` ere (Ex : 7/2=3). Si au moins un des deux op erandes est un ottant, alors, cest la division usuelle : (Ex : 7./2=3.5 ou 7/2.=3.5). lop erateur modulo % ne sapplique quant ` a lui qu` a des entiers.

2.5.4

Op erateurs daectation compos ee et op erateurs dincr ementation

Les op erateurs daectation compos ee usuels sont les suivants : += -= *= /= Ce sont des raccoucis : Lexpression exp1 op= exp2 est equivalente ` a exp1 = exp1 op exp2 . Cependant, lavantage dutiliser ces notations et que exp1 nest evalu ee quen seule fois lorsquon utilise lop erateur compos e, alors quelle est evalu ee 2 fois sinon. Le plus souvent lorsquon programme en C, on utilise des compteurs : les op erations du type i=i+1 ou j=j-1 apparaissent tr` es souvent dans des boucles. On a donc mis ` a notre disposition lop erateur dincr ementation ++ et lop erateur de d ecr ementation --. Ils sutilisent en suxe ou en pr exe : les instructions i=i+1;, i++; et ++i; incr emente i de 1. La di erence entre ces deux notations r eside dans la valeur de lexpression : si i=2, lexpression i++ a pour valeur 2, la valeur de i avant incr ementation, et ++i a pour valeur 3, la valeur de i apr` es incr ementation.

2.5.5

Op erateurs de comparaison

Pour comparer deux expressions exp1 et exp2 , on dispose des op erateurs bool eens de comparaison classiques : egalit e : exp1 ==exp2 , In egalit es strictes : exp1 <exp2 et exp1 >exp2 , In egalit es larges : exp1 <=exp2 et exp1 >=exp2 , Di erence : exp1 !=exp2 . Les deux expressions exp1 et exp2 sont evalu ees puis compar ees. Si la relation est fausse, alors cet op erateur renvoie le bool een false ( equivalent ` a lentier 0) et renvoie le bool een true ( equivalent a un entier non nul) si la relation est v ` eri ee. Attention Ne pas confondre lop erateur daectation = et le test d egalit e ==, surtout car les deux peuvent etre valides comme test de conditionnement.

2.5.6

Op erateurs logiques

Les op erateurs logiques sont la n egation : !(exp ), le ET logique : exp1 && exp2 , le OU logique : exp1 || exp2 . Si la relation est fausse, alors ces op erateurs renvoient le bool een false ( equivalent ` a lentier 0) et renvoie le bool een true ( equivalent ` a un entier non nul) si la relation est v eri ee. Voici un exemple dutilisation : int i,j,k; if((i>0)&&((j<=2)||(j>10))&&!(k==2)){ ... } else{ ... } 25

CHAPITRE 2. LES BASES DE LA PROGRAMMATION EN C

Dans cet exemple, les intructions du bloc if seront evalu ees si et seulement si la valeur de i est strictement positive, si la valeur de j est inf erieure ` a 2 ou strictement plus grande que 10, et si la valeur de k est di erente de 2. Si une de ces trois conditions nest pas v eri ee, alors les instructions contenues dans le block else seront execut ees. Remarque : Une expression du type exp1 && exp2 && exp3 peut etre ambig ue, notamment si deux des trois expressions peuvent cr eer des eets de bords et portent sur la m eme variable (les deux expressions d epend lune de lautre...). Dans ce cas, on pr efererai utiliser un cascade de conditionnement (en utilisant des if/else).

2.5.7

Op erateurs bit ` a bit

Il existe egalement des op erateurs logiques permettant de modier les entiers directement chire par chire ` a partir de leur ecriture binaire. Les op erateurs unaires bit ` a bit : & : ET bit ` a bit, qui retourne le chire 1 si les deux chires de m eme poids sont ` a 1. Ex : 9 & 12 (00001001 & 00001100) retourne 8 (00001000). | : OU bit-` a-bit, qui retourne le chire 1 si un des deux chires de m eme poids est ` a 1. Ex : 9 | 12 (00001001 | 00001100) retourne 13 (00001101). ^ : OU bit-` a-bit exclusif, qui retourne 1 si seulement lun des deux bits de m eme poids est ` a 1 (mais pas les deux). Ex : 9 ^ 12 (00001001 ^ 00001100) retourne 5 (00000101). Les op erateurs binaires bits ` a bit : ~ : compl ement ` a 1 , qui retourne 1 si le bit vaut 0 et vis-versa. Ex : ~9 (~00001001) retourne -10 (11110110). >> : d ecalage ` a droite (suivi dun nombre entier), qui eectue la division enti` ere par 2 ou une de ses puissances (d ecalage de l ecriture binaire) : l ecriture est d ecal ee ` a droite, les places lib er es ` a gauche sont remplies avec des 0. Ex : 9>>1 (00001001>>1) retourne 4 (00000100), Ex : 9>>2 (00001001>>2) retourne 2 (00000010), << : d ecalage ` a gauche) (suivi dun nombre entier), qui eectue und ecalage de l ecriture binaire ` a gauche, les places lib er es ` a droite sont remplies avec des 0. Ex : 9<<1 (00010010<<1) retourne 18 (00010010), Ex : 9<<2 (00001001<<2) retourne 36 (01001000). Attention : Ne confondez pas les op erateurs binaires bit ` a bit & et | avec les op erateurs de comparaison && et ||.

2.5.8

Op erateur adresse

La m emoire dun ordinateur est ordonn ee : chaque mot m emoire a une adresse. Lop erateur dadresse & appliqu e ` a une variable objet selon la syntaxe &objet renvoie ladresse-m emoire de cette variable. Cet op erateur sera etudi e plus en d etails au Chapitre 5. Il sera aussi utilis e pour la fonction de lecture sur lentr ee standard scanf. 26

2.6. Les instructions de branchement

2.5.9

Op erateur daectation conditionnel ternaire

exp1 ? exp2 : exp3 Cet op erateur renvoie exp2 si exp1 est vraie (si le r esultat nest pas nul) et renvoie exp3 sinon. Ex : linstruction m= a>b? a:b; aecte ` a m le maximum de a et de b.

2.6
2.6.1

Les instructions de branchement


Branchement conditionnel : if... else ...

Une instruction de branchement conditionnel a g en eralement la forme suivante : if ( test-1 ){ instructions-1 } else if ( test-2 ){ instructions-2 } else if ( test-3 ){ instructions-3 } ... else{ instructions-N } Les accolades ne sont pas n ecessaires si le bloc dinstruction instructions-i ne contient quune instruction. Si le test-1 est positif, alors seul le bloc instructions-1 est eectu e, sinon, on eectue le test-2 , sil est positif, alors seul le bloc instructions-2 est eectu e, etc... Dautre part, les blocs else if(){} et le test test-N sont optionnels et peuvent etre supprim es, le bloc else faisant alors oce de cas par d efaut donnant une syntaxe plus simple du type : if( test ){ instructionsIF } else{ instructionsELSE } Le bloc else est optionnel.

2.6.2

Branchement multiple : switch

Le bloc switch est une instruction de choix multiple qui permet de choisir un bloc dinstructions a r ` ealiser parmi un ensemble dinstructions possibles, en fonction de la valeur dune expression. switch( exp ){ case val-1 : instructions-1 break ; case val-2 : 27

CHAPITRE 2. LES BASES DE LA PROGRAMMATION EN C

instructions-2 break ; ... case val-N : instructions-N break ; default : instructions break ; } Si l evaluation de lexpression exp donne une valeur v parmi val-1 , val-2 , ... val-N alors les instructions instructionsv relatives ` a cette valeurs sont eectu ees. Le cas default : permet denglober toutes les cas o` u exp donne une valeur qui nest pas parmi val-1 , val-2 , ... val-N . Linstruction break ; fait ensuite sortir le programme du block switch : m eme si la valeur de lexpression exp est modi ee par les instructions instructions-v , les instructions relatives ` a cette nouvelle valeur ne seront pas eectu ees. Le bloc introduit par la valeur default nest pas non plus execut e. Des instructions peuvent etres relatives ` a plusieurs valeurs de exp . Par exemple, si le bloc dinstructions instructions est ` a executer sur la valeur vaut val-1 et val-2 , on peut replacer : case val-1 : instructions break ; case val-2 : instructions break ; par : case val-1 : case val-2 : instructions break ;

2.7

Les boucles

Une boucle, dun mani` ere g en erale, permet deectuer une suite dinstructions tant quune certaine condition est v eri ee, par exemple, rajouter 2 ` a un entier initialis e` a 1, tant que le r esultat ne d epasse pas 100. A son execution, le programme repassera plusieurs fois sur les m emes instructions (do` u le nom de boucle), autant de fois que n ecessaire : jusqu` a ce que la condition ne soit plus v eri ee. Trois structures sont ` a notre disposition pour executer une boucle : while do ... while for

2.7.1

La boucle while

Linstruction while ` a la forme suivante. while( exp ){ instructions }

28

2.7. Les boucles

Lexpression exp est le test de continuation (qui renvoie un bool een) et le bloc dinstructions instructions constitue le corps de la boucle. Tant que le test de continuation est vrai, le corps de la boucle est eectu e. Si le test de continuation est faux au d epart, le corps de la boucle nest pas eectu e. Attention : si le corps de la boucle ne modie pas la valeur de lexpression exp , il se peut que le programme eectue ind eniment la boucle. Il faut sassurer que le test de continuation va nir par devenir faux. Ci-dessous, un exemple de boucle while qui ache ` a l ecran les entiers positifs dont les carr es sont strictement inf erieurs ` a 100. int i=0; int n=0; while(n<100){ printf("%d\n",i); i++; n=i*i; }

2.7.2

La boucle do ... while

Linstruction do ... while prend la forme suivante : do{ instructions } while( exp ) ; Comme pour la boucle while, lexpression exp est le test de continuation (qui renvoie un bool een) et le bloc dinstructions instructions constitue le corps de la boucle. ` Important :A la di erence de la boucle while, levaluation du test de continuit e se fait APRES lexecution des instructions. Par exemple, si lon veut demander ` a lutilisateur dun programme de rentrer un nombre entre 1 et 10, le bloc dinstructions : int a; do{ printf("/n Entrez un nombre compris entre 1 et 10: "); scanf("%d",a); } while((a<0)||(a>10)) permet de sassurer que le nombre rentr e par lutilisateur est eectivement bien compris entre 1 et 10 avant de poursuivre son execution. Si on reprend lexemple pr ec edent dune boucle qui ache a ` l ecran les entiers positifs dont les carr es sont strictement inf erieurs ` a 100 en utilisant do ... while, on obtient fois-ci : int i=0; int n; do{printf("%d\n",i); n=i*i; i++; } while(n<100); 29

CHAPITRE 2. LES BASES DE LA PROGRAMMATION EN C

Contrairement ` a la m eme boucle en utilisant while, lincr ementation de i se fait apr` es avoir calcul e son carr e.

2.7.3

La boucle for

Linstruction for prend la forme suivante : for( exp1 ; exp2 ; exp3 ){ instructions } Une formulation equivalente plus intuitive (mais ne r epondant pas au standard ANSI-C...) est la suivante : exp1 ; while(exp2 ){ exp3 ; instructions } Linstruction for int` egre donc : linitialisation (exp1 ), la condition de continuation (exp2 ), l evolution (exp3 ) de la variable dont d epend la condition de continuation. Par exemple, pour calculer et ache la somme des entiers de 1 ` a 10, on utlise le bloc dinstructions suivant : int somme,n; somme=0; for(n=1;n<=10;n=n+1){ somme=somme+n; } printf("Somme des entiers de 1 ` a 10: %d",somme); On peut aussi utiliser les op erateurs dincr ementation et aectation compos ee et ecrire : int somme,n; somme=0; for(n=1;n<=10;n++){ somme+=n; } printf("Somme des entiers de 1 ` a 10: %d",somme);

2.7.4

Choisir entre while, do ... while et for

Ces trois instructions sont equivalentes. Voici quelques consid erations pour vous aider ` a choisir laquelle utiliser selon la nature du bloc que vous souhaitez programmer : Lorsque le nombre dit erations d epend dun param` etre dont les valeurs initiale et nale et lincr ementation sont connus avant lexecution de la boucle, on utilise plut ot une boucle for. 30

2.8. Fonctions dentr ees-sorties

Dans le cas o u le test de sortie, et donc le nombre dit erations, d ependent dun calcul fait dans le corps de la boucle ou que celui-ci ne peut sapparenter ` a une incr ementation simple, on utilisera un boucle while ou do ... while plutot quune boucle for. Pour choisir entre while ou do ... while, tout d epend du programme : garder en t ete que do ... while eectue une fois les instructions avant de faire le test (on rentre forc ement pour au moins une it eration dans la boucle) alors que while eectue en premier lieu le test (les instructions peuvent ne jamais etre eectu ees).

2.7.5

Instructions de branchement non conditionnel dans les boucles

Linstruction break; peut etre employ e` a linterieur dune boucle ( while, do ... while ou for) pour interrompre son d eroulement. Il interompt lexecution des instructions du bloc et termine la boucle la plus interne (comme si le test de continuation n etait plus v eri e). Par exemple le bloc : int i; for(i=1;i<5;i++){ printf("i=%d, ",i); if(i==3) break; } printf("\n valeur de i en sortie de boucle: %d",i); va imprimer ` a l ecran la chose suivante : i=1, i=2, i=3 valeur de i en sortie de boucle: 3 Linstruction continue; quant ` a elle permet dinterompre lexecution des instructions du bloc et de retourner au test de continuation. Par exemple, le bloc dinstruction : int i; for(i=1;i<5;i++){ if(i==3) continue; printf("i=%d, ",i); } printf("\n valeur de i en sortie de boucle: %d",i); va imprimer ` a l ecran la chose suivante : i=1, i=2, i=4, i=5 valeur de i en sortie de boucle: 5

2.8

Fonctions dentr ees-sorties

Chaque unit e dentr ee ou de sortie constitue un ux de donn ees. Les fonctions de lecture et d ecriture sur lentr ee et les sorties standards agissent exactement comme les fonctions de lecture et d ecriture dans des chiers (que lon d etaillera au Chapitre ??). Len-t ete <stdio.h> (abr eviation de STandarD In - Out) fournit trois ux que lon peut utiliser directement : stdin, lentr ee standard, stdout, la sortie standard, 31

CHAPITRE 2. LES BASES DE LA PROGRAMMATION EN C

stderr, la sortie standard des erreurs. Souvent, lentr ee standard envoie au programme les donn ees issues du clavier, et les sorties standard envoient les donn ees que le programme g en` ere ` a l ecran. Mais do` u viennent et o` u vont ces donn ees d epend etroitement du contexte et de limplantation.

2.8.1

Les fonctions putchar et getchar

Les op erations dentr ees-sorties les plus simples sont celles qui lisent et ecrivent un caract` ere. Il sagit de fonctions dentr ees-sorties non format ees. La lecture dun caract` ere sur le ux dentr ee standard est r ealis ee par la fonction getchar. Cette fonction na pas dargument et retourne un int qui correspond au code du caract` ere lu : pour mettre le caract` ere lu dans la variable caractere (de type int), on utilise linstruction suivante : caractere=getchar(); Puisque le ux dentr ee est toujours consid er e comme un chier, cette fonction peut eventuellement d etecter la n du chier et retourner lentier EOF (End Of File) qui, par convention, termine toujours un chier et dont la valeur est d enie dans la biblioth` eque stdio.h (en g en eral, cet entier vaut 1). Lentier EOF est aussi renvoy e en cas derreur de lecture. L ecriture dun caract` ere sur lunit e de sortie standard est r ealis ee par la fonction putchar qui prend en argument une variable de type int. Pour acher le caract` ere dont la conversion en int est caractere, on utilise linstruction suivante : putchar(caractere); La valeur de retour est lentier caractere ou EOF en cas derreur. Un petit exemple tout simple de bloc qui lit un caract` ere unique sur le ux dentr ee standard et l ecrit sur le ux de sortie standard : { int c; c=getchar(); if(c!=EOF) putchar(c); } Un autre exemple : le bloc suivant lit lentr ee standard et la recopie, caract` ere par caract` ere dans la sortie standard jusquau premier retour-chariot (si lentr ee standard est le clavier, le retour chariot correspond au caract` ere \n) : { char c; while((c=getchar())!=\n) putchar(c); } Petite Remarque : A propos du test (c=getchar())!=\n : dune part, laectation c=getchar() convertit implicitement lentier renvoy e par getchar en char. Dautre part, la valeur de lexpression (c=getchar()) est lentier renvoy e par getchar, cest ` a dire lentier codant le caract` ere lu dans lentr ee standard. Le test (c=getchar())!=\n compare deux objets de type int car la valeur de lexpression \n est lentier correspondant au caract` ere de retour chariot. 32

2.8. Fonctions dentr ees-sorties

2.8.2

Ecriture avec format : printf

La fonction printf, contenue dans la biblioth` eque stdio.h ecrit des evaluations dexpressions sur lunit e de sortie standard, selon un format sp eci e. Son appel a la forme suivante : printf("chaine-de-controle-du-format ", exp1,exp2,...,expN ) Le parametre chaine-de-controle-du-format contient les caract` eres ` a acher et les sp ecications de format d ecriture correspondants aux param` etres exp1 , exp2 , ..., expN . La chaine de controle du format est indispensable car la fonction printf a un nombre variable darguments. Ces sp ecications sont introduites par le signe % (pour acher simplement le signe pourcentage ` a l ecran, on utilisera %%). Les formats sont donn es dans le Tableau 2.5. Format %d %ld %u %o %x %lu %lo %lx %f %e %g %lf %le %lg %c %s %p Type de donn ee int long int unsigned int Ecriture d ecimale sign ee d ecimale sign ee d ecimale non sign ee octale non sign ee h exad ecimale non sign ee d ecimale non sign ee octale non sign ee h exad ecimale non sign ee d ecimale, virgule xe d ecimale, notation exponentielle d ecimale, repr esentation la plus compacte parmi %f et %e d ecimale, virgule xe d ecimale, notation exponentielle d ecimale, repr esentation la plus compacte parmi %f et %e caract` ere cha ne de caract` eres h exad ecimale non sign ee ( equivalent ` a %lu)

unsigned long int

double

long double char char* pointeur

Tab. 2.5 Tableau des formats pour la fonction printf Les chaines du type %zz apparaissant dans chaine-de-controle-du-format seront remplac ees a limpression par les valeurs des expressions exp1 , exp2 , ..., expN (dans lodre dapparition). Si la ` valeur dune expression nest pas du type indiqu e par le format, celle-ci sera convertie. Attention : V erier bien que vous avez autant dexpressions en param` etre que de signes % dans votre chaine de contr ole.... On peut eventuellement pr eciser certains param` etres du format dimpression en ins erant une pr ecision entre le symbole % et le ou les caract` eres pr ecisant le format : Pour les entiers, on peut sp ecier la largeur du champs minimal dimpression. Ex : linstruction %10b indique quau moins 10 caract` eres seront r eserv es ` a lachage de la valeur dune variable de type int. Par d efaut, la donn ee est cadr ee ` a droite du champs de 10 caract` eres, pour quelle soit cadr ees ` a gauche, on utilise %-10d. Pour les ottants, on peut raner lutilisation de %f et %lf en choisissant la pr ecision (nombre de chires apr` es la virgule). Ex : si on veut acher uniquement les 4 premi` eres d ecimale de la valeur dun variable de type double, on utilise le format %.4f. SI la pr ecision nest pas sp eci ee, elle est de 6 chires apr` es 33

CHAPITRE 2. LES BASES DE LA PROGRAMMATION EN C

la virgule. De m eme, le format %6.4f indique que lon r eserve 12 caract` eres ` a l ecriture de la valeur dune variable de type double, avec une pr ecision de 4 chires apr` es la virgule. Pour les cha nes de caract` eres, on peut egalement sp ecier la plage r eserv ee ` a l ecriture des caract` eres dune chaine ainsi que le nombre de caract` eres qui seront imprim e. Ex : en utilisant le format %30.4s On r eserve un champs de 30 caract` eres pour ecrire les 4 premiers caract` eres dune chaine de caract` eres (sacheront alors les 4 premiers lettres composant la cha ne, suivis de 26 blancs). Voici un petit exemple de programme regroupant plusieurs impressions ` a l ecran en utilisant di erentes sp ecications de format : #include<stdio.h> int main(){ int n=45; double x=1e-8+100; char lettre=A; char * chaine="est le double de"; printf("\n1. petite chaine toute simple sans argument"); printf("\n2. Le code ASCII du caract` ere %c est %d ",lettre,lettre); printf("\n3. Lentier %d a pour code octal %o et pour code h exad ecimal %x",n,n,n); printf("\n4. %s %d %s %d","Le nombre",n*2,chaine,n); printf("\n5. %.3f est le nombre flottant %.10e pr ecis e au milli` eme",x,x); printf("\n6. Utiliser le format %%f nest pas suffisant pour afficher x:\n son utili sation donne %f",x); printf("\n7. |%-6s||%6s|\n", "un","deux"); return 0; } L execution de ce programme achera ` a l ecran les lignes suivantes : 1. petite chaine toute simple sans argument 2. Le code ASCII du caract` ere A est 65 3. Lentier 45 a pour code octal 55 et pour code h exad ecimal 2d 4. Le nombre 90 est le double de 45 5. 100.000 est le nombre flottant 1.0000000001e+02 pr ecis e au milli` eme 6. Utiliser le format %f nest pas suffisant pour afficher x: son utilisation donne 100.000000 7. |un || deux|

2.8.3

Lecture avec format : scanf

La fonction scanf, egalement d enie dans la biblioth` eque stdio.h permet de lire des valeurs sur lunit e dentr ee standard, selon un format sp eci e en argument et les inscrire dans des cases m emoires dont les adresses sont fournies en arguments. Son appel a la forme suivante : scanf("chaine-de-controle-du-format ", arg-1,arg-2,...,arg-N ) 34

2.8. Fonctions dentr ees-sorties

En fait la plupart du temps, les arguments arg-i des adresses m emoires o` u sont stock ees des variables et sont donc de la forme &var-i o` u var-i est lidenticateur dune variable et & est lop erateur dadressage. Ainsi, le plus souvent, on fait appel ` a la fonction scanf de la mani` ere suivante : scanf("chaine-de-controle-du-format ", &var-1,&var-2,...,&var-N ) Comme pour printf, la chaine-de-controle-du-format contient les sp ecications de format des caract` eres ` a r ecup erer ` a partir du ux dentr ee et pour leur mise en m emoire aux adresses &var-1 , &var-2 ,..., &var-N . Ces sp ecications sont introduites par le signe %. Les formats sont donn es dans le Tableau 2.6. Format %d %o %x %hd %ho %hx %hd %ho %hx %u %hu %lu %f %e %g %lf %le %lg %Lf %Le %Lg %c %s Type de donn ee int Repr esentation de la donn ee saisie d ecimale sign ee octale h exad ecimale d ecimale sign ee octale h exad ecimale d ecimale sign ee octale h exad ecimale d ecimale non sign ee d ecimale non sign ee d ecimale non sign ee d ecimale, virgule xe d ecimale, notation exponentielle d ecimale, repr esentation la plus compacte parmi %f et %e d ecimale, virgule xe d ecimale, notation exponentielle d ecimale, repr esentation la plus compacte parmi %f et %e d ecimale, virgule xe d ecimale, notation exponentielle d ecimale, repr esentation la plus compacte parmi %f et %e caract` ere cha ne de caract` eres

short int

long int unsigned int unsigned short int unsigned long int float

double

long double char char*

Tab. 2.6 Tableau des formats pour la fonction scanf Lorsquune fonction fait appel ` a scanf, elle se met en attente dune saisie sur le ux dentr ee standard (le clavier, a priori). Tant que la lecture de la chaine-de-controle-du-format nest pas termin ee, ses caract` eres sont lus les uns apr` es les autres et compar es ` a ceux du ux dentr ee et les actions suivantes sont r ealis ees : Si le caract` ere courant de la chaine de contr ole nest ni un %, ni un caract` ere despacement, alors le caract` ere courant du ux dentr ee doit etre lui etre identique. Si ce nest pas le cas, la lecture est interrompue. Les valeurs saisies dans la suite du ux dentr ee ne seront pas mises en m emoire aux adresses des variables pass ees aux arguments suivants. Si le caract` ere courrant de la chaine de contr ole est un caract` ere despacement (espace, tabulation), alors tous les caract` eres despacement et les retour chariot du ux dentr ee sont lus. Tous les caract` eres despacement de la chaine de contr ole du format sont egalement lus. Si le caract` ere courant de la chaine de contr ole est un %, alors la sp ecication que ce symbole introduit est lue puis la suite des caract` eres du ux dentr ee est lue jusquau prochain caract` ere 35

CHAPITRE 2. LES BASES DE LA PROGRAMMATION EN C

despacement. Si cette chaine de caract` eres est conforme ` a la sp ecication, alors elle est mise en m emoire au bon format ` a ladresse pr ecis ee en argument. sinon, la lecture est interrompue. La saisie se termine par un retour chariot. Attention : Lorsque la lecture du ux dentr ee est interrompue de mani` ere inopin ee, les el ements qui nont pas encore et es lus sont toujours en le dattente dans le ux dentr ee. Cela peut donner des choses etranges si le format nest pas respect e par lutilisateur, surtout si le programme comporte plusieurs appels ` a scanf. Dautre part, la fonction scanf g` ere mal les caract` eres depacement et les occurences de retour-chariot. Elle est ` a eviter si on doit lire des caract` eres et des chaines de caract` eres. Ci-dessous un petit exemple de programme et des probl` emes que la saisie peut engendrer. #include<stdio.h> int main(){ int n=0; int m=0; char caractere=A; printf("\nEntrez 2 nombres au format \"** et **\":"); scanf("%d et %d",&n,&m); printf("\nEntrez un caract` ere:"); scanf("%c",&caractere); printf("\nNombres choisis: %d et %d. Caract` ere choisi: %c.\n",n,m,caractere); return 0; } Voici plusieurs executions de ce programme (les caract` eres saisis par lutilisateur sont en gras, le symb ole repr esentera la saisie dune touche ENTREE). Lordre dachage est indiqu e: Ex ecution avec saisie correcte : 1. Entrez 2 nombres au format "** et **": 12 et 13 2. Entrez un caract` ere: B 3. Nombres choisis: 12 et 13. Caract` ere choisi: B. Execution avec saisie correcte : 1. Entrez 2 nombres au format "** et **": 12 2. Entrez un caract` ere: Ex ecution avec saisie incorrecte : 1. Entrez 2 nombres au format "** et **": 12 te 13 2. Entrez un caract` ere: Nombres choisis: 12 et 0. Caract` ere choisi: t. Ici, la lecture de 12 te 13 sest arr et ee au t. Dans le ux dentr e, il reste donc te 13 ` a lire. e Le 2 appel ` a scanf met donc en m emoire le caract` ere t ` a ladresse de lettre. cela peut egalement arriver si on saisit par exemple une lettre au lieu dun chire : 1. Entrez 2 nombres au format "** et **": 12 et B 2. Entrez un caract` ere: Nombres choisis: 12 et 0. Caract` ere choisi: B. 36 B et 13

3. Nombres choisis: 12 et 13. Caract` ere choisi: B.

2.8. Fonctions dentr ees-sorties

ou encore si on sait trop de parametres : 1. Entrez 2 nombres au format "** et **": 12 et 13 B 2. Entrez un caract` ere: Nombres choisis: 12 et 13. Caract` ere choisi: .

Il se peut egalement que sur un coup de bol, la saisie soit correcte, mais il ne faut pas y compter... Exemple : 1. Entrez 2 nombres au format "** et **": 12 et 13B 2. Entrez un caract` ere: Nombres choisis: 12 et 13. Caract` ere choisi: B.

37

CHAPITRE 2. LES BASES DE LA PROGRAMMATION EN C

NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US SP

d ec. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

oct. 0 1 2 3 4 5 6 7 10 11 12 13 14 15 16 17 20 21 22 23 24 25 26 27 30 31 32 33 34 35 36 37 40

Hex. 0 1 2 3 4 5 6 7 8 9 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20

Bin. 0 1 10 11 100 101 110 111 1000 1001 1010 1011 1100 1101 1110 1111 10000 10001 10010 10011 10100 10101 10110 10111 11000 11001 11010 11011 11100 11101 11110 11111 100000

Signication Null (nul) Start of Header (d ebut den-t ete) Start of Text (d ebut du texte) End of Text (n du texte) End of Transmission (n de transmission) Enquiry (demande) Acknowledge (accus e de r eception) Bell (Cloche) Backspace (espacement arri` ere) Horizontal Tab (tabulation horizontale) Line Feed (saut de ligne)ut de ligne) Vertical Tab (tabulation verticale) Form Feed (saut de page) Carriage Return (Retour chariot) Shift Out (n dextension) Shift In (d emarrage dextension) Data Link Escape Device Control 1 to 4 (contr ole des p eriph eriques) Negative Acknowledge (Accus e de r eception n egatif) Synchronous Idle End of Transmission Block (n du bloc de transmission) Cancel (annulation) End of Medium (n de support) Substitute (substitution) Escape ( echappement) File Separator (s eparateur de chier) Group Separator (s eparateur de groupe) Record Separator (s eparateur denregistrement) Unit Separator (s eparateur dunit e) Space(caract` ere Espace) en anglais

Tab. 2.7 Correspondance entre caract` eres non imprimables et code ASCII

38

2.8. Fonctions dentr ees-sorties

! # $ % & ( ) * + , . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P

d ec. 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80

oct. 41 42 43 44 45 46 47 50 51 52 53 54 55 56 57 60 61 62 63 64 65 66 67 70 71 72 73 74 75 76 77 100 101 102 103 104 105 106 107 110 111 112 113 114 115 116 117 120

Hex. 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50

Bin. 100001 100010 100011 100100 100101 100110 100111 101000 101001 101010 101011 101100 101101 101110 101111 110000 110001 110010 110011 110100 110101 110110 110111 111000 111001 111010 111011 111100 111101 111110 111111 1000000 1000001 1000010 1000011 1000100 1000101 1000110 1000111 1001000 1001001 1001010 1001011 1001100 1001101 1001110 1001111 1010000

Q R S T U V W X Y Z [ \ ] a b c d e f g h i j k l m n o p q r s t u v w x y z { } DEL

d ec. 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127

oct. 121 122 123 124 125 126 127 130 131 132 133 134 135 136 13 140 141 142 143 144 145 146 147 150 151 152 153 154 155 156 157 160 161 162 163 164 165 166 167 170 171 172 173 174 175 176 177

Hex. 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 7 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F

Bin. 1010001 1010010 1010011 1010100 1010101 1010110 1010111 1011000 1011001 1011010 1011011 1011100 1011101 1011110 1011111 1100000 1100001 1100010 1100011 1100100 1100101 1100110 1100111 1101000 1101001 1101010 1101011 1101100 1101101 1101110 1101111 1110000 1110001 1110010 1110011 1110100 1110101 1110110 1110111 1111000 1111001 1111010 1111011 1111100 1111101 1111110 1111111

39 Tab. 2.8 Correspondance entre caract` eres imprimables et code ASCII

CHAPITRE 2. LES BASES DE LA PROGRAMMATION EN C

40

Chapitre 3

Fonctions et programmes
Tout langage de programmation ore un moyen de d ecouper un programme en unit ees ind ependantes qui peuvent partager des donn ees communes et sappeller les unes les autres. En langage C, ces unit ees sont appell ees fonctions et les donn ees partag ees sont appell ees variables globales. Le langage C, et contrairement ` a dautre langages comme le Pascal, les d enitions de fonctions ne peuvent pas etre imbriqu ees : une d eclaration de fonction ne peut pas contenir dautre d eclarations de fonctions. Elle peut cependant contenir des d eclarations dobjets qui sont appel ees variables locales. Nous reviendrons sur la visibilit e des variables au paragraphe 3.3. Par convention, lexecution dun programme commence par lappel de la fonction main qui doit exister OBLIGATOIREMENT. Cest cette fonction qui fera appel ` a deventuelles autres fonctions auxiliaires. Chaque fonction auxiliaire peut egalement faire appel ` a dautres fonctions, ou sappeller elle-m eme (dans ce dernier cas, on dit que la fonction est r ecursive).

3.1
3.1.1

Notion de fonction
D enition dune fonction

En math ematiques, une fonction f de n variables associe ` a chaque n-uplet (x1 , x2 , . . . , xn ) appartenant une partie E dun ensemble produit E1 E2 En un et un seul el ement dun autre ensemble F , appell e ensemble darriv ee. Cet el ement est not e en g en eral f (x1 , x2 , . . . , xn ). Lensemble E est appell e domaine de d enition et F est appell e lensemble darriv ee. La d enition de la fonction f se r esume en langage math ematiques comme suit : f: E F x f (x1 , x2 , . . . , xn ) Dans un langage de programmation comme le C, le principe est le m eme. Pour d enir une fonction, on a besoin de : son nom, son domaine de d enition (un ou plusieurs types de donn ees), son ensemble darriv ee (un type de donn ees), lalgorithme qui permet de calculer l el ement de lensemble darriv e associ e ` a un el ement du domaine de d enition. Voici la structure dune d enition de fonction en langage C : type Nom fonction (type-1 arg-1, type-2 arg2, ... type-N argN ) { [d eclaration des variables locales ] liste dinstruction } 41

CHAPITRE 3. FONCTIONS ET PROGRAMMES

La premi` ere ligne : type Nom fonction (type-1 arg-1, type-2 arg2, ... type-N argN ) est len-t ete de la fonction. Elle comprend : le type de la valeur de retour : (type ) , qui pr ecise lensemble darriv ee de la fonction. On lappelle le type de la fonction. Si la fonction ne doit retourner aucune valeur, on remplace le type par le mot-cl e void. On parle dans ce cas de proc edure plutot que de fonction. le nom de la fonction : Nom fonction , les types des variables, qui pr ecise le domaine de d enition de la fonction. Les variables sont appell ees arguments formels de la fonction. Leurs identicateurs nont dimportante qua linterieur m eme de la fonction. Si la fonction na aucun argument, on remplace la liste darguments par le mot-cl e void. Dautre part, si deux arguments sont du m eme type, on ne peut PAS grouper leurs d eclaration. Chaque argument doit avoir sa propre d eclaration avec son type. Lorsquon rencontre lexpression Nom fonction (exp-1, exp-2, ..., exp-N) o` u exp-1, exp-2, ..., exp-N sont des expressions de type respectifs type-1 , type-2 , ... , type-N dans le corps dune autre fonction (dite fonction appelante), on dit que lon fait appel ` a la fonction Nom fonction . les arguments exp-1, exp-2, ..., exp-N sont appell es arguments eectifs ou param` etres eectifs. Le bloc dinstruction qui suit len-t ete de la fonction est appell e corps de la fonction. Cest l` a quest explicit e lalgorithme qui permet de calculer la valeur de retour de la fonction, en fonction des arguments. Le corps de la fonction d ebute eventuellement par la d eclaration de variables dites locales pour stocker la valeur ` a retourner et si on a besoin de variables annexes autres que les arguments. Il doit se terminer par une instruction de retour ` a la fonction appelante qui utilise le mot-cl e return. La syntaxe de cette instruction est : return (exp ) ; a la fonction qui a fait La valeur de rexp est la valeur que retournera la fonction Nom fonction ` appel ` a elle. Son type doit etre le type de la fonction. Si le type de la fonciton est void, on utilise simplement linstruction return;. Plusieurs instruction return peuvent appara tre dans le corps dune m eme fonction. Dans ce cas, le retour au programme appelant sera provoqu e par le premier return recontr e lors que l execution. Par exemple, la fonction min qui calcule le minimum de deux nombres entiers peut etre d enie en langage C comme suit : int min(int a, int b) { if(a < b) return a; else return b; } la valeur de lexpression min(15,4) est la valeur retourn ee par le corps de la fonction min en aectant la valeur 15 ` a a et la valeur 4 ` a b. Voici une fonctions qui, ` a partir dun nombre ottant a et dun entier n, calcule an . float puissance(float a, int n) { float p=1; int i; if (n > 0){ 42

3.1. Notion de fonction

for(i = 1; i<=n ; i++) p=p*a; } else if (n < 0){ for (i = -1; i>=n ; i--) p=p/a; } return p; } Il ny a pas quune unique mani` ere de d enir une m eme fonction. Tout d epend de lalgorithme choisi. La fonction power d ecrite ci-dessous fait exactement le m eme calcul que la fonction puissance d ecrite au dessus mais utilise un algorithme r ecursif : elle fait appel ` a elle-m eme. float power(float a, int n) { if (n == 0) return(1); else if (n > 0) return(a*power(a,n-1)); else return(power(a,n+1)/a); }

3.1.2

Prototype dune fonction

Le langage C nautorise pas les fonctions imbriqu ees. les d enitions des fonctions auxiliaires doivent donc etre plac ees avant ou apr` es la fonction main. Il est cependant nec essaire que le compilateur connaisse la fonction avant son premier appel. A priori, les fonctions doivent donc etre d enies avant que lon fasse appel ` a elles. Cela peut etre probl ematique si on utilise une premi` ere fonction qui fait appel ` a une deuxi` eme fonction qui fait elle-m eme appel ` a la premi` ere... la quelle des deux doit-on d enir en premier ? Pour eviter ce genre de d esagr ements, on peut d eclarer un fonction par son prototype, qui donne seulement son nom, son type et celui de ces arguments, sous la forme : type Nom fonction (type-1 arg-1, type-2 arg2, ... type-N argN ) ; et donner plus loin, apr` es la fonction main par exemple, la d enition de compl` ete de la fonction esentes dans le prototype sont susantes pour manipuler formelleNom fonction . Les informations pr erier le nombre et le ment les appels ` a la fonction Nom fonction : elle permet au compilateur de v type des param` etres utilis es dans la d enition concordent bien avec ceux de la d enition, de mettre en place d eventuelles conversions des param` etres eectifs lorsque la fonction est appel ee avec des param` etres dont les types ne correspondent pas aux types annonc es par le prototype... Les chiers den-t ete (dextension .h pour chier Headers) de la biblioth` eque standard contiennent notamment des prototypes de fonctions (ils peuvent egalement contenir les d enitions de constantes globales). Leur d enitions compl` etes sont stock ees dans une biblioth` eque logicielle qui sera li ee au reste du programme lors que la phase d edition des liens. Par exemple, Supposons que lon souhaite utiliser la fonction printf(). Le compilateur ne conna t pas, a priori, cette fonction bien quil sagisse dune fonction standard du langage C. Le prototype de la fonction printf() se trouve dans le chier stdio.h. Il sagit donc de pr eciser au compilateur dans quel chier se trouve le prototype de la fonction printf(). Pour cela, on ajoute la directive au microprocesseur #include<stdio.h>. 43

CHAPITRE 3. FONCTIONS ET PROGRAMMES

3.2

La fonction main

Jusqua pr esent, dans les exemples de programme complet quon a recontr e dans ce cours, la fonction main avait pour d enition quelque chose du type : int main(void) { ... return 0; } Linstruction return statut ;, qui renvoie un entier statut est utilis ee dans le main pour fermer le programme. En fait, la valeur de statut est transmise ` a lenvironnement dex ecution (terminal, programme appellant, Syst` eme dexploitation... selon do` u est lanc e le programme). La valeur de retour 0 correspond a une terminaison du programme correcte, et la valeur 1 ` ` a une terminaison sur une erreur. A la place de 0 ou 1, on peut utiliser comme valeur de retour la constante symbolique EXIT_SUCCESS (pour 0) et la constante symbolique EXIT_FAILURE (pour 1) qui sont d enies dans stdlib.h, ou encore dautres valeurs quon souhaite faire passer ` a lenvironnement dexecution. Une autre fonction permettant de quitter le programme est la fonction exit de la biblioth` eque stdlib.h, dont le prototype est : void exit(statut ) ; o` u statut a le m eme role et les m emes valeurs possibles que lors de linstruction return statut ;. Si return renvoie une valeur a la fonction appelante, donc ne quitte le programme que si lorsquelle est dans le main. Lappel ` a la fonction exit() quitte le programme quelque soit la fonction dans laquelle a lieu lappel. La fonction exit() est donc ` a eviter si on fait des appels r ecursifs ` a la fonction main : return fera terminer lappel courrant ` a main alors que exit() fera terminer le premier appel au main et donc tous les suivants (lappel recursif a la fonction main nest pas quelque chose de courrant ou souhaitable dans un programme en C, mais on sait jamais...). La fonction main peut avoir des param` etres formels. Pour recevoir une liste darguments au lancement de lex ecution du programme, la ligne de commande qui sert ` a lancer le programme est alors compos ee du nom du chier ex ecutable, suivi par les valeurs des param` etres. Linterpr eteur de commande passe alors ` a la fonction main ces arguments. Par convention, la fonction main son prototype standard est par convention : int main(int argc,char* argv[]); Elle poss` ede deux param` etres formels : un entier argc (pour ARGument Count) qui compte le nombre de mots qui constituent la ligne de commande d execution, un tableau de chaines de caract` eres de longueur variable argv (pour ARGument Vector) o` u sont stock es les mots de la ligne de commande : argv[0] contient le nom de lexecutable et les chaines argv[1]... argv[argc-1] contiennent les param` etres. Remarque : Le parametre argv est en fait un tableau d el ement de type char * (pointeur sur des el ements de type char). Ces objets seront etudi es au Chapitre 5. On utilisera dans la suite de ce cours l ecriture standard de la fonction main : int main(int argc,char * argv[]){ ... return EXIT_SUCCESS; } 44

3.3. Stockage des variables et Visibilit e des identicateurs

Lutilisation dans le code de lentier argc permet davoir un controle sur le nombre de parametres rentr es par lutilisateur. De plus, les param` etres pass es par lutilisateur sont utilisables dans le code. Ces param` etres qui sont stock es sous forme de chaines de caract` ere (voir Chapitre suivant), doivent etre converties si elles repr esentent un nombre, entier ou ottant. Pour cela, on utilise les fonctions suivantes, qui prennent toutes en argument une chaine de caract` ere s : atoi(s) qui renvoie une variable de type int dont l ecriture d ecimale, octale ou h exad ecimale est donn ee par s. atol(s) qui renvoie une variable de type long dont l ecriture d ecimale, octale ou h exad ecimale est donn ee par s. atof(s) qui renvoie une variable de type double dont l ecriture d ecimale, octale ou h exad ecimale est donn ee par s. Voici comme exemple un programme qui calcule le produit de deux entiers que lutilisateur passe en argument de l executable : #include<stdio.h> #include<stdlib.h> int main (int argc,char* argv[]){ int a,b; if(argc!=3){ printf("Nombres darguments invalide.\n Usage:%s int int\n",argv[0]); return EXIT_FAILURE; } a=atoi(argv[1]); b=atoi(argv[2]); printf("Le produit de %d et %d vaut %d. \n",a,b,a*b); return EXIT_SUCCESS; } Pour lancer le programme, on utilise par exemple la ligne de commande ./a.out 12 8 Dans cet exemple, largument argv est alors un tableau de 3 chaines de caract` eres : argv[0] est "./a.out", argv[1] est "12", argv[2] est "8". Ce sont des chaines de caract` eres : pour utiliser les deux nombres en tant quentier, il est n ecessaire dutiliser la fonction atoi() qui ` a une chaine de caract` eres renvoie lentier dont elle est l ecriture d ecimale (fonction de la biblioth` eque stdlib). Cette exexution renvoie donc : Le produit de 12 et 8 vaut 96. Par contre, la commande ./a.out 12 renvoie ` a lutilisateur : Nombres darguments invalide. Usage: ./a.out int int

3.3

Stockage des variables et Visibilit e des identicateurs

Les variables manipul ees dans un programme en C ne sont pas toutes log ees ` a la m eme enseigne. En particulier, elle nont pas toutes la m eme fa con d etre stock ee, ni la m eme dur ee de vie. 45

CHAPITRE 3. FONCTIONS ET PROGRAMMES

3.3.1

Variables permanentes et variables temporaires

Il y a deux cat egories de variables, selon la mani` ere dont le compilateur d enit leur stockage. On a dune part les les variables permanentes (ou statiques). Une variable permanente occupe un emplacement m emoire qui reste xe pendant toute lexecution du programme. Cet emplacement est allou e une fois pour toute lors de la compilation. La partie de la m emoire contenant ces variables est appell ee segment de donn ees. Par d efaut, les variables permanentes sont initiali ees ` a z ero par le compilateur. Elles sont caract eris ees par le mot-cl e static. Leet de la classe static d epend de lendroit o` u lobjet est d eclar e (voir les deux paragraphes ci-dessous). Dautre part, il y a les les variables temporaires. Les variables temporaires se voient allouer un emplacement m emoire de fa con dynamique durant lex ecution du programme. Elle ne sont pas initialis ee par d efaut et lemplacement m emoire est lib er e` a la n de lex ecution de la fonction dans laquelle elle est situ ee. La partie de la m emoire contenant ces variables est appell ee segment de pile, il existe un sp ecicateur de type correspondant ` a ces variables : auto pour variable de classe automatique, mais ce sp ecicateur est inutile puisque il sapplique uniquement aux variables temporaires qui sont des variables automatiques par d efaut (cest un vestige du langage B, pr ed ecesseur du langage C). Une variable temporaire peut egalement etre stock ee dans le registre de la machine, qui est beaucoup plus rapide dacces qui le reste de la m emoire (cest utile lorsquune variable est utilis ee fr equamment dans un programme). Pour stocker une variable temporaire dans le registre, on sp ecie le type avec le mot-cl e register. Comme le nombre de registres est limit e, cette requ ete nest satisfaite que sil reste des emplacements disponibles. Cela dit, les options doptimisation du compilateur gcc (comme loption -O) sont maintenant plus ecaces que le for cage manuel de stockage dans le registre eectu e grace ` a register. Dautre part, il peut eventuellement etre n ecessaire de pouvoir, ` a des moments arbitraires de lex ecution, demander au syst` eme lallocation de nouvelles zones de m emoire, et de pouvoir restituer au syst` eme ces zones (allocation et d esallocation dynamique de la m emoire), comme par exemple, lorsque la taille des variables ` a stocker est variable elle aussi (d ependant dun parametre que lutilsateur doit choisir). Dans ce cas, lallocation et la lib eration de la m emoire sont sous la responsabilit e du programmeur et ces zones sont prises dans une partie de la m emoire appel e segment de tas. La compilation dun programme cr ee des chiers binaires contenant les instructions des fonctions (et les liens vers les di erents autres segments) qui seront plac es dans une zone m emoire appel ee segment de code. Lors du lancement de lex ecutable, le processeur ira chercher ses premi` eres instructions dans le segment de code de la fonction main du programme. les segments de pile et les segments de tas sont allou es au programme ` a chaque execution puis lib er es lorsque le programme se termine. Une fois la compilation eectu ee, le segment de code et le segment de donn ees sont xes. La gure 3.1 repr esente lorganisation dans la m emoire des di erents segments pour un programme prog.c contenant la fonction main et une fonction auxiliaire aux.

3.3.2

Variables globales et variables locales

La dur ee de vie des variables est li ee ` a leur port ee, cest-` a-dire ` a la portion du programme qui les contient. On appelle variable globale une variable d eclar ee en dehors de toute fonction. Elle est alors connue du compilateur dans toute la portion de code qui suit sa d eclaration et modiable par toute fonction qui lutilise dans cette portion de code. Les variables globales sont syst` ematiquement permanentes et donc initialis ees par d efaut ` a 0. 46

3.3. Stockage des variables et Visibilit e des identicateurs

11111111111111111111111 00000000000000000000000 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 Segment de code de main 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 Segment de code de aux 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111 00000000000000000000000 11111111111111111111111
Segment de donn ees de prog

sens de num erotation des cases m emoires

Segment de tas de prog

Segment de pile de main

Segment de pile de aux

Fig. 3.1 Sch ema de lorganisation m emoire Comme une variable globale est d ej` a permanente, le mot-cl e static aura pour eet de limiter la port ee de la variable ou de la fonction au seul chier o` u elle est d eclar ee. Toutes les variables globales et fonctions qui ne sont pas d eclar ees (ou d enies) en static sont externes par d efaut. Par exemple, le programme suivant : #include<stdio.h> int n; void compteur() { n++; printf(" %d -",n); return; } int main(int argc, char *argv[]) { int i; for(i = 0;i < 5; i++) compteur(); return EXIT_SUCCESS; } 47

CHAPITRE 3. FONCTIONS ET PROGRAMMES

va acher ` a l ecran : 1 - 2 - 3 - 4 - 5 -. Linitialisation de n ` a z ero est automatique car n est globale donc permanente et chaque appel de la fonction compteur incr emente sa valeur de 1.

On appelle variable locale une variable d eclar ee ` a linterieur dune fonction (ou dun bloc dinstructions) du programme. Elles sont temporaires par d efaut. Lorsquune fonction est appell ee, elle place ces variables locales dans la pile. A la sortie de la fonction, la pile est vid ee et les valeurs des variables locales sont perdues. Leurs valeurs ne sont pas conserv ees pour les prochains appels de la fonction. Les variables locales nont aucun lien avec les variables globales du m eme nom. La variable locale prime sur la variable globale. Par exemple, le programme suivant :

#include<stdio.h> int n=500000; void compteur() { int n=0; n++; printf(" %d -",n); return; } int main(int argc, char *argv[]) { int i; for(i = 0;i < 5; i++) compteur(); printf(" %d\n",n); return EXIT_SUCCESS; }

va acher ` a l ecran : 1 - 1 - 1 - 1 - 1 - 500000. A chaque appel de la fonction compteur, une variable locale n est initialis ee ` a z ero puis incr ement ee de 1. Cest la variable locale n qui est utilis ee durant tout lappel ` a la fonction compteur : la variable globale n na pas et e modi ee.

On peut cependant forcer une variable locale ` a etre permanente, en utilisant le sp ecicateur de type static. La valeur de la variable sera alors permanente entre les di erents appels de la fonction. La variable ne sera visible que dans la fonction, mais ne sera pas r einitialis ee ` a chaque appel de la fonction. Lint er et est de garantir une certaine encapsulation, an d eviter des usages multiples dune variable globale. Qui plus est, cela permet davoir plusieurs fois le m eme nom, dans des fonctions di erentes. Le programme suivant : 48

3.4. Transmission des arguments par valeurs et par adresses

#include<stdio.h> int n=500000; void compteur() { static int n; n++; printf(" %d -",n); return; } int main(int argc, char *argv[]) { int i; for(i = 0;i < 5; i++) compteur(); printf(" %d\n",n); return EXIT_SUCCESS; } va acher ` a l ecran : 1 - 2 - 3 - 4 - 5 -500000. La variable locale n est permanente, initialis ee implicitement ` a 0 et sa derni` ere valeur est conserv ee pour les appels suivants ` a la fonction compteur. Cependant, en dehors de la fonction compteur, seule la variable globale n est visible.

3.4

Transmission des arguments par valeurs et par adresses

Les param` etres dune fonction sont trait es de la m eme mani` ere que les variables locales de classe automatique : Lors de lappel dune fonction, les param` etres eectifs sont copi es dans le segment de pile et la fonction travaille uniquement sur ces copies, qui disparaissent lors du retour ` a la fonction appelante. Par d efaut, si la fonction modie un de ses param` etres, seule la copie sera en fait modi ee, la variable de la fonction appelante restant inchang ee. On dit que les param` etres dune fonction sont transmis par valeurs. Par exemple, si on fait tourner le programme suivant, sens e intervertir les valeurs de deux variables : #include<stdio.h> void flip(int a, int a) { int t; printf("debut flip: a=%d et b=%d\n", a,b); t=a; a=b; b=t; printf("fin flip: a=%d et b=%d\n", a,b); return; } int main(int argc, char *argv[]) { int x=2; int y=5; printf("debut main: x=%d et y=%d\n", x,y); flip(x,y); printf("fin main: x=%d et y=%d\n", x,y); return EXIT_SUCCESS; }

49

CHAPITRE 3. FONCTIONS ET PROGRAMMES

on voit sacher : debut main: x=2 et y=5 debut flip: a=2 et b=5 fin flip: a=5 et b=2 fin main: x=2 et y=5 Les valeurs de x et y nont pas et e echang ees... Pourquoi donc ? Tout simplement car ` a lappel de la fonction flip dans la fonction main, les valeurs de x et y sont copi ees dans le segment de pile d evolu au stockage des variables a et b. Leurs valeurs sont ensuite interverties mais la fonction flip ne touche pas aux valeurs contenues au emplacements m emoires d evolus ` a x et y... les valeurs quils contiennent sont donc inchang ees. Pour quune fonction modie la valeur dun de ces arguments, il faut quelle ait pour param` etre ladresse m emoire de lobjet et non sa valeur. On dit que les arguments sont transmis par adresses. Connaissant leurs adresses, le programme ira modiera les cases m emoire qui contiennent les variables a modier. ` On verra comment construire des fonctions modiant leur param` etres plus tard, en utilisant les pointeurs, au Chapitre 5, Paragraphe 5.5.

3.5

Qualicateurs de type const et volatile

Les qualicateurs const et volatile permettent dencadrer les possibilit es de modications dune variable. Il se place juste avant le type de la variable. La classe const ne d eclare pas une vraie constante, mais indique au compilateur que la valeur de la variable ne peut pas etre chang ee par le programme. Il est donc imp eratif dassigner une valeur ` a la d eclaration de la variable, sans quoi toute tentative de modication ult erieure entrainera une erreur de la part du compilateur. Attention toutefois ` a son utilisation avec les pointeurs : char * const p; d enit un pointeur constant sur un caract` ere : le pointeur p est garanti constant mais pas son contenu. const char * p; d enit un pointeur sur un caract` ere constant : Le caract` ere point e p est garanti constant mais le pointeur p peut etre modi e. const char * const p; d enit un pointeur constant sur un caract` ere constant : le pointeur p et son contenu garanti constant. Le mot-cl e volatile sert ` a sp ecier au compilateur que la variable peut etre modi ee ` a son insu. Cette classe de variable sert lors de la programmation syst` eme. Elle indique quune variable peut etre modi ee en arri` ere-plan par un autre programme (par exemple par une interruption, par un thread, par un autre processus, par le syst` eme dexploitation ou par un autre processeur dans une machine parall` ele). Cela n ecessite donc de recharger dans un registre du processeur cette variable ` a chaque fois quon y fait r ef erence et ce m eme si elle se trouve d ej` a dans un de ces registres (ce qui peut arriver si on a demand e au compilateur doptimiser le programme). Remarque : On peut combiner const et volatile dans certaines situations. Par exemple : extern const volatile int horloge_temps_reel; 50

3.6. Directives au pr eprocesseur et compilation conditionnelle

d eclare une variable enti` ere, quon ne peut modier ` a partir du programme, mais dont la valeur peut changer quand m eme. Elle pourrait d esigner une valeur incr ement ee r eguli` erement par une horloge interne.

3.6
3.6.1

Directives au pr eprocesseur et compilation conditionnelle


la directive #include

La directive au pr eprocesseur #include permet dinorposer dans le chier source un texte dun autre chier. Ce chier peut- etre une en-t ete de biblioth` eque (stdlib.h, math.h...) o` u nimporte quel autre chier (biblioth` eque personnelle de fonctions...). Deux syntaxes voisines peuvent etre utilis ees : #include<nom fichier > pour inclure des chiers qui se trouvent dans des dossiers d enis par limpl ementation (ex : /usr/include ou /usr/local/include). #include"nom fichier " pour inclure des chiers du r epertoire courant. On peut specier dautres repertoire ` a laide de loption -I du compilateur gcc. EN g en eral, on utilise la premi` ere syntaxe pour les chiers en-t ete des biblioth` eques standard et la deuxi` eme syntaxe pour les chiers cr ees par lutilisateur.

3.6.2

D enitions de macros : la directive #define

Une macro est une association entre un indenticateur et un texte. Cette directive permet de d enir des pseudo-constantes ou des pseudo-fonctions. Le pr eprocesseur remplacera chaque occurrence de lidenticateur rencontr ee dans le programme par le texte sp eci e. On d enit une macro gr ace ` a la directive suivante au pr eprocesseur : #define NOM texte Lusage est de mettre les identicateurs des pseudo-constantes en majuscules. Exemple : #define TAILLE 100 #define SAUT_DE_LIGNE putchar(\n) Attention : Quelques r` egles pour la d enition des macros : 1. Eviter de mettre un point virgule ` a la n de la directive. Par exemple, si on d enit : #define TAILLE 100; /* ; de trop */

Linstruction i = TAILLE - 2; serait remplac ee par le pr eprocesseur par : i = 100; - 2;

Notez que linstrcution - 2; est une expression valide en C... 2. Bien parenth eser les expressions compos ees. Par exemple, si on d enit : #define TAILLE 10 #define MAXI 2 * TAILLE + 3 Linstruction int p=MAXI*2; xe la valeur de lentier p ` a 26 (2 * 10 + 3 *2) au lieu de 46. La bonne d enition de MAXI est #define (MAXI 2 * TAILLE + 3) 51

CHAPITRE 3. FONCTIONS ET PROGRAMMES

On peut egalement d enit des pseudo-fonctions, en utilisant des directives au pr eprocesseur avec la syntaxe suivante : #define NOM Macro (param-1,param-2,... ) texte Le pr eprocesseur remplace toute occurrence de NOM Macro (X-1,X-2,...) par texte . Les param` etres formels param-i apparaissant dans texte sont remplac es par les param` etres eectifs X-i. Par exemple si lon d enit : #define CARRE(X) #define SOMME(X,Y) Les instructions z = CARRE(a); y = CARRE(c+1); seront remplac ees par le pr eprocesseur remplace par : z = a*a; y = c+1*c+1; /* etonnant !!! equivalent de : c+(1*c)+1 */ X*X X+Y

Comme pour la d enitions des pseudo-constantes, il faut utiliser des parenth` eses : #define CARRE(X) (X)*(X) y = CARRE(c+1); /* --> y = (c+1)*(c+1); */ z = (short) CARRE(c+1); /* --> z = (short) (c+1)*(c+1); */ /* equivalent ((short) (c+1)) * (c+1) */ Ce qui nest pas encore satisfaisant. Il faut d enir la pseudo-fonction CARRE ainsi : #define CARRE(X) ((X)*(X)) z = (short) CARRE(c+1); /* --> z = (short) ((c+1)*(c+1)); */ Attention : Quelques r` egles pour la d enition des macros : 1. NOM Macro doit etre imm ediatement suivi dune parenth` ese ouvrante. 2. Il ne faut pas utiliser dans les param` etres r eels des op erateurs pouvant g en erer des eets de bord. Par exemple, si on d enit : #define abs(X) ( ((X) < 0) ? -(X) : (X) ) Lappel abs(i++); incr ementerait 2 fois i car dans la macro abs, i est evalu e 2 fois. 3. Une macro ne peut pas etre utilis ee comme param` etre dune fonction, car il ny a pas dadresse de point dentr ee. 4. Une macro peut tenir sur plusieurs lignes si chaque ligne de source est termin ee par le symbole \ (dans la signication UNIX, \ annule le caract` ere suivant, ici cest la n de ligne qui est annul ee). Exemple : 52

3.6. Directives au pr eprocesseur et compilation conditionnelle

#define erreur(msg) { \ fprintf(stderr, "%s\n", msg); \ exit(1); \ } 5. Comme pour la d enition des pseudo-constante, eviter de mettre un point virgule ` a la n de la directive. Pour supprimer une d enition, on utilise la directive #undef dont la syntaxe est : #undef NOM Macro Cette directive annule le symbole sp eci e par NOM Macro qui avait et e d eni auparavant ` a laide de la directive #define. #define max(a,b) ( ((a) > (b)) ? (a) : (b) ) z = max(x,y); #undef max z = max(x,y); /* appel de la macro max */ /* appel de la fonction max */

3.6.3

Compilation conditionnelle

La compilation conditionnelle permet dinclure ou dexclure des parties de code source dans le texte g en er e par le pr eprocesseur. La compilation du programme pourra alors sadapter au mat eriel ou ` a lenvironnement dans lequel le programme doit etre ex ecut e selon les choix faits par lutilisateur, pass es en option de la commande de compilation. Deux type de conditionnement sont pris en compte par les directives de compilation conditionnelle : les conditions sur la valeur dune expression, les conditions dexistence (ou pas...) de symb oles. Les valeurs des expressions et lexistence (ou pas) des symb oles en questions sont pass es en options de gcc dans la ligne de commande de compilation : Certaines options de cette ligne de commande permettent de d enir ou non des constantes symbolique utilis ees par le pr eprocesseur et ainsi dorienter ses traitements. -D const permet de d enir la constante symbolique const , -D const =valeur permet en plus, lui donner la valeur valeur , -U const permet dannuler la d enition de la constante symbolique const . La syntaxe dune instruction de compilation conditionnelle concernant la valeur dune ou plusieurs constantes symboliques est la suivante : #if condition-val1 /* Code ` a compiler si la condition 1 est vraie */ #elif condition-2 /* Sinon si la condition 2 est vraie compiler ce bout de code */ ... #elif condition-N /* Sinon si la condition N est vraie compiler ce bout de code */ #else /* Sinon on compile ce bout de code */ #endif Les valeurs des expressions #elif condition-i sont les r esultats dune op eration bool eenne mettant en jeu des tests sur des constantes symboliques. 53

CHAPITRE 3. FONCTIONS ET PROGRAMMES

Les op erateurs bool eens utilisables ont la m eme syntaxe que pour ceux du C : &&, ||, !, de m eme pour les op erateurs de comparaison : ==, <, <=, >, >=. Il existe egalement un op erateur bool een defined(SYMBOLE ) qui est VRAI si SYMBOLE est d eni. Voici un exemple de directive de compilation conditionnelle au preprocesseur #if (DEBUG==2) && !defined(ESSAI) (void)puts("DEBUG defini a 2 et ESSAI non defini"); #endif Dans cet exemple, issu dun programme prog.c, si la constante symbolique DEBUG est d enie et contient la valeur 2 et la constante symbolique ESSAI nest pas d enie, alors la ligne (void)puts("DEBUG defini a 2 et ESSAI non defini"); sera conserv ee par le pr eprocesseur dans le code ` a compiler. La ligne de compilation suivante remplit ces conditions : gcc -c -D DEBUG=2 prog.c Lors de lexecution de la ligne de commande gcc -c -D DEBUG=2 -D ESSAI prog.c, par contre, la ligne (void)puts("DEBUG defini a 2 et ESSAI non defini"); ne sera pas conserv ee par le pr eprocesseur dans le code ` a compiler. On peut egalement utiliser la commande #ifdef, qui marche un peu comme un #if a ` la seul di erence quil v erie seulement si une constante a et e d enie. Par exemple : #define Linux #ifdef Linux /* code pour Linux */ #endif est equivalent ` a: #define Linux #if defined(Linux) /* code pour Linux */ #endif Attention lop erateur defined ne peux etre utilis e que dans le contexte dune commande #if et #elif. Lop erateur dened combin e` a des commandes #if et #elif permet de construire des conditions logiques plus complexes. Dautre part, la commande #ifndef SYMBOLE permet de tester si une constante symbolique ici SYMBOLE nest pas d enie. Cest un equivalent de #if !defined(SYMBOLE ). #ifndef est tr` es utilis e dans les chiers dent ete (ceux dextension .h) pour eviter les inclusions innies ou multiples. En eet, si on suppose le cas de gure suivant : Un programme fait appel ` a deux chiers den-t ete bib1.h et bib2.h. On suppose que le chier bib1.h contient la directive au pr eprocesseur #include"bib2.h" et le chier bib2.h contient ` a son tour la directive au pr eprocesseur #include"bib1.h" : Le premier chier a besoin du second pour fonctionner, et le second a besoin du premier. Ce qui va se passer au moment de la pr ecompilation : 1. Lordinateur lit bib1.h et voit quil faut inclure bib2.h 2. Il lit bib2.h pour linclure, et l` a il voit quil faut inclure bib1.h 54

3.6. Directives au pr eprocesseur et compilation conditionnelle

3. Donc il inclut bib1.h dans bib2.h, mais dansbib1.h, on lui indique quil doit inclure bib2.h... Ce sc enario va boucler ` a linni. Pour eviter cela, il faut sp ecier au pr eprocesseur dinclure le chier bib1.h sil na pas et e inclu avant. Cela seectue dans le chier bib1.h avec la syntaxe suivante : #ifndef BIB1_H #define BIB1_H /* Contenu de bib1.h */ #endif Dune mani` ere g en erale, on utilise la syntaxe : #ifndef FICHIER_H #define FICHIER_H /* Contenu de fichier.h */ #endif o` u FICHIER_H repr esente le len-t ete fichier.h en majuscule. Ce m ecanisme va eviter les inclusion en boucle. Si plusieurs chiers dent ete inclus demandent tous les deux dinclure le m eme troisi` eme. Toutes les d enitions, prototypes... contenues dans ce dernier chier vont etre r ep et es dans le r esultat produit par le pr eprocesseur. Remarque : Il existe aussi une directive #error qui sert ` a arr eter la compilation lorsque vous jugez que votre programme ne pourra pas fonctionner, si par exemple une plate-forme nest pas support ee, une ressource na pas et e trouv ee, une constante symbolique na pas la bonne valeur. . Elle est souvent plac ee dans la partie #else dune instruction de compilation conditionnelle #if ... #else ... #endif. Sa syntaxe est la suivante : #error "message derreur". Lorsque le compilateur arrive ` a cette ligne, il arr ete la compilation et ache message derreur. Un petit exemple : #if defined(HAVE_DIRENT_H) && defined(HAVE_SYS_TYPES_H) #include <dirent.h> #include <sys/types.h> #else /* Arret : dirent.h et sys/types.h non trouves sur ce systeme */ #error "Readdir non implemente sur cette plateforme" #endif Si les chiers den-t ete dirent.h et sys/types.h sont pr esents dans le syst` eme, alors on les inclut, sinon, on renvoie un message derreur : c a ne sers ` a rien daller plus loin dans la compilation.

55

Chapitre 4

Types compos es
Dans un programme, il est souvent n ecessaire de manipuler des valeurs structur ees. Par exemple, on peut vouloir manipuler un relev e de mesure compos ee dun nombre xe ou variable de nombres entiers ; un point de lespace qui a un nom, une abscisse et une ordonn ee, cest-` a-dire quon peut le voir comme un trplet form e dun caract` ere et deux nombres ottants ; une gure g eom etrique compos ee elle-m eme de plusieurs points... Pour cela, les types de base du langage C ne sont pas susants. Pour construire de telles valeurs, le langage C ore deux constructions possibles : Les tableaux, pour les structures dont tous les el ements sont du m eme type, Les structures, pour lesquelles les composantes peuvent etre de types di erents.

4.1

Tableaux

Un tableau est une suite de variables du m eme type (type de base ou type structur e), appell es el ements du tableau. On distinguera les tableaux monodimensionnels dont les el ements ont pour valeurs des nombres (entiers ou ottants), des adresses ou des structures, et les tableaux multidimensionnels dont les el ements sont eux-m eme des tableaux. La d eclaration dun tableau monodimensionnel a la forme suivante : type Nom Tableau [NOMBRE ELEMENTS ] ; Cette d eclaration cr ee du tableau, compos e de NOMBRE ELEMENTS el ements de type type et d esign e etre un entier par lindenticateur Nom Tableau . NOMBRE ELEMENTS est la taille du tableau et doit strictement positif. Une telle d eclaration alloue un espace m emoire de sizeof(type ) * NOMBRE ELEMENTS octets cons ecutifs pour stocker le tableau. Les el ements du tableau sont num erot es de 0 ` a NOMBRE ELEMENTS -1. On peut acc eder ` a chaque el ement en utilisant lop erateur []. Par exemple, pour aecter la valeur 0 au 3e el ement dun tableau Tab de 10 entiers, on utilise linstruction Tab[2]=0;. Pour mettre ` a 1 tous les el ements de m eme tableau Tab, on utilise le bloc suivant : int i; for(i=0; i<10 ; i++){ Tab[i] = 1; } On peut egalement initialiser un tableau lors de sa d eclaration par une liste de constantes : 57

CHAPITRE 4. TYPES COMPOSES

type Nom Tableau [N] = {const-1,const-2,...,const-N }; Si le nombre de donn ees dans la liste dinitialisation est inf erieur ` a la taille du tableau, seuls les premiers el ements seront initialis es. On peut omettre la taille du tableau dans le cas dune d eclaration avec initialisation et utiliser linstruction suivante : type Nom Tableau [] = {const-1,const-2,...,const-N }; Pour r esumer, on a trois mani` eres equivalentes de d eclarer et initialiser un tableau tab repr esentant par exemple la s erie dentiers [2, 3, 4, 5] : int tab[]={2,3,4,5};, int tab[4]={2,3,4,5};, int tab[4]; tab[0]=2; tab[1]=3; tab[2]=4; tab[3]=5; Le langage C permet egalement de d eclarer des tableaux multidimensionnels (des tableaux de tableaux en quelque sorte). Par exemple, on d eclare un tableau bidimensionnel de la mani` ere suivante : type Nom Tableau [NB lignes][NB colonnes] ; Cest en fait un tableau dont les el ements sont eux-m emes des tableaux d elements de type type . On acc` ede ` a l el ement ligne i, colonne j par linterm ediaire de lexpression Nom_Tableau[i][j]. Linitialisation dun tableau bidimensionnel, comme par exemple un tableau dentiers repr esentant 1 2 3 la matrice se fait pas le biais dune liste de listes comme suit 4 5 6 int TAB[2][3] = {{1,2,3},{4,5,6}}; Plus simplement, on pourra d eclarer ce tableau ainsi : int TAB[2][3] = {1,2,3,4,5,6}; ou en initialisant ses el ements un ` a un comme cela : int TAB[2][3]; TAB[0][0] = 1; TAB[0][1] = 2; TAB[0][2] = 3; TAB[1][0] = 4; TAB[1][1] = 5; TAB[1][2] = 6; On peut bien evidemment d eclarer des tableaux de dimension plus grande que 2. Par exemple, on d eclare un tableau de dimension 4 et de taille taille-1 taille-2taille-3 taille-4 de la mani` ere suivante : type Nom Tableau [taille-1][taille-2][taille-3][taille-4] ; Dans ce tableau, on acc` ede ` a l el ement de coordonn ees (i,j,k,m) par linterm ediaire de lexpression Nom_Tableau[i][j][k][m]. Remarque : On peut, comme pour les types simples, regrouper les d eclarations de tablaux de dir eentes trailles d el ements du m eme type (MAIS CEST PAS BIEN...). Par exemple, linstruction : 58

4.2. Chaines de caract` eres

int i, ligne[4], matrice[2][3]; d eclare simultan ement une variable i de type int, une liste ligne de 4 int et un tableau matrice ` a 2 lignes et 3 colonnes. ATTENTION : Un tableau ne peut pas gurer ` a gauche dun op erateur daectation (il ne peut pas etre une Lvalue). Par exemple, si Tab1 et Tab2 sont deux tableaux, ON NE PEUT PAS ecrire Tab1 = Tab2;. Pour remplir le tableau Tab1 avec les el ements de ceux du tableau Tab2, en supposant quil sagit de deux tableaux de 10 entiers, on devra eectuer laectation el ement par el ement comme suit : int Tab1[10],Tab2[10]; int i; for(i=0;i<10;i++){ Tab1[i] = Tab2[i]; } Les phrases suivantes, expliquant le pourquoi de ce ph enom` ene prendront leur sens apr` es le Chapitre 5 : Un tableau est en fait un pointeur constant, vers le premier el ement du tableau. La valeur de lexpression Tab1 est donc ladresse m emoire du premier el ement de ce tableau. Dautre part, le pointeur est constant, ce qui implique en particulier quil ne peut etre modi e : aucune op eration globale nest autoris ee sur un tableau.

4.2

Chaines de caract` eres

La plupart des applications informatiques n ecessitent le traitement de donn ees textuelles. Pour repr esenter ces donn ees, les langages de programmation mettent ` a disposition le type caract` ere et les cha nes de caract` eres. Comme ces objets sont tr` es frequemment utilis es, la biblioth` eque standard du C ore de nombreuses fonctions sp eciques pour ces objets. On appelle cha ne de caract` ere un tableau de caract` eres dont la n est marqu ee par le caract` ere de code ASCII egal ` a z ero (symbole \0). Une chaine de caract` ere est donc d esign ee par ladresse de son premier caract` ere (ou par ladresse du caract` ere \0 si la chaine est vide). Ainsi, une chaine s contenant le mot Bonjour est initialis ee ainsi : char s[8] = {B,o,n,j,o,u,r,\0}; ou simplement char s[] = {B,o,n,j,o,u,r,\0}; Cette ecriture etant assez lourde, on peut utiliser l ecriture abr eg ee suivante : char s[8] = "Bonjour"; ou encore plus compact : char s[] = "Bonjour"; Cest en g en eral cette derni` ere fa con dinitialiser les chaines de caract` eres que lont utilisera. Remarque : Attention ` a ne pas confondre un caract` ere (ex : A) et une chaine de caract` ere de longueur 1 (ex : "A"). 59

CHAPITRE 4. TYPES COMPOSES

Nous avons d eja rencontr e les chaines de caract` ere lors de lutilisation des fonctions dentr ees/sorties format ees (Chapitre 2, Paragraphe 2.8) : les chaines de controle du format sont des chaines de caract` eres. Pour acher une chaine de caract` eres, via un appel ` a la fonction printf, on utilise le sp ecicateur de format %s ` a linterieur de la chiane de format elle m eme. Ce sp ecicateur de format ecrit dans la sortie standard la chaine de caract` ere jusquau symbole \0 quil n ecrit pas. On peut egalement utiliser une chaine de caract` ere pour stocker une chaine de controle du format, comme par exemple dans le bout de code suivant : int j; char s1[] = "%d"; char s2[] = "Journ ee"; char s3[] = "Bonne"; printf("%s %s\n",s3,s2); printf(s1,j);

ATTENTION 1 : Si on a d eclar e une chaine de caract` ere sans linitialiser char s[8]; on ne pourra PAS eectuer ` a posteriori une aectation du type : s = "Bonjour"; car s est le nom dun tableau et donc ne peut pas etre une Lvalue. Chaque lettre de la chaine de caract` ere doit etre aect ee s epar ement. On peut cependant initialiser une chaine de caract` eres avec une chaine plus courte : char ch1[20] = "bonjour"; char ch2[20] = {b,o,n,j,o,u,r,\0}; les 12 derniers caract` eres sont initialis es ` a 0 ou non en fonction de la classe dallocation. Une autre erreur classique consiste ` a comparer deux chaines via leurs identicateurs en pensant comparer les chaines elles-m emes (alors que lon compare uniquement les adresses m emoire adresses de leur premiers el ements). Par exemple, si on d enit deux chaines par : char s1[] = "Bonjour", s2[] = "Bonjour"; Le test s1 == s2 sera faux (valeur 0), car s1 et s2 sont les adresses des d ebuts des chaines quelles d esignent. Pour comparer deux chaines de caract` eres, il faudra les comparer caract` ere par caract` ere... Le module string.h de la biblioth` eque standard regroupe pleins de fonctions utiles ` a la manipulation des chaines de caract` eres : calculer leur longueurs, les comparer, les concatener, extraire des sous-chaines, etc... Ci-dessous 4 fonctions sur les chaines qui penvent etre utiles (mais il en existe beaucoup dautres) : Obtenir la longueur dune chaine de caract` eres : size t strlen(const char *s) ; (le type size_t est un d eriv e du type entier) Comparer une chaine de caract` eres ` a une autre : int strcmp(const char *s1, const char *s2) ; concat ener deux chaines de caract` eres : char *strcat(char *s1, const char *s2) ; copier une chaine de caract` eres dans une autre... char *strcpy(char *s1, const char *s2) ; 60

4.3. Structures

A propose de lecture des cha nes de caract` eres Au chapitre 2, Paragraphe 2.8, nous avons vu les fonctions dentr ee-sortie format ees scanf et printf qui peuvent lire et ecrire des chaines de caract` eres. La fonction scanf utilis ee avec le format %s, lit sur lentr ee standard une suite de caract` eres jusqu` a et cest l` a son principal inconvenient la rencontre dun caract` ere despacement espace, tabulation ou saut de ligne, la range ` a ladresse indiqu ee et la compl` ete par un symb ole \0. Voici ce que cela donne, avec deux ex ecutions du bloc dinstructions suivant : char s[20]; scanf("%s",&s); printf("s = %s", s); Si lutilisateur saisi au clavier : Bonjour il obtiendra lachage : s = Bonjour Si maintenant lutilisateur saisi au clavier : Bonne journ ee il obtiendra lachage : s = Bonne Le caract` ere despacement entre Bonne et journ ee met n ` a la lecture. Or il est fr equent davoir ` a lire une ligne, cest-` a-dire une chaine de caract` eres contenant plusieurs mots espac ees pas des espaces ou des tabulations et se terminant par un caract` ere de saut de ligne \n. Pour lire une ligne compl` ete, on peut utiliser la fonction fgets, dont le prototype est le suivant : char *fgets (char *s, int size, FILE *stream ) ; Cest en fait une fonction qui permet de lire dans les chiers (voir Chapitre ??) mais que lon peut utiliser pour lire dans le ux dentr ee standard (stdin). Cette fonction lit au plus size-1 depuis le ux stream (que nous pouvons remplacer par stdin) et les stocke dans la chaine de caract` ere s . La lecture sarr ete apr` es EOF ou un saut de ligne. Un caract` ere nul \0 est ajout e en n de chaine. Cette fonction renvoie le pointeur s rempli ou un pointeur NULL si il y a eu un probl` eme. Ainsi, en utilisant le bloc : char s[20]; fgets(s,20,stdin); printf("s = %s", s); La saisie au clavier de Bonne journ ee provoquera bien lachage : s = Bonne journ ee

4.3

Structures

Une structure est une suite nie dobjets de di erents types, stock ees de mani` eres contig ue. Chacun de ces objets est appell e champs ou membre de la structure et poss` ede un identicateur. La d eclaration dun mod` ele de structure permet de d enir le nom du mod` ele ainsi ques les types et les identicateurs des di erents champs que comporte le mod` ele. La d eclaration dune structure suit la syntaxe suivante : struct Nom Structure { type1 Nom champs-1 ; 61

CHAPITRE 4. TYPES COMPOSES

type2 Nom champs-2 ; ... typeN Nom champs-N ; }; Par exemple, si on souhaite cr eer une structure qui repr esente un point du plan, d ecrit par son nom (une lettre), son abcisse et son ordonn ee, on d enit la structure Point comme suit : struct Point { char nom; float x, y; }; Lorsquune structure a et e d eclar ee, on peut cr eer des objets de ce nouveau type. Par exemple, si la structure Nom Structure a et e pr ec edemment d eclar ee, on cr ee un objet Nom variable de type structure correspondant ` a la structure modele via la d eclaration : struct Nom Structure Nom variable ; Si la structure Nom Structure na pas et e pr ec edemment d eclar ee, on peut faire dune pierre deux coups (d eclaration de la structure ET d eclaration de la variable de ce type structur e) en utilisant la syntaxe suivante : struct Nom Structure { type1 Nom champs-1 ; type2 Nom champs-2 ; ... typeN Nom champs-N ; } Nom variable ; Losrquune variable dun type structur e est d eclar ee, on peut acc eder ` a ses di erents champs via lop erateur champs de structure, mat erialis e par un point .. Ainsi, le ie champs de lobjet Nom variable de type struct Nom Structure est d esign e par lexpression : Nom variable.Nom champs-i Chacun de ces champs peut ensuite etre modi e s epar ement et sera toujours accessible via cette expression. On peut eectuer sur cet objet toutes les op erations valides sur les el ements de type typei . On peut initialiser les variables structur ees lors de leur d eclarations. Par exemple, si la structure Point est d enie comme en d ebut de paragraphe, on peut ecrire : struct Point p1 = {A,2.5,1.}; Mais on peut aussi remplir les champs un ` a un : struct p1.nom p1.x = p1.y = Point p1; = A; 2.5; 1.0;

Par exemple, le programme suivant d enit la structure complexe, calcule et ache le module dun nombre complexe : 62

4.4. Enum erations

#include<stdio.h> #include<math.h> struct complexe{ double re,im; }; double module(struct complexe z){ return sqrt(z.re*z.re + z.im*z.im); } int main(){ struct complexe z; double r; ... r=module(z); printf(" le module du nombre complexe %.2f+i%.2f est %f\n", z.re,z.im,r); return 0; } Remarque concernant le match Structure VS Tableau : Dans le petit programme pr ec edent, faisant intervenir la structure complexe, on aurait pu eventuellement representer un nombre complexe comme un tableau double[2]. Cependant le fait den faire un type structur e rend leurs manipulation plus souple : contrairement aux tableaux, on peut appliquer lop erateur daectation aux structures. Par exemple, si z1 et z2 sont deux objets de type struct complexe, on peut ecrire linstruction z1 = z2;. Remarque sur la visibilit e des structures : 0n peut d eclarer des nouvelles structures ` a lexterieur de toute fonction. Cela permet de pouvoir sen servir dans tous les fonctions du programme. Cette nouvelle structure a une port ee globale. Si par contre, elle est d enie ` a linterieur dune fonction (ce qui est le cas dans la d eclaration de la structure et d eclaration de la variable de ce type structur e simultan ee), les autres fonctions du programme ne pourront pas sen servir. Remarque sur lutilisation des structures dans les fonctions : Les nouveaux types, d enis par des structures, peuvent etre utilis es comme type de param` etre pour des fonctions mais egalement comme type des valeurs de retour des fonctions. D enir une fonction dont la valeur de retour est une structure permet en quelque sorte de d enir une fonction ayant plusieurs valeurs de retour (mat erialis es par les champs de la structure).

4.4

Enum erations

Les enum erations permettent de d enir un type par la liste (nie) des valeurs quil peut prendre. On d enit un objet de type enum eration via le mot-cl e enum et un identicateur de mod` ele, suivi de la liste des valeurs quil peut prendre : enum Nom Enum { Constante-1, Constante-2, ..., Constante-N } ; La liste de noms symboliques { Constante-1, Constante-2, ..., Constante-N }correspond en fait ` a des valeurs enti` eres : Tout type construit sur ce mod` ele est de fait une copie du type int dans laquelle un certain nombre de constantes symboliques ont et e d enies par exemple ` a des ns de lisibilit e des applications. D enir une enum eration ne d enit donc pas un domaine de d enition mais simplement quelques constantes symboliques pour des entiers (un entier peut dailleurs poss eder 63

CHAPITRE 4. TYPES COMPOSES

plusieus constantes symboliques dans une m eme enum eration). Par d efaut, les entiers sont attribu es aux constantes par ordre croissant en commen cant par 0. Par exemple, si on d eclare le type Couleurs comme suit : enum Couleurs { BLEU, ROUGE, JAUNE}; Lexecution de linstruction printf("%d - %d - %d", BLEU, ROUGE, JAUNE); renvoie ` a l ecran : 0 - 1 - 2. Dans le programme, on pourra ensuite utiliser indi eremment lentier 1 ou la constante symbolique ROUGE. On peut egalement forcer lattribution des entiers aux constantes symboliques. enum Couleurs { BLEU = 1, ROUGE = 3, JAUNE = 1}; Lexecution de linstruction printf("%d - %d - %d", BLEU, ROUGE, JAUNE); renvoie alors ` a l ecran : 1 - 3 - 1.

4.5

Unions

Une union d esigne un ensemble de variables de types di erents succeptibles doccuper alternativement une m eme plage m emoire. Cela permet de d enir des objets qui peut etre dun type au choix parmi un ensemble ni de types. Si les membres dune union sont de tailles di erentes, la place r eserv ee en m emoire pour repr esenter lunion est la taille un membre le plus grand. Modier lun des champ ecrase donc tous les champs de lunion. Typiquement, une union sutilise lorsquun enregistrement peut occuper plusieurs fonctions bien distinctes et que chaque fonction ne requiert pas lutilisation de tous les champs. union Nom union { type1 Nom champs-1 ; type2 Nom champs-2 ; ... typeN Nom champs-N ; }; On acc` ede au champs dune union de la m eme mani` ere que pour une structure. Dans lexemple suivant, on cr ee une variable Note_Exam de type union Note qui peut etre soit un entier, soit une lettre (selon le syst` eme de notation choisi) : union Note { char lettre; int nombre; }; union note Note_Exam; Par exemple, pour rentrer une note en lettre, on utilise linstruction Note_Exam.lettre=A; pour rentrer une note en chire, on utilise linstruction Note_Exam.nombre=20;. Les unions peuvent etre utiles lorsquon a besoin de voir un objet sous des types di erents (mais en g en eral de m eme taille). Voici par exemple ce que lon peut faire pour repr esenter les nombres complexes : 64

4.6. Indentication des types compos es avec typedef

/*Structure repr esentant les nombres complexes en notation cart esienne*/ struct Cart{ float x,y; }; /*Structure repr esentant les nombres complexes en notation exponentielle*/ struct Exp{ float rho,theta; }; /*Structure pour les nombres complexe en notation exp. ou cart esienne*/ struct complexe{ char test; /* test=c -> Notation cart esienne, test=e -> Notation exponentielle*/ union { struct Cart cart; struct Exp exp; } Not; }; Ainsi, lorsquon initialise une variable de type struct complexe on peut indi eremment l initialiser sous forme cart esienne ou sous forme exponentielle. Pour entrer les nombres z1 = 2 + 2i et z 2 = e0,3i , on ecrira : struct complexe z1 = {c, {1.,2.}}; struct complexe z2 = {e,{1., 0.3}}; Par exemple, pour acher une variable de type struct complexe, on eectuera un switch sur le caract` ere test : struct complexe z; ... switch(z.test){ case c: printf("z= %.2f+i%.2f\n",z.Not.cart.x,z.Not.cart.y); break; case e: printf("z= %.2f e^(i%.2f)\n",z.Not.exp.rho,z.Not.exp.theta); break; }

4.6

Indentication des types compos es avec typedef

Pour all eger les programmes, on peut aecter un nouvel identicateur ` a un type compos e (structures, tableaux ou chaines de caract` ere) ` a laide de typedef, dont la syntaxe est : typedef type Structure synonyme ; Cela peut aider grandement ` a simplier les d eclaration de structures compliqu ees, imbriquant tableaux, structures, unions... 65

CHAPITRE 4. TYPES COMPOSES

Par exemple typedef struct Point[3] TRIANGLE; d eclare un type dont les instantes seront des tableaux de 3 structures de type struct Point. Linstruction TRIANGLE T1; cr ee un tableau de taille 3, dont les elements sont des instances de struct Point. Dautre part, on peut egalement combiner la d eclaration de structure et lutilisation de typedef : typedef struct { char nom; float x,y; } POINT; POINT X; est equivalent ` a: struct Point { char nom; float x,y; }; struct Point X;

4.7

A propos des valeurs des identicateurs de tableaux et des chaines de caract` eres

On a vu, pour les tableaux et les chaines de caract` eres, quaucune op eration globale netait autoris ee. En particulier, un tableau ne peut pas gurer ` a gauche dun op erateur daectation : on ne peut pas ecrire Tab1 = Tab2; si Tab1 et Tab2 sont deux tableaux ou deux chaines de caract` eres. La valeur de lexpression Tab1 est en fait ladresse m emoire de la portion de m emoire contenant le premier el ement du tableau. On dit que Tab1 pointe vers Tab1[0]. Il est donc logique, dans un sens, de ne pas vouloir modier la valeur de Tab1, car cela reviendrai ` a perdre ladresse o` u ont et e stock es les valeurs des el ements du tableau. Un identicateur de tableau est ce quon appelle un pointeur constant. Cela nous am` ene donc naturellement au chapitre suivant...

66

Chapitre 5

Pointeurs
5.1 Adresses et pointeurs

Lorsquon manipule une variable dans un programme, sa valeur est stock ee quelque part dans la m emoire centrale. Celle-ci est constitu ee doctets, qui sont identi ees de mani` ere unique par un num ero appell e adresse. Lorsquon d eclare une variable, le compilateur lui associe un ou plusieurs octets cons ecutifs o` u sera stock ee sa valeur. La taille de cette portion m emoire associ e etant d etermin ee par le type de la variable. Pour retrouver la valeur dune variable, il sut en fait de connaitre le type de la variable ainsi que ladresse du premier des octets o` u cette variable est stock ee. Bien quil soit a priori plus intuitif de d esigner des variables par leur identicateurs plut ot que par leurs adresses, laissant le soin au compilateur de faire le lien entre variable et adresse-m emoire, il est de temps en temps tr` es pratique et parfois n ecessaire de manipuler directement les adresses... Tout objet pouvant etre plac e ` a gauche dun op erateur daectation (on appelle ces objets des Lvalues ) poss` ede : une adresse, qui indique loctet ` a partir duquel les valeurs de cet objet seront stock ees, une valeur, qui est stock ee dans la m emoire ` a partir de loctet indiqu e par son adresse. On acc` ede ` a ladresse dune variable via lop erateur dadresse & (voir Chapitre 2, Paragraphe 2.5.8). Voici un exemple de portion de code : int n,m; char a; n=3; m=n; a=a; printf("adresse de n: %lu\nadresse de m: %lu\nadresse de a: %lu\n",&n,&m,&a); Une execution de cette portion de programme renvoie par exemple (Notez que chaque execution donne lieu ` a une allocation dadresses di erentes) : adresse de n: 3214431708 adresse de m: 3214431704 adresse de a: 3214431715 Laectation des adresses aux variables est sch ematis ee Tableau 5.1. Deux variables ayant des identicateurs di erents ont deux adresses di erentes. Les variables n et m sont de type int donc cod ees sur 4 octets, tandis que la variable a qui est de type char est stock e sur un seul octet. 67

CHAPITRE 5. POINTEURS

Objet m n

Valeur 3 3

Adresse &m=3214431704 &m=3214431708

&a=3214431715

Num eros doctet 3214431704 ` a 3214431707 3214431708 ` a 3214431711 3214431712 3214431713 3214431715

Tab. 5.1 Allocation-m emoire Linstruction m=n; nop` ere pas sur les adresses mais sur les valeurs : Les cases m emoires d evolues a m sont mise ` ` a la m eme valeur que les cases m emoires d evolues ` a n. Notez que ladresse dun objet etant un num ero doctet, cest donc un entier. Son format d epend de larchitecture cest le format interne (16,32 ou 64 bits). Dautre part, si on peut acc eder ` a ladresse dune variable n avec lop erateur dadresse &, la valeur de &i est une constante : on ne peut pas faire gurer &i ` a gauche dun op erateur daectation ni la modier. Cependant, comme il est souvent plus pratique de travailler avec les adresses, plut ot quavec les valeurs et les objets. Pour pouvoir manipuler les adresses, on doit recourir ` a une autre classe dobjet : les pointeurs.

5.1.1

D enition de variable de type pointeur

Un pointeur est un objet (qui peut etre une Lvalue) dont la valeur est egale ` a ladresse dun autre objet. Cela revient en quelque sorte ` a d eclare un identicateur dadresse. On d eclare un pointeur en utilisant linstruction : type *Nom pointeur ; e` a un entier (en g en eral de type unsigned long int) Lindenticateur Nom pointeur est associ dont la valeur pourra etre ladresse dune variable de type type (qui peut eventuellement etre un type structur e). On dit que le pointeur Nom pointeur pointe vers un objet de type type . Ainsi, par exemple, les instructions suivantes : int n, *p1; char *p2; struct personne *p3; d enissent successivement : une variable n de type int et un pointeur p1 vers une variable de type int, un pointeur p2 vers une variable de type char, un pointeur p3 vers une variable de type struct personne Par d efaut, lorsquon d enit un pointeur sans linitialiser, il ne pointe sur rien : la valeur dun pointeur est alors egale ` a une constante symbolique not e NULL d enie dans len-t ete <stdio.h> qui vaut \0 en g en eral. Le test Nom pointeur ==NULL permet donc de savoir si un pointeur pointe vers quelque chose ou pas. M eme si la valeur dun pointeur est toujours un entier, le type dun pointeur d epend de lobjet vers lequel il pointe. Cette distinction est indispensable ` a linterpr etation de la valeur dun pointeur car il renseigne sur la taille de lobjet point e. Sil pointe sur un caract` ere, sa valeur est celle de ladresse de loctet ou est stock e le caract` ere, ou sil pointe sur un entier, sa valeur est celle de ladresse du premier des 4 octets o` u est stock e lentier.

68

5.1. Adresses et pointeurs

Par exemple, lorsque le code suivant est execut e: int *p, n; p=&n; n=3; On se trouve dans la conguration suivante (les emplacements m emoires attribu es change ` a chaque ex ecution) : Objet n p Valeur 3 3218448700 Adresse &m=3218448700 &p=3218448704 Num eros doctet 3218448700 ` a 3218448703 3218448704 ` a 3218448707

Notez bien que latout principal des pointeurs r eside dans le fait suivant : contrairement ` a une adresse, la valeur dun pointeur est initialisable et modiable. Les pointeurs peuvent egalement etre des el ements dun tableau, ou un champs dune structure. Par exemple, linstruction : struct personne *liste[50]; d enit une liste de 50 pointeurs vers des variables de type struct personne, et linstruction : struct livre { char titre[30]; struct personne *auteur; } d eclare une structure livre, dont le champs auteur est la donn ee dun pointeur sur une variable de type struct personne. Lutilisation des pointeurs peut egalement permettre de d eclarer un type de structure de mani` ere r ecursive. Par exemple : struct personne { char nom[30]; struct personne *mere; struct personne *pere; } Les pointeurs sur des structures seront d etaill es au Paragraphe 5.4. Les pointeurs peuvent egalement etre pass es comme arguments ou comme type de retour de fonctions, permettant de d enir des fonctions qui modient leurs param` etres. Nous d etailleront cela au Paragraphe 5.5.

5.1.2

Op erateur dindirection

Pour acc eder ` a la valeur dune variable point ee par un pointeur, on utilise lop erateur unaire dindirection : *. Si par exemple, un pointeur p pointe vers une variable n de type double, on peut acc eder ` a partir p ` a la valeur courante de n en utilisant *p. Par exemple, le morceau de code : double *p; double n; n=3; p=&n; printf("*p= %d\n",*p); 69

CHAPITRE 5. POINTEURS

imprimera ` a l ecran : *p=3. En fait les objets *p et n sont identiques : ils ont m eme valeur et m eme adresse. Modier la valeur de *p revient donc ` a modier la valeur de n. Les relations entre n et p sont d ecrites ` a la Figure 5.1. &n
1 2 3 4 5 6 7 8 9 10 11

*p

&p

12 13 14 15

&n

Fig. 5.1 Relations entre une variable double n et un pointeurs double *p=&n.

On peut manipuler dans un m eme programme ` a la fois un pointeur p et son indirection *p mais il faut faire attention car ils r eagissent di eremment. Si par exemple, apr` es execution du code suivant : int *p1, *p2; int n = 3; int m = 100; p1=&n; p2=&m; On se retrouve dans la conguration m emoire suivante : Objet n m p1 p2 Valeur 3 100 3218448700 3218448704 Adresse &n=3218448700 &m=3218448704 &p1=3218448710 &p2=3218448714 Num eros 3218448700 ` a 3218448704 ` a 3218448710 ` a 3218448714 ` a doctet 3218448703 3218448707 3218448713 3218448717

Linstruction *p1=*p2; modierait la conguration m emoire comme suit :

70

5.2. Allocation dynamique

Objet n m p1 p2

Valeur 100 100 3218448700 3218448704

Adresse &n=3218448700 &m=3218448704 &p1=3218448710 &p2=3218448714

Num eros 3218448700 ` a 3218448704 ` a 3218448710 ` a 3218448714 ` a

doctet 3218448703 3218448707 3218448713 3218448717

Tandis que linstruction p1=p2; modierait la conguration m emoire comme suit : Objet n m p1 p2 Valeur 3 4 3218448700 3218448700 Adresse &n=3218448700 &m=3218448704 &p1=3218448710 &p2=3218448714 Num eros 3218448700 ` a 3218448704 ` a 3218448710 ` a 3218448714 ` a doctet 3218448703 3218448707 3218448713 3218448717

5.1.3

Arithm etique des pointeurs

Les valeurs des pointeurs etant des entiers, on peut leur appliquer certains op erations. Les seules op erations valides pour les pointeurs sont : laddition dun entier ` a un pointeur, qui renvoie un pointeur de m eme type, la soustraction dun entier ` a un pointeur, qui renvoie un pointeur de m eme type, la di erence entre deux pointeurs de m eme type qui renvoie un entier. On ne peut pas additionner des pointeurs. Si p est un pointeur sur un objet de type type et i un entier, lexpression p+i d esigne un pointeur sur un objet de type type et dont la valeur est celle de p incr ement ee de i*sizeof(type ). Cest le m eme principe pour la soustraction et les op erateurs dincr ementation ++ et --. Si on d eclare par exemple un pointeur p1 sur un int, et que p1 a la valeur 3218448700, alors p1+10 a pour valeur 3218448700 + 4 10 = 3218448740 puisquun int est cod e sur 4 octets. Si par contre le pointeur p1 pointait sur un objet de type double, alors p1+10 aurait eu pour valeur 3218448700 + 8 10 = 3218448780puisquun double est cod e sur 8 octets. Dautre part, si p1 et p2 sont deux pointeurs sur des objets de type type , lexpression p1-p2 p-q . d esigne lentier egal ` a sizeof(type ) On peut egalement appliquer ` a un pointeur lop erateur dindexation : [ ]. Il est li e` a lop erateur dindirection via la formule p[i] = *(p+i). Ces op erations peuvent etre utiles pour parcourir des tableaux, cela sera d etaill e Paragraphe 5.3.

5.2

Allocation dynamique

Les d eclarations de variables en C et dans beaucoup dautres langages ont une limitation tr` es importante : la taille des variables doit etre connue ` a la compilation. On parle dans ce cas dallocation automatique : le programme se charge de tout : il alloue de la m emoire ` a la d eclaration de la variable et ` a la n du bloc supprime automatiquement la variable. Cela pose probl` eme quand on ne sait le nombre de donn ees quon doit traiter et leurs tailles quau moment de lex ecution. Pour r esoudre ce probl` eme, et pouvoir d ecider durant lex ecution dun programme du nombre de variables ` a cr eer, il faudra n ecessairement passer par de lallocation dynamique de m emoire. Dans ce cas, le programmeur dispose de fonctions qui permettent de demander 71

CHAPITRE 5. POINTEURS

au syst` eme une zone m emoire dune certaine taille, quil pourra utiliser comme il le souhaite. En C, ces fonctions sont disponibles dans len-t ete <stdlib.h>. Si lon utilise beaucoup plus souvent lallocation automatique (o` u cest le programme qui ce charge de tout), lallocation dynamique sav` ere parfois etre lunique solution. Voici un exemple o` u lon doit utiliser lallocation dynamique : vous souhaitez que lutilisateur entre une s erie de chires, qui soit stock ee dans la m emoire mais ne savez pas combien d el ement comportera cette s erie. Vous pourriez pr eparer un certain nombre de place n, et occuper uniquement celles qui vous servent mais cela vous limiterait ` a n entr ees, et utiliserait de toute mani` ere la place m emoire pour n donn ees. Lallocation dynamique de m emoire vous permet de red enir la taille du tableau en cours dex ecution, dajouter ou supprimer des entr ees, sans limites ou presque.

5.2.1

Allouer de la m emoire : les fonctions malloc et calloc

Lorsquon d enit un pointeur sans linitialiser, il pointe sur la constante symbolique NULL. Linitialisation dun pointeur peut seectuer par une aectation (du type pointeur=&variable), si on souhaite faire pointer le pointeur sur une variable existant d ej` a dans le programme, ou en reservant pour *pointeur un espace m emoire de taille ad equate, puis en aectant directement une valeur ` a *pointeur (sans avoir besoin dindenticateur de variable). Lallocation dynamique est lop eration consistant ` a reserver une place m emoire pour stocker lobjet point e par pointeur. Cette op eration seectue ` a laide de la fonction malloc dont le prototype est le suivant : void* malloc (size t size ) ; Cette fonction renvoie un pointeur pointant vers un objet de taille size ) octets. Le type void* est un passe-partout, il permet de remplacer nimporte quel autre type. Pour initialiser les pointeurs vers des objets de type di erents, on remplace void* par le type souhait e ou on eectue un transtypage (voir Chapitre 2, Paragraphe 2.5.2) au moment de linitialisation. Pour allouer une plage m emoire ` a un tableau de N el ements de type type (soit un espace m emoire de taille N *sizeof(type )) qui sera point e par Nom pointeur , on appelle la fonction malloc ainsi : type *Nom pointeur = malloc(N *sizeof(type )) ; Notez que laectation eectue direment le transtypage du type void* vers type *. Si le pointeur Nom pointeur a d eja et e d eclar e pr ec edemment comme pointant sur des el ements de type type , la synthaxe est un peu di erente, puisquun transtypage doit etre eectu e: type *Nom pointeur ; Nom pointeur =(type *) malloc(N *sizeof(type )) ; Remarques sur lutilisation de sizeof : La fonction sizeof peut prendre en argument un type ou lidentiacteur dune variable : sizeof(type ) retourne le nombre doctets n ecessaire ` a la mise en m emoire dun el ement de type type . Linstruction sizeof(Indentificateur ) ; retourne le nombre doctets n ecessaire ` a la mise en m emoire dun el ement de m eme type que Identificateur . Il faut donc faire attention avec les tableaux. Si Tab est un tableau de N el ements de type type , linstruction sizeof(Tab); renvoie N* sizeof(type ) et pas N. Pour obtenir le nombre d el ements du tableau Tab, on utilisera sizeof(Tab)/sizeof(type ) ou encore sizeof(Tab)/sizeof(Tab[0]).

72

5.2. Allocation dynamique

La fonction calloc, egalement disponible dans le chier den-t ete <stdlib.h> permet dallouer dynamiquement de la place m emoire mais initialise egalement toutes les valeurs de cet espace m emoire a z ` ero. Son prototype est le suivant : void* calloc(size t Nb Elements, size t Taille Element ) ; Elle renvoie un pointeur sur une plage m emoire de taille Nb Elements *Taille Element . Pour allouer une plage m emoire ` a un tableau de N el ements de type type (soit un espace m emoire de taille N *sizeof(type )) qui sera point e par Nom pointeur et dont les valeurs sont initialis ees ` a z ero, on appelle la fonction calloc ainsi : type *Nom pointeur = calloc(N,sizeof(type )) ; Si le pointeur Nom pointeur a d eja et e d eclar e pr ec edemment comme pointant sur des el ements de type type , comme pour malloc, la synthaxe est un peut di erente, puisquun cast doit etre eectu e: type *Nom pointeur ; Nom pointeur =(type *) malloc(N,sizeof(type )) ;

5.2.2

Reaecter la m emoire : la fonction realloc

La fonction realloc sert modier une taille r eserv ee par un appel ` a la fonction malloc (g en eralement pour agrandir bien que ce ne soit pas obligatoire le cas). Et petit gadget suppl ementaire on peut aussi sen servir pour une simple r eservation (et non une modication) si on lui passe un pointeur NULL en premier param` etre (elle fait dans ce cas double emploi avec la fonction malloc mais est moins rapide puisquil y a un test suppl ementaire). Bien que souvent d ecri ee pour sa lenteur, elle ore une alternative int eressante pour g erer des tableaux de taille variable. Bien s ur il ne faut pas allouer les objets un par un, mais par blocs (doublage, par exemple). Son prototype est le suivant : void * realloc(void * Nom pointeur , size t Nouvelle taille ) ; Voici un bout de code utilisant malloc et realloc : /*Allocation dun tableau de 10 int*/ size_t size = 10; int *p = malloc (size * sizeof(int)); /* doublement du stockage, stock e dabord dans un pointeur temporaire*/ size*=2; int *p_tmp = realloc (p, size * sizeof(int));

if (p_tmp != NULL) { p = p_tmp; } else { /*lancien bloc point e par p est valide, mais il na pas e t e agrandi*/ }

73

CHAPITRE 5. POINTEURS

5.2.3

Allocation automatique VS Allocation dynamique

Ce paragraphe illustre les di erences entre les deux processus dallocation m emoire. Allocation Automatique 1 2 int n = 3 ; int *p ; VERSUS Allocation Dynamique int n = 3 ; int *p=malloc(sizeof(int)) ;

Apr` es la ligne 2, supposons quon se trouve dans la conguration suivante dans les deux cas : Allocation Automatique Objet Valeur Adresse n 3 3218448700 p ??? 3218448704 *p ??? ??? Allocation Dynamique Objet Valeur Adresse n 3 3218448700 p 3218448708 3218448704 *p ??? 3218448708

A ce stade-l` a, la manipulation de *p na aucun sens dans les deux cas. Cependant, cot e allocation automatique, lappel ` a la valuer *p peut g en` erer une violation m emoire : la valeur de p peut faire r ef erence ` a une case m emoire ne faisant pas partie des segments du programme, cela est d etect e par lesyst` eme dexploitation qui naime pas c a du tout, entrainant linterruption du programme et le doux message derreur segmentation fault. Cot e allocation dynamique, lallocation m emoire a cr ee une place doctets n ecessaires pour skocker un entier sur laquelle pointe p (dans le segment de tas). A ce moment l` a, *p vaut ce que la m emoire vaut ` a cet instant sur ces cases m emoires-l` a, converti en int. Pour illustrer cela, voyons comment deux instructions di erentes modient l etat de la m emoire dans les deux cas : Instruction *p=n; Allocation Automatique Objet Valeur Adresse n 3 3218448700 p ??? 3218448704 *p ??? ??? Allocation Dynamique Objet Valeur Adresse n 3 3218448700 p 3218448708 3218448704 *p 3 3218448708

Si on a utilis e lallocation automatique, cette instruction g en` ere une violation m emoire et une erreur segmentation fault. Dans le cas de lutilisation de lallocation dynamique, cela a parfaitement un sens et remplace la valeur de *p par la valeur de la variable n. Lavantage dutiliser ici lallocation dynamique r eside dans le fait que *p et n nont d egal que leur valeurs : modier *p naecte pas n et vice-versa. Instruction p=&n; Allocation Automatique Objet Valeur Adresse n 3 3218448700 p 3218448700 3218448704 *p 3 3218448700 74 Allocation Dynamique Objet Valeur Adresse n 3 3218448700 p 3218448700 3218448704 --??? 3218448708

5.2. Allocation dynamique

Dans les deux cas, cette instruction initalise le pointeur p ` a la valeur de ladresse &n : Manipuler *p revient ` a manipuler n : ces deux expressions ont m eme valeur et m eme adresse et toute modication de lune entraine la m eme modication sur lautre. Cependant, si on utilis e lallocation dynamique, la plage-m emoire stockant *p avant linstruction nest pas lib er ee... Le programme ne pourra plus utiliser cette place : cest ce quon appelle une fuite m emoire. En fait, lorsquon a plus besoin des donn ees pr esentes dans une plage-m emoire allou ee dynamiquement ` a un pointeur, il faut la lib erer...

5.2.4

Lib erer la m emoire : la fonction free

Pour lib erer lespace m emoire allou e` a un pointeur Nom Pointeur , on utilise la fonction free, dont le prototype est le suivant : void free(void *Nom Pointeur ). Comme pour la fonction malloc le type g en erique void * indique que cette fonction peut prendre en argument un pointeur de nimporte quel type. Cette fonction doit etre utilis ee lorsquon na plus besoin dutiliser les donn ees vers lesquelles pointent le pointeur *Nom Pointeur et permet ` a les cases etre de nouveau utilis ees en cas dautre aectation. m emoires contenant *Nom Pointeur de pouvoir Le pointeur *Nom Pointeur existe toujours et peut etre reutilis e dans le programme. Par contre ee. la valeur de *Nom Pointeur nest pas conserv Le programme suivant illustre les possibilit es et les cons equences possibles de lutilisation des identiants de pointeurs ayant et es lib er e: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include<stdio.h> #include<stdlib.h> int main(int argc,char* argv[]) { int n=3; int *p=malloc(sizeof(int)); int *q=malloc(sizeof(int)); *p=12; *q=40; free(p); free(q); p=&n; int *p1=malloc(sizeof(int)); int *q1=malloc(sizeof(int)); *p1=5; *q=3; return EXIT_SUCCES; }

Apr` es la ligne 8, la m emoire est dans l etat suivant (les adresses sont not es en h exad ecimal) : Objet p q *p *q Valeur 0x804a008 0x804a018 12 40 75 Adresse 0xbfb52aec 0xbfb52ae8 0x804a008 0x804a018

CHAPITRE 5. POINTEURS

Apr` es la ligne 10, les valeurs de *p et *q ne sont plus garanties mais les pointeurs conservent leurs valeurs (adresses des premi` eres cases m emoires des anciens *p et *q) : Objet p q *p *q Valeur 0x804a008 0x804a018 0 134520832 Adresse 0xbfb52aec 0xbfb52ae8 0x804a008 0x804a018

A la ligne 11, le pointeur lib er e p est initialis e comme pointant vers une autre variable, ici n : Objet p *p n Valeur 0xbfb52af0 3 3 Adresse 0xbfb52aec 0xbfb52af0 0xbfb52af0

Ligne 12 et 13, deux nouveaux pointeurs p1 et q1 sont cr ees et la m emoire est allou ee dynamiquement ; les m eme plages m emoires pr ec edemment utilis ees pour p et q sont utilis ees : Remarquez que Objet n=*p p q p1 q1 *q *p1 *q1 Valeur 3 0xbfb52af0 0x804a018 0x804a018 0x804a008 134520832 134520832 0 Adresse 0xbfb52af0 0xbfb52aec 0xbfb52ae8 0xbfb52ae4 0xbfb52ae0 0x804a018 0x804a018 0x804a008

p1 et q pointent toujours vers la m eme case m emoire : les modications de *p1 aecterons *q et vice versa. Ainsi, la ligne 14 transforme ` a la fois *p1 et *q : Objet q p1 *q *p1 Valeur 0x804a018 0x804a018 5 5 Adresse 0xbfb52ae8 0xbfb52ae4 0x804a018 0x804a018

La ligne 15 modie egalement *p1 et *q en mettant leurs valeurs ` a 3.

5.2.5

Quelques bons reexes

Initialiser les pointeurs : Lorsquon d eclare une variable ou quon alloue une zone m emoire, on ne dispose daucune garantie sur le contenu de cette zone ou de cette variable. Initialiser syst ematiquement les variables d` es leur d eclaration, en particulier lorsquil sagit de pointeurs, fait partie des bons r eexes ` a prendre et pour la plupart des applications ne d egrade pas le temps dex ecution de mani` ere signicative. Les erreurs provenant dune non-initialisation de pointeurs peuvent etre extr` emement dicile ` a localiser ` a lint erieur dun programme complexe. 76

5.3. Pointeurs, tableaux et chaines de caract` eres

Un seul pointeur par zone m emoire : En r` egle g en erale, il faut eviter (autant que faire se peut...) que plusieurs variables pointent la m eme zone m emoire allou ee. Eviter les fuites de m emoire : La perte du pointeur associ e ` a un secteur m emoire rend impossible la lib eration du secteur ` a laide de free. On qualie cette situation de fuite m emoire, car des secteurs demeurent r eserv es sans avoir et e d esallou es. Cette situation perdure jusqu` a la n du programme principal.

5.3

Pointeurs, tableaux et chaines de caract` eres

Lusage des pointeurs en C est essentiellement tourn e vers la manipulation des tableaux et des chaines de caract` eres.

5.3.1

Tableaux unidimensionnels

Un tableau en C est un pointeur constant (cest-` a-dire non modiable). Dans la d eclaration : int Tab[10]; Tab est un pointeur constant vers une plage-m emoire de taille 10*sizeof(int), dont la valeur est ladresse du premier el ement du tableau : Tab a pour valeur &Tab[0]. On peut donc se servir dun pointeur pour parcourir le tableau comme dans le code suivant : int Tab[5]={1,2,3,6,4}; int *p; p=Tab; for (int i = 0; i<N;i++) { printf("%d \n",*p); p++; } qui est equivalent ` a: int Tab[5]={1,2,3,6,4}; for (int i = 0; i<N;i++) { printf("%d \n",Tab[i]); } qui est encore equivalent ` a: int Tab[5]={1,2,3,6,4}; int *p; p=Tab; for (int i = 0; i<N;i++) { printf("%d \n",p[i]); } Les deux di erences principales entre tableaux est pointeurs sont les suivantes : un pointeur doit toujours etre initialis e, soit par un appel aux fonctions malloc ou calloc, soit par aectation dune expression dadresse (du type p=&i;), un tableau ne peut pas etre une Lvalue (ne peut jamais gurer ` a gauche dun op erateur daffectation). En particulier, il ne supporte pas larithm etique des pointeurs (on ne peut pas ecrire Tab++;). 77

CHAPITRE 5. POINTEURS

Dautre part, la manipulation de tableaux plut ot que de pointeurs a deux inconvenients majeurs, du fait quils sont des pointeurs constants : On ne peut pas cr eer des tableaux dont la taille est une variable du programme, on ne peut pas cr eer de tableaux bidimensionnels dont les lignes nont pas toutes la m eme taille. Ces op erations deviennent possibles lorsque la m emoire est allou ee dynamiquement, avec malloc ou calloc, et donc que lon a ` a faire ` a de vrais pointeurs. Ainsi, pour d enir un tableau ` an el ements, o` u n est une variable du programme, on utilise : int n; int *Tab; Tab=(int *)malloc(n*sizeof(int)); Le pointeur Tab sutilisera ensuite comme un tableau de n el ements de type int. En particulier, son ie el ement est donn ee par lexpression Tab[i].

5.3.2

Tableaux et pointeurs multidimensionnels

Un tableau multidimensionnel est un tableau de tableaux. Il sagit donc dun pointeur constant qui pointe vers un pointeur constant. Par exemple, dans la d eclaration de tableau suivante : int Tab[N][M]; L el ement Tab est un pointeur vers un objet qui est lui-m eme de type pointeur sur des entiers. Tab a une valeur constante, egale ` a ladresse du premier el ement du tableau : &Tab[0][0]. Les pointeurs Tab[i], pour i variant de 0 ` a N-1 sont des pointeurs constants pointant respectivement sur le premier el ement des lignes i : Tab[i] a donc pour valeur &Tab[i][0]. Par exemple, supposons que lon souhaite repr esenter ` a laide dun tableau la matrice ` a coecients entiers suivante : 1 2 3 4 5 6 On initialise par exemple le tableau ainsi : int Tab[2][3]={{1,2,3},{4,5,6}}; La m emoire sera par exemple organis ee ainsi : Objet Tab Tab[0] Tab[1] Tab[0][0] Tab[0][1] Tab[0][2] Tab[1][0] Tab[1][1] Tab[1][2] Valeur 4687351800 4687351800 4687351812 1 2 3 4 5 6 Adresse 3218448700 3218448704 3218448708 4687351800 4687351804 4687351808 4687351812 4687351816 4687351820

On peut egalement d enir des tableaux tridimensionnels et m eme ` a plus de 3 dimensions (en utilisant une d eclaration du type type tab[N1][N2]...[Nn] ;). Par exemple, si on souhaite d ecrire la famille de matrices suivante : 78

5.3. Pointeurs, tableaux et chaines de caract` eres

A0 =

1 2 3 4 5 6

A2 =

3 2 1 6 5 4

A3 =

1 1 1 4 1 1

A4 =

0 0 0 1 0 0

a laide dun tableau multidimensionnel, on peut la d ` eclarer ainsi : int A[4][2][3]; A[0]={{1,2,3},{4,5,6}}; A[1]={{3,2,1},{6,5,4}}; A[2]={{1,1,1},{4,1,1}}; A[3]={{0,0,0},{1,0,0}}; Les tableaux multidimensionnels sont cependant contraignants : tous les sous-tableaux doivent avoir la m eme taille. On ne pourrait par exemple pas utiliser un tableau pour stocker proprement la famille de matrices suivante : 0 0 0 2 1 1 2 3 , A3 = 1 1 1 , A4 = 1 0 0 . , A2 = A0 = 5 4 4 5 6 1 0 0 Pour stocker ce type de donn ees, on peut en revanche utiliser des pointeurs multidimensionnels. Ces pointeurs poss` edent plusieurs avantages par rapport aux taleau multidimensionnels. Outre le fait que lon puisse stocker des el ements de taille di erentes, on peut egalement allouer dynamiquement la m emoire d edi ee ` a chaque sous-pointeur. Pour d eclarer un pointeur multidimensionnel, on proc` ede comme pour les tableaux. D enir un pointeur bidimensionnel Nom pointeur sur des objets de type type , cest d enir un pointeur sur des objets de type type * . On d eclarera donc un pointeur didimensionnel via linstruction : type ** Nom pointeur ; De m eme, on d eclarera un pointeur tridimensionnel ainsi : type *** Nom pointeur ; Par exemple, si on souhaite stocker et initialiser, ` a laide dun pointeur de pointeurs, les premi` eres lignes triangle de Pascal, qui contient les coeciants binomiaux : 1 1 1 1 1 1 4 1 5 10 20 ... o` u chaque coecient est obtenu en faisant la somme des deux qui sont au dessus de lui. Pour d eclarer et initialiser les 10 premi` eres lignes du triangle, on proc` ede comme suit : int i,j; int n=10; int ** Pascal; 79 1 6 15 2 1 3 3 1 6 4 10 15 1 5 1 6 1

CHAPITRE 5. POINTEURS

/*Allocation de la m emoire*/ Pascal =(int **)malloc(n*sizeof(int *)); for (i=0;i<n;i++){ Pascal[i]=(int *) malloc((i+1)*sizeof(int)); } /*Initialisation des valeurs*/ Pascal[0][0]=1; for (i=1;i<n;i++){ Pascal[i][0]=1; for(j=1;j< i;j++){ Pascal[i][j]=Pascal[i-1][j-1]+Pascal[i-1][j]; } Pascal[i][i]=1; }

5.3.3

Pointeurs et chaines de caract` eres

Les chaines de caract` eres sont des tableaux ` a une dimension dobjets de type char qui se terminent par le caract` ere nul (\0). On peut manipuler toute chaine de caract` ere ` a laide dun pointeur vers des objets de type char. A une chaine d enie par : char * s; on pourra par exemple aecter la valeur suivante : s="Bonjour"; et on pourra faire subir ` a s toute op eration valide sur les pointeurs, comme par exemple s++. La quantit e s+3 d esignera la chaine de caract` eres jour et s+7 la chaine de caract` eres vide.
B s o n j s+3 o u r \0 s+7

5.4
5.4.1

Pointeurs et structures
Pointeur sur une structure

Contrairement aux tableaux, les objets de type structure sont des Lvalues (qui peuvent etre ` a gauche dun op erateur daectation). Ils poss` edent une adresse, correspondant ` a ladresse du premier el ement du premier champs (ou membre) de la structure. Si une structure a et e d enie comme suit : struct Structure { type1 champs-1 ; type2 champs-2 ; ... typeN champs-N ; }; 80

5.4. Pointeurs et structures

et que p est un pointeur sur un el ement de type struct Structure (d eclar e par linstruction struct Structure *p;), on peut acc eder au champs champs-i de la structure point ee par p via lexpression : p->champs-i Lop erateur -> est lop erateur de pointeur de membre de structure. Cette expression est equivalente ` a lexpression suivante : (*p).champs-i Notez ici que les parenth` eses sont indispensables car lop erateur dindirection a une priorit e plus elev ee que lop erateur de membre de structure. Voici un exemple de programme qui manipule des pointeurs sur des structures. #include <stdio.h> #include <stdlib.h> struct Etudiant { char nom[30]; int note; }; typedef struct Etudiant * Groupe; int main(int argc,char *argv[]){ int n,i; Groupe examen; printf("Entrez le nombre d etudiants: "); scanf("%d",&n); examen=(Groupe)malloc(n*sizeof(struct Etudiant)); for(i=0;i<n;i++){ printf("\n Saisie du nom de l el` eve num ero %d: ",i+1); scanf("%s",&examen[i].nom); printf("\n Saisie de la note de %s: ",examen[i].nom); scanf("%d",&examen[i].note); } printf("\n Entrez un num ero d etudiant: "); scanf("%d",&i); if(i<n+1){ printf("\n Etudiant num ero %d: %s Note: %d",i,examen[i-1].nom,examen[i-1].note); } else{ printf("\n Il ny a pas d etudiant num ero %d",i); } return EXIT_SUCCESS; } Remarque les expressions examen[i].nom et examen[i].note auraient pu etre remplac ees respectivement par (examen+i)->nom et (examen+i)->note. 81

CHAPITRE 5. POINTEURS

5.4.2

Structures auto-r ef erenc ees et listes chain ees

En C, on a souvent besoin de structures dont lun ou plusieurs des champs sont des pointeurs vers une structure du m eme type comme dans les deux exemples suivants : struct cellule{ double valeur; struct cellule *cellulesuivante; }; struct personne { int dateNaissance; struct personne *pere; struct personne *mere; }; Utiliser des structures auto-r ef erenc ees permet en particulier de cr eer des listes chain ees. Pour repr esenter des listes d el ements de m eme type, on peut utiliser un tableau ou un pointeur mais cette repr esentation (dite repr esentation contig ue) suppose a priori que la taille maximale de la liste soit donn ee (pour lallocation dynamique). Ce probl` eme peut etre r esolu en utilisant la repr esentation des donn ees par une liste chain ee : Chaque el ement de la liste est repr esent e dans une structure appell ee cellule qui contient un champs pour repr esenter la donn ee ` a stocker et un autre champs qui est un pointeur sur l el ement suivant de la liste. La liste est un pointeur sur la premi` ere cellule et le pointeur de la derni` ere cellule pointe sur l el ement NULL. L
val1 val2 val3 val4

NULL

Fig. 5.2 Sch ema dune liste chain ee Par exemple, pour repr esenter une liste d el ements de type double ` alaide dune liste chain ee, on d enit le mod` ele de structure cell de la mai` ere suivante : struct cell { double value; struct cell *nextcell; }; On peut egalement d enir le type liste comme etant un pointeur sur une structure cell : typedef struct cell *list; Un fois cela eectu e, on peut ins erer un el ement en d ebut de liste assez facilement, en d enissant par exemple la fonction insere : liste insereDebut(double element, list L) { liste Q; Q = (liste)malloc(sizeof(struct cell)); Q->value=element; Q->nextcell=L; return Q; }; 82

5.5. Pointeurs et fonctions

ou apr` es le ne el ement de la liste : liste insereMilieu(double element, list L, int n) { int i=1; list P,Q; P = (list)malloc(sizeof(struct cell)); Q = (list)malloc(sizeof(struct cell)); P=L; while(P->nextcell!=NULL && i<n) { P=P->nextcell; i++; } Q->nextcell=P->nextcell; Q->value=element; P->nextcell=Q; return L; }; Pour acher la liste chain ee L, on pourra utiliser la fonction suivante : void afficherListe(list L) { list P=L; while(P!=NULL){ printf("%f\t",P->value); P=P->nextcell; } }

5.5
5.5.1

Pointeurs et fonctions
Fonctions modiant leur param` etres : Passage darguments par adresses

Comme il avait et e sugg er e au au Chapitre 3, Paragraphe 3.4, si on cherche par exemple ` a fabriquer une fonction qui modie les valeurs dun couple de param etres (un ottant double et un entier), le : prototype est INADAPTE void modif(double x, int a); car le passage des arguments se fait par valeurs. Les valeurs des arguments sont copi es dnas le segment de pile de la fonction modif et ce sont ces copies-l` a qui pourront etre modi ees et pas les valeurs des variables originales (celles de la fonction appellante). Lorsquon veut quune fonction puisse modier ces param` etres, il est n ecessaire de passer les adresses des variables et non leurs valeurs. Dans la fonction, on pourra ensuite demander de modier les valeurs rang ees aux adresses m emoire dont les valeurs sont pass ees en param` etres : les param` etres dune telle fonction sont de type pointeurs. On utilisera le prototype suivant : void modif(double *adr_x, int *adr_a); En proc edant ainsi, les valeurs des adresses sont copi ees dans le segment de pile de la fonction modif, et par le biais de lop erateur dindirection, on pourra alors modier les valeurs des variables originales. En utilisant le passage dargument par adresse, on peut modier par exemple le programme donn e au Paragraphe 3.4 pour quil echange bien les valeurs de deux variables : 83

CHAPITRE 5. POINTEURS

#include<stdio.h> void flip(int *adr_x, int *adr_y) { int t; printf("debut flip: x=%d et y=%d\n", *adr_x, *adr_y); t=*adr_x; *adr_x=*adr_y; *adr_x=t; printf("fin flip: x=%d et y=%d\n", *adr_x, *adr_y); return; } int main(int argc,char *argv[]) { int x=2; int y=5; printf("debut main: x=%d et y=%d\n", x,y); flip(&x,&y); printf("fin main: x=%d et y=%d\n", x,y); return EXIT_SUCCESS; } achera bien : debut main: x=2 et y=5 debut flip: a=2 et b=5 fin flip: a=5 et b=2 fin main: x=5 et y=2 A lappel de la fonction flip avec les param` etres &x et &y, voici ce quil se passe : On stocke dans la pile d evolue ` a flip deux pointeurs sur des entiers adr_x et adr_y pointant vers les cases m emoires dadresses respectives &x et &y (la valeur de adr_x est &x et la valeur de adr_y est &y), La variable t est ensuite initialis ee avec la valeur de la variable point ee par adr_x, cest ` a dire la valeur de x, On aecte ` a la variable point ee par adr_x (valeur de x) la valeur de la variable point ee par adr_y (valeur de y). On aecte ` a la variable point ee par adr_y (valeur de y) la valeur de t.

84

5.5. Pointeurs et fonctions

5.5.2

Retour sur la pile, le tas, les segments de donn ees et les segments de code...

Le programme d ecrit ci-dessous permet de visualiser lorganisation m emoire d ecrite au Paragraphe 3.3 et sch ematis ee ` a la Figure 3.1. #include <stdio.h> #include <stdlib.h> int Max=4; int Min=-4; int Aux(int a){ int b; int c=1; int * r=malloc(Max*sizeof(int)); printf("------Segments de pile de Aux()------\n"); printf("Adresse b: %d\n",&b); printf("Adresse c: %d\n",&c); printf("Adresse r: %d\n",&r); printf("------Segments de tas------\n"); printf("Cases point ees par r: %d\n",r); free(r); return a; } int main(int argc,char *argv[]){ int i,j; int n=0; int m=1; int * p=malloc(Max*sizeof(int)); int * q=malloc(Max*sizeof(int)); printf("------Segments de code------\n"); printf("Adresse de la fonction main(): %d\n",(*main)); printf("Adresse de la fonction Aux() : %d\n",(*Aux)); printf("------Segments de donn ees------\n"); printf("Adresse Max: %d\n",&Max); printf("Adresse Min: %d\n",&Min); printf("------Segments de pile de main()------\n"); printf("Adresse i: %d\n",&i); printf("Adresse j: %d\n",&j); printf("Adresse n: %d\n",&n); printf("Adresse m: %d\n",&m); printf("Adresse p: %d\n",&p); printf("Adresse q: %d\n",&q); Aux(i); printf("Cases point ees par q: %d\n", q); printf("Cases point ees par p: %d\n",p); free(p); free(q); return EXIT_SUCCESS; } Ce programme ache ` a l ecran les valeurs suivantes : 85

CHAPITRE 5. POINTEURS

------Segments de code-----Adresse de la fonction main(): 134513832 Adresse de la fonction Aux(): 134513684 ------Segments de donn ees-----Adresse Max: 134519356 Adresse Min: 134519360 ------Segments de pile de main()-----Adresse i: -1075017872 Adresse j: -1075017876 Adresse n: -1075017880 Adresse m: -1075017884 Adresse p: -1075017888 Adresse q: -1075017892 ------Segments de pile de Aux()-----Adresse b: -1075017932 Adresse c: -1075017936 Adresse r: -1075017940 ------Segments de tas-----Cases point ees par r: 134520888 Cases point ees par q: 134520864 Cases point ees par p: 134520840 Remarque ` a propos des pointeurs sur des fonctions et du passage de fonctions en argument : Il est parfois utile de passer des fonctions comme param` etres dune autre fonction. Cest ce qui a et e fait dans le programme ci-dessus, lors des appels printf("Adresse de la fonction main(): %d\n",(*main)); printf("Adresse de la fonction Aux() : %d\n",(*Aux)); Comme pour les variables, on a utilis e un m ecanisme de pointeur, car comme toute chose mise en m emoire dans lordinateur, une fonction a une adresse de d ebut de code. Un pointeur sur une fonction contiendra ladresse de la premi` ere instruction du code de la fonction. Un pointeur sur une fonction dont le prototype est type Nom fonction (type-1 arg-1, type-2 arg2, ... type-N argN ) ; est de type type (*) (type-1 arg-1, type-2 arg2, ... type-N argN ) ;. Par exemple, une fonction Operateur prenant par exemple deux entiers ainsi quune fonction qui prend elle-m eme deux entiers en param` etres sera d eclar ee ainsi : int Operateur( int, int, int (*)(int,int)); Pour lappliquer ` a deux entiers a et b et a ` une fonction f, on utilise lappel Operateur(a,b,f). On nutilise pas la notation &f. Dans le corps dune fonction dont un param` etre f est de type fonction, pour faire r ef erence ` a ce param` etre f, on utilise la notation (*f). Par exemple, on peut d enir la fonction Operateur comme suit : int Operateur(int a, int b, int (*f)(int,int)){ int x=(*f)(a,b); 86

int y=(*f)(b,a); if (x<y){ return y; } else{ return x; } }

Chapitre 6

Quelques conseils...
Quelques conseils et r` egles ` a ne pas oublier pour permettre une programmation sereine....

Des conseils pour facilit e la lisibilit e des programmes : Commentaires : Les commentaires sont souvent utiles lorsquon reprend le code dun programme apr` es un certain temps... Ils doivent expliquer la logique de traitement et justier les choix eectu es (pourquoi tel algorithme, telle structure de donn ees, ...). et ils permettent de retrouver facilement des parties de code que lon pourrait vouloir r eutiliser... On peut egalement se servir des commentaire pour fabriquer des cartouche den-t ete : Chaque chier source (.c, .h) et chaque fonction C doivent etre pr ec ed es dun cartouche qui pourra contenir le nom de lunit e ou de la fonction, son r ole et ses points de programmation critiques, pour une fonction la description des arguments et des codes retours, la date lauteur et le r ole de chaque modication... Le cartouche de lunit e permet de faciliter sa compr ehension dun point de vue g en eral. Les cartouches de fonctions permettent de comprendre leur r ole et conditions dappel sans se plonger dans le code source. Ces ent etes sont indispensables ` a la gestion de la conguration et ` a la maintenance. Indentation : Jindente correctement les blocs... ce nest pas seulement pour faire joli, mais pour sy retrouver facilement. Dautre part, lutilisation de la tabulation est a eviter... Il vaut mieux utiliser des indentations de 2 ou 3 caract` eres espace. Nomenclature des identicateurs : Je me xe une nomenclature pr ecise ET coh erente pour les indenticateurs des fonctions, ceux des variables et ceux des programmes et je nen change pas... Jutilise si possible des identicateurs dont je peux me souvenir facilement. Je ne mets NI daccents NI despaces dans les identicateurs, les noms de mes chiers, les fonctions.... Je nutilise pas non plus des noms commen cant pas des chires... Nomenclature des constantes symboliques et des macros : Les noms des constantes symboliques et des macros instructions doivent etre ecrits en majuscule. Dans un chier source, cela permet des distinguer rapidement les constantes des variables. Accolades : Lorsque jouvre une accolade... je la ferme directement. On pr ef` erera aussi les accolades sur des lignes seules, histoire de pouvoir v erier facilement quil ne manque ni parenth` ese ouvrante, ni parenth` ese fermante... Parenth esage : Je nabuse pas des parenth esages inutiles... Lignes : Nh esitez pas ` a aller ` a la ligne... Lid eal etant davoir une seule instruction par ligne, une d eclaration de variable par ligne. 89

Des conseils pour facilit e la modication des programmes : Editeur de texte : Il vaut mieux privil egier un editeur de texte minimaliste, du type emacs. Certains editeurs de textes utilisant des caract` eres sp eciaux masqu es, il se peut alors que des surprises vous attendent lors de la compilation... Eviter les constantes litt erales : Les constantes cha ne, num erique doivent etre repr esent ees par des symboles. Les constantes litt erales sont interdites car les constantes litt erales dupliqu ees et r eparties dans le code source rendent les modications plus diciles : risque dalt eration, doubli dune occurrence. Il faut les regrouper sous formes de constantes symboliques (d enition du pr e-processeurs) en t ete de chier source ou dans des chiers inclus. Pas d el ements inutiles ou non initialis e : Toute fonction d enie doit etre utilis ee et toute variable d eclar ee doit etre initialis ee. La valeur par d efaut dune variable d epend de facteurs qui peuvent echapper au programmeur. Cette r` egle evite les fonctionnements al eatoires de certains programmes. En g en eral, les compilateurs et les outils de v erications emettent des avertissements en cas de probl` eme. Limiter le nombre des variables externes ou globales : Lutilisation de variables externes ou globales doit etre limit e au maximum. Les variables globales peuvent etre modi ees par plusieurs unit es de compilation, le contr ole des modications est donc tr` es d elicat, les eets de bord sont possibles (modications non d esir ees dune variable ou dune zone m emoire par du code li e). Un trop grand nombre de variables externes d enote une mauvaise analyse des interfaces entre unit es de compilation ou une mauvaise gestion de la port ee des variables. Ne pas r einventer la roue : Penser ` a utiliser les fonctions et biblioth` eques ecrites par dautres programmeurs, principalement dans les biblioth` eques standards du langage et des biblioth` eques reconnues Pas de code dans les .h : Ne jamais ecrire de fonctions ou dinstructions dans les chiers dent ete .h (except e les macros instructions). Ecrire le corps de fonction dans un chier .h et linclure dans plusieurs sources dupliquerait ce code et pourrait provoquer des erreurs due aux symboles d enis plusieurs fois. Plut ot que dutiliser le pr e-processeur, il faut r epartir le code source dans di erents chiers .c qui seront compil es s epar ement et r eunit lors de l edition de lien.

Pour dautres pi` eges ` a eviter, on se ref` erera ` a l echelle du Goret, liste gradu ee des choses quil vaut mieux eviter de faire lorsquon programme en C : http ://www.bien-programmer.fr/goret.htm

Chapitre 7

Bibliographie
Langage C, Patrick TRAU http://www-ipst.u-strasbg.fr/pat/program/tpc.htm Programmation C -Wikibook http://fr.wikibooks.org/wiki/Programmation_C Programmation en langage C, Anne CANTEAUT http://www-roq.inria.fr/codes/Anne.Canteaut/COURS_C Initiation ` a la programmation proc edurale, ` a lalgorithmique et aux structures de donn ees par le langage C, Maude MANOUVRIER http://www.lamsade.dauphine.fr/~manouvri/ Langage C, Patrick CORDE http://www.idris.fr/data/cours/lang/c/IDRIS_C_cours.html C, Un premier langage de programmation, Jacques LE MAITRE http://lemaitre.univ-tln.fr/supports-cours.htm ... et un nombre incalculable de visites sur des forums...

91

S-ar putea să vă placă și