Você está na página 1de 94

Foire aux questions sur le langage C

19 avril 2003

Rsum e e Ce document regroupe les questions les plus frquemment poses (avec les rponses) du groupe e e e de discussion francophone news:fr.comp.lang.c sur le langage C. Cette FAQ (voir la question 2.1, page 9) est base sur celle de news:comp.lang.c maintenue par Steve Summit (http: e //www.eskimo.com/~scs/C-faq/top.html). Une version HTML de ce document est disponible ` ladresse suivante : http:// a www.isty-info.uvsq.fr/~rumeau/fclc/. Une version texte de ce document est disponible : http://www.isty-info.uvsq. fr/~rumeau/fclc/fclc.txt. Les sources XML sont galement rcuprables ici : e e e http://www.isty-info.uvsq.fr/~rumeau/fclc/pack.tar.gz. Une version PDF de la FAQ est ` nouveau disponible ici : http://www.isty-info.uvsq.fr/ a ~rumeau/fclc/fclc.pdf. Enn, il existe aussi une version poscript : http://www.isty-info. uvsq.fr/~rumeau/fclc/fclc.ps. Mme si ce document nest plus si rcent que a, il est probable quil y traine encore quelques e e c coquilles ou erreurs. Aussi, si vous en trouvez, nhsitez pas ` les indiquer aux mainteneurs (voir e a la question 2.5, page 10).

Derniers changements Version 2.15, le 18/04/2003 Quelques modications mineures et corrections de liens morts. Version 2.14, le 18/01/2003 Corrections dans le source XML Une version PDF est ` nouveau disponible a Modication de la question 14.3, page 75 Version 2.13, le 11/11/2002 Correction du code de la question 14.12, page 78 Ajout de la question 14.8, page 77 Correction des questions 3.10, page 19 et 15.10, page 84. Version 2.12, le 27/08/2002 Correction de lexemple dans la question 14.2, page 74. Version 2.11, le 03/07/2002 Prcisions sur la question 13.9, page 66 Et quelques corrections typographiques. e Version 2.10, le 09/04/2002 Complments sur la question 3.6, page 14 e Ajout de la question 5.8, page 30 Ajout de la question 11.10, page 56 Ajout de la question 9.10, page 50 De nombreuses autres corrections et modications.

Table des mati`res e


1 Copyright (Droits de reproduction) 1.1 1.2 Copyright de la FAQ de comp.lang.c . . . . . . . . . . . . . . . . . . . . . . . . . Quen est-il de ce document ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 8 8 9 9 9 9

2 Introduction 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 Quest-ce quune FAQ ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Qui la maintient ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Qui y contribue ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

O` puis-je la trouver ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 u Jai trouv une erreur ! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 e Et mes questions ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Dois-je poster sur fr.comp.lang.c ? . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Comment poster sur fr.comp.lang.c ? . . . . . . . . . . . . . . . . . . . . . . . . . 11 Comment comprendre le langage utilis sur fr.comp.lang.c ? . . . . . . . . . . . . 11 e 13

3 Le langage C 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9

Quest-ce que le langage C ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 ` A quoi a sert ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 c Do` vient le C ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 u Que peut-on faire en C ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Portabilit, matriel, syst`me ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 e e e Et le C++ dans tout a ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 c ISO, ANSI, K&R, ..., ques aquo ? . . . . . . . . . . . . . . . . . . . . . . . . . . 15 De quoi ai-je besoin pour programmer en C ? . . . . . . . . . . . . . . . . . . . . 18 Quel(s) bouquin(s) ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

3.10 O` trouver... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 u 4 Outils, environnement de dveloppement et autres gadgets e 2 21

4.1 4.2 4.3 4.4 4.5 4.6 4.7

Environnements de dveloppement intgrs . . . . . . . . . . . . . . . . . . . . . 21 e e e Compilateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 Dbogueurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 e Graphisme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 Biblioth`ques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 e Outils divers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 O` trouver du code ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 u 27

5 Dclarations et initialisations e 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9

Quels types utiliser ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 Comment dnir une structure qui pointe sur elle-mme ? . . . . . . . . . . . . . 27 e e Comment dclarer une variable globale ? . . . . . . . . . . . . . . . . . . . . . . . 28 e Quelle est la dirence entre const et #define ? . . . . . . . . . . . . . . . . . . 28 e Comment utiliser const avec des pointeurs ? . . . . . . . . . . . . . . . . . . . . . 28 Comment bien initialiser ses variables ? . . . . . . . . . . . . . . . . . . . . . . . . 29 Comment dclarer un tableau de fonctions ? . . . . . . . . . . . . . . . . . . . . . 29 e Comment conna le nombre dlments dun tableau ? . . . . . . . . . . . . . . 30 tre ee Quelle est la dirence entre char a[] et char * a ? . . . . . . . . . . . . . . . . 30 e

5.10 Peut-on dclarer un type sans spcier sa structure ? . . . . . . . . . . . . . . . . 31 e e 6 Structures, unions, numrations e e 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 33

Quelle est la dirence entre struct et typedef struct ? . . . . . . . . . . . . . 33 e Une structure peut-elle contenir un pointeur sur elle-mme ? . . . . . . . . . . . . 33 e Comment implmenter des types cachs (abstraits) en C ? . . . . . . . . . . . . . 33 e e Peut-on passer des structures en param`tre de fonctions ? . . . . . . . . . . . . . 34 e Comment comparer deux structures ? . . . . . . . . . . . . . . . . . . . . . . . . . 34 Comment lire/crire des structures dans des chiers ? . . . . . . . . . . . . . . . . 34 e Peut-on initialiser une union ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 Quelle est la dirence entre une numration et des #define ? . . . . . . . . . . 34 e e e Comment rcuprer le nombre dlments dune numration ? e e ee e e . . . . . . . . . . 34

6.10 Comment imprimer les valeurs symboliques dune numration ? . . . . . . . . . 35 e e 7 Tableaux et pointeurs 7.1 7.2 37

Quelle est la dirence entre un tableau et un pointeur ? . . . . . . . . . . . . . . 37 e Comment passer un tableau ` plusieurs dimensions en param`tre dune fonction ? 37 a e

7.3 7.4 7.5 7.6 7.7 7.8

Comment allouer un tableau ` plusieurs dimensions ? . . . . . . . . . . . . . . . . 38 a Comment dnir un type pointeur de fonction ? . . . . . . . . . . . . . . . . . . . 39 e Que vaut (et signie) la macro NULL ? . . . . . . . . . . . . . . . . . . . . . . . . 39 Que signie lerreur NULL-pointer assignment ? . . . . . . . . . . . . . . . . . 40 Comment imprimer un pointeur ? . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 Quelle est la dirence entre void * et char * ? . . . . . . . . . . . . . . . . . . 40 e 41

8 Cha nes de caract`res e 8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8

Comment comparer deux cha ? . . . . . . . . . . . . . . . . . . . . . . . . . . 41 nes Comment recopier une cha dans une autre ? . . . . . . . . . . . . . . . . . . . 41 ne Comment lire une cha au clavier ? . . . . . . . . . . . . . . . . . . . . . . . . . 41 ne Comment obtenir la valeur numrique dun char (et vice-versa) ? . . . . . . . . . 42 e Que vaut sizeof(char) ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Pourquoi sizeof(a) ne vaut pas 1 ? . . . . . . . . . . . . . . . . . . . . . . . . 43 Pourquoi ne doit-on jamais utiliser gets ? . . . . . . . . . . . . . . . . . . . . . . 43 Pourquoi ne doit-on presque jamais utiliser scanf ? . . . . . . . . . . . . . . . . . 43 45

9 Fonctions et prototypes 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9

Pour commencer ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Quest-ce quun prototype ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 O` dclarer les prototypes ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 u e Quels sont les prototypes valides de main ? . . . . . . . . . . . . . . . . . . . . . . 47 Comment printf peut recevoir dirents types darguments ? . . . . . . . . . . . 47 e Comment crire une fonction ` un nombre variable de param`tres ? . . . . . . . . 48 e a e Comment modier la valeur des param`tres dune fonction ? . . . . . . . . . . . . 49 e Comment retourner plusieurs valeurs ? . . . . . . . . . . . . . . . . . . . . . . . . 49 Peut-on, en C, imbriquer des fonctions ? . . . . . . . . . . . . . . . . . . . . . . . 50

9.10 Quest-ce quun en-tte ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 e 10 Expressions 51

10.1 Le type Boolen existe-t-il en C ? . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 e 10.2 Un pointeur NULL est-il assimil ` une valeur fausse ? . . . . . . . . . . . . . . . . 51 ea 10.3 Que donne loprateur ! sur un nombre ngatif ? . . . . . . . . . . . . . . . . . . 51 e e 10.4 Que vaut lexpression a[i] = i++ ? . . . . . . . . . . . . . . . . . . . . . . . . . . 51 10.5 Pourtant, i++ vaut i ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

10.6 En est-il de mme pour i++ * i++ ? . . . . . . . . . . . . . . . . . . . . . . . . . 52 e 10.7 Peut-on utiliser les parenth`ses pour forcer lordre dvaluation dune expression ? 52 e e 10.8 Quen est-il des oprateurs logiques && et || ? . . . . . . . . . . . . . . . . . . . . 52 e 10.9 Comment sont values les expressions comprenant plusieurs types de variables ? e e 53

10.10Quest-ce quune lvalue ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 11 Nombres en virgule ottante 54

11.1 Jai un probl`me quand jimprime un nombre rel. . . . . . . . . . . . . . . . . . 54 e e 11.2 Pourquoi mes extractions de racines carres sont errones ? . . . . . . . . . . . . 54 e e 11.3 Jai des erreurs de compilation avec des fonctions mathmatiques . . . . . . . . . 54 e 11.4 Mes calculs ottants me donnent des rsultats tranges et/ou dirents selon les e e e plateformes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 11.5 Comment simuler == entre des ottants ? . . . . . . . . . . . . . . . . . . . . . . 55

11.6 Comment arrondir des ottants ? . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 11.7 Pourquoi le C ne dispose-t-il pas dun oprateur dexponentiation ? . . . . . . . . 56 e 11.8 Comment obtenir Pi ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 11.9 Quest-ce quun NaN ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 11.10Faut-il prfrer les double aux float ? . . . . . . . . . . . . . . . . . . . . . . . . 56 ee 12 Allocation dynamique 58

12.1 Doit-on ou ne doit-on pas caster malloc ? . . . . . . . . . . . . . . . . . . . . . . 58 12.2 Comment allouer proprement une variable ? . . . . . . . . . . . . . . . . . . . . . 58 12.3 Pourquoi mettre ` NULL les pointeurs apr`s un free ? a e . . . . . . . . . . . . . . . 59

12.4 Pourquoi free ne met pas les pointeurs ` NULL ? . . . . . . . . . . . . . . . . . . 59 a 12.5 Quelle est la dirence entre malloc et calloc ? . . . . . . . . . . . . . . . . . . 59 e 12.6 Que signie le message assignment of pointer from integer quand jutilise malloc ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 12.7 Mon programme plante ` cause de malloc, cette fonction est-elle bugge ? . . . . 60 a e 12.8 Que signient les erreurs segmentation fault et bus error ? . . . . . . . . 60 12.9 Doit-on librer explicitement la mmoire avant de quitter un programme ? . . . . 60 e e 12.10Du bon usage de realloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 13 Le pr-processeurs e 62

13.1 Quel est le rle du prprocesseur ? . . . . . . . . . . . . . . . . . . . . . . . . . . 62 o e 13.2 Quest-ce quun trigraphe ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 ` 13.3 A quoi sert un backslash en n de ligne ? . . . . . . . . . . . . . . . . . . . . . . . 63

13.4 Quelles sont les formes possibles de commentaires ? . . . . . . . . . . . . . . . . . 63 13.5 Comment utiliser #include ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 13.6 Comment viter linclusion multiple dun chier ? . . . . . . . . . . . . . . . . . . 64 e 13.7 Comment dnir une macro ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 e 13.8 Comment dnir une macro avec des arguments ? . . . . . . . . . . . . . . . . . . 65 e 13.9 Comment faire une macro avec un nombre variable darguments ? . . . . . . . . . 66 13.10Que font les oprateurs # et ## ? . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 e 13.11Une macro peut-elle invoquer dautres macros ? . . . . . . . . . . . . . . . . . . . 68 13.12Comment rednir une macro ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 e 13.13Que peut-on faire avec #if ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 13.14Quest-ce quun #pragma ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 13.15Quest-ce quun #assert ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 13.16Comment dnir proprement une macro qui comporte plusieurs statements ? . . 70 e 13.17Comment viter les eets de bord ? . . . . . . . . . . . . . . . . . . . . . . . . . . 70 e 13.18Le prprocesseur est-il vraiment utile ? . . . . . . . . . . . . . . . . . . . . . . . . 72 e 13.19Approfondir le sujet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 14 Fonctions de la biblioth`que e 74

14.1 Comment convertir un nombre en une cha de caract`res ? . . . . . . . . . . . . 74 ne e 14.2 Comment convertir une cha en un nombre ? . . . . . . . . . . . . . . . . . . . 74 ne 14.3 Comment dcouper une cha ? . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 e ne 14.4 Pourquoi ne jamais faire fflush(stdin) ? . . . . . . . . . . . . . . . . . . . . . . 76 14.5 Comment vider le buer associ ` stdin ? . . . . . . . . . . . . . . . . . . . . . . 76 ea 14.6 Pourquoi mon printf ne sache pas ? . . . . . . . . . . . . . . . . . . . . . . . . 76 14.7 Comment obtenir lheure courante et la date ? . . . . . . . . . . . . . . . . . . . . 77 14.8 Comment faire la dirence entre deux dates ? . . . . . . . . . . . . . . . . . . . . 77 e 14.9 Comment construire un gnrateur de nombres alatoires ? . . . . . . . . . . . . . 77 e e e 14.10Comment obtenir un nombre pseudo-alatoire dans un intervalle ? e . . . . . . . . 77 ` 14.11A chaque lancement de mon programme, les nombres pseudo-alatoires sont toue jours les mmes ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 e 14.12Comment savoir si un chier existe ? . . . . . . . . . . . . . . . . . . . . . . . . . 78 14.13Comment conna la taille dun chier ? . . . . . . . . . . . . . . . . . . . . . . 79 tre 14.14Comment lire un chier binaire proprement ? . . . . . . . . . . . . . . . . . . . . 79 14.15Comment marquer une pause dans un programme ? . . . . . . . . . . . . . . . . . 79 14.16Comment trier un tableau de cha ? . . . . . . . . . . . . . . . . . . . . . . . . 79 nes

14.17Pourquoi jai des erreurs sur les fonctions de la biblioth`que, alors que jai bien e inclus les enttes ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 e 15 Styles 81

15.1 Comment bien programmer en C ? . . . . . . . . . . . . . . . . . . . . . . . . . . 81 15.2 Comment indenter proprement du code ? . . . . . . . . . . . . . . . . . . . . . . . 81 15.3 Quel est le meilleur style de programmation ? . . . . . . . . . . . . . . . . . . . . 82 15.4 Quest-ce que la notation hongroise ? . . . . . . . . . . . . . . . . . . . . . . . . . 82 15.5 Pourquoi certains crivent-ils if(0==x) et non if(x==0) ? . . . . . . . . . . . . . 82 e 15.6 Pourquoi faut-il mettre les { et } autour des boucles ? . . . . . . . . . . . . . . 83 15.7 Pourquoi certains disent-ils de ne jamais utiliser les goto ? . . . . . . . . . . . . . 83 15.8 Pourquoi commenter un #endif ? . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 15.9 O` trouver de la doc sur les dirents styles ? . . . . . . . . . . . . . . . . . . . . 83 u e 15.10Comment bien structurer son programme ? . . . . . . . . . . . . . . . . . . . . . 84 16 Autres 85

16.1 Comment rendre un programme plus rapide ? . . . . . . . . . . . . . . . . . . . . 85 16.2 Quelle est la dirence entre byte et octet ? . . . . . . . . . . . . . . . . . . . . . 85 e 16.3 Peut-on faire une gestion dexceptions en C ? . . . . . . . . . . . . . . . . . . . . 86 16.4 Comment grer le numro de version de mon programme ? . . . . . . . . . . . . . 87 e e 16.5 Pourquoi ne pas mettre de devant les identiants ? . . . . . . . . . . . . . . . . 87 ` 16.6 A quoi peut servir un cast en (void) ? . . . . . . . . . . . . . . . . . . . . . . . . 89 17 En guise de conclusion Index 90 91

Chapitre 1

Copyright (Droits de reproduction)


1.1 Copyright de la FAQ de comp.lang.c

The comp.lang.c FAQ list is Copyright 1990-1999 by Steve Summit. Content from the book C Programming FAQs : Frequently Asked Questions is made available here by permission of the author and the publisher as a service to the community. It is intended to complement the use of the published text and is protected by international copyright laws. The content is made available here and may be accessed freely for personal use but may not be republished without permission.

1.2

Quen est-il de ce document ?

Steve Summit a rdig la version anglo-saxonne sur laquelle nous nous sommes bass. On en e e e a traduit de longs passages, rorganis, annot, repens une partie et rcrit compltement e e e e ee e dautres paragraphes, ce avec son consentemment. Les auteurs comme les contributeurs de cette FAQ ne garantissent rien, ni que les conseils donns ici fonctionnent, ni que le code compile et fonctionne correctement, ni que votre e machine ne va pas sautodtruire instantanment apr`s lecture de ce message. Autrement dit, e e e vous tes enti`rement responsable de ce que vous faites des informations donnes ici. En cas e e e de ppin, vous ne pouvez vous en prendre qu` vous mme. e a e Les copies conformes et versions intgrales de ce document sont autorises sur tout support e e pour peu que cette notice soit prserve. Une utilisation commerciale devra faire lobjet dune e e autorisation pralable, notamment de Steve Summit. e

Chapitre 2

Introduction
2.1 Quest-ce quune FAQ ?

Cest une Foire Aux Questions (les anglos-saxons disent Frequent Asked Questions ou questions frquemment poses). Elle regroupe les rponses aux questions rcurrentes sur news:fr.comp. e e e e lang.c.

2.2

Qui la maintient ?

Elle est rdige ` linitiative de Guillaume Rumeau (mailto:guillaume.rumeau@wanadoo.fr). e e a

2.3

Qui y contribue ?

Les rdacteurs de la FAQ sont : e Guillaume Rumeau (mailto:guillaume.rumeau@wanadoo.fr), Thomas Pornin (mailto:Thomas.Pornin@ens.fr), Pascal Cabaud (mailto:pascal.cabaud@wanadoo.fr), mais elle ne saurait exister sans les contributions de : After , EpSylOn , ironl , MacLord , Edgar Bonnet, Erwan David, Thomas Deniau, Emmanuel Delahaye, Gabriel Dos Reis, Laurent Dupuis, Horst Kraemer, Antoine ` Leca, Vincent Lefevre, Fabien Le Lez, Eric Levenez, Serge Paccalin, Yves Roman, Michel Simian, et tous les autres... Merci aussi ` Thomas Baruchel pour ces nombreuses corrections orthographiques et typoa graphiques, et ` Stphane Muller pour son script python qui permet davoir des versions a e postscript et pdf de la FAQ. et noublions pas bien sr Steve Summit. u

Remarque Si vous pensez quun nom a t injustement omis, nhsitez pas ` nous le faire savoir, ee e a nous nous ferons un plaisir de lajouter.

2.4

O` puis-je la trouver ? u

a ` la page de Guillaume Rumeau : http://www.isty-info.uvsq.fr/~rumeau/fclc/ et deux fois par mois sur news:fr.comp.lang.c et news:fr.usenet.reponses.

2.5

Jai trouv une erreur ! e

Ce document tant rdig par des humains, il peut contenir des erreurs. Vous tes vivee e e e ment invits ` les signaler soit en postant sur news:fr.comp.lang.c soit en crivant ` e a e a lun des (aux) rdacteurs. e (voir aussi 2.3, page 9)

2.6

Et mes questions ?

Il faut commencer par les reformuler le plus prcisment possible (par exemple les refore e muler en questions moins ouvertes) ; dcrire les probl`mes aide beacoup. e e Quand on commence ` avoir une ide, chercher les mots clefs dcrivant le probl`me ` a e e e a laide de moteurs de recherche, par exemple http://www.google.com/. Regardez aussi ce que sait faire http://www.copernic.com/fr. Si vous navez toujours pas de reponse, analysez les dirents forums disponibles, et posez e la question dans celui qui vous semblera le plus adapt ; si il y a plusieurs questions qui e semblent pouvoir aller dans plusieurs forums, rdigez plusieurs messages (il y aura plus e de rponses, car les rponses seront moins longues ` crire ...). e e ae Si la question est tr`s technique et nobtient pas de rponse valable, faites leort de la e e re-rdiger en anglais et/ou demandez gentiment une traduction si vous ntes pas sr e e u de votre anglais, il y a susament de francophones sur les groupes internationaux pour quune bonne me vous rende service. a Avant de poster (sur news:fr.comp.lang.c), mieux vaut se demander si : la question porte-t-elle sur le C ISO ? cf. 3.7, page 15. la rponse est-elle dans la doc ? dans mon bouquin ? Bonne lecture ! e la rponse est-elle dans la FAQ ? Bonne lecture ! e la rponse a-t-elle dj` t donne ? Pour le savoir, il faut chercher sur http://groups. e eae e e google.com/. Comme il est dit et redit quotidiennement sur news:fr.comp.lang.c, le forum ne traite ni de graphisme, ni de Windows. ` A ce sujet, rappelons que la FAQ de news:fr.comp.os.ms-windows.programmation est disponible ici : http://www.usenet-fr.news.eu.org/fr.usenet.reponses/comp/os/ faq-winprog.html. (Merci ` Emmanuel Delahaye pour lURL de copernic et ` Antoine Leca de mavoir a a prt sa plume ;-)). ee

10

2.7

Dois-je poster sur fr.comp.lang.c ?

Ainsi que le stipule la charte du groupe, les seules questions traites sur news:fr.comp. e lang.c portent sur le C ISO. Pour ce qui a trait avec la programmation Be, DOS, Mac, Unix, Windows, il y a des forums ddis. e e En particulier, news:fr.comp.os.ms-windows.programmation pour la programmation sous Windows, news:fr.comp.sys.mac.programmation pour la programmation sous MacOS, MacOS X. Pour les autres syst`mes, il ny a pas de forum spcique dans la hierarchie news:fr.*. e e Pour ce qui concerne les Unix, news:fr.comp.os.unix et/ou news:fr.comp.os.bsd (ou encore news:news:fr.comp.sys.next) devraient faire laaire. Dans la hirarchie internationale, on trouve : e news:comp.os.msdos.programmer pour DOS, news:comp.unix.programmer pour Unix, news:comp.sys.be.programmer pour Be, news:comp.os.os2.programmer pour OS/2, Enn, en ce qui concerne la programmation graphique, il existe dsormais un forum ddi e e e a ` ce sujet dans la hirarchie fr : news:fr.comp.graphisme.programmation e

2.8

Comment poster sur fr.comp.lang.c ?

En nenvoyant pas de pi`ce jointe, en postant en texte ASCII. Les accents (ISO-8859-1) e sont accepts. Outre les pi`ces jointes, le HTML, le quoted printable et tout ce qui nest e e pas text/plain est ` proscrire (pas mal de serveurs de news supprimeront ces contributions a indsirables sans autre forme de proc`s). e e Plus la question est prcise, meilleure sera la rponse. On peut utiliser des balises dans le e e sujet, une liste est maintenue par Alexandre Lenoir, disponible l` : http://www.planete. a net/~alenoir/fcsm.html. Ne donnez pas de noms propres dans les sujets ! Il est de bon ton de poster les morceaux de code problmatique. Veillez ` poster du code e a qui compile (sauf sil ne sagit que de quelques lignes). Si le programme est trop gros, donnez une URL o` on peut le lire. u Pour rpondre, veillez ` ne citer que le strict ncessaire et ` rpondre apr`s le message e a e a e e ou passage cit. e Je vous rappelle quil est tr`s impoli, pour des raisons videntes, de demander une rponse e e e par mail ; et en gnral vous naurez pas satisfaction. Si vraiment vous y tenez, tentez e e votre chance avec le service fourni ici : http://francette.net/bdr.html (je nai pas test). e

2.9

Comment comprendre le langage utilis sur fr.comp.lang.c ? e

Ceci nest pas une FAQ sur Usenet mais sur le langage C donc on va faire court :

11

OEQLC signie O` Est la Question sur Langage C autrement dit, votre question est u hors-sujet (o topic), OT ou HS signie Hors-Sujet autrement dit, cest du bruit, RTFM signie Read The F*cking Manual autrement dit, cest dans tout bouquin digne de ce nom (voir 3.9, page 18 et 3.10, page 19). Pour les mes sensibles, on a traduit parfois par Read The Fine Manual , ou en franais par Regarde Ton Fichu c Manuel .

12

Chapitre 3

Le langage C
3.1 Quest-ce que le langage C ?

Donner une dnition du C est assez dicile, je vous propose celle-ci (avec laquelle je me e suis le moins fait insult ;-)) : e Cest un langage structur, gnralement compil, de haut niveau. e e e e ` Pour le haut niveau , cela dpend un peu du point de vue en fait. A la n des annes 70, e e les rfractaires disaient de lui que ctait encore un assembleur . Dautres le trouvent e e donc de bas niveau ... On vous renvoie ` lintroduction et aux avant-propos de K&R (cf. a 3.9, page 18). Il dispose dune biblioth`que standard (normes ANSI, ISO, IEEE, AFNOR,...) permettant e un minimum dinteractions avec la machine. Il a t normalis (cf. 3.7, page 15) ce qui ee e permet de recompiler un source (nutilisant que la biblioth`que standard) sur nimporte e quelle machine disposant dun compilateur respectueux de la norme. Cett derni`re ne porte que sur le langage proprement dit et sur le contenu de la biblioe th`que standard. Cette derni`re ne contient que le strict minimum pour interagir avec la e e machine. Ainsi, peut-on manipuler du texte et des chiers, dterminer le temps de calcul e ou grer la mmoire mais gu`re plus. Le reste est ` la charge du programmeur, ou des e e e a biblioth`ques spciques du syst`me. e e e Au vu de ce qui prc`de, on peut donc donner cette autre dnition : e e e Le C est un langage de programmation dont la structure est proche de la machine de Von Neumann. Donc ce qui importe, ce nest pas tant quon puisse recompiler notre programme type sans modication sur toutes les plateformes, cest la smantique . Le fait dcrire en C e e standard nimplique nullement que le programme soit portable en ce sens quil a la mme e smantique sur tous les compilateurs. Il est important de noter que la dnition de C e e dnit une machine abstraite paramtre les param`tres variant dun compilateur e e e e a ` un autre. Rappelons ici encore que le langage ne g`re ni la souris, ni lcran, ni votre store lectrique e e e (pardon, nuclaire) dernier cri. Tout cela est du ressort de votre OS. e

13

Voir aussi 3.5, page 14. ` Merci ` Emmanuel Delahaye, Gabriel Dos Reis, Vincent Lefevre, Thomas Pornin a et tous les autres pour la rdaction de cet article. e

3.2

` A quoi a sert ? c

` e A crer ses propres logiciels, selon ses besoins, de mani`re portable ie. indpendemment e e de la machine et du syst`me dexploitation. e

3.3

Do` vient le C ? u

Il a t cr par Brian Kernighan et Dennis Ritchie, sur les cendres du BCPL (Basic e e ee Combined Programming Language de Martin Richard, le BCPL tant une simplication e du CPL, Cambridge Programming Language) et du B (langage exprimental de Ken e Thompson), dans les annes 70 lorsquils crivaient Unix. e e Voir aussi 3.7, page 15.

3.4

Que peut-on faire en C ?

On peut tout faire ou presque. On peut crer son propre syst`me dexploitation (les Unixe e like sont encore crits en C pour la majorit), son interface graphique, sa base de donnes, e e e son driver (pilote) pour la derni`re machine ` caf USB, etc. e a e Tout compilateur C est fourni avec une biblioth`que de fonctions, en principe standard. e Voir aussi 3.5, page 14.

3.5

Portabilit, matriel, syst`me ... e e e

Si la norme du C permet une bonne portabilit, il faut noter que lon peut faire des choses e parfaitement dpendantes de la cible. Ainsi, les Unix sont-ils crits en C. Mais point nest e e besoin dutiliser des API1 exotiques pour perdre en portabilit, prsupposer quun char e e fait 8 bits est lexemple le plus agrant (cf. 16.2, page 85). Un autre exemple classique est de croire que toutes les machines supportent lASCII ...

3.6

Et le C++ dans tout a ? c

Le C++ est un langage ` objets bas sur le C. Il y a des dirences susantes pour quil a e e sagisse dun autre langage, ayant son forum propre : news:fr.comp.lang.c++.
Application Programming Interface i.e. interface de programmation dapplications littralement ; autremente dit biblioth`que. e
1

14

Il existe un autre langage ` objets bas sur le C. Il sagit dObjective-C. Il est principaa e lement utilis dans Mac OS X et dans GNUStep. Son forum est news:fr.comp.lang. e objective-c.

3.7

ISO, ANSI, K&R, ..., ques aquo ?

Les origines Le langage C est le fruit des eorts conjoints de Brian Kernighan, Denis Ritchie et Ken Thompson. Le dernier dirigeait le projet de rcriture de Multics (un OS multiee utilisateurs, projet abandonn) et il voulait crer un OS qui soit portable rpondant ` ses e e e a attentes, permettant un acc`s simple aux priphriques. De l en aiguille, cest devenu un e e e ` e syst`me largement indpendant du hardware. A lpoque les plateformes taient toutes si e e e direntes quil tait commun de donner ses sources et la moindre recompilation dune e e machine ` une autre demandait souvent un eort de portage. a Pour faire ce syst`me, il en est venu ` penser Unix de sorte que les deux seuls lments e a ee qui dpendent du hardware se rsument au strict minimum ` savoir le compilateur et e e a le noyau. Il lui fallait un langage dassez bas niveau et simple. Se basant sur le BCPL, Thompson a donc mis au point le langage B (pour Unics, 1969-1972) puis Ritchie la amlior pour en faire le langage C (Unix, 1973). e e Voir aussi 3.3, page 14 et surtout le papier de Dennis Ritchie : http://cm.bell-labs. com/cm/cs/who/dmr/chist.html pour plus de dtails. e La popularit du C tient alors autant ` sa simplicit qu` la pntration dUnix (et e a e a e e donc lapparition de compilateurs C sur les machines ie. la nouvelle portabilit des e programmes). Des compilateurs sont alors assez vite apparus sur dautres plateformes quUnix, contribuant ainsi ` la diusion (un peu anarchique) du langage. a En 1978, Brian W. Kernighan et Denis M. Ritchie ont publi The C Programming e Language (ISBN :0131101633) (On trouve tous les numros ISBN des direntes ditions e e e sur http://cm.bell-labs.com/cm/cs/cbook/index.html). D`s lors, les compilateurs ont commenc ` suivre les recommandations et indications des e ea auteurs. Cet ouvrage a fait oce de norme pendant longtemps, le langage qui y est dcrit e sappelle le C K&R, en rfrence aux 2 auteurs. ee La normalisation Devant la popularit du C, lAmerican National Standard Institut (http://www.ansi. e org) charge en 1983 le comit X3J11 de standardiser le langage C. On parle ` ce momente a l` de C pr-ANSI. Apr`s un processus long et complexe, le travail du comit a nalement a e e e t approuv : le 14 dcembre 1989, le standard ANSI X3.159-1989 (ou C89) est n. Il est ee e e e publi au printemps 1990. e Entre-temps, en 1988, durant la priode de travail du comit, la deuxi`me dition du K&R e e e e a t publie (ISBN : 0131103709). Elle a t compl`tement rcrite et on y a ajout des ee e ee e ee e exemples et des exercices an de clarier limplmentation de certaines constructions e complexes du langage. Dans sa plus grande partie, le standard C ANSI de 1989 ocialise les pratiques existantes,
15

en ajoutant quelques nouveauts provenant du C++ comme les prototypes de fonctions e et le support de jeux de caract`res internationaux (notamment les tr`s controverses e e e squences trigraphes). Le standard C ANSI dcrit aussi les routines pour le support des e e biblioth`ques dexcution du C. e e e LInternational organization for standardization (http://www.iso.ch) a adopt en 1990 ce standard en tant que standard international sous le nom de ISO/IEC 9899 :1990 (ou C90). Ce standard ISO remplace le prcdent standard ANSI (C89) mme ` lintrieur e e e a e des USA, car rappelons-le, ANSI est une organisation nationale, et non internationale comme lISO. Aux USA, on parle alors de ANSI/ISO 9899-1990 [1992], en France de ISO/CEI 9899 :1990. Dtails sur la normalisation e Durant les annes 1990, lorsquon parle de C89 (C ANSI) ou de C90 (C ISO), ce sont e deux appellations direntes pour en fait une seule et mme norme. Il existe aussi une e e norme europenne et une franaise (AFNOR) dont on entend beaucoup moins parler, e c qui sont aussi semblables ` la norme ISO. Noter que la norme franaise (AFNOR) est a c parfaitement identique ` lISO, lAFNOR se contentant de publier lISO (en anglais) en a guise de norme franaise (le site du groupe de normalisation du C ` lAFNOR : http: c a //forum.afnor.fr/afnor/WORK/AFNOR/GPN2/Z65B/, ne pas se er aux apparences, il sagit du groupe sur le C, la page est un peu vieille et le groupe rduit pass un temps e e a ` Antoine Leca a t recueilli par le groupe charg du C++). ee e Les standards ISO, en tant que tel, sont sujets ` des rvisions, par la diusion de Techa e nical Corrigenda 2 et de Normative Addenda 3 . Cest ainsi quen 1995, le Normative Addendum 1 (NA1) (http://www.lysator.liu.se/c/na1.html) parfois appel Amende ment 1 (AM1) fut approuv en 1995. Il ajouta environ 50 pages de spcications diverses e e concernant notamment de nouvelles fonctions dans la biblioth`que standard pour lintere nationalisation, et les squences digraphes pour le jeu de caract`res ISO 646, autorisant e e ainsi les terminaux ne possdant pas certains caract`res ` utiliser une criture alternative e e a e (<% %> pour { et } ou encore < : :> pour [ et ]). Peu de temps apr`s, toujours en 1995, le Technical Corrigendum 1 (TCOR1) (http: e //anubis.dkuug.dk/JTC1/SC22/WG14/www/docs/tc1.htm) fut approuv et modia le e standard ISO en environ 40 points, la plupart dentre eux tant des corrections mineures e ou des clarications. En 1996, on publia aussi le TCOR2 (http://anubis.dkuug.dk/ JTC1/SC22/WG14/www/docs/tc2.htm) qui apporta des changements encore plus mineurs que le TCOR1. TCOR2 reformulait certains points abscons. ` A partir de 1997, on dsigne par C95 lensemble des documents TCOR1, TCOR2 et e AMD1 et de la norme C90. Le terme C95 est utilis dans le rationale de C99. e En fait ce nest pas directement lISO qui travaille sur les standards, elle ne fait que les approuver conjointement avec lIEC (http://www.iec.ch). Cest pour cette raison que les standards ISO du C commencent par ISO/IEC... De plus, lISO charge des comits e techniques de travailler dans tels et tels domaines. En loccurrence, le JTC1 (http:// www.jtc1.org) est le comit spcialis dans le domaine informatique. Le JTC1 ` son e e e a tour rpartit le travail dans plusieurs sous-comits : celui qui nous intresse est le SC22, e e e
2 3

Recticatif technique . Amendement .

16

dont le but est la standardisation des technologies de linformation. Or le SC22 lui-mme e est subdivis en Working Groups, le WG14 tant celui qui est en relation avec le C. e e Finalement, cest le ISO/IEC JTC1/SC22/WG14 qui rdige la norme ISO du C, le SC2 e approuve alors le projet nal (FDIS), puis le transmet au JTC1 qui approuve la nouvelle norme ISO. En ralit, le groupe de travail WG14 est compos dorganismes nationaux tels ANSI e e e (le plus actif), AFNOR, BSI, CSA, DS, ... reprsentants les pays prenant part ` e a la normalisation (je vous passe les dtails de pays votants, observateurs, et autres). e Chacun de ces organismes travaille sur le langage. LANSI dispose elle aussi dun comit spcialis dans le domaine informatique : le X3 (http://www.x3.org), qui dee e e puis 1996 sappelle NCITS (prononcez insights en anglais) (http://www.ncits.org) pour National comittee for Information Technology Standards. Lui aussi dispose de comits techniques qui travaillent chacun dans un domaine particulier : le J11 (http: e //www.ncits.org/tc\protect\T1\textunderscorehome/j11.htm) a en charge le langage C. Cest donc le X3J11 qui dveloppe la norme C ANSI aux USA, et qui travaille e avec le WG14. Il se trouve quen 1993, lors des runions bi-annuelles entre le WG14 (ISO) et le X3J11 e (ANSI), tout le monde sest accord pour dire, e que la rvision (prvue dans les textes ISO) de 1995 ne se ferait pas, et e e que la rvision de 2000 environ aboutirait ` une nouvelle version de la norme (le futur e a C9X). Lide a alors merge de crer un nouveau standard du C qui regrouperait le C90, le e e e e NA1, le TCOR1 et le TCOR2, apporterait dautres modications an de maintenir le C en phase avec les techniques de programmation daujourdhui, et qui minimiserait les incompatibilits avec le C++, sans pour autant vouloir transformer le C en C++. Ce e projet de nouveau standard du C a pris le nom de code C9X avec lintention quil serait publi dans la n des annes 1990 (http://anubis.dkuug.dk/JTC1/SC22/WG14/www/ e e charter.html). Vers la n de la normalisation de C99, SC22/WG21 le groupe de travail qui soccupe de C++ a adress une requte formelle (par lintermdiaire du bureau SC22) ` SC22/WG14 e e e a pour documenter les ventuelles incompatibilits introduites par C99 par rapport ` C++ e e a (SC22/WG21 lavait fait par rapport ` C90). SC22/WG14 a rpondu quil navait ni le a e temps ncessaire ni la comptence pour faire cela. e e Cependant, un tel travail (inspir partiellement de ce que C++ a dj` fait) a t entrepris ` e ea ee a titre personel par David Tribble dont la contribution se trouve ici : http://www.david. tribble.com/text/cdiffs.htm. Tout au long du projet C9X, des drafts (brouillons) du projet sont distribus an e que tout le monde puisse donner son avis et, le cas chant, revoir certaines parties. e e Le dernier draft disponible est le document n869 (http://www.dkuug.dk/jtc1/sc22/ wg14/www/docs/n869/) datant de janvier 1999. Ce document est celui le plus proche de la norme et que lon peut obtenir gratuitement : il dnit C9X, le projet de la norme. e Derni`res nouvelles e Tr`s rcemment, le 1er dcembre 1999, la norme ocielle a t adopte par lISO sous le e e e ee e

17

nom de ISO/IEC 9899 :1999, ou plus simplement C99. Elle a aussi t publie par lANSI, ee e qui travaille conjointement avec lISO, sous le nom de ANSI/ISO/IEC 9899-1999, mais que lon appelle C2k. ` A lheure actuelle, C99, qui est quivalent ` C2k, nest pas encore totalement supporte e a e par les compilateurs. Il faut un certain temps pour implmenter toutes les nouvelles e fonctionnalits de C99. Tout ce dont on peut tre sr, cest que nimporte quel bon e e u compilateur supporte au moins la norme C90. Voici dailleurs au passage quelques unes des nouveauts de C99 : e tableau ` longueur variable, a support des nombres complexes grce ` complex.h, a a types long long int et unsigned long long int dau moins 64 bits, famille de fonctions vscanf, les fameux commentaires `-la-C++ //, a les familles de fonctions snprintf, le type boolen, e etc. Thoriquement, ISO rvise les normes tous les cinq (5) ans. Les groupes de travail nont e e pas besoin dattendre les cinq ans avant de commencer ` travailler sur les ventuelles a e extensions. Cependant le travail de normalisation prend un certain temps pensez par exemple que ANSI a dbut le travail de normalisation de C89 en 1983 et na ni quen e e 1989. Il est possible quun C04 soit publi en 2004, ce serait C99 augment de quelques e e amendements. Le travail de normalisation est long et pnible par moment. e Un grand merci ` After pour le brouillon de cet article et ` Gabriel Dos Reis, Eric a a Levenez et Antoine Leca pour leurs relectures avises. e

3.8

De quoi ai-je besoin pour programmer en C ?

Dun diteur de texte basique, dun compilateur (voir 4.2, page 22), dun bon bouquin e (voir 3.9, page 18), dun dbogueur et de beaucoup de patience. En principe, compilateur, e biblioth`que(s), (dbogueurs) et doc sont fournis ensembles. e e Pour apprendre le C, il vous faudra un bon compilateur, de la doc papier, un bon dictionnaire danglais et un stock daspirine ;-)

3.9

Quel(s) bouquin(s) ?

Le livre que tout programmeur C se doit de conna tre et davoir sur son bureau est Kernighan B.W. & Ritchie D.M. (1997), Le langage C Norme ANSI, 2`me dition, e e Dunod, Paris. On trouvera les exercices corrigs du prcdent dans : Tondo C.L. & Gimpel S.E. e e e (2000), Exercices corrigs sur le langage C, Dunod, Paris. e En complment, Braquelaire J.-P. (2000), Mthodologie de la programmation en C, e e Biblioth`que standard, API POSIX, 3`me dition, Dunod, Paris. sera une excellente rese e e source.
18

Enn, citons lexcellent Kernighan B.W. & Pike R., (1999) The Pratice of Programming, Addison-Wesley, Reading. (http://cm.bell-labs.com/cm/cs/tpop/index.html). Il arrive souvent au programmeur de devoir rsoudre des probl`mes dAlgorithmique. Il e e peut se reporter ` la bible en la mati`re : Knuth D.E. (1997), The Art of Computer Proa e gramming, third edition, Addison-Wesley, Reading. (communment abrg en TAoCP). e e e Il y a aussi : Sedgewick R. (1991), Algorithmes en langage C, InterEditions, Paris. ou cet autre : Loudon K. (2000), Ma triser les Algorithmes en C, OReilly, Paris. Une bonne introduction ` lAnalyse Numrique en C est : Press W.H., Flannery a e B.P., Teukolsky S.A., Vetterling W.T. (1992), Numerical Recipes in C, The Art of Scientic Computing, second edition, Cambridge University Press. (communment abrg e e e en NR, PFTW ou Numerical Recipes selon). Cest disponible en ligne (voir 4.5, page 23). Il existe aussi : Engeln-Mllges G. & Uhlig F. (1996), Numerical Algorithms with u C, Springer, Berlin. (fourni avec les sources et djgpp, pour plateformes Wintel, sur CD).

3.10

O` trouver... u

de la doc ? L` par exemple : http://cm.bell-labs.com/cm/cs/who/dmr/, cest la page de Denis a Ritchie. Il y a aussi celle de Brian Kernighan : http://cm.bell-labs.com/cm/cs/ who/bwk/. On trouve des cours de C sur le web en franais sur les sites universitaires. Ainsi, on peut c citer : ftp://ftp.ltam.lu/TUTORIEL/COURS-C/COURS-C.ZIP, http://www.enseignement. polytechnique.fr/profs/informatique/Eric.Goubault/poly/cours.ps.gz, http:// www.loria.fr/~mermet/CoursC/coursC.ps, http://www.enseignement.polytechnique. fr/profs/informatique/Jean-Jacques.Levy/poly/polyx-cori-levy.ps.gz, http:// www-inf.int-evry.fr/COURS/COURSC/ On lira aussi tr`s attentivement : ftp://ftp.laas.fr/pub/ii/matthieu/c-superflu/ e c-superflu.pdf qui contient tout ce quil faut savoir pour commencer ` programmer a proprement en C. Il contient aussi une grosse bibliographie. Un CD complet en ligne sur le C : http://www.infop6.jussieu.fr/cederoms/Videoc2000/ Les sources du bouquin de Braquelaire (derni`re dition : la 3`me, 2`me tirage...) : e e e e http://dept-info.labri.u-bordeaux.fr/~achille/MPC-3/2T/MPC-3-2t.tar.gz Les sources du bouquin de Loudon : http://www.editions-oreilly.fr/archives/ algoc.zip. La biblioth`que standard : http://www.dinkumware.com/htm\protect\T1\textunderscorecl/ e index.html#Table-of-Contents Le IOCCC est un concours de hackers qui rcompense chaque anne le pire programme e e C : http://www.ioccc.org/index.html Un freezzine en anglais : http://www.gmonline.demon.co.uk/cscene/ Sur les sites universitaires on trouve toujours des cours en ligne, du code, etc ...

19

Signalons aussi (mme si cest hors-sujet) le chier ftp://ftp.laas.fr/pub/ii/matthieu/ e tpp/tpp.ps.gz qui explique comment utiliser make. Je recommande aussi vivement lutilisation doutils tels que CVS : http://www.cvshome.org/docs/ http://www.idealx. org/fr/doc/cvs/cvs.html http://matrix.samizdat.net/serveurs/cvs/tut\protect\ T1\textunderscorecvs.html la norme ? L` : http://wwwold.dkuug.dk/jtc1/sc22/open/n2794/n2794.txt On peut aussi lachea ter soit aupr`s de lAFNOR (tr`s cher) soit en ligne aux USA (environ 20 $ US), a se e e c passe ici : http://www.cssinfo.com/ncitsgate.html ou encore l` http://webstore. a ansi.org/ansidocstore/product.asp?sku=ANSI%2FISO%2FIEC+9899%2D1999 (Merci ` Antoine Leca). a la FAQ de comp.lang.c ? L` : http://www.eskimo.com/~scs/C-faq/top.html a les pages des manuels Unix en franais ? c On peut trouver les pages de manules en Franais pour la plupart des Unix, sous la c forme de packages du syst`me. Pour Linux, vous les trouverez ici : http://perso. e club-internet.fr/ccb/ ou encore l` : http://www.delafond.org/traducmanfr/ a Le C et les CGI ? Tout ce qui concerne le C ` propos des CGI est l` : http://www.chez.com/nospam/cgi. a a html Il y a aussi une FAQ ici : http://www.htmlhelp.com/faq/cgifaq.html (informations bienvenues ` mailto:pascal.cabaud@wanadoo.fr) a La Programmation Objet en C ? Oui, on peut programmer Orient Objet en C. Voici un document qui prsente ces teche e niques : http://ldeniau.home.cern.ch/ldeniau/html/oopc/oopc.html

20

Chapitre 4

Outils, environnement de dveloppement et autres gadgets e


Note Cette section a du mal ` vivre sans laide des lecteurs. Par soucis dquit et pour limiter a e e un peu le volume dinformations, on sen tient aux logiciels, programmes, codes, etc. libres, gratuits ou du domaine public.

4.1
Apple

Environnements de dveloppement intgrs e e e

Sur Mac OS, MPW combin avec le terrible MacsBug vous donneront enti`re satisface e tion. Pour Mac OS X, Project Builder est lIDE de choix utilisant GNU CC. http: //developer.apple.com/tools/projectbuilder/ Unix Sur Unix-like, vi[m] et [x]emacs, combins avec make, GNU CC et GNU DB forment e un environnement de dveloppement intgr. e e e Wintel Borland fournit lenvironnement Turbo complet avec diteur, dbogueur, etc. Mais cest e e passablement vieux : la version 2.0 distribue sur les sites de Borland date de 1988, il e sagit donc dun compilateur pr-ANSI (cf. 3.7, page 15). Si lon tient ` utiliser du matriel e a e Borland, Turbo C++ 1.01 sera plus confortable et plus conforme ` la norme (en ayant a rcupr larchive BC20P2.ZIP qui tra un peu partout). e ee ne Mieux vaut se rabattre sur le couple djgpp-RHIDE (tentez votre chance ici : http://www. rhide.com), qui dailleurs existe sous Linux. Il y a aussi Source Navigator disponible ici : http://sources.redhat.com/sourcenav/. On pourra aussi utiliser NTemacs (cf. http://www.linux-france.org/article/appli/ emacs/Gnus+Emacs/Windows/emacs.html). Signalons enn lexistence dun IDE assez complet, gratuit et open-source : Dev-C++ ; il

21

est bas sur MinGW (mais peut aussi utiliser Cygwin) pour la compilation et le debugger. e On le retrouvera ` ladresse : http://www.bloodshed.net/devcpp.html. a (voir aussi 4.2, page 22).

4.2

Compilateurs

Le choix dpend du syst`me dexploitation et du processeur cible. e e Apple

Pour les machines sous Mac OS, le compilateur dApple est gratuit, il est l` : ftp://ftp. a apple.com/devworld/Tool\protect\T1\textunderscoreChest/Core\protect\T1\textunderscoreM protect\T1\textunderscoreOS\protect\T1\textunderscoreTools/MPW\protect\T1\ textunderscoreetc./. Pour les machines sous Mac OS X, le compilateur est GNU CC Unix Pour Unix-like, le GNU C Compiler ( alias GNU CC ou gcc) est fourni avec toute distribution *BSD ou GNU/Linux. Wintel Pour machines Wintel, il existe un portage du cl`bre GNU C Compiler : http://www. ee delorie.com/djgpp/. Un autre portage existe pour Windows : http://sources.redhat.com/cygwin/. MinGW (Minimalist-GnuWin32), encore un portage Win32 de gcc. Frustre mais puissant, optimise bien, adapt si lon conna bien lenvironnement de programmation UNIX : e t http://mingw.sourceforge.net/ Borland a aussi mis en tlchargement gratuit certains de ses compilateurs l` : http:// ee a www.borland.com/bcppbuilder/freecompiler/ ou l` : http://www.borland.fr/download/ a compilateurs/. Il y a aussi lcc-win32, qui est un driv du lcc originel de Chris Fraser et David Hanson. e e lcc-win32 vient avec un diteur intgr, et contient ce quil faut comme documentation e e e et biblioth`ques pour ouvrir des fentres sous Windows. Il est plus lger en termes de e e e consommation mmoire et de CPU que GNU CC ; il produit un code dcent (mais nane e e moins pas optimal) : http://www.cs.virginia.edu/~lcc-win32/

4.3
Apple

Dbogueurs e

Allez l` : http://devworld.apple.com/tools/debuggers/. Pour Mac OS X, le dboga e geur fourni avec le syst`me est GNU DB. e Unix GNU DB (alias gdb) est fourni avec les distributions *BSD et GNU/Linux. Wintel
22

` A part celui de lenvironnement Turbo (cf. 4.1, page 21), regardez du ct de http: oe //sources.redhat.com/insight/.

4.4
Apple

Graphisme

Pour Mac OS classique, il vous faut rcuprer QuickDraw, fournie par Apple : http: e e //devworld.apple.com/macos/quickdraw.html Pour Mac OS X, lenvironnement graphique sappelle Quartz et il utilise OpenGL : http://developer.apple.com/techpubs/ macosx/CoreTechnologies/graphics/Quartz2D/quartz2d.html Unix Pour environnement Unix, il existe un environnement graphique libre, reproduisant le syst`me X11 (dit aussi X-Window ). Il est fourni avec toute distribution *BSD ou GNU/Linux. e Allez le voir sur http://www.xfree86.org/. Wintel Pour du graphisme sous DOS avec djgpp, il y a Allegro qui se trouve l` : http://www. a talula.demon.co.uk/allegro

4.5

Biblioth`ques e

Calcul scientique Pour du calcul scientique, ici : http://www.netlib.org/ (mis ` jour tr`s rguli`rement). a e e e Ajay Shah maintenait un index de codes libres et/ou du domaine public implmentant e diverses routines de calcul. On le trouve par exemple l` : ftp://ftp.math.psu.edu/pub/ a FAQ/numcomp-free-c mais a date. c Numerical Recipes in C L` : http://www.ulib.org/webRoot/Books/Numerical\protect\T1\textunderscoreRecipes/ a bookcpdf.html. Les sources ont t incluses dans la distribution Linux pour les plateee formes ` base de PowerPC : LinuxPPC 1999 . Les sources ont disparu des miroirs et a LinuxPPC 2000 ne lint`gre plus mais les miroirs ont encore (en septembre 2000) le e package de binaires (pour PPC uniquement) et certains sont prts ` envoyer les sources. e a Gnrateurs de nombres pseudo-alatoires e e e (Les anglo-saxons disent Pseudo-Random Numbers Generators et abr`gent en PRNG) e Par exemple l` : http://www.io.com/~ritter/NETLINKS.HTM#RandomnessLinks, http: a //burtleburtle.net/bob/rand/isaacafa.html ou http://random.mat.sbg.ac.at/. Voir aussi http://www.netlib.org/ et cherchez enn r250, RANLIB et FSULTRA. Calcul multi-prcision e On trouve le code des fonctions quad de BSD quelque part l` : ftp://ftp.uu.net/ a systems/unix/bsd-sources/ dans un rpertoire src/lib/libc/quad/. e
23

Il y a aussi la biblioth`que GNU MP (GNU Multiple Precision ou GMP) que lon trouve e ici : http://www.swox.com/gmp/. Enn, il y a le package MIRACL (voir http://indigo.ie/~mscott/). Les sources de la biblioth`que standard e Le livre Plauger P.J. (1994), La biblioth`que C standard chez Masson, rimplmente la e e e plupart des fonctions. Attention, les sources ne sont pas libres. On peut aussi consulter les sources des Unix-based libres *BSD ou encore celles du projet GNU. (informations bienvenues ` mailto:pascal.cabaud@wanadoo.fr). a

4.6

Outils divers

Notez que la majorit des outils cits ici ont initialement t crits pour des plateformes e e eee Unix. Il existe assez souvent des portages sur dautres OS, que lon cite parfois. Il arrive frquemment que je ne donne pas dURL ; une recherche par exemple avec le merveilleux e http://www.google.com/ vous donnera des liens ` ne plus savoir quen faire ... a Certains sont l` : ftp://gatekeeper.dec.com/pub/usenet/comp.sources.unix/ ou a sur le mirroir : ftp://ftp.uu.net/usenet/comp.sources.unix/. Pour les outils GNU, voir sur le site du projet : http://www.gnu.org/. Indentation et mise en forme Cherchez cb, enscript, indent, lgrind, vgrind. Vrication de sources e Cherchez lint et regardez http://www.elirion.com/lintpage.html. On trouve splint(anciennement LCLint) l` : ftp://www.splint.org. a Gnration de rfrences croises e e ee e Cherchez cow, cxref, calls, cscope, xscope, ou ixfw. Cest pratique pour voir les graphes de dpendances des fonctions. e Dgnration de code C e e e Cherchez obfus, shroud, ou opqcp. Ce gadget sert ` rendre un source parfaitement illisible. a Interprtation des dclarations e e Cherchez cdecl dans le volume 14, ` http://www.uu.net/usenet/comp.sources.unix/. a Ca sert essentiellement ` comprendre des dclarations ou ` en gnrer. a e a e e Gnration de dpendances e e e Cherchez makedepend ou tentez cc -M ou cpp -M. Gestion de projets Cherchez GNU make. Voir aussi 3.10, page 19. Contrle de versions o Cherchez CVS, RCS et SCCS. Voir aussi 3.10, page 19.
24

Traducteurs Fortran/C et Pascal/C Cherchez ftoc et f2c pour le Fortran. On trouve f2c sur http://www.netlib.org/. Il existe une version pour MacOS : http://www.alumni.caltech.edu/~igormt/Mac\ protect\T1\textunderscoreF2C.html. Pour le Pascal, a se trouve l` : ftp://csvax. c a cs.caltech.edu/pub/p2c-1.20.tar.Z. Cherchez aussi ptoc. Calcul dunits e Cherchez ccount, Metre, lcount, csize, http://www.qucis.queensu.ca/. Calcul du nombre de lignes dun chier source Utilisez les commandes wc ou grep -c " ;". Gnration de prototypes e e Cherchez cproto (http://www.vex.net/~cthuang/cproto/) et cextract. Gestion correcte des appels ` malloc a Cherchez dbmalloc, MallocDebug, JMalloc.c et JMalloc.h, zieutez la page http://www. cs.colorado.edu/homes/zorn/public\protect\T1\textunderscorehtml/MallocDebug. html et regardez : ftp://ftp.crpht.lu/pub/sources/memdebug. Ceci fait, il vous reste a ` compiler et installer dmalloc provenant de l` : http://dmalloc.com/. a Gnration de documentation ` partir de code comment e e a e Il existe divers outils dont doc++ et doxygen. Le dernier g`re le format LaTeX2e, HTML e et nro et se trouve ici : http://www.stack.nl/~dimitri/doxygen/. Voir aussi 4.7, page 25. Prprocesseur slectif e e Cherchez unifdef, rmifdef et scpp. Ils suppriment les directives #ifdef inutiles, rendant ainsi un code plus lisible. Gestion des expressions rguli`res e e Le projet GNU propose rx. Il y a aussi regexp sur ftp://ftp.cs.toronto.edu/pub/ regexp.shar.Z. Proleur Le projet GNU dveloppe gprof ( cf. 16.1, page 85). Il existe aussi FunctionCheck : e http://www710.univ-lyon1.fr/~yperret/fnccheck/profiler.html

4.7

O` trouver du code ? u

L` : http://www.gnu.org par exemple. Les logiciels GNU, les Unix-like *BSD et GNU/Linux a sont distribus (ils sont libres) avec les sources. La pluparts dentre eux tant crits en e e e C, cela en fait une tr`s grande collection de code. e On trouvera du code (du domaine public) l` : ftp://ftp.cdrom.com. Il y a aussi ftp:// a ftp.brokersys.com/pub/snippets et sur le web ` http://www.brokersys.com/snippets/ a ou http://www.ping.de/sites/systemcodes/ ou encore par ftp par exemple ` ftp: a
25

//ftp.funet.fi/pub/languages/C/Publib/. Les serveurs ftp://ftp.uu.net/, ftp://archive.umich.edu/, ftp://oak.oakland. edu/, ftp://sumex-aim.stanford.edu/, et ftp://wuarchive.wustl.edu/, hbergent e une grande quantit de logiciels, code et documentation libres et/ou gratuits. le serveur e princicpal du projet GNU est ftp://prep.ai.mit.edu/. Voir aussi 4.6, page 24 et 4.5, page 23. (informations bienvenues ` mailto:pascal.cabaud@wanadoo.fr) a

26

Chapitre 5

Dclarations et initialisations e
5.1 Quels types utiliser ?

Il existe en C plusieurs types de nombres entiers. Si vous avez ` grer de grands nombres, a e il faut utiliser le type long. Avec la norme C99 (cf. 3.7, page 15), un type long long est disponible. Pour des nombres de petites tailles, et si la place mmoire manque, cest le e type short quil faut prendre. Le type char peut parfois tre utilis comme tr`s petit entier. Mais cela doit tre vit au e e e e e e maximum. En eet, outre les probl`mes de signe, le code gnr peut tre plus complexe e e ee e et risque nalement de prendre plus de place et de faire perdre du temps. Mme les short e font parfois perdre du temps. Dans les autres cas, int est bien adapt. e Le mot cl unsigned est ` utiliser pour les nombres positifs, si vous avez des probl`mes e a e dus aux dbordements ou pour les traitements par bits. e Le choix entre float et double ne se pose pas. On devrait toujours utiliser double, sauf si on a vraiment des contraintes de mmoire (cf. 11.1, page 54 et 11.10, page 56). e

5.2

Comment dnir une structure qui pointe sur elle-mme ? e e

Il y a plusieurs faons correctes de le faire. Le probl`me est que dans la dnition de la c e e structure avec un typedef, le type nest pas encore dni. Voici la mani`re la plus simple, e e o` pour contourner le probl`me, on ajoute un tag ` la structure. u e a typedef struct node { char * item; struct node * next; } node_t;

Une autre solution consiste ` dclarer le type de la structure avant sa dnition, avec un a e e pointeur.
27

typedef struct node * node_p; typedef struct node { char * item; node_p next; } node_t;

Cette construction rcursive est utilise pour obtenir des listes cha ees ou des arborese e n cences.

5.3

Comment dclarer une variable globale ? e

Sauf dans des cas prcis, vous devriez viter dutiliser des variables globales. e e Si vous y tenez vraiment, la meilleure solution est de dclarer la variable dans un e chier xxx.c, et de mettre la dclaration extern dans un xxx.h associ. Ceci vitera des e e e rednitions de la variable lors des inclusions den-ttes. e e Le mot cl static permet davoir des variables locales persistantes, ce qui peut tre une e e bonne alternative aux globales.

5.4

Quelle est la dirence entre const et #define ? e

Cela na rien ` voir. Le #define permet de dnir une macro, alors que le mot cl const a e e indique que lobjet quali est protg en criture. e e e e Lors de la compilation, la premi`re phase est eectue par le pr-processeur qui reme e e place les macros par leurs valeurs. Cest un simple copier/coller. Une variable dclare e e const reste quant ` elle une variable, mais on ne peut lui aecter une valeur que lors de a linitialisation. Apr`s, elle est nest plus modiable. e Le mot-cl const permet aussi au compilateur deectuer des optimisations en plaant e c par exemple la variable dans un registre. Il est ` noter quavec la nouvelle norme C99, il est possible de dclarer un tableau dont a e la taille est donne par une variable const. Auparavant, il fallait utiliser une macro. e Voir aussi les questions 13.7, page 65 et 5.5, page 28.

5.5

Comment utiliser const avec des pointeurs ?

Le mot-cl const permet de protger une variable de modications ultrieures. Une vae e e riable constante nest modiable quune fois, lors de linitialisation. Un pointeur tant une variable comme les autres, const sutilise de la mme faon. Voici e e c un exemple : const char * sz1;
28

char const * sz2; char * const sz3; char const * const sz4; Les variables sz1 et sz2 sont des pointeurs sur objet constant de type char. La variable sz3 est un pointeur constant sur un objet (non constant) de type char. Enn, sz4 est un pointeur constant sur un objet constant de type char. Un petit amusement pour terminer, que signie la dclaration const char * (* e f)(char * const * s) ; ? Voir aussi la question 5.4, page 28.

5.6

Comment bien initialiser ses variables ?

Les variables globales, ou dclares static, sont initialises automatiquement lors de leur e e e dnition. Si aucune valeur nest spcie, cest un zro qui est pris (suivant le type de e e e e la variable, 0, 0.0 ou NULL). Ce nest pas le cas pour les variables automatiques (les autres). Il est donc ncessaire de e le faire ` la main . a Les variables alloues dynamiquement avec malloc ne le sont pas non plus. On pourra e utiliser calloc qui initialise les variables alloues. Il est ` noter que calloc met les bits e a a ` 0 comme le ferait memset. Cette initialisation est valide pour les types entiers (char, short, int et long), mais non portable pour les pointeurs et les ottants. Une bonne mthode qui initialise correctement les variables suivant leur type est celle-ci : e { type a[10]={0}; struct s x ={0}; } Avec la variante dynamique : [static] const struct s x0 ={0}; { struct s *px =malloc(sizeof *px); *px=x0; }

5.7

Comment dclarer un tableau de fonctions ? e

Deux cas se prsentent. Si toutes les fonctions ont le mme prototype, il sut de faire e e ainsi :
29

extern char * f(int, int); /* une fonction char * (*fp[N])(int, int); /* Un tableau de N fonctions char * sz; fp[4] = f; /* affectation de f dans le tableau sz = fp[4](42, 12); /* utilisation

*/ */ */ */

Si les fonctions ont des prototypes dirents, il faut dclarer un tableau de fonctions e e gnriques. Les fonctions gnriques nexistent pas ` proprement parler. Il faut utiliser e e e e a une fonction sans argument spci et retournant un int. e e int (*fp[N])(); /* Un tableau de N fonctions quelconques*/

Cela capte la plupart des cas, sauf les fonctions au nombre darguments variables, comme printf. Voir aussi la question 7.4, page 39.

5.8

Comment conna tre le nombre dlments dun tableau ? ee

On peut utiliser une macro de ce type l` : a #define NELEMS(n) (sizeof(n) / sizeof *(n))

5.9

Quelle est la dirence entre char a[] et char * a ? e

Il faut bien se rappeler quen C, un tableau nest pas un pointeur, mme si ` lusage a e a c y ressemble beaucoup. char a[] dclare un tableau de char de taille inconnue (type incomplet). D`s linitialie e sation, par char a[] = "Hello";

a se transforme en char[6], soit un tableau de six char (type complet). Il arrive souvent de voir la confusion entre tableau et pointeur constant (moi mme je la fais parfois ;-) ) e Ce pointeur constant est inspir par K&R, 1`re dition. Ctait une mtaphore male e e e e heureuse de K&R qui voulait exprimer quun tableau se comporte en gnral comme une e e valeur (rvalue) du type pointeur , et bien entendu une valeur est toujours constante. Par cette mtaphore ils ont essay dexpliquer pourquoi on ne pouvait pas prendre ladresse e e dun tableau.
30

Malheureusement ceci nexplique pas pourquoi sizeof a donne la taille du tableau et non la taille dun pointeur. Sur ce sujet, la norme est plus prcise en disant quun tableau dans une expression e sauf dans &a ou sizeof a ou dans des initialisations par des cha nes littrales xyz e est converti automatiquement en une valeur du type pointeur qui pointe sur llment ee initial du tableau (merci ` Horst Kraemer pour ces prcisions). a e Voir aussi la question 7.1, page 37.

5.10

Peut-on dclarer un type sans spcier sa structure ? e e

Plus prcisment, est-il possible de dnir un type de donnes dont on veut cacher ` e e e e a lutilisateur de la biblioth`que limplmentation ? e e Oui cest possible, en encapsulant ce type dans une structure. Il y a au moins deux mthodes. La premi`re est de dnir une structure (publique) qui contient un pointeur e e e void *, qui pointe vers une variable du type priv. Ce type priv est dni avec les e e e fonctions, dans un .c. Il faut alors prvoir des fonctions dinitialisation et de destruction e des objets. Une autre solution consiste ` dclarer une structure qui contient une donne du type ` a e e a protger, et ` accder aux types par des pointeurs uniquement. Voici un exemple : e a e /* data.h */ struct Data_s ; typedef struct Data_s Data_t ; extern Data_t * DataNew(int x, int y); extern int DataFonction(Data_t * this); /* data.c */ #include <stdio.h> #include <stdlib.h> #include "data.h" struct Data_s { int x; int y; }; Data_t * DataNew(int x , int y) { Data_t * pData; pData = malloc(sizeof *pData) ; if (pData) { pData->x = x; pData->y = y; }
31

return pData; } int DataFonction(Data_t * this) { if (!this) return 0; return (this->x * this->x) + (this->y * this->y); } /* main.c */ #include <stdio.h> #include "data.h" int main(void) { Data_t *psData; psData = DataNew(3, 4); printf("%d\n", DataFonction(psData)); return 0; }

(Merci ` Yves Roman pour cet exemple.) a

32

Chapitre 6

Structures, unions, numrations e e


6.1 Quelle est la dirence entre struct et typedef struct ? e
struct x1 { ... }; typedef struct { ... } x2;

La premi`re criture dclare un tag de structure x1. La deuxi`me dclare un nouveau e e e e e type nomm x2. e La principale dirence est lutilisation. La deuxi`me criture permet un peu plus labse e e traction de type. Cela permet de cacher le vritable type derri`re x2, lutilisateur ntant e e e pas sens savoir que cest une structure. e struct x1 v1; x2 v2;

6.2
Oui.

Une structure peut-elle contenir un pointeur sur elle-mme ? e

Voir aussi la question 5.2, page 27.

6.3

Comment implmenter des types cachs (abstraits) en C ? e e

Lune des bonnes faons est dutiliser des pointeurs sur des structures qui ne sont pas c publiquement dnies. On peut en plus cacher les pointeurs par des typedef. e Voir aussi la question 5.10, page 31.

33

6.4

Peut-on passer des structures en param`tre de fonctions ? e

Oui, cest parfaitement autoris. Toutefois, rappelons que les param`tres en C sont passs e e e par valeurs et copis dans la pile. Pour des grosses structures, il est prfrable de passer e ee un pointeur dessus, et ventuellement un pointeur constant. e

6.5

Comment comparer deux structures ?

Il nexiste pas en C doprateur ou de fonction pour comparer deux structures. Il faut e donc le faire ` la main, champs par champs. a Une comparaison bit ` bit nest pas portable, et risque de ne pas marcher, en raison du a padding (alignement sur certains octets).

6.6

Comment lire/crire des structures dans des chiers ? e

Il faut utiliser les fonctions fread et fwrite. Attention : les chiers obtenus ne sont pas portables. Une mthode plus portable consiste ` enregistrer les structures dans un chier texte. e a

6.7

Peut-on initialiser une union ?

La norme prvoit dinitialiser le premier membre dune union. Pour le reste, ce nest pas e standard. En C99, on peut initialiser les champs dune union : union { /* ... */ } u = { .any_member = 42 };

6.8

Quelle est la dirence entre une numration et des #dee e e fine ?

Il y a peu de dirences. e Lun des avantages de lnumration est que les valeurs numriques sont assignes autoe e e e matiquement. De plus, une numration se manipule comme un type de donnes. Certains e e e programmeurs reprochent aux numrations de rduire le contrle quils ont sur la taille e e e o des variables de type numration. e e

6.9

Comment rcuprer le nombre dlments dune numrae e ee e e tion ?

Ceci nest possible de faon automatique que si les valeurs se suivent. c


34

typedef enum { A, B, C, D} type_e;

Dans cette numration, les valeurs sont donnes par le compilateur dans lordre croissant, e e e a ` partir de 0 et avec un pas de 1. Ainsi, le nombre dlments de type_e est D + 1. ee On peut rajouter un lment ` lnumration qui donne directement le nombre dlee a e e ee ments : typedef enum { RED, BLUE, GREEN, YELLOW, NB_COLOR} color_e;

le nombre dlments de color_e est donc NB_COLOR. ee Si lon est oblig, pour des raisons diverses et varies, de xer dautres valeurs aux e e constantes, alors cette solution ne marche pas. On peut toujours rajouter un champs dans lnumration et xer manuellement sa valeur. e e

6.10

Comment imprimer les valeurs symboliques dune nume e ration ?

On ne peut pas le faire simplement. Il faut crire une fonction qui le fait. Un probl`me e e qui se pose alors est la maintenance, car une modication des valeurs de lnumration e e entra la ncessit dune mise ` jour de cette fonction. ne e e a Voici un code qui limite les probl`mes de mise ` jour : e a /* Fichier foo.itm */ ITEM(FOO_A) ITEM(FOO_B) ITEM(FOO_C) /**/ /* Fichier foo.h */ #ifndef FOO_H #define FOO_H #define ITEM(a) a, typedef enum { #include "foo.itm" FOO_NB } foo_t; #undef ITEM #define ITEM(a) #a, const char * const aFoo[] = { #include "foo.itm" };
35

#undef ITEM #endif /**/ /* Fichier foo.c */ #include <stdio.h> #include "foo.h" int main(void) { foo_t foo; for (foo = FOO_A; foo < FOO_NB; foo++) { printf("foo=%d (%s)\n",foo, aFoo[foo]); } return 0; }

Merci ` Emmanuel Delahaye pour cet exemple. a

36

Chapitre 7

Tableaux et pointeurs
7.1 Quelle est la dirence entre un tableau et un pointeur ? e

Un tableau nest pas un pointeur. Un tableau est une zone mmoire pouvant contenir e N lments conscutifs de mme type. Un pointeur est une zone mmoire qui contient ee e e e ladresse dune autre zone mmoire. Toutefois, dans un grand nombre de cas, tout se e passe comme si ctait la mme chose. e e ` A ce titre, il faut bien faire la dirence entre a[i] pour un tableau et ap[i] pour un e pointeur. Voici un exemple : char a[] = "Bonjour"; char *ap = "Au revoir";

Lexpression a[3] signie que lon acc`de aux quatri`me lment du tableau. ap[3] sie e ee gnie que lon acc`de ` la zone mmoire pointe par (ap+3). Autrement dit, a[3] est e a e e lobjet situ 3 places apr`s a[0] (a est le tableau entier ), alors que ap[3] est lobjet situ e e e 3 places apr`s lobjet point par ap. Dans lexemple, a[3] vaut j et ap[3] vaut r. e e Voir aussi la question 5.9, page 30.

7.2

Comment passer un tableau ` plusieurs dimensions en paa ram`tre dune fonction ? e

Ce nest pas si facile. La r`gle de base est quil faut conna la taille des N-1 derni`res e tre e dimensions. Pour un tableau ` deux dimensions, la deuxi`me doit tre connue, et la a e e fonction doit tre dclare ainsi : e e e int f1(int a[][NCOLUMNS]); /* a est un tableau a deux dimensions (cf. remarque) */ int f2(int (*ap)[NCOLUMNS]); /* ap est un pointeur sur un tableau */

37

Si elle nest pas connue, il faut passer la taille du tableau en param`tre (ligne ET colonne) e et un pointeur sur le tableau : int f(int * a, int nrows, int ncolumns);

On acc`de aux lments du tableau ainsi : e ee a[i * ncolumns + j] /* element de la ieme ligne * et de la jeme colonne */

Une remarque : Dans une dclaration de param`tre e e int f1(int a[][NCOLUMNS])

ou int f1(int a[42][NCOLUMNS])

a est un pointeur sur int[NCOLUMS] malgr lcriture. La dclaration est interprte e e e ee comme int (*a) [NCOLUMS]

Ainsi les dclarations de f1 et f2 dans lexemple initial sont exactement les mmes. e e

7.3

Comment allouer un tableau ` plusieurs dimensions ? a

La premi`re solution est dallouer un tableau de pointeurs, puis dinitialiser chacun de e ces pointeurs par un tableau dynamique. #include <stdlib.h> int ** a = malloc(nrows * sizeof *a ); for(i = 0; i < nrows; i++) a[i] = malloc(ncolumns * sizeof *(a[i]));

Dans la vraie vie, le retour de malloc doit tre vri. e e e Une autre solution est de simuler un tableau multi-dimensions avec une seule allocation :
38

int *a = malloc(nrows * ncolumns * sizeof *a);

Lacc`s aux lments se fait par : e ee a[i * ncolumns + j] /* element de la ieme ligne * et de la jeme colonne */

7.4

Comment dnir un type pointeur de fonction ? e

On utilise typedef, comme pour nimporte quel autre type. Voici un exemple : int f(char * sz); /* une fonction */ int (*pf)(char *);/* un pointeur sur une fonction */ typedef int (*pf_t)(char *); /* un type pointeur sur fonction*/

Il est toutefois prfrable de ne pas cacher le pointeur dans un typedef. La solution ee suivante est plus jolie : typedef int (f_t)(char *); /* un type fonction */ f_t * pf; /* un pointeur sur ce type */

On lutilise alors de cette faon : c pf = f; int ret = pf("Merci pour cette reponse");

Voir aussi la question 5.7, page 29.

7.5

Que vaut (et signie) la macro NULL ?

NULL est une macro qui reprsente une valeur spciale pour dsigner un pointeur nul e e e lorsque converti au type appropri. Elle est dnie dans <stddef.h> ou dans <stdio.h>. e e La valeur relle de NULL est dpendante de limplmentation, et nest pas ncessairement e e e e un pointeur, ni de type pointeur. Des valeurs possibles sont ((void *)0) ou 0. NULL permet de distinguer les pointeurs valides des pointeurs invalides. Par exemple, malloc renvoie une valeur comparable a NULL quand elle choue. ` e Voir aussi la question 12.3, page 59.
39

7.6

Que signie lerreur NULL-pointer assignment ?

Cela signie que vous avez essay daccder ` ladresse 0 de la mmoire. Vous avez probae e a e blement drfrenc un pointeur NULL, ou oubli de tester la valeur retour dune fonction, eee e e avant de lutiliser.

7.7

Comment imprimer un pointeur ?

La seule mani`re prvue par la norme pour imprimer correctement un pointeur est dutie e liser la fonction printf avec le code de format %p. Le pointeur doit tre dabord cast en e e un pointeur gnrique void *. e e char * p; printf("Pointeur p avant initialisation: %p\n", (void *)p);

7.8

Quelle est la dirence entre void * et char * ? e

Le premier est un pointeur gnrique, qui peut recevoir ladresse de nimporte quel type e e dobjet. Le second est un pointeur sur un caract`re, gnralement utilis pour les cha e e e e nes. Avant la norme ANSI, le type void nexistait pas. Ctait donc char * qui tait utilis e e e pour faire des pointeurs gnriques. Depuis la norme, ce nest plus valide. De nombreux e e programmeurs ont toutefois gard cette habitude, notamment dans le cast de fonctions e comme malloc (cf. 12.1, page 58).

40

Chapitre 8

Cha nes de caract`res e


8.1 Comment comparer deux cha nes ?

Pour comparer deux cha nes entre elles, il faut utiliser la fonction strcmp, et non lope rateur ==. Celui-ci comparera les pointeurs entre eux, ce qui nest probablement pas ce qui est voulu ! const char * sz = "non"; if(strcmp(sz, "oui") == 0) { /* sz et "oui" sont egaux */ } Il existe aussi la fonction strncmp qui permet de contrler la longueur de comparaison. o

8.2

Comment recopier une cha dans une autre ? ne

Il faut utiliser la fonction strcpy, et non loprateur daectation =. Il faut sassurer que e lon dispose despace susant dans la cha cible avant dutiliser strcpy, qui ne fait ne aucun contrle de dbordement. o e Pour une copie plus scurise, on prfrera la fonction strncpy. e e ee

8.3

Comment lire une cha au clavier ? ne

Il y a de nombreuses fonctions qui le font. Le plus sr est dutiliser fgets. u char tab[20]; fgets(tab, sizeof tab, stdin); Voici un exemple complet dutilisation propre de fgets pour la lecture dune ligne, avec un contrle derreurs. Cette fonction est fournie par Emmanuel Delahaye. o
41

#include <stdio.h> #include <string.h> int get_line(char *buf, size_t size) { int ret; /* 0=Ok 1=Err 2=Incomplet * On peut aussi definir des constantes. (Macros, enum...) */ if (fgets(buf, size, stdin) != NULL) { char *p = strchr(buf, \n); /* search ... */ if (p != NULL) { *p = 0; /* ... and kill */ ret = 0; } else { ret = 2; } } else { ret = 1; } return ret; }

Il peut tre intressant dans certains cas de faire un traitement particulier dans le cas 2 e e (lecture incompl`te), comme vider le buer du ux ou redimentionner la zone de rception e e (voir la question 14.5, page 76). La fonction gets est ` proscrire, car il ny a aucun contrle de dbordement, ce qui peut a o e engendrer de nombreux bugs (stack overow ) (cf. 8.7, page 43).

8.4

Comment obtenir la valeur numrique dun char (et vicee versa) ?

En C, un char est un petit entier. Il ny a donc aucune conversion ` faire. Quand on a a un char, on a aussi sa valeur, et vice-versa.

8.5

Que vaut sizeof(char) ?

Un char vaut et vaudra toujours 1 indpendamment de limplmentation. En eet, les e e tailles dallocation en C se calculent en char (size_t). Or, un char a une taille de 1 char. Donc sizeof(char) == (size_t) 1

42

Pour autant, un char ne fait pas forcment 8 bits (un octet) (voir aussi 16.2, page 85). e

8.6

Pourquoi sizeof(a) ne vaut pas 1 ?

En C, les caract`res constants sont des int, et non des char. Ainsi, e sizeof(a) == sizeof(int)

ce qui peut valoir 2 ou 4 sur votre machine, ou autre chose.

8.7

Pourquoi ne doit-on jamais utiliser gets ?

Serge Paccalin donne lexemple suivant : #include <stdio.h> int main(void) { char chaine[16]; printf("Tapez votre nom :\n"); gets(chaine); printf("Vous vous appelez %s.\n",chaine); return 0; }

Quand on tape une cha de plus de 15 caract`res, rien ne va plus : gets accepte sans ne e broncher la cha mais lorsquil est question de la stocker dans ne char chaine[16]

on peut obtenir un magnique Segmentation fault (core dumped). Dans certains cas, avec certains compilateurs sur certaines machines et certains OS, il est possible que a passe. c Mais attention ! Cest un leurre, et le changement de cible dmontrera la malfaon. e c

8.8

Pourquoi ne doit-on presque jamais utiliser scanf ?

scanf est une fonction de la biblioth`que standard, qui est souvent la premi`re que lon e e apprend pour lire des donnes au clavier. Cette fonction nest pas plus dangereuse quune e autre, ` condition de bien savoir lutiliser, ce qui nest pas donn ` tout le monde. a ea Par exemple, regardez le programme suivant donn par Serge Paccalin : e
43

#include <stdio.h> int main(void) { int val = 0; while (val != 1){ printf("Tapez un nombre" "(1 pour arreter le programme) :\n"); scanf(" %d",&val); printf("Vous avez saisi %d.\n",val); } return 0; }

Et il explique : Quand le programme demande un nombre, taper "toto" suivi de la touche Entre. Le programme part en boucle parce que tous les scanf successifs butent e sur "toto" qui reste indniment dans stdin. e Dans la plupart des cas, lutilisation de la fonction fgets sera plus simple et moins risque. e scanf est une fonction qui peut tre utilise dans certaines conditions, et ` condition de e e a bien savoir ce que lon fait. Lutilisation de scanf pour la lecture de nombres (entiers ou ottants) est lune des plus acceptable. Par contre, la lecture dune cha avec scanf ne sans contrle de format est aussi dangereux que gets. o scanf("%s", astring) ;

est donc a proscrire. scanf nest prfrable ` fgets que dans le cas ou lon veut lire mot par mot et non ligne ee a par ligne, et conserver le reste dans le buer du ux dentre. Sinon, lire un mot avec e fgets ne pose pas de probl`me et est mme plus simple. e e scanf("%4s", astring) ;

Cette construction, par exemple, ne pose pas les probl`mes cits plus haut (si le contrle e e o derreurs est fait), et est parfois utile. Voir aussi les questions 8.7, page 43 et 8.3, page 41

44

Chapitre 9

Fonctions et prototypes
9.1 Pour commencer ...

Il y a trois notions : dclaration, e dnition, e prototype. La dclaration dune fonction, cest annoncer que tel identicateur correspond ` une e a fonction, qui renvoie tel type. La dnition dune fonction est une dclaration o`, en plus, e e u on donne le code de la fonction elle-mme. Le prototype est une dclaration de fonction e e o` le type des arguments est galement donn. u e e Par exemple : int f(); /* declaration de f(), renvoyant un int, pas de prototype */

int f(void);/* declaration de f(), renvoyant un int, prototype (0 arg) */ int f(void) /* definition de f() avec declaration avec prototype { return 42; } int f(x) /* definition de f() avec declaration sans prototype int x; { return x; } int f() /* definition de f() avec declaration sans prototype { return 42; } */

*/

*/

45

Ce qui nest pas possible : avoir une dnition sans dclaration, e e avoir un prototype sans dclaration. e Ce qui est autoris : e appeler une fonction dclare, quelle ait un prototype ou pas. e e Ce qui tait autoris en C90 mais ne lest plus en C99 : e e appeler une fonction non dclare ; lappel valait dclaration dite implicite , sans e e e prototype et avec type de retour int. Ce qui est encore autoris en C99 mais dispara bientt : e tra o dclarer/dnir une fonction sans prototype. e e Ce quil faut faire quand on veut programmer lisiblement, en dtectant les bugs et en e gardant du code maintenable et compatible avec le futur : utiliser des dclarations et dnitions avec prototype. e e

9.2

Quest-ce quun prototype ?

Un prototype est une signature de fonction. Comme tout objet en C, une fonction doit tre dclare avant son utilisation. Cette dclaration est le prototype de la fonction. Le e e e e prototype doit indiquer au compilateur le nom de la fonction, le type de la valeur de retour et le type des param`tres (sauf pour les fonctions ` arguments variables, comme e a printf. (cf. 9.6, page 48). int fa(int a, char const * const b); int fb(int, char const * const);

Les noms de param`tre sont optionnels, mais il est fortement conseill de les laisser. Cela e e donne une bonne indication sur leurs rles. o Les fonctions de la biblioth`que ont galement leur prototype. Avant lutilisation de cellese e ci, il faut inclure les chiers den-tte contenant les prototypes. Par exemple, le prototype e de malloc se trouve dans stdlib.h. Certains prf`rent ajouter le mot cl extern au prototype, an de rester cohrent avec ee e e la dclaration des variables globales. e Voir aussi les questions 9.3, page 46, 12.1, page 58, 13.5, page 64 et 14.17, page 80.

9.3

O` dclarer les prototypes ? u e

Un prototype de fonction doit tre dclar avant lutilisation de la fonction. Pour une e e e plus grande lisibilit, mais aussi pour simplier la maintenance du code, il est conseill de e e regrouper tous les prototypes dun module (chier xxx.c) dans un en-tte (<xxx.h>). e Ce dernier na plus alors qu` tre inclus dans le code qui utilise ces fonctions. Cest le ae cas des fonctions de la biblioth`que standard. e Voir aussi les questions 13.5, page 64 et 9.10, page 50.
46

9.4

Quels sont les prototypes valides de main ?

La fonction main renvoie toujours un int. Les prototypes valides sont : int main(void); int main(int argc, char * argv[]);

Tout autre prototype nest pas du tout portable et ne doit jamais tre utilis (mme sil e e e est accept par votre compilateur). e En particulier, vous ne devez pas terminer la fontion main sans retourner une valeur positive (non nulle en cas derreur). Les valeurs de retour peuvent tre 0, EXIT_SUCCESS e ou EXIT_FAILURE. On pourra aussi rencontrer (sous Unix ) le prototype suivant : int main (int argc, char* argv[], char** arge);

dans le but dutiliser les variables denvironnement du shell actif. Ce nest ni portable ni standard, dautant plus que les fonctions getenv, setenv et putenv le sont et susent largement. Enn, rappelons que le prototype suivant int main () ;

est parfaitement valide en C++ (et est synonyme du premier prsent ici), mais ne lest e e pas en C.

9.5

Comment printf peut recevoir dirents types darguments ? e

printf est une fonction ` nombre variable de param`tres. Son prototype est le suivant : a e int printf(const char * format, ...); /* C 90 */ int printf(const char * restrict format, ...); /* C 99 */

Le type et le nombre des param`tres nest pas dni dans le prototype, cest le traitement e e eectu dans la fonction qui doit les vrier. e e Pour utiliser cette fonction, il est donc impratif dinclure len-tte <stdio.h>. e e Pour crire une fonction de ce type, lire la question suivante (9.6, page 48). e

47

9.6

Comment crire une fonction ` un nombre variable de pae a ram`tres ? e

La biblioth`que standard fournit des outils pour faciliter la gestion de ce type de fonctions. e On les trouve dans len-tte <stdarg.h>. e Le prototype dune fonction ` nombre variable de param`tres doit contenir au moins un a e param`tre explicite, puis se termine par ... Exemple : e int f(int nombre, ...);

Il faut, dune faon ou dune autre, passer dans les param`tres le nombre darguments relc e e lement transmis. On peut le faire en donnant ce nombre explicitement (comme printf), ou passer la valeur NULL en dernier. Attention toutefois avec la valeur NULL dans ce cas. En eet, NULL nest pas ncessairement e une valeur du type pointeur mais une valeur qui donne un pointeur nul si elle est aecte e ou passe ou compare ` un type pointeur. Le passage dune valeur ` un param`tre nest e e a a e pas une aectation ` un pointeur mais une aectation qui obit aux lois spciales pour a e e les param`tres ` nombre variable (ou pour les param`tres dune fonction sans prototype). e a e Les lois de promotion pour les types arithmtiques sont appliques). Si NULL est dni e e e par #define NULL 0 alors (int)0 est pass ` la fonction. Si un pointeur na pas la mme taille quun int ou ea e si un pointeur nul nest pas reprsent par tous les bits 0 le passage dun 0 ne passe e e donc pas de pointeur nul. La mthode portable est donc e f(toto,titi,(void*)NULL);

ou f(toto,titi,(void*)0);

Cest le seul cas o` il faut caster NULL parce quil ne sagit pas dun contexte syntactique u de pointeur , seulement dun contexte de pointeur par contrat . Apr`s cela, les fonctions va_start, va_arg et va_end permettent de parcourir la liste des e param`tres. e Voici un petit exemple : #include <stdarg.h>

48

int vexemple(int nombre, ...){ va_list argp; int i; int total = O; if(nombre < 1) return 0; va_start(argp, nombre); for (i = 0; i < nombre; i++) { total += va_arg(argp, int); } va_end(argp); return total; }

Merci ` Horst Kraemer pour ces remarques. a

9.7

Comment modier la valeur des param`tres dune fonction ? e

En C, les param`tres sont passs par valeur. Dans la plupart des implmentations, cela se e e e fait par une copie dans la pile. Lors du retour de la fonction, ces valeurs sont simplement dpiles, et les modications ventuelles sont perdues. Pour pallier cela, il faut simuler un e e e passage des param`tres par rfrence, en passant un pointeur sur les variables ` modier. e ee a Voici lexemple classique de lchange des valeurs entre deux entiers : e void echange(int * a, int * b) { int tmp = *a; *a = *b; *b = tmp; }

9.8

Comment retourner plusieurs valeurs ?

Le langage C ne permet pas aux fonctions de renvoyer plusieurs objets. Une solution consiste a passer ladresse des objets ` modier en param`tre. Une autre solution consiste ` a e a ` renvoyer une structure, ou un pointeur sur une structure qui contient lensemble des valeurs. Gnralement, quand on a ce genre de choses ` faire, cest quil se cache une e e a structure de donnes que lon na pas identie. La pire des solutions est dutiliser des e e variables globales.

49

9.9

Peut-on, en C, imbriquer des fonctions ?

Non, on ne peut pas. Les concepteurs ont jug cela trop compliqu ` mettre en oeuvre e ea (porte des variables, gestion de la pile etc.). Certaines implmentations, comme GNU CC e e le supportent toutefois. Ceci dit, on peut tr`s bien sen passer, en utilisant des pointeurs e sur les structures de donnes ` partager, ou en utilisant des pointeurs de fonctions. e a

9.10
.

Quest-ce quun en-tte ? e

Un en-tte est un ensemble de dclarations, dnitions et prototypes ncessaires pour come e e e piler et pour utiliser un module. Par exemple, pour utiliser les fonctions dentres/sorties e de la biblioth`que standard, il est ncessaire dinclure dans son programme len-tte e e e <stdio.h>. Par abus, on parle souvent de chier den-tte, car historiquement, et encore aujourdhui e pour de nombreuses implmentations, ces en-ttes sont des chiers. Cest galement le cas e e e pour les en-ttes personnels. Toutefois, la norme nexige pas que les en-ttes standards e e soient des chiers ` proprement parl. a e

50

Chapitre 10

Expressions
10.1 Le type Boolen existe-t-il en C ? e

Oui, il existe un type boolen en C. Cest un ajout de la derni`re version de la norme e e (C99). Il sagit du type _Bool dni dans <stdbool.h>. Cet en-tte contient galement e e e les dnitions de true et false. Une macro bool est souvent dnie comme quivalent e e e a ` _Bool. Rappelons galement quen C, une valeur est fausse si elle est nulle (ou quivalent), e e et vraie sinon. Les dnitions de true et false suivent cette r`gle. Ainsi, la valeur e e enti`re de true est 1 et celle de false est 0. e

10.2

Un pointeur NULL est-il assimil ` une valeur fausse ? ea

Oui, la valeur NULL est apparente ` un 0. Les critures if(p != NULL) et if(p) sont e a e donc quivalentes. De mme, if(p == NULL) est quivalent ` if( !p). e e e a Voir aussi les questions 10.1, page 51 et 7.5, page 39.

10.3

Que donne loprateur ! sur un nombre ngatif ? e e

Loprateur ! sur un nombre ngatif donne bien ce que lon attend, ` savoir 0. e e a Voir aussi la question 10.1, page 51.

10.4

Que vaut lexpression a[i] = i++ ?

Ce genre dexpression fait partie des undened behaviour , ou comportement indni. e Cela signie que le rsultat dune telle opration dpend du compilateur. Loprateur ++ e e e e modie la valeur de i, alors que celle-ci est utilise ailleurs dans lexpression. Cest ce que e lon appelle un eet de bords .

51

10.5

Pourtant, i++ vaut i ?

Eectivement, i++ vaut i, avant lincrmentation. Toutefois, rien ne dit dans quel sens e est calcule lexpression a[i] = i++. Est-ce le i++ qui est valu avant le a[i], ou le e e e contraire ? On nen sait rien, cest pourquoi lon dit que cest un comportement indni. e

10.6

En est-il de mme pour i++ * i++ ? e

Oui. La norme ne prcise pas pour les oprateurs binaires dans quel ordre les oprandes e e e sont valus. e e

10.7

Peut-on utiliser les parenth`ses pour forcer lordre dvae e luation dune expression ?

Pas en gnral. Les parenth`ses ne donnent quun ordre partiel dvaluation, entre les e e e e oprateurs. Voici un petit exemple : e a = f() + g() * h(); b = (f() + g()) * h();

Les parenth`ses dans la deuxi`me expression modient lordre dvaluation de laddition e e e et de la multiplication. Par contre, lordre dans lequel seront values les fonctions est e e indni, dans lune ou lautre des deux expressions. Cela ne dpend que du compilateur. e e Pour forcer un ordre dvaluation, il faut utiliser une criture squentielle, avec des vae e e riables temporaires. tf = f(); tg = g(); th = h(); b = (tf + tg) * th;

On a alors un comportement parfaitement dni sur toutes les cibles, quel que soit le e compilateur.

10.8

Quen est-il des oprateurs logiques && et || ? e

Ces deux oprateurs forment une exception ` la r`gle. La norme prvoit que les oprandes e a e e e de ces deux oprateurs soient valus de gauche ` droite. De plus, lvaluation sarrte e e e a e e d`s que le rsultat est connu. e e

52

10.9

Comment sont values les expressions comprenant plue e sieurs types de variables ?

Le principe est simple. Si les variables sont de types dirents, il y aura un cast implicite e vers le type le plus prcis. On parle de promotion. Voici les r`gles de base : e e Si lun des oprandes est un long double, lautre est converti en un long double. e Sinon, si lun des oprandes est un double, lautre est converti en un double. e Sinon, si lun des oprandes est un float, lautre est converti en un float. e Sinon, les oprandes de types char et short sont convertis en int. e Enn, si lun des oprandes est un long, lautre est converti en un long. e En C99, il faut rajouter le type long long. Cela se complique dans le cas doprandes unsigned. Les comparaisons entre valeurs e signes et non signes dpendent de la machine et de la taille des dirents types. e e e e

10.10

Quest-ce quune lvalue ?

lvalue est un terme qui est utilis pour dnir les expressions que lon peut mettre ` e e a gauche dune aection. Toute variable modiable est une lvalue. Quand le compilateur prvient que le membre gauche dune aectation nest pas une lvae lue, cest souvent parce que lon ne voulait pas faire une aectation, mais une comparaison (cf. 15.5, page 82).

53

Chapitre 11

Nombres en virgule ottante


11.1 Jai un probl`me quand jimprime un nombre rel. e e

Sur la plupart des architectures, les nombres rels (dits ottants) sont reprsents en base e e e 2, comme pour les entiers. Ainsi, le nombre 3.1 ne peut scrire exactement en base 2. La e reprsentation binaire est un arrondi qui dpend de la prcision du codage des ottants, e e e et des choix du compilateur. De plus, avec une fonction comme printf, le nombre ` a imprimer est converti en base 2 puis reconverti en base 10, ce qui augmente encore les imprcisions. e Il est prfrable dutiliser les double, qui ont une prcision suprieure aux float, sauf si ee e e lconomie de mmoire est vraiment critique. Voir ` ce sujet la question 11.10, page 56. e e a

11.2

Pourquoi mes extractions de racines carres sont erroe nes ? e

Assurez-vous davoir inclus math.h, davoir correctement dclar les autres fonctions rene e voyant des double. Une autre fonction de la biblioth`que standard avec laquelle il faut e faire attention est atof, dans stdlib.h.

11.3

Jai des erreurs de compilation avec des fonctions mathe matiques

Il faut sassurer davoir link (li) son code avec la biblioth`que mathmatique. Par e e e e exemple, sous Unix, vous devez gnralement passer loption -lm ` la n de la ligne e e a de commande.

54

11.4

Mes calculs ottants me donnent des rsultats tranges e e et/ou dirents selon les plateformes e

Pour commencer, relisez 11.1, page 54. Si le probl`me est plus complexe, il convient de se rappeler que les ordinateurs utilisent des e formats de reprsentation des ottants qui ne permettent pas des calculs exacts. Pertes de e prcision, accumulation derreurs et autres anomalies sont le lot commun du numricien. e e Rappelez-vous quaucun calcul sur des ottants na de chance dtre exact, en particulier, e nutilisez jamais == entre deux ottants. Ces probl`mes ne sont pas spciques au C. e e Dans certains probl`mes, une solution peut tre dintroduire un petit param`tre de relaxae e e tion, par exemple #define EPS 1e-10, puis de multiplier lun des termes (judicieusement choisi) de vos calculs par (1 + EPS). Pour plus de renseignements, on se reportera par exemple aux Numerical Recipes ou ` a Numerical Algorithms with C (cf. 3.9, page 18).

11.5

Comment simuler == entre des ottants ?

Etant donn quil y a perte de prcision tr`s vite, pour comparer deux valeurs ottantes, e e e on teste si elles sont assez proches. Plutt que dcrire une horreur du genre : o e double a, b; /* ... */ if (a == b) /* HORREUR ! */ /* ... */

on crira : e #include <math.h> /* ... */ double a, b; /* ... */ if (fabs (a - b) <= epsilon * fabs (a) ) /* ... */

o` lon aura judicieusement choisi epsilon (non-nul ! ). u

11.6

Comment arrondir des ottants ?

La mthode la plus simple et la plus expditive est (int)(x + 0.5). Cette technique ne e e fonctionne pas correctement pour les nombres ngatifs aussi vaut-il mieux utiliser e
55

(int)(x < 0 ? x - 0.5 : x + 0.5)

11.7

Pourquoi le C ne dispose-t-il pas dun oprateur dexpoe nentiation ?

Parce que certains processeurs ne disposent pas dune telle instruction. Il existe une fonction pow dclare dans math.h bien que la multiplication soit prfrable pour de e e ee petits exposants.

11.8

Comment obtenir Pi ?

Parfois une constante prdnie M_PI est dclare dans math.h mais ce nest pas standard e e e e aussi vaut-il mieux calculer pi soi-mme via 4 * atan (1.0). e

11.9

Quest-ce quun NaN ?

NaN is Not a Number , ce qui signie Ce nest pas un nombre . Un NaN est un nombre ottant qui est le rsultat dune opration non conforme, par exemple 0/0. e e Lorsquun NaN est produit, la plupart des architectures produisent une interruption (ou un signal) qui termine le programme, au moment de lutilisation de celui-ci. Il est parfois possible de vrier si un nombre est NaN. Un bon test est celui-ci : e #define isNaN(x) ((x) != (x))

Certains compilateurs fournissent des facilits quant ` la gestion des NaN. GCC fournit e a dans la biblioth`que mathmatique (math.h) les fonctions isnan, isinf et finite. e e

11.10

Faut-il prfrer les double aux float ? ee

La vitesse de traitement dun double nest pas forcment plus longue quun float, cela e dpend du compilateur (de ses options) et du processeur. Ainsi avec lexemple suivant, e en remplaant le typedef par float ou double, on saperoit que sur un Pentium ou un c c PowerPC, le double est plus rapide ` calculer que le float tout en ayant une prcision a e plus grande. #include <stdio.h> #include <math.h> typedef float reel; /* float ou double */

56

int main(void) { long i ; reel d = 3.0 ; for (i = 0; i < 100000000; i++) { d = cos(d) ; } (void)printf("%f\n", d); return 0; }

Le C comprend des instructions mathmatiques pour traiter les oat directement au lieu e de toujours passer par des double depuis la derni`re norme (C99). Par exemple il existe e cosf en plus de cos. En faisant des essais on saperoit que dans notre exemple, le cosf c appliqu ` un float devient aussi rapide que le cos appliqu ` un double. ea ea En conclusion, nous pouvons dire quil est prfrable dutiliser des double ` la place des ee a float, sauf lorsque la place mmoire devient critique. e

57

Chapitre 12

Allocation dynamique
12.1 Doit-on ou ne doit-on pas caster malloc ?

Cette question est probablement celle qui revient le plus souvent dans la discussion. Et ` a chaque fois, elle engendre une longue discussion. Certains intervenants pensent que caster la valeur de retour de malloc est inutile, voire dangereux. En eet, malloc renvoie un void *. Or, en C, un pointeur void * est implicitement cast lors dune aectation vers le type de la variable aecte. Bien sr, expliciter e e u le cast nest pas interdit, et est parfois utile. Toutefois, caster le retour de malloc risque de cacher au compilateur loubli du prototype de malloc. Ce prototype se trouve dans le chier den-tte <stdlib.h>. Sans lui, malloc sera par dfaut une fonction retournant un e e int et dont les param`tres seront du type des arguments passs, ce qui peut provoquer e e de srieux bugs. e La vritable erreur est loubli du chier den-tte <stdlib.h>, et non pas le cast de e e malloc en lui mme. Mais le cast de malloc risque de cacher au compilateur cette erreur. e ` A noter quil existe des outils de vrication de code et des options sur la plupart des e 1 compilateurs qui permettent de dtecter ce genre derreur. e Dautres intervenants jugent quil faille tout de mme caster le retour de malloc, an e de conserver une compatibilit avec danciens compilateurs pr-ANSI, ou pour intgrer e e e plus facilement le code avec C++. Evidemment, les programmeurs avertis sauront dans quelles situations il est utile ou non de caster les void *. Voir aussi la question 7.8, page 40

12.2

Comment allouer proprement une variable ?

Le plus portable et le plus simple est de faire ainsi : var_t * ma_var = malloc(N * sizeof *ma_var);
1

pour GCC par exemple, loption -Wall active -Wmissing-prototypes -Wmissing-declarations

58

` Si le type de la variable change, lallocation est toujours valide. A noter que lon ne caste pas le retour de malloc Voir la question 12.1, page 58 ` ce sujet, ainsi que la question 12.10, page 60. a

12.3

Pourquoi mettre ` NULL les pointeurs apr`s un free ? a e

La fonction free lib`re lespace mmoire point par le pointeur en question. Mais la e e e valeur de celui-ci ne peut-tre change, car en C les arguments sont passs par valeur aux e e e fonctions. La variable pointeur contient apr`s le free une adresse invalide. Son utilisation peut e entra ner de srieux embtements. Pour viter cela, une bonne solution consiste ` aecter e e e a la valeur NULL au pointeur apr`s lappel ` free. e a Il existe aussi certaines implmentations de lallocation dynamique qui fonctionnent en e Garbage Collector, cest-`-dire, que la mmoire nest rellement libre que lorsque le a e e ee pointeur est mis ` NULL. a Dans tous les cas, cela permet de tester facilement la validit des pointeurs. e

12.4

Pourquoi free ne met pas les pointeurs ` NULL ? a

Rappelons que les param`tres des fonctions sont passs par valeur (ou par copie). Ainsi, e e pour modier la valeur du pointeur, il faudrait passer un pointeur sur le pointeur, ce qui compliquerait lutilisation de free. Mais ce nest pas le cas, il faut donc le faire soi-mme. e

12.5

Quelle est la dirence entre malloc et calloc ? e

Pratiquement, calloc est quivalent ` : e a /* p = calloc(m, n); */ p = malloc(m * n); memset(p, 0, m * n);

Chaque lment est initialis ` 0. Ce 0 est un tout bit ` zro . La valeur des lments ee ea a e ee nest pas forcment valide, suivant leur type. e Voir aussi la question 5.6, page 29.

12.6

Que signie le message assignment of pointer from integer quand jutilise malloc ?

Cela signie que vous avez oubli dinclure le chier stdlib.h. e


59

Voir ` ce sujet la question 12.1, page 58. a

12.7

Mon programme plante ` cause de malloc, cette fonction a est-elle bugge ? e

Il est assez facile de corrompre les structures de donnes internes de malloc. Les sources e les plus plausibles du probl`me sont : e lemploi de malloc(strlen(s)) au lieu de malloc(strlen(s)+1). la libration dun pointeur deux fois. e Il y en a dautres... Voir aussi les questions 12.2, page 58 et 12.8, page 60.

12.8

Que signient les erreurs segmentation fault et bus error ?

Cela signie que vous avez essay daccder ` une zone mmoire non autorise. Cest e e a e e souvent lutilisation dun pointeur non initialis ou NULL qui en est la cause. Ce genre e derreur peut aussi provenir dune mauvaise allocation (cf. 12.7, page 60 et 12.2, page 58) ou de loubli du 0 en n de cha ne.

12.9

Doit-on librer explicitement la mmoire avant de quitter e e un programme ?

Oui, car tous les syst`mes ne le font pas deux-mmes. e e

12.10

Du bon usage de realloc

La fonction realloc permet de modier la taille de lespace mmoire allou ` une variable. e ea Elle est souvent utilise pour augmenter cette taille. e Rappelons que la mmoire alloue par malloc, calloc et realloc est fournie sous la e e forme dune zone continue (en un seul bloc). Or, il peut arriver que la nouvelle taille demande dpasse lespace disponible derri`re la zone initiale. Dans ce cas, la fonction e e e realloc alloue une nouvelle zone ailleur, l` ou il y a de la place, et y recopie les donnes a e initiales. Lancienne zone est alors libre. ee Cest pourquoi realloc renvoie un pointeur sur la nouvelle zone mmoire, mme si lauge e mentation de taille (ou la rduction) a pu se faire sur place. Bien sr, comme malloc, e u realloc peut chouer. e Voici pour rsumer une bonne mani`re dutiliser realloc : e e

60

#include <stdlib.h> /* pour realloc() et free() */ /* ... */ int * var = NULL ; var = malloc(sizeof * var * 42) ; if (!var) { /* gestion des erreurs */ } /* ... */ int * tmp = NULL ; tmp = realloc(var, 84) ; if (tmp) { var = tmp ; } else { /* gestion de lerreur */ }

61

Chapitre 13

Le pr-processeurs e
13.1 Quel est le rle du prprocesseur ? o e

Le prprocesseur interpr`te les directives qui commencent par #. Principalement, ces e e directives permettent dinclure dautres chiers (via #include) et de dnir des macros e (via #define) qui sont remplaces lors de la compilation. e Chaque directive de compilation commence par un # situ en dbut de ligne (mais vene e e tuellement prcd par des espaces, des tabulations ou des commentaires) et se termine e e e en n de ligne. Le prprocesseur est galement responsable de la reconnaissance des trigraphes, des backse e lashs terminaux, et de lablation des commentaires.

13.2

Quest-ce quun trigraphe ?

Dans les temps anciens, les ordinateurs nutilisaient pas ASCII ; chaque machine avait son propre jeu de caract`res. LISO a dni un jeu de caract`res supposs prsents sur e e e e e toutes les machines, cest lInvariant Code Set ISO 646-1983. Ce jeu de caract`res ne e comporte pas certains caract`res intressants, tels que les accolades et le backslash. Aussi, e e le standard C89 a introduit les trigraphes, squences de trois caract`res commenant e e c par ? ?. Il existe neuf squences remplaces par le prprocesseur. Ce remplacement a e e e lieu avant toute autre opration, et agit galement dans les commentaires, les cha e e nes constantes, etc. Les trigraphes sont, de fait, rarement utiliss. On les voit appara occasionnellement e tre et par erreur, quand on crit a : e c printf("Kikoo ??!\n");

Le trigraphe est remplac par un pipe, donc ce code ache ceci : e Kikoo |

62

De toutes faons, le redoublement du point dinterrogation est de mauvais got. c u Les compilateurs modernes soient ne reconnaissent plus les trigraphes, soit mettent des e avertissements quand ils les rencontrent.

13.3

` A quoi sert un backslash en n de ligne ?

Apr`s le remplacement des trigraphes, le prprocesseur recherche tous les caract`res backse e e lash situs en n de ligne ; chaque occurrence de ce caract`re est supprime, de mme e e e e que le retour ` la ligne qui le suit. Ceci permet dunier plusieurs lignes en une seule. a Ce comportement est pratique pour crire des macros ou des cha e nes de caract`res sur e plusieurs lignes : printf("Hello\ World !\n");

Pour les cha nes de caract`res, on peut aussi crire plusieurs cha e e nes cte ` cte, et le o a o compilateur les uniera (mais pas le prprocesseur : pour lui, ce seront deux cha e nes ` la a suite lune de lautre). Cest une technique qui doit tre gnralement prfre. On crira e e e eee e donc pour lexemple prcdent : e e printf("Hello" "World !\n");

13.4

Quelles sont les formes possibles de commentaires ?

Un commentaire en C commence par /* et se termine par */, ventuellement plusieurs e lignes plus loin. Les commentaires ne simbriquent pas. On peut aussi utiliser la compilation conditionnelle, comme ceci : #if 0 /* ceci est ignore */ #endif /* 0 */

Dans ce cas, il faut que ce qui est ignor soit une suite de tokens valide (le prprocesseur e e va quand mme les regarder, an de trouver le #endif). Ceci veut dire quil ne faut pas e de cha non termines. Ce genre de commentaire nest pas adapt ` du texte, ` cause ne e ea a des apostrophes. La nouvelle norme du C (C99) permet dutiliser les commentaires du C++ : ils commencent par // et se terminent en n de ligne. Ce type de commentaire nest pas encore support partout, donc mieux vaut ne pas sen servir si on veut faire du code vraiment e portable, mme si avant C99, des compilateurs acceptaient ce type de commentaires. e
63

13.5

Comment utiliser #include ?

#include comporte trois formes principales : #include <fichier> #include "fichier" #include tokens

La premi`re forme recherche le chier indiqu dans les rpertoires syst`me ; on peut les e e e e ajuster soit via des menus (dans le cas des compilateurs avec une interface graphique), soit en ligne de commande. Sur un syst`me Unix, le rpertoire syst`me classique est e e e /usr/include/. Une fois le chier trouv, tout se passe comme si son contenu tait tel e e quel dans le code source, l` o` se trouve le #include. a u La deuxi`me forme recherche le chier dans le rpertoire courant. Si le chier ne sy trouve e e pas, il est ensuite cherch dans les rpertoires syst`mes, comme dans la premi`re forme. e e e e La troisi`me forme, o` ce qui suit le #include ne correspond pas ` une des deux formes e u a prcdentes, commence par eectuer tous les remplacements de macros dans la suite de e e tokens, et le rsultat doit tre dune des deux formes prcdentes. e e e e Si le chier nest pas trouv, cest une erreur, et la compilation sarrte. On notera que si e e on se sert dhabitude de #include pour inclure des chiers den-tte (tels que stdio.h), e ce nest pas une obligation.

13.6

Comment viter linclusion multiple dun chier ? e

Il arrive assez souvent quun chier inclus en incluse un autre, qui lui-mme en inclut un e autre, etc. On peut arriver ` des boucles, qui peuvent conduire ` des redoublements de a a dclarations (donc des erreurs, pour typedef par exemple), voire des boucles innies (le e compilateur nissant par planter). Pour cela, le moyen le plus simple est de protger chaque chier par une construction e de ce genre : #ifndef FOO_H_ #define FOO_H_ /* ici, contenu du fichier */ #endif /* FOO_H_ */

Ainsi, mme si le chier est inclus plusieurs fois, son contenu ne sera actif quune fois. e Certains prprocesseurs iront mme jusqu` reconna ces structures, et ne pas lire le e e a tre chier si la macro de protection (ici FOO_H_) est encore dnie. e Il y a eut divers autres moyens proposs, tels que #import ou #pragma once, mais ils ne e sont pas standards, et encore moins rpandus. e
64

13.7

Comment dnir une macro ? e

On utilise #define. La forme la plus simple est la suivante : #define FOO 3 + 5

Apr`s cette dclaration, toute occurrence de lidenticateur FOO est remplace par son e e e contenu (ici 3 + 5). Ce remplacement est syntaxique et na pas lieu dans les cha nes de caract`res, ou dans les commentaires (qui nexistent dj` plus, de toutes faons, ` cette e ea c a tape). e On peut dnir un contenu vide. La macro sera remplace, en cas dutilisation, par rien. e e Le contenu de la macro est une suite de tokens du C, qui na pas ` vouloir dire quelque a chose. On peut dnir ceci, cest valide : e #define FOO (({/coucou+}[\}+ "zap" 123

La tradition est dutiliser les identicateurs en majuscules pour les macros ; rien noblige cependant ` appliquer cet usage. Les r`gles pour les identicateurs de macros sont les a e mmes que pour celles du langage. e

13.8

Comment dnir une macro avec des arguments ? e

On fait ainsi : #define FOO(x, y) ((x) + (x) * (y))

Ceci dnit une macro qui attend deux arguments ; notez quil ny a pas despace entre e le nom de la macro (FOO) et la parenth`se ouvrante. Toute invocation de la macro par la e suite est remplace par son contenu, les arguments ltant aussi. Ainsi, ceci : e e FOO(bla, "coucou")

devient ceci : ((bla) + (bla) * ("coucou"))

(ce qui ne veut pas dire grandchose en C, mais le prprocesseur nen a cure). Le premier e argument est remplac deux fois, donc, sil a des eets de bord (appel dune fonction, par e exemple), ces eets seront prsents deux fois. e
65

Si la macro est invoque sans arguments, elle nest pas remplace. Cela permet de dnir e e e une macro sense remplacer une fonction, mais en conservant la possibilit dobtenir un e e pointeur sur la fonction. Ainsi : int min(int x, int y) { return #define min(x, y) ((x) < (y) min(3, 4); /* invocation de (min)(3, 4); /* invocation de x < y ? x : y; } ? (x) : (y)) la macro */ la fonction, via le pointeur */

Cest une erreur dinvoquer une macro ` argument avec un nombre incorrect darguments. a En C99, on peut utiliser des arguments vides ; en C89, cest ou et mieux vaut viter. e

13.9

Comment faire une macro avec un nombre variable darguments ?

Ce nest possible quen C99. On utilise la construction suivante : #define error(l, ...) { \ fprintf(stderr, "line %d: ", l); \ fprintf(stderr, __VA_ARGS__); \ }

Ceci dnit une macro, qui attend au moins un argument ; tous les arguments supple e mentaires sont concatns, avec leurs virgules de sparation, et on peut les obtenir en e e e utilisant __VA_ARGS__. Ainsi, ceci : error(5, "boo: %s\n", bla)

sera remplac par ceci : e { fprintf(stderr, "line %d: ", 5); \ fprintf(stderr, "boo: %s\n", bla); }

Ce mcanisme est support par les derni`res versions de la plupart des compilateurs C e e e actuellement dvelopps ; mieux vaut lviter si le code doit aussi fonctionner avec des e e e compilateurs un peu plus anciens. Il existe aussi des extensions sur certains compilateurs. Par exemple, sous GCC, le code suivant est quivalent ` lexemple prcdent : e a e e

66

#define error(l, format...) { \ fprintf(stderr, "line %d: ", l); \ fprintf(stderr, format); \ }

Une autre mthode consiste ` utiliser le parenth`sage des arguments : e a e #define PRINTF(s) printf s ... PRINTF (("Vitesse du vent %d m/s", v));

13.10

Que font les oprateurs # et ## ? e

Loprateur # permet de transformer un argument dune macro en une cha de carace ne t`res. On fait ainsi : e #define BLA(x) BLA(5 * x + y); printf("lexpression %s retourne %d\n", #x, x);

ce qui donne le rsultat suivant : e printf("lexpression %s retourne %d\n", "5 * x + y", 5 * x + y);

Les ventuelles cha e nes de caract`res et backslashs dans largument sont protgs par des e e e backslashes, an de constituer une cha valide. ne Loprateur ## eectue la concatnation de deux tokens. Si le rsultat nest pas un token e e e valide, alors cest une erreur ; mais certains prprocesseurs sont peu stricts et se contentent e de re-sparer les tokens. On lutilise ainsi : e #define FOO(x, y) FOO(bar, qux)(); x ## y

qui donne ceci : barqux();

67

13.11

Une macro peut-elle invoquer dautres macros ?

Oui. Mais il est prvu un mcanisme qui empche les boucles innies. e e e Tout dabord, les invocations de macros ne sont constates que lors de lutilisation de la e macro, pas lors de sa dnition. Si on fait ceci : e #define FOO #define BAR BAR 100

alors on obtient bien 100, pas BAR. Si la macro poss`de des arguments, chaque fois que cet argument est utilis (sans tre e e e prcd dun # ou prcd ou suivi dun ##), il est dabord examin par le prprocesseur, e e e e e e e e qui, sil y reconna une macro, la remplace. Une fois les arguments traits, le prprocesseur t e e les implante ` leur place dans la suite de tokens gnrs par la macro, et g`re les oprateurs a e ee e e # et ##. ` A la suite de cette opration, le rsultat est de nouveau examin pour rechercher dautres e e e remplacements de macros ; mais si une macro est trouve, alors quon est dans le remplae cement de ladite, cette macro nest pas remplace. Ceci vite les boucles innies. e e Je sais, cest compliqu. Quelques exemples : e #define FOO #define BAR FOO coucou BAR zoinx FOO

FOO est remplace par coucou BAR, et le BAR rsultant est remplac par zoinx FOO. Ce e e e FOO nest pas remplac, parce quon est dans le remplacement de FOO. Donc, on obtient e coucou zoinx FOO. Un autre exemple, plus tordu : #define FOO(x) FOO(FOO); x(5)

La macro FOO est invoque ; elle attend un argument, qui est FOO. Cet argument est e dabord examin ; il y a FOO dedans, mais non suivi dune parenth`se ouvrante (largument e e est examin tout seul, indpendamment de ce qui le suit lors de son usage), donc le e e remplacement na pas lieu. Ensuite, largument est mis en place, et on obtient FOO(5). Ce rsultat est rexamin ; cette fois, FOO est bien invoque avec un argument, mais on e e e e est dans le deuxi`me remplacement, ` lintrieur de la macro FOO, donc on ne remplace e a e pas. Le rsultat est donc : FOO(5) ; e Si vous voulez utiliser ce mcanisme, allez lire une douzaine de fois la documentation du e GNU cpp, et surtout le paragraphe 12 de lannexe A du Kernighan & Ritchie (2`me e dition). e
68

13.12

Comment rednir une macro ? e

On peut rednir ` lidentique une macro ; ceci est prvu pour les chiers den-tte inclus e a e e plusieurs fois. Mais il est en gnral plus sain de protger ses chiers contre linclusion e e e multiple (cf. 13.6, page 64). Rednir une macro avec un contenu ou des arguments dirents, est une erreur. Certains e e prprocesseurs laxistes se contentent dun avertissement. La bonne faon est dabord e c dindnir la macro via un #undef. Indnir une macro qui nexiste dj` pas, nest pas e e ea une erreur.

13.13

Que peut-on faire avec #if ?

#if permet la compilation conditionnelle. Lexpression qui suit le #if est value ` la e e a compilation, et, suivant son rsultat, le code qui suit le #if jusquau prochain #endif, e #elif ou #else est valu, ou pas. Quand le code nest pas valu, les directives de e e e e prprocesseur ne le sont pas non plus ; mais les #if et similaires sont nanmoins compts, e e e an de trouver la n de la zone non compile. e Lorsque le prprocesseur rencontre un #if, il : e rcup`re lexpression qui suit le #if e e remplace les defined MACRO et defined(MACRO) par la constante 1 si la macro nomme e est dnie, 0 sinon e remplace les macros qui restent dans lexpression remplace par la constante 0 tous les identicateurs qui restent value lexpression e Lexpression ne doit comporter que des constantes enti`res (donc, ventuellement, des e e constantes caract`res), qui sont promues au type (unsigned) long (en C89) ou (u)intmax_t e (en C99). Les ottants, les pointeurs, lacc`s ` un tableau, et surtout loprateur sizeof e a e ne sont pas utilisables par le prprocesseur. e Il nest pas possible de faire agir un #if suivant sizeof(long), pour reprendre un desiderata frquent. Par ailleurs, les constantes de type caract`re nont pas forcment la e e e mme valeur pour le prprocesseur et pour le compilateur. e e

13.14

Quest-ce quun #pragma ?

Cest une indication pour le compilateur. Le prprocesseur envoie cette directive sans la e modier. Le standard C89 ne prvoit aucune directive standard, mais le prprocesseur e e comme le compilateur sont senss ignorer les directives inconnues. e Le C99 dnit trois #pragma qui permettent dajuster le comportement du compilateur, e quant au traitement des nombres ottants et complexes.

69

13.15

Quest-ce quun #assert ?

Cest une extension gre par GNU et (au moins) le compilateur Sun (Workshop Compiee ler, pour Solaris). Cest une sorte dalternative ` #ifdef, avec une syntaxe plus agrable. a e Cest a viter, car non standard. `e

13.16

Comment dnir proprement une macro qui comporte e plusieurs statements ?

On peut ouvrir un bloc, car tout statement est remplaable, en C, par un bloc, mais c cela pose des probl`mes avec le ; terminal du statement. La mani`re recommande est la e e e suivante : #define foo(x) do { f(x); printf("coucou\n"); } while (0)

On peut ainsi lutiliser comme ceci : if (bar) foo(1); else foo(2);

Si on avait dni foo sans le do et le while (0), le code ci-dessus aurait provoqu une e e erreur de compilation, car le else serait spar du if par deux statements : le bloc et le e e statement vide, termin par le point-virgule. e

13.17

Comment viter les eets de bord ? e

Les macros peuvent tre dangereuses si lon ne fait pas attention aux eets de bord. Par e exemple si lon a le code suivant : #define MAX int i = MAX; 3 + 5

La variable i vaudra bien 8, mais si on utilise la macro MAX ainsi : int i = MAX * 2;

La variable i ne vaudra pas 16, mais 13. Pour viter ce genre de comportement, il faut e crire la macro ainsi : e

70

#define MAX

(3 + 5)

Dans certains cas, une macro reprsente une expression C compl`te. Il est alors plus e e cohrent de placer des parenth`ses vides pour simuler une fonction. Et dans ce cas il ne e e faut pas la terminer par un point virgule, et #define PRTDEBUG() (void)printf("Coucou\n")

ainsi, on pourra utiliser la macro par : if (i == 10) { PRTDEBUG(); } else { i++; }

Quand une macro a des arguments il faut faire attention ` la faon de les utiliser. Ainsi a c la macro : #define CALCUL(x, y) (x + y * 2)

a des eets de bord suivant la faon de lutiliser : c int i = CALCUL(3, 5);

donnera bien un rsultat de 13, alors que le mme rsultat serait attendu avec : e e e int i = CALCUL(3, 2 + 3);

qui donne 11. Pour viter cela, il sut de placer des parenth`ses sur les arguments de la e e macro : #define CALCUL(x, y) ((x) + (y) * 2)

Un eet de bord qui ne peut tre contourn survient quand la macro utilise plusieurs fois e e une variable :
71

#define MAX(x, y)

((x) > (y) ? (x) : (y))

Si on utilise la macro comme cela : i = MAX(j, k);

On obtiendra un rsultat correct, alors quavec : e i = MAX(j++, k++);

une des variables j ou k sera incrmente 2 fois. Pour viter ce genre de comportement, e e e il faut remplacer la macro par une fonction, de prfrence inline (C99) : ee inline int max(int x, int y) { return x > y ? x : y; }

En r`gle gnrale, quand on utilise une fonction avec un nom en majuscule (comme MAX), e e e on sattend ` ce que ce soit en fait une macro, avec les eets de bord qui en dcoulent. a e Alors que si le nom est en minuscule, il sagit srement dune vritable fonction. Cette u e r`gle nest hlas pas gnrale et donc il convient de vrier le vritable type dune fonction e e e e e e si lon ne veut pas tre surpris lors de son utilisation. e

13.18

Le prprocesseur est-il vraiment utile ? e

La rponse est oui. Il existe un mouvement qui voudrait faire dispara le prprocesseur, e tre e en remplaant les #define par des variables constantes, et avec un quelconque artice c syntaxique pour importer les dclarations dun chier dentte. e e Il sav`re quon a vraiment besoin dun mcanisme polymorphe (comme une fonction qui e e accepterait plusieurs types dirents), et que seules les macros apportent ce mcanisme e e en C. Les anti-prprocesseurs acharns parlent dadopter le mcanisme des templates du e e e C++, mais a ne risque pas darriver de sitt. c o Dans la vie de tous les jours, lutilisation du prprocesseur, avec quelques #include et e des #define sans surprise, ne pose pas de probl`me particulier, ni de maintenance, ni de e portabilit. e

13.19

Approfondir le sujet.

Outre le Kernighan & Ritchie, qui comporte quelques pages tr`s bien faites sur le e sujet, on peut lire la documentation au format info du GNU cpp ; elle est assez partiale
72

dans certains cas (condamnation explicite des trigraphes, par exemple) mais assez riche en enseignements. Pour le reste, chaque compilateur C vient avec un prprocesseur, et il existe quelques e prprocesseurs indpendants (un crit par Dennis Ritchie en personne, quon voit inclus e e e dans lcc, et aussi ucpp, une oeuvre ` moi : http://www.di.ens.fr/~pornin/ucpp/). a

73

Chapitre 14

Fonctions de la biblioth`que e
14.1 Comment convertir un nombre en une cha ne de caract`res ? e

Il sut dutiliser sprintf. char sz[4]; sprintf(sz, "%d", 123);

Pour convertir un double ou un long, il faut utiliser %f ou %ld. Le probl`me de sprintf est quil faut rserver assez de place pour la cha rsultat. e e ne e Ainsi le code char sz[6]; sprintf(sz, "%u", i);

marchera sur des machines o` i est un entier 16 bits, mais il plantera si i est un entier u plus grand (> 99999). En C99, la fonction snprintf permet dviter les dbordements : e e char sz[4]; n = snprintf(sz, sizeof sz, "%d", 123); if (n < 0 || n >= sizeof sz) erreur();

14.2

Comment convertir une cha en un nombre ? ne

Si le nombre espr est un entier, il faut utiliser la fonction strtol. Elle convertit une ee cha en un entier long, dans une base donne. ne e
74

Si le nombre est un rel (float ou double), alors la fonction strtod fera tr`s bien laaire. e e char test[] = " -123.45e+2"; char * err = NULL; errno = 0; double result = strtod(test, &err); if (err == test) { printf("Erreur de conversion :\n"); } else if (errno == ERANGE) { printf("Depassement :\n"); } else { printf("Conversion reussie :\n"); if(*err == \0) { printf("Pour toute la chaine\n"); } }

Si le nombre est un long double (C99 seulement) alors la fonction strtold est ` prfrer. a ee

14.3

Comment dcouper une cha ? e ne

La fonction strtok est faite pour a ! c char sz1[] = "this is,an char sz2[] = ",; "; char *p; example ; ";

p = strtok(sz1, sz2); if (p != NULL) { puts(p); while ((p = strtok(NULL, sz2)) != NULL) { puts(p); } }

Attention, la fonction strtok soure au moins des probl`mes / caractristiques suivants : e e Elle fusionne les dlimiteurs adjacents. En cas dutilisation dune virgule comme spae e rateur, a b,c est spare en trois lments et non quatre. e e ee Le caract`re du dlimiteur est perdu, car il est remplac par un caract`re nul (0). e e e e

75

Elle modie la chaine quelle analyse. Cest un dfaut, car cela oblige ` faire une copie e a de la chaine en cas dutilisation ultrieure. Cela signie aussi que lon ne peut pas e sparer une chaine littrale avec. e e On ne peut utiliser quun appel de cette fonction ` la fois. Si une squence de strok a e est en cours, et quune autre dmarre, ltat de la premi`re est perdue. Ce nest pas e e e grave pour les petits programmes, mais il est facile de se perdre dans la hirarchie des e fonctions imbriques dans des programmes plus importants. En dautre termes, strtok e brise les principes de lencapsulation. Dans des cas simples, on pourra utiliser la fonction strchr.

14.4

Pourquoi ne jamais faire fflush(stdin) ?

La fonction fflush a un comportement dni uniquement sur les ux ouverts en criture e e tels que stdout. Il est possible que sur votre syst`me, appliquer cette fonction ` stdin soit e a possible, mais cest alors une extension non standard. Le comportement est indtermin, e e et imprvisible. e Il faut bien comprendre que stdin nest pas forcment reli au clavier, mais peut tre e e e rattach ` un rseau, un chier, etc. ea e

14.5

Comment vider le buer associ ` stdin ? ea

Une bonne mani`re est de lire sur le ux tant quil nest pas vide, avec les fonctions e habituelles comme fgets ou getchar. Voici un exemple avec cette derni`re : e c = getchar(); if (c != \n) while ( (getchar()) != };

\n) {

Ce morceau de code permet de lire un caract`re, et vide ce qui peut rester dans le buer, e notamment le \n nal.

14.6

Pourquoi mon printf ne sache pas ?

Le ux standard stdout, sur lequel crit printf est bueris. Cest ` dire que les cae e a ract`res sont crits dans un tampon (une zone mmoire). Lorque celui-ci est plein, ou e e e lorsquune demande explicite est faite, il est vid dans le ux proprement dit (sur lcran e e gnralement). Tant que le buer nest pas vid, rien ne sache. e e e Pour vider le buer, il y a trois possibilits : e Le buer est plein Il est vid explicitement par lappel de la fonction fflush (cf. 14.4, page 76) e La cha de caract`res se termine par un \n ne e
76

14.7

Comment obtenir lheure courante et la date ?

Il faut simplement utiliser les fonctions time, ctime et/ou localtime, qui contrairement a ` leurs noms donnent lheure et la date. Voici un petit exemple : #include <stdio.h> #include <time.h> int main(void) { time_t now; time(&now); printf("Il est %.24s.\n", ctime(&now)); return 0; }

14.8

Comment faire la dirence entre deux dates ? e

Il faut simplement utiliser la fonction difftime. Cette fonction prend deux time_t en param`tres et renvoie un double. e

14.9

Comment construire un gnrateur de nombres alatoires ? e e e

Ce nest pas possible. La biblioth`que standard inclut un gnrateur pseudo-alatoire, la e e e e fonction rand. Toutefois, limplmentation dpend du syst`me, et celle-ci nest gnralement pas tr`s e e e e e e bonne (en terme de rsultats statistiques). Si rand ne vous sut pas (simulation nume e rique ou cryptologie), il vous faudra regarder du cot de biblioth`ques mathmatiques, e e e dont de nombreuses se trouvent sur Internet. En particulier, on consultera les paragraphes 7-0 et 7-1 des Numerical Recipes in C (cf. 4.5, page 23) et le volume 2 de TAoCP (cf. 3.9, page 19).

14.10

Comment obtenir un nombre pseudo-alatoire dans un e intervalle ?

La mthode la plus simple ` faire, e a rand() % N

77

qui renvoie un nombre entre 0 et N-1 est aussi la moins bonne. En eet, les bits de poids faibles ont une distribution tr`s peu alatoire. Par exemple, le bit de poids le plus faible e e a une distribution qui peut tre celle-ci sur un mauvais gnrateur : e e e 0 1 0 1 0 1 0 1 0 1 ... Voici la mthode prconise dans Numerical Recipes (cf.4.5, page 23) : e e e (int)((double)rand() / ((double)RAND_MAX + 1) * N)

RAND_MAX est dni dans stdlib.h, et N doit tre plus petit que RAND_MAX. e e

14.11

` A chaque lancement de mon programme, les nombres pseudo-alatoires sont toujours les mmes ? e e

Cest normal, et cest fait expr`s. Pour contrer cela, il faut utiliser une graine pour le e gnrateur qui change ` chaque lancement du programme. Cest la fonction srand qui e e a sen charge. On peut utiliser lheure syst`me, avec time, de la facon suivante : e srand(time(NULL));

Notez quil est peu utile dappeler la fonction srand plus dune fois par programme.

14.12

Comment savoir si un chier existe ?

En C ISO, le seul moyen de savoir si un chier existe, cest dessayer de louvrir. { FILE *fp = fopen ("fichier.txt", "r"); if (fp == NULL) { fputs ("Le fichier nexiste pas,\n" "ou vous navez pas les droits necessaires\n" "ou il est inaccessible en ce moment\n" , stderr); } else { /* ... operation sur le fichier */ fclose(fp);
78

} }

Dans la norme POSIX, il existe la fonction access, mais certains syst`mes nimplmentent e e pas cette interface.

14.13

Comment conna tre la taille dun chier ?

Malheureusement, les fonctions stat et fstat de la norme POSIX ne sont pas reprises dans la norme ISO. La seule solution standard est dutiliser fseek et ftell. Toutefois, cela ne marche pas pour les tr`s gros chiers (suprieurs ` LONG_MAX). e e a

14.14

Comment lire un chier binaire proprement ?

Il faut ouvrir le chier en mode binaire , en passant la cha "rb" en mode douverture ne a ` la fonction fopen. Cela vite les transformations inopportunes et les probl`mes des e e caract`res de contrle. e o De mme, pour crire dans un chier binaire, on utilise le mode "wb". e e

14.15

Comment marquer une pause dans un programme ?

Il ny a pas de fonction standard pour cela. Il existe toutefois la fonction sleep en POSIX, elle provoque une attente passive pour une dure donne en secondes. e e

14.16

Comment trier un tableau de cha nes ?

La fonction qsort est une bonne fonction de tri, qui implmente le Quick Sort. Le plus e simple est de donner un exemple : /* Fonction qui compare deux pointeurs vers des chaines pour qsort */ int pstrcmp(const void * p1, const void * p2){ return strcmp(*(char * const *)p1, *(char * const *)p2); }

Les param`tres doivent tre des pointeurs gnriques pour qsort. p1 et p2 sont des e e e e pointeurs sur des cha nes. Un tableau de cha nes doit tre pris au sens dun tableau de e pointeurs vers des char *. Lappel ` qsort ressemble alors ` : a a
79

qsort(tab, sizeof tab, sizeof *tab, pstrcmp);

14.17

Pourquoi jai des erreurs sur les fonctions de la biblioth`que, alors que jai bien inclus les enttes ? e e

Les en-ttes (les .h) ne contiennent que les prototypes des fonctions. Le code propremente dit de ces fonctions se trouve dans des chiers objets. Ce code doit tre li au tien. e e Cela est fait par un diteur de liens. e Pour certaines fonctions, il faut spcier explicitement ` lditeur de liens o` il peut les e a e u trouver (et ce particuli`rement pour les fonctions non-standard). e Par exemple, sous Unix, pour utiliser les fonctions mathmatiques, il faut gnralement e e e lier le programme avec la biblioth`que adquate : e e cc -lm monfic.o -o monprog

80

Chapitre 15

Styles
15.1 Comment bien programmer en C ?

La chose la plus importante est de commenter un programme. Il ne sagit pas de dcrire e en franais tout ce que fait chaque ligne de code, mais de prciser le fonctionnement des c e oprations complexes, dexpliquer le rle des variables, de dire ` quoi servent les fonctions. e o a Choisir des noms de variables et de fonctions explicites est une bonne faon de commenter c un programme. Tout morceau de code qui nest pas standard doit tre abondamment comment an de e e rendre le portage vers dautres cibles le moins fastidieux possible, lidal tant dutiliser e e des macros. Il est galement important de bien structurer son programme en modules, puis en fonce tions. Certains vont jusqu` dire quune fonction ne doit pas dpasser la taille dun cran. a e e Les dclarations et prototypes doivent tre regroups dans des chiers den-ttes, avec les e e e e macros. Enn, il est tr`s important de bien prsenter le code, avec une indentation judicieuse et e e e e des sauts de ligne. (cf. 15.2, page 81) Il est gnralement admis que les lignes ne doivent pas dpasser 80 caract`res. e e Pour le reste, cest une histoire de got. u

15.2

Comment indenter proprement du code ?

Lindentation du code est une chose essentielle pour la lisibilit. Certaines personnes e utilisent des tabulations, ce qui est une mauvaise habitude. La largeur de ces tabulations varie dun diteur ` un autre. Des diteurs remplacent les tabulation par un nombre e a e despaces xe et dautres encore utilisent des tabulations de taille variable. Ne parlons pas des imprimantes ou des lecteurs de news... Tout ceci rend lutilisation des tabulations dicile. Pour viter tout probl`me, et amliorer la lisibilit du code, il faut utiliser uniquement e e e e des espaces. Un diteur correct doit pouvoir gnrer un nombre despaces xe (voire une e e e
81

indentation automatique) lorsquon appuye sur la touche <TAB> (ou autre raccourci). Personnellement, je r`gle ` 4 espaces par tabulation. e a

15.3

Quel est le meilleur style de programmation ?

Comme vous vous en doutez, il ny en a pas. Le plus important est den avoir un, et de le suivre. Quand on utilise un type dindentation ou un style de nommage, il faut lutiliser dans tout le programme (voire dans tout ses programmes). Cest la rgularit qui donne e e la lisibilit. e Il existe des styles de programmations frquemment utiliss en C, comme les styles K&R e e ou GNU. Le style K&R est le style historique , et cest pourquoi il est tr`s utilis. Le e e style GNU est utilis pour tous les projets de la Free Software Fundation. e Sur le site FTP ftp://caramba.cs.tu-berlin.de, le repertoire /pub/doc/style contient quelques documents intressants sur la question. e

15.4

Quest-ce que la notation hongroise ?

Cest une convention de nommage des objets, invente par Charles Simonyi. Le principe e est de faire prcder le nom des variables par un identicateur de type. Par exemple, e e une cha de caract`re reprsentant un nom sera nomme szname, sz signiant string ne e e e zero , ou cha termine par un \0. ne e Personnellement, je ne trouve pas cette convention toujours pratique. Le nom de la variable doit avant tout reter son rle. e o

15.5

Pourquoi certains crivent-ils if(0==x) et non if(x==0) ? e

Il arrive souvent que lon crive = au lieu de ==. Comme 0 nest pas une lvalue (cf. 10.10, e page 53), cette tourderie provoquera une erreur, simple ` dtecter. Dans le mme genre, e a e e on peut crire while (0 == x). e Certains compilateurs prviennent lorsque lon fait une aectation l` ou est attendu un e a test. Cest le cas de GNU CC, avec loption -Wall. Lorsque lon crit while ( c = fct() ), certains compilateurs rlent en croyant que e a lon sest tromp entre le = et le ==. Pour viter cela, il sut de rajouter un paire de e e parenth`ses. e while ( (c= fct()) ) { /* ... */ }

82

15.6

Pourquoi faut-il mettre les { et } autour des boucles ?

Cest une prcaution contre les erreurs du genre : e for(i = 0; i < N; i++); tab[i] = i;

De plus, cela permet une plus grande simplicit dans lvolution du code. En eet, les e e programmes ont tendance ` spaissir avec le temps. a e

15.7

Pourquoi certains disent-ils de ne jamais utiliser les goto ?

Le goto est une instruction qui permet de casser laspect structur dun programme. e Des goto mal utiliss permettent de rendre un code totalement illisible (code spaghetti ), e dautant plus quavec les structures de boucles traditionnelles, on peut souvent sen passer. Toutefois, il arrive parfois que lutilisation dun goto rende le code plus propre. Cest le cas, par exemple, des sorties de boucles imbriques en cas derreur. Cela rejoint le cas e plus gnral des gestions dexceptions internes. e e Poser comme r`gle de ne jamais utiliser les goto est une absurdit. Par contre, avertir le e e programmeur de lutiliser avec parcimonie, et avec beaucoup de prcautions me semble e une bonne chose.

15.8

Pourquoi commenter un #endif ?

#endif ne peut tre suivi par autre chose quun commentaire. On commente donc pour e savoir ` quelle directive il correspond : a #if FOO /* du code ou des directives */ #ifdef BAR /* encore du code ou des directives */ #endif /* BAR */ /* encore du code ou des directives */ #endif /* FOO */

15.9

O` trouver de la doc sur les dirents styles ? u e

Le document suivant contient des r`gles de base ` suivre pour programmer proprement : e a ftp://ftp.laas.fr/pub/ii/matthieu/c-superflu/c-superflu.pdf

83

Sachez galement quil existe un programme indent issu de BSD qui rindente automae e tiquement du code, suivant un style donn. Les options classiques de la version GNU de e cet utilitaire sont -kr (pour le style dcrit dans K&R), -gnu (pour le style utilis dans e e les projets GNU ) ou encore -orig (pour le style BSD). Sous Unix, on trouve galement la commande cb avec loption -sj pour avoir le style e K&R.

15.10

Comment bien structurer son programme ?

Il ny a pas de rponse dnitive, mais voici une piste : e e Suite a lanalyse (conception) il est possible de dcouper le projet en une multitude de ` e syst`mes et de sous-syst`mes. e e Chaque sous-syst`me fait lobjet dun module compos dun chier source(xxx.c) et e e dun chier den-tte (xxx.h). e Les syst`mes sont organiss hirarchiquement (arbre) de faon ` viter les dpendences e e e c ae e croises. e Evidement, tout a est peu thorique. De plus, il existes des techniques avances (ADT, c e e callback) qui permettent les appels croiss propres. (c ` d sans dpendances croises). e a e e Lavantage est que chaque module est testable individuellement (et le plus souvent rutie lisable). La phase suivante du dveloppement est lintgration. Elle consiste ` mettre la colle qui e e a va bien pour faire tenir tout les modules ensembles et ` tester le fonctionnement de a lensemble. Nous ne traitons pas ici des direntes mthodes danalyse et de conception. Rappelons e e toutefois les 5 phases (en V) de dveloppement dun projet : e 1 - Spcification e 5 - Validation \ / 2 - Conception 4 - Integration \ / 3 - Codage et test unitaire

Voir ` ce sujet les questions 9.3, page 46 et 13.5, page 64. a Merci ` Emmanuel Delahaye pour cette rponse. a e

84

Chapitre 16

Autres
16.1 Comment rendre un programme plus rapide ?

Il y a deux raisons possibles ` la lenteur dun programme. a La premi`re vient de lcriture du code lui-mme, les entres/sorties, les allocations dye e e e namiques, de nombreux appels de fonctions, des boucles, etc. Le programmeur a gne e ralement peu intrt ` modier son code, tout au plus pourra-t-il remplacer les petites ee a fonctions le plus souvent appeles par des macros et tenter de limiter les boucles. On e pourra aussi amliorer les entres/sorties et les allocations si cest possible. Il reste enn e e les options de compilation sur lesquelles on peut jouer. Lautre raison vient de la complexit thorique des algorithmes utiliss. Dans ce dernier e e e cas, il faut chercher un meilleur algorithme. Cet aspect est dvelopp par exemple dans e e http://www.enseignement.polytechnique.fr/profs/informatique/Jean-Jacques.Levy/ poly/polyx-cori-levy.ps.gz Il existe des outils de prolage de programmes. Il sagit de compiler les sources avec une biblioth`que puis de lancer le programme. On lance alors un lociciel associ ` la e e a biblioth`que. Le rsultat est un chier o` est dtaill le temps pass dans chaque fonction, e e u e e e le nombre dappels, etc. Sur Unix-like, le projet GNU propose gprof (cf. 4.6, page 24). Rappelons tout de mme que la vitesse dexcution dun programme (hors probl`mes e e e dalgorithmique) est peu souvent critique, et quil est bien plus important de fournir un code lisible.

16.2

Quelle est la dirence entre byte et octet ? e

Lunit de mmoire lmentaire du C est le byte. Le byte au sens anglo-saxon et donc pas e e ee loctet au sens francophone (caract`re). e En fait un byte correspond ` un caract`re non-sign (unsigned char), lequel peut prendre a e e plus (ou moins) de 8 bits. En principe, en franais, on parle dans ce cas de multiplet (et c peut-tre bientt de codet) comme traduction ocielle de byte dans ce sens. e o (Merci ` Antoine Leca). a
85

16.3

Peut-on faire une gestion dexceptions en C ?

Oui, cest possible, en utilisant le couple setjmp/longjmp. #include <stdio.h> #include <stdlib.h> #include <setjmp.h> jmp_buf env; long fact(long x) { long i, n; if (x < 0) longjmp(env, 1); for (n = 1, i = 2; i <= x; i ++) n *= i; return n; } long comb(long k, long n) { if (k < 0 || n < 1 || k > n) longjmp(env, 2); return fact(n) / (fact(k) * fact(n - k)); } int main(int argc, char *argv[]) { if (argc < 3) { fprintf(stderr, "pas assez darguments\n"); return EXIT_FAILURE; } if (setjmp(env)) { fprintf(stderr, "erreur de calcul\n"); return EXIT_FAILURE; } printf("%ld\n", comb(strtol(argv[1], 0, 0), strtol(argv[2], 0, 0))); return 0; }

Voil` un programme qui calcule le coecient binomial des deux arguments ; main appelle a comb qui appelle fact. Ces fonctions vrient un peu les arguments, et on voudrait e renvoyer une erreur en cas de probl`me ; mais : e Je renvoie dj` le rsultat, il faudrait quil y ait des rsultats impossibles pour y ea e e coder les erreurs ; cest le cas ici (le rsultat est toujours suprieur ou gal ` 1) mais a e e e a c demande une analyse mathmatique pas toujours facile. e
86

Je ne veux pas tester les codes derreurs ` chaque invocation dune fonction. Cela peut a devenir lourd syntaxiquement, et a consomme du temps CPU. c setjmp sauvegarde ltat du programme au moment de lappel, et renvoie 0. longjmp e remplace le contenu de la pile dexcution par la sauvegarde, et le programme se trouve e a ` nouveau ` lendroit de lappel ` setjmp. Celle-ci renvoie alors une valeur passe en a a e param`tre de longjmp (dans lexemple, 1 pour une erreur dans fact et 2 pour une erreur e dans comb). La mthode prsente ici est assez rustique. Il existe des mcanismes de POO1 en C bien e e e e plus volus. Vous pouvez ` ce sujet allez voir lexcellent document : http://ldeniau. e e a home.cern.ch/ldeniau/html/oopc/oopc.html. Allez voir aussi le document suivant : http://cern.ch/Laurent.Deniau/html/exception/exception.html. Voir aussi la question 3.10, page 19.

16.4

Comment grer le numro de version de mon programme ? e e

Serge Paccalin propose la chose suivante : #define STR_(a) #define STR(a) #define VERSION printf("This is #a STR_(a) 4 version " STR(VERSION) " of the program\n");

En eet, quelque chose comme : #define STR(a) #a #define VERSION a printf("This is version " STR(VERSION) " of the program\n");

fait intervenir la concatnation des cha e nes trop tt ce qui fait que le rsultat de cette o e derni`re squence renvoie : e e This is version VERSION of the program

16.5

Pourquoi ne pas mettre de devant les identiants ?

Well well well, ce nest pas si facile. Les vrais identicateurs rservs sont : e e les mots cls tels que if, for, switch, long, ... e les identicateurs commenant par deux c les identicateurs commenant par un suivi dune lettre majuscule. c
1

Programmation Orient Objet e

87

Ensuite, il y a les headers standards et la biblioth`que. Les headers sont libres dutiliser e des identicateurs commenant par un et suivis dune lettre minuscule, comme liste, c mais cest pour dnir quelque chose qui a un le scope , cest-`-dire une porte globale e a e a ` la translation unit (le chier source C et les chiers quil inclut). Donc, globalement, on ne doit pas sen servir pour dnir quelque chose qui a ce le scope . e Ca veut dire quoi ? Que les choses suivantes sont interdites : typedef int _foo; struct _bar { int x; char * y; }; void _f(void);

En revanche, les choses suivantes sont autorises : e void f(int _x[]) { typedef int _foo; struct _bar { int x; char *y; }; extern void _g(void); } struct qux { long _truc; };

Jattire lattention du public bahi sur les quatre points suivants : e En dnissant des identicateurs ` porte rduite (bloc, prototype, fonction), on peut e a e e masquer des identicateurs dnis par les headers standards, identicateurs qui poue vaient intervenir dans des macros dnies dans lesdits headers ; comme toutes les fonce tions standards peuvent tre surcharges par des macros, ` lintrieur dun bloc o` on e e a e u a jou ` dnir un identicateur de type _foo, toute utilisation dune facilit fournie ea e e par un header peut dclencher un undened behaviour (par exemple, lordinateur e utilise spontanment son modem pour tlphoner ` la belle-m`re du programmeur et e ee a e linviter ` venir d a ner ` la maison). a Le extern void _g(void) ; explicite le fait que la restriction est sur le scope et pas le linkage. Bien entendu, il faut que la fonction _g() soit dnie quelque part, avec e un external linkage, ce qui ne peut pas se faire en C standard. Donc cette dclaration, e quoique valide, doit avoir un pendant dans une autre translation unit, qui ne peut pas tre fabriqu de faon standard. Pour complter, rajoutons que la dnition na besoin e e c e e dexister que si on se sert eectivement de la fonction. Donc on est en fait autoris ` faire ea une dclaration inutile qui pourrait faire planter des implmentations non conformes e e mais courantes. How useful.
88

Quand bien mme on aurait le droit, rares sont les implmentations compl`tement e e e conformes de ce point de vue. Par pur pragmatisme, on vite donc de jouer avec des e identicateurs commenant par un suivi dune lettre minuscule. c Chaque header apporte ses propres restrictions ; par exemple, <ctype.h> peut dclarer e nimporte quel identicateur qui commence par is ou to suivi dune lettre minuscule. Ces identicateurs sont rservs pour ce qui est de lexternal linkage, ce qui veut dire e e que mme si on ninclut pas <ctype.h>, on ne doit pas dnir, entre autres, de variable e e globale iszoinx qui ne soit pas protg par le mot cl static. e e e

16.6

` A quoi peut servir un cast en (void) ?

Il y a principalement deux utilits ` caster une expression en (void). e a La premi`re utilisation est pour indiquer explicitement au compilateur quune valeur e est ignore, comme au retour dune fonction. Par exemple, il arrive souvent dutiliser la e fonction printf sans utiliser ni mme tester la valeur de retour. Ecrire lappel ` printf e a ainsi (void)printf("%s\n", "Un message ` la con") ; a

est une mani`re de dire au compilateur, et aux lecteurs du code, que je sais que printf e renvoie une valeur, mais que jai dcid de lignorer. Cela peut tre utile pour des utilitaires e e e de vrication de code, comme lint. e La seconde utilisation est dans des dnitions de macro. Voici un exemple : e #undef the_truc #ifdef __TRUC__DEPENDANT__ # define the_truc(a) ((void)0) #else # define the_truc(a) /* du code */ #endif

Ainsi, the_truc(a) est utilisable l` ou une expression est requise, comme ici : a i = (the_truc(a), 5) ;

Avec une dnition de the_truc comme ceci, e # define the_truc(a)

il y aurait une erreur ` la compilation. a


89

Chapitre 17

En guise de conclusion
--- Hou l`... a --- Jai crit a moi ? e c --- Toujours est-il que  quelquun  la crit. e Tant pis, maintenant tu cris la doc. e --- Oh non ! --- Bon alors tu dbogues. e --- Ca va, a va, jcris la doc ... c e --- OK, ensuite tu dbogues. e --- ...

90

Index
numration, 34, 35 e e valuation, 52, 53 e abstraction, 31, 33 alatoire, 77, 78 e ANSI, 15 argument, 48 arrondis, 55 assert, 70 B, 14, 15 backslash, 63 BCPL, 14, 15 biblioth`que, 23 e binaire, 79 bool, 51 boolen, 52 e boucles, 83 byte, 85 C, 13, 15 C++, 14 C89, 15 C99, 15 calloc, 29, 59 cast, 89 cha 41, 74, 75 ne, chaine, 30 char, 27, 40, 42, 43 commentaires, 63 comparaison, 41 compilateur, 22 const, 28 conversion, 74 copie, 41 copyright, 8 ctime, 77 dbogueur, 22 e dclaration, 45, 46 e dnition, 45 e
91

date, 77 ditime, 77 double, 27, 54, 56 environnement, 21 exception, 86 exponentiation, 56 expression, 52, 53 ush, 76 fgets, 41, 43 chier, 34, 78, 79 oat, 27, 5456 fonction, 29, 34, 39, 46 free, 59, 60 gets, 41, 43 globale, 28 goto, 83 graphisme, 23 heure, 77 hongroise, 82 IDE, 21 impression, 40, 54 include, 64 indentation, 81 initialisation, 29 int, 27 ISO, 15 K&R, 15, 18 langage, 13 licence, 8 livres, 18 localtime, 77 logique, 51, 52 long, 27 lvalue, 53, 82 macro, 28, 65, 68, 69

main, 47 malloc, 29, 38, 5860 multi-dimensions, 37, 38 Multics, 15 NaN, 56 norme, 15 notation, 82 NULL, 39, 40, 48, 51, 59 Objective-C, 14 octet, 85 optimisation, 85 origine, 14 outils, 24 param`tre, 49 e parenth`ses, 52 e pause, 79 pi, 56 pointeur, 28, 30, 37, 39, 40, 48 portabilit, 14 e prcision, 55 e prprocesseur, 62 e pragma, 69 precision, 54 printf, 47, 76 prototype, 45, 46 qsort, 79 rcursivit, 27, 33 e e racine, 54 rand, 77 realloc, 60 restrict, 47 return, 49 scanf, 43 short, 27 sizeof, 42, 43 sleep, 79 sprintf, 74 srand, 78 stdin, 76 stdout, 76 strcmp, 41 strcpy, 41 strncmp, 41 strncpy, 41
92

strtok, 75 structure, 27, 31, 33, 34 style, 81, 82 tableau, 29, 30, 37, 38 taille, 79 time, 77 tri, 79 trigraphe, 62 union, 34 Unix, 15 unsigned, 27 variadique, 47, 48 version, 87 void, 40, 89

Você também pode gostar