Article View: fr.comp.lang.c
Article #15608[FAQ] fr.comp.lang.c - partie 4/4
From: Guillaume Rumeau
Date: Sat, 17 Sep 2005 09:52
Date: Sat, 17 Sep 2005 09:52
1686 lines
53996 bytes
53996 bytes
Archive-Name: fr/comp/lang/faq-c-4
Archive-Name: fr/comp/lang/faq-c-4
---------------------------------------------------------------------------
FAQ de fr.comp.lang.c
18 avril 2003
Partie 4/4 (sections 13 � 17)
---------------------------------------------------------------------------
13. Le pr�-processeurs
13.1 Quel est le r�le du pr�processeur ?
Le pr�processeur interpr�te les directives qui commencent par
#. Principalement, ces directives permettent d'inclure
d'autres fichiers (via #include) et de d�finir des
macros (via #define) qui sont remplac�es lors de la
compilation.
Chaque directive de compilation commence par un # situ�
en d�but de ligne (mais �ventuellement pr�c�d� par des espaces,
des tabulations ou des commentaires) et se termine en fin de
ligne.
Le pr�processeur est �galement responsable de la reconnaissance
des trigraphes, des backslashs terminaux, et de
l'ablation des commentaires.
13.2 Qu'est-ce qu'un trigraphe ?
Dans les temps anciens, les ordinateurs n'utilisaient pas ASCII ;
chaque machine avait son propre jeu de caract�res. L'ISO a d�fini
un jeu de caract�res suppos�s pr�sents sur toutes les machines,
c'est l'Invariant Code Set ISO 646-1983. Ce jeu de
caract�res ne comporte pas certains caract�res int�ressants, tels
que les accolades et le backslash. Aussi, le standard
C89 a introduit les trigraphes, s�quences de trois caract�res
commen�ant par ??.
Il existe neuf s�quences remplac�es par le pr�processeur.
Ce remplacement a lieu avant toute autre op�ration, et agit
�galement dans les commentaires, les cha�nes constantes, etc.
Les trigraphes sont, de fait, rarement utilis�s. On les voit
appara�tre occasionnellement et par erreur, quand on �crit �a :
printf("Kikoo ??!\n");
Le trigraphe est remplac� par un pipe, donc ce code
affiche ceci :
Kikoo |
De toutes fa�ons, le redoublement du point d'interrogation est de
mauvais go�t.
Les compilateurs modernes soient ne reconnaissent plus les
trigraphes, soit �mettent des avertissements quand ils les
rencontrent.
13.3 � quoi sert un backslash en fin de ligne ?
Apr�s le remplacement des trigraphes, le pr�processeur recherche
tous les caract�res backslash situ�s en fin de ligne
; chaque occurrence de ce caract�re est supprim�e, de m�me que le
retour � la ligne qui le suit. Ceci permet d'unifier plusieurs
lignes en une seule.
Ce comportement est pratique pour �crire des macros ou des
cha�nes de caract�res sur plusieurs lignes :
printf("Hello\
World !\n");
Pour les cha�nes de caract�res, on peut aussi �crire plusieurs
cha�nes c�te � c�te, et le compilateur les unifiera (mais pas le
pr�processeur : pour lui, ce seront deux cha�nes � la suite l'une
de l'autre). C'est une technique qui doit �tre g�n�ralement pr�f�r�e.
On �crira donc pour l'exemple pr�c�dent :
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 lignes plus loin. Les
commentaires ne s'imbriquent 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 pr�processeur va quand m�me les
regarder, afin de trouver le #endif). Ceci veut dire
qu'il ne faut pas de cha�ne non termin�es. Ce genre de
commentaire n'est pas adapt� � du texte, � cause des apostrophes.
La nouvelle norme du C (C99) permet d'utiliser les commentaires
du C++ : ils commencent par // et se terminent en fin de
ligne. Ce type de commentaire n'est pas encore support� partout,
donc mieux vaut ne pas s'en servir si on veut faire du code vraiment
portable, m�me si avant C99, des compilateurs acceptaient ce type
de commentaires.
13.5 Comment utiliser #include ?
#include comporte trois formes principales :
#include <fichier>
#include "fichier"
#include tokens
La premi�re forme recherche le fichier indiqu� dans les
r�pertoires syst�me ; on peut les 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 r�pertoire
syst�me classique est /usr/include/. Une fois le fichier
trouv�, tout se passe comme si son contenu �tait tel quel dans le
code source, l� o� se trouve le #include.
La deuxi�me forme recherche le fichier dans le r�pertoire
courant. Si le fichier ne s'y trouve pas, il est ensuite cherch�
dans les r�pertoires syst�mes, comme dans la premi�re forme.
La troisi�me forme, o� ce qui suit le #include ne
correspond pas � une des deux formes pr�c�dentes, commence par
effectuer tous les remplacements de macros dans la suite de
tokens, et le r�sultat doit �tre d'une des deux formes
pr�c�dentes.
Si le fichier n'est pas trouv�, c'est une erreur, et la
compilation s'arr�te. On notera que si on se sert d'habitude de
#include pour inclure des fichiers d'en-t�te (tels que
stdio.h), ce n'est pas une obligation.
13.6 Comment �viter l'inclusion multiple d'un fichier ?
Il arrive assez souvent qu'un fichier inclus en incluse un autre,
qui lui-m�me en inclut un autre, etc. On peut arriver � des
boucles, qui peuvent conduire � des redoublements de d�clarations
(donc des erreurs, pour typedef par exemple), voire des
boucles infinies (le compilateur finissant par planter).
Pour cela, le moyen le plus simple est de � prot�ger � chaque
fichier par une construction de ce genre :
#ifndef FOO_H_
#define FOO_H_
/* ici, contenu du fichier */
#endif /* FOO_H_ */
Ainsi, m�me si le fichier est inclus plusieurs fois, son contenu
ne sera actif qu'une fois. Certains pr�processeurs iront m�me
jusqu'� reconna�tre ces structures, et ne pas lire le fichier
si la macro de protection (ici FOO_H_) est encore
d�finie.
Il y a eut divers autres moyens propos�s, tels que #import
ou #pragma once, mais ils ne sont pas standards, et encore
moins r�pandus.
13.7 Comment d�finir une macro ?
On utilise #define. La forme la plus simple est la
suivante :
#define FOO 3 + 5
Apr�s cette d�claration, toute occurrence de l'identificateur
FOO est remplac�e par son contenu (ici 3 + 5).
Ce remplacement est syntaxique et n'a pas lieu dans les cha�nes
de caract�res, ou dans les commentaires (qui n'existent d�j� plus,
de toutes fa�ons, � cette �tape).
On peut d�finir un contenu vide. La macro sera remplac�e, en cas
d'utilisation, par rien. Le contenu de la macro est une suite
de tokens du C, qui n'a pas � vouloir dire quelque
chose. On peut d�finir ceci, c'est valide :
#define FOO (({/coucou+}[\}+ "zap" 123
La tradition est d'utiliser les identificateurs en majuscules pour
les macros ; rien n'oblige cependant � appliquer cet usage. Les
r�gles pour les identificateurs de macros sont les m�mes que pour
celles du langage.
13.8 Comment d�finir une macro avec des arguments ?
On fait ainsi :
#define FOO(x, y) ((x) + (x) * (y))
Ceci d�finit une macro qui attend deux arguments ; notez qu'il
n'y a pas d'espace entre le nom de la macro (FOO) et la
parenth�se ouvrante. Toute invocation de la macro par la suite
est remplac�e par son contenu, les arguments l'�tant aussi.
Ainsi, ceci :
FOO(bla, "coucou")
devient ceci :
((bla) + (bla) * ("coucou"))
(ce qui ne veut pas dire grand'chose en C, mais le pr�processeur
n'en a cure). Le premier argument est remplac� deux fois, donc,
s'il a des effets de bord (appel d'une fonction, par exemple),
ces effets seront pr�sents deux fois.
Si la macro est invoqu�e sans arguments, elle n'est pas
remplac�e. Cela permet de d�finir une macro sens�e remplacer une
fonction, mais en conservant la possibilit� d'obtenir un pointeur
sur la fonction.
Ainsi :
int min(int x, int y) { return x < y ? x : y; }
#define min(x, y) ((x) < (y) ? (x) : (y))
min(3, 4); /* invocation de la macro */
(min)(3, 4); /* invocation de la fonction, via le pointeur */
C'est une erreur d'invoquer une macro � argument avec un nombre
incorrect d'arguments. En C99, on peut utiliser des arguments
vides ; en C89, c'est flou et mieux vaut �viter.
13.9 Comment faire une macro avec un nombre variable d'arguments ?
Ce n'est possible qu'en C99. On utilise la construction suivante :
#define error(l, ...) { \
fprintf(stderr, "line %d: ", l); \
fprintf(stderr, __VA_ARGS__); \
}
Ceci d�finit une macro, qui attend au moins un argument ; tous
les arguments suppl�mentaires sont concat�n�s, avec leurs virgules
de s�paration, et on peut les obtenir en utilisant
__VA_ARGS__. Ainsi, ceci :
error(5, "boo: '%s'\n", bla)
sera remplac� par ceci :
{ fprintf(stderr, "line %d: ", 5); \
fprintf(stderr, "boo: '%s'\n", bla); }
Ce m�canisme est support� par les derni�res versions de la plupart
des compilateurs C actuellement d�velopp�s ; mieux vaut l'�viter
si le code doit aussi fonctionner avec des compilateurs un peu
plus anciens.
Il existe aussi des extensions sur certains compilateurs. Par exemple,
sous GCC, le code suivant est �quivalent � l'exemple pr�c�dent :
#define error(l, format...) { \
fprintf(stderr, "line %d: ", l); \
fprintf(stderr, format); \
}
Une autre m�thode consiste � utiliser le parenth�sage des arguments :
#define PRINTF(s) printf s
...
PRINTF (("Vitesse du vent %d m/s", v));
13.10 Que font les op�rateurs # et ## ?
L'op�rateur # permet de transformer un argument d'une
macro en une cha�ne de caract�res. On fait ainsi :
#define BLA(x) printf("l'expression '%s' retourne %d\n", #x, x);
BLA(5 * x + y);
ce qui donne le r�sultat suivant :
printf("l'expression '%s' retourne %d\n", "5 * x + y", 5 * x + y);
Les �ventuelles cha�nes de caract�res et backslashs dans
l'argument sont prot�g�s par des backslashes, afin de
constituer une cha�ne valide.
L'op�rateur ## effectue la concat�nation de deux
tokens. Si le r�sultat n'est pas un
token valide, alors c'est une erreur ; mais certains
pr�processeurs sont peu stricts et se contentent de re-s�parer
les tokens.
On l'utilise ainsi :
#define FOO(x, y) x ## y
FOO(bar, qux)();
qui donne ceci :
barqux();
13.11 Une macro peut-elle invoquer d'autres macros ?
Oui. Mais il est pr�vu un m�canisme qui emp�che les boucles
infinies.
Tout d'abord, les invocations de macros ne sont constat�es que
lors de l'utilisation de la macro, pas lors de sa d�finition.
Si on fait ceci :
#define FOO BAR
#define 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 pr�c�d� d'un # ou pr�c�d� ou suivi
d'un ##), il est d'abord examin� par le pr�processeur,
qui, s'il y reconna�t une macro, la remplace. Une fois les
arguments trait�s, le pr�processeur les implante � leur place
dans la suite de tokens g�n�r�s par la macro, et g�re
les op�rateurs # et ##.
� la suite de cette op�ration, le r�sultat est de nouveau examin�
pour rechercher d'autres remplacements de macros ; mais si une
macro est trouv�e, alors qu'on est dans le remplacement de ladite,
cette macro n'est pas remplac�e. Ceci �vite les boucles infinies.
Je sais, c'est compliqu�. Quelques exemples :
#define FOO coucou BAR
#define BAR zoinx FOO
FOO
FOO est remplac�e par coucou BAR, et le
BAR r�sultant est remplac� par zoinx FOO. Ce
FOO n'est pas remplac�, parce qu'on est dans le
remplacement de FOO. Donc, on obtient coucou zoinx
FOO.
Un autre exemple, plus tordu :
#define FOO(x) x(5)
FOO(FOO);
La macro FOO est invoqu�e ; elle attend un argument, qui
est FOO. Cet argument est d'abord examin� ; il y a
FOO dedans, mais non suivi d'une parenth�se ouvrante
(l'argument est examin� tout seul, ind�pendamment de ce qui le
suit lors de son usage), donc le remplacement n'a pas
lieu. Ensuite, l'argument est mis en place, et on obtient
FOO(5). Ce r�sultat est r�examin� ; cette fois,
FOO est bien invoqu�e avec un argument, mais on est dans
le deuxi�me remplacement, � l'int�rieur de la macro FOO,
donc on ne remplace pas.
Le r�sultat est donc : FOO(5);
Si vous voulez utiliser ce m�canisme, allez lire une douzaine
de fois la documentation du GNU cpp, et surtout le
paragraphe 12 de l'annexe A du Kernighan &
Ritchie (2�me �dition).
13.12 Comment red�finir une macro ?
On peut red�finir � l'identique une macro ; ceci est pr�vu pour
les fichiers d'en-t�te inclus plusieurs fois. Mais il est en
g�n�ral plus sain de prot�ger ses fichiers contre l'inclusion
multiple (cf. 13.6).
Red�finir une macro avec un contenu ou des arguments diff�rents,
est une erreur. Certains pr�processeurs laxistes se contentent
d'un avertissement. La bonne fa�on est d'abord d'ind�finir la
macro via un #undef. Ind�finir une macro qui n'existe
d�j� pas, n'est pas une erreur.
13.13 Que peut-on faire avec #if ?
#if permet la compilation conditionnelle. L'expression
qui suit le #if est �valu�e � la compilation, et, suivant
son r�sultat, le code qui suit le #if jusqu'au prochain
#endif, #elif ou #else est
�valu�, ou pas.
Quand le code n'est pas �valu�, les directives de pr�processeur
ne le sont pas non plus ; mais les #if et similaires sont
n�anmoins compt�s, afin de trouver la fin de la zone non compil�e.
Lorsque le pr�processeur rencontre un #if, il :
- r�cup�re l'expression qui suit le #if
- remplace les defined MACRO et
defined(MACRO) par la constante 1 si
la macro nomm�e est d�finie, 0 sinon
- remplace les macros qui restent dans l'expression
- remplace par la constante 0 tous les
identificateurs qui restent
- �value l'expression
L'expression ne doit comporter que des constantes enti�res (donc,
�ventuellement, des constantes caract�res), qui sont promues au
type (unsigned) long (en C89) ou (u)intmax_t (en
C99). Les flottants, les pointeurs, l'acc�s � un tableau, et
surtout l'op�rateur sizeof ne sont pas utilisables par le
pr�processeur.
Il n'est pas possible de faire agir un #if suivant
sizeof(long), pour reprendre un desiderata
fr�quent. Par ailleurs, les constantes de type caract�re n'ont pas
forc�ment la m�me valeur pour le pr�processeur et pour le
compilateur.
13.14 Qu'est-ce qu'un #pragma ?
C'est une indication pour le compilateur. Le pr�processeur
envoie cette directive sans la modifier. Le standard C89 ne
pr�voit aucune directive standard, mais le pr�processeur
comme le compilateur sont sens�s ignorer les directives
inconnues.
Le C99 d�finit trois #pragma qui permettent d'ajuster
le comportement du compilateur, quant au traitement des nombres
flottants et complexes.
13.15 Qu'est-ce qu'un #assert ?
C'est une extension g�r�e par GNU et (au moins) le
compilateur Sun (Workshop Compiler, pour
Solaris). C'est une sorte d'alternative �
#ifdef, avec une syntaxe plus agr�able.
C'est � �viter, car non standard.
13.16 Comment d�finir proprement une macro qui comporte plusieurs statements ?
On peut ouvrir un bloc, car tout statement est
rempla�able, en C, par un bloc, mais cela pose des probl�mes avec
le ; terminal du statement. La mani�re
recommand�e est la suivante :
#define foo(x) do { f(x); printf("coucou\n"); } while (0)
On peut ainsi l'utiliser comme ceci :
if (bar) foo(1); else foo(2);
Si on avait d�fini foo sans le do et le
while (0), le code ci-dessus aurait provoqu� une erreur
de compilation, car le else serait s�par� du if
par deux statements : le bloc et le statement vide, termin� par le
point-virgule.
13.17 Comment �viter les effets de bord ?
Les macros peuvent �tre dangereuses si l'on ne fait
pas attention aux effets de bord. Par exemple si l'on
a le code suivant :
#define MAX 3 + 5
int i = MAX;
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 �crire la macro
ainsi :
#define MAX (3 + 5)
Dans certains cas, une macro repr�sente une expression C
compl�te. Il est alors plus coh�rent de placer des parenth�ses
vides pour simuler une fonction. Et dans ce cas il ne 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 fa�on de les utiliser. Ainsi la macro :
#define CALCUL(x, y) (x + y * 2)
a des effets de bord suivant la fa�on de l'utiliser :
int i = CALCUL(3, 5);
donnera bien un r�sultat de 13, alors que le m�me r�sultat
serait attendu avec :
int i = CALCUL(3, 2 + 3);
qui donne 11. Pour �viter cela, il suffit de placer des
parenth�ses sur les arguments de la macro:
#define CALCUL(x, y) ((x) + (y) * 2)
Un effet de bord qui ne peut �tre contourn� survient quand
la macro utilise plusieurs fois une variable :
#define MAX(x, y) ((x) > (y) ? (x) : (y))
Si on utilise la macro comme cela :
i = MAX(j, k);
On obtiendra un r�sultat correct, alors qu'avec :
i = MAX(j++, k++);
une des variables j ou k sera incr�ment�e 2
fois. Pour �viter ce genre de comportement, il faut remplacer la
macro par une fonction, de pr�f�rence inline (C99) :
inline int max(int x, int y)
{
return x > y ? x : y;
}
En r�gle g�n�rale, quand on utilise une fonction avec un nom
en majuscule (comme MAX), on s'attend � ce que ce soit en fait
une macro, avec les effets de bord qui en d�coulent. Alors que
si le nom est en minuscule, il s'agit s�rement d'une v�ritable
fonction. Cette r�gle n'est h�las pas g�n�rale et donc il
convient de v�rifier le v�ritable type d'une fonction si l'on
ne veut pas �tre surpris lors de son utilisation.
13.18 Le pr�processeur est-il vraiment utile ?
La r�ponse est oui. Il existe un mouvement qui voudrait faire
dispara�tre le pr�processeur, en rempla�ant les #define
par des variables constantes, et avec un quelconque artifice
syntaxique pour importer les d�clarations d'un fichier d'ent�te.
Il s'av�re qu'on a vraiment besoin d'un m�canisme polymorphe
(comme une fonction qui accepterait plusieurs types diff�rents),
et que seules les macros apportent ce m�canisme en C. Les
anti-pr�processeurs acharn�s parlent d'adopter le m�canisme
des templates du C++, mais �a ne risque pas d'arriver de sit�t.
Dans la vie de tous les jours, l'utilisation du pr�processeur,
avec quelques #include et des #define sans
surprise, ne pose pas de probl�me particulier, ni de maintenance,
ni de portabilit�.
13.19 Approfondir le sujet.
Outre le Kernighan & Ritchie, qui comporte
quelques pages tr�s bien faites sur le sujet, on peut lire la
documentation au format info du GNU cpp ;
elle est assez partiale dans certains cas (condamnation explicite
des trigraphes, par exemple) mais assez riche en enseignements.
Pour le reste, chaque compilateur C vient avec un pr�processeur,
et il existe quelques pr�processeurs ind�pendants (un �crit
par Dennis Ritchie en personne, qu'on voit inclus dans
lcc, et aussi ucpp, une oeuvre � moi :
<http://www.di.ens.fr/~pornin/ucpp/>).
14. Fonctions de la biblioth�que
14.1 Comment convertir un nombre en une cha�ne de caract�res ?
Il suffit d'utiliser 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 qu'il faut r�server
assez de place pour la cha�ne r�sultat. 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 plus grand (>
99999).
En C99, la fonction snprintf permet d'�viter les d�bordements :
char sz[4];
n = snprintf(sz, sizeof sz, "%d", 123);
if (n < 0 || n >= sizeof sz)
erreur();
14.2 Comment convertir une cha�ne en un nombre ?
Si le nombre esp�r� est un entier, il faut utiliser la fonction
strtol(). Elle convertit une cha�ne en un entier long,
dans une base donn�e.
Si le nombre est un r�el (float ou double),
alors la fonction strtod() fera tr�s bien l'affaire.
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 � pr�f�rer.
14.3 Comment d�couper une cha�ne ?
La fonction strtok() est faite pour �a !
char sz1[] = "this is,an example ; ";
char sz2[] = ",; ";
char *p;
p = strtok(sz1, sz2);
if (p != NULL) {
puts(p);
while ((p = strtok(NULL, sz2)) != NULL) {
puts(p);
}
}
Attention, la fonction strtok() souffre au moins des
probl�mes / caract�ristiques suivants :
- Elle fusionne les d�limiteurs adjacents. En cas
d'utilisation d'une virgule comme s�parateur, "a,,b,c" est
s�par�e en trois �l�ments et non quatre.
- Le caract�re du d�limiteur est perdu, car il est
remplac� par un caract�re nul (0).
- Elle modifie la chaine qu'elle analyse. C'est un
d�faut, car cela oblige � faire une copie de la chaine en cas
d'utilisation ult�rieure. Cela signifie aussi que l'on ne peut
pas s�parer une chaine litt�rale avec.
- On ne peut utiliser qu'un appel de cette fonction � la
fois. Si une s�quence de strok() est en cours, et
qu'une autre d�marre, l'�tat de la premi�re est perdue. Ce
n'est pas grave pour les petits programmes, mais il est facile
de se perdre dans la hi�rarchie des fonctions imbriqu�es dans
des programmes plus importants. En d'autre termes,
strtok() brise les principes de l'encapsulation.
Dans des cas simples, on pourra utiliser la fonction
strchr().
14.4 Pourquoi ne jamais faire fflush(stdin) ?
La fonction fflush() a un comportement d�fini
uniquement sur les flux ouverts en �criture tels que
stdout. Il est possible que sur votre syst�me, appliquer
cette fonction � stdin soit possible, mais c'est alors
une extension non standard. Le comportement est ind�termin�, et
impr�visible.
Il faut bien comprendre que stdin n'est pas forc�ment
reli� au clavier, mais peut �tre rattach� � un r�seau, un fichier,
etc.
14.5 Comment vider le buffer associ� � stdin ?
Une bonne mani�re est de lire sur le flux tant qu'il n'est pas
vide, avec les fonctions habituelles comme fgets() ou
getchar().
Voici un exemple avec cette derni�re :
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 buffer, notamment le '\n' final.
14.6 Pourquoi mon printf() ne s'affiche pas ?
Le flux standard stdout, sur lequel �crit
printf() est bufferis�. C'est � dire que les caract�res
sont �crits dans un tampon (une zone m�moire). Lorque celui-ci est
plein, ou lorsqu'une demande explicite est faite, il est vid� dans
le flux proprement dit (sur l'�cran g�n�ralement).
Tant que le buffer n'est pas vid�, rien ne s'affiche.
Pour vider le buffer, il y a trois possibilit�s :
- Le buffer est plein
- Il est vid� explicitement par l'appel de la
fonction fflush()
(cf. 14.4)
- La cha�ne de caract�res se termine par un
'\n'
14.7 Comment obtenir l'heure courante et la date ?
Il faut simplement utiliser les fonctions time(),
ctime() et/ou
localtime(), qui contrairement � leurs noms donnent
l'heure 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 diff�rence entre deux dates ?
Il faut simplement utiliser la fonction difftime().
Cette fonction prend deux time_t en param�tres et renvoie
un double.
14.9 Comment construire un g�n�rateur de nombres al�atoires ?
Ce n'est pas possible.
La biblioth�que standard inclut un g�n�rateur pseudo-al�atoire,
la fonction rand().
Toutefois, l'impl�mentation d�pend du syst�me, et celle-ci n'est
g�n�ralement pas tr�s bonne (en terme de r�sultats statistiques).
Si rand() ne vous suffit pas (simulation num�rique ou
cryptologie), il vous faudra regarder du cot� de
biblioth�ques math�matiques, 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) et le volume 2 de TAoCP
(cf. 3.9).
14.10 Comment obtenir un nombre pseudo-al�atoire dans un intervalle ?
La m�thode la plus simple � faire,
rand() % N
qui renvoie un nombre entre 0 et N-1 est aussi
la moins bonne.
En effet, les bits de poids faibles ont une distribution tr�s peu
al�atoire. Par exemple, le bit de poids le plus faible a une
distribution qui peut �tre celle-ci sur un mauvais g�n�rateur :
0 1 0 1 0 1 0 1 0 1 ...
Voici la m�thode pr�conis�e dans Numerical Recipes
(cf.4.5) :
(int)((double)rand() / ((double)RAND_MAX + 1) * N)
RAND_MAX est d�fini dans stdlib.h,
et N doit �tre plus petit que RAND_MAX.
14.11 � chaque lancement de mon programme, les nombres pseudo-al�atoires sont toujours les m�mes ?
C'est normal, et c'est fait expr�s. Pour contrer cela, il faut
utiliser une graine pour le g�n�rateur qui change � chaque
lancement du programme. C'est la fonction srand() qui
s'en charge.
On peut utiliser l'heure syst�me, avec time(), de la
facon suivante :
srand(time(NULL));
Notez qu'il est peu utile d'appeler la fonction srand()
plus d'une fois par programme.
14.12 Comment savoir si un fichier existe ?
En C ISO, le seul moyen de savoir si un fichier existe, c'est
d'essayer de l'ouvrir.
{
FILE *fp = fopen ("fichier.txt", "r");
if (fp == NULL)
{
fputs ("Le fichier n'existe pas,\n"
"ou vous n'avez pas les droits necessaires\n"
"ou il est inaccessible en ce moment\n"
, stderr);
}
else
{
/* ... operation sur le fichier */
fclose(fp);
}
}
Dans la norme POSIX, il existe la fonction
access(), mais certains syst�mes n'impl�mentent pas
cette interface.
14.13 Comment conna�tre la taille d'un fichier ?
Malheureusement, les fonctions stat() et
fstat() de la norme POSIX ne sont pas
reprises dans la norme ISO.
La seule solution standard est d'utiliser fseek() et
ftell().
Toutefois, cela ne marche pas pour les tr�s gros fichiers
(sup�rieurs � LONG_MAX).
14.14 Comment lire un fichier binaire proprement ?
Il faut ouvrir le fichier en mode � binaire �, en passant la cha�ne
"rb"
en mode d'ouverture � la fonction fopen().
Cela �vite les transformations inopportunes et les probl�mes des
caract�res de contr�le.
De m�me, pour �crire dans un fichier binaire, on utilise le mode
"wb".
14.15 Comment marquer une pause dans un programme ?
Il n'y a pas de fonction standard pour cela.
Il existe toutefois la fonction sleep() en
POSIX, elle provoque une attente passive pour une
dur�e donn�e en secondes.
14.16 Comment trier un tableau de cha�nes ?
La fonction qsort() est une bonne fonction de tri,
qui impl�mente le Quick Sort.
Le plus 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 g�n�riques pour
qsort().
p1 et p2 sont des pointeurs sur des cha�nes.
Un tableau de cha�nes doit �tre pris au sens d'un tableau de
pointeurs vers des char *.
L'appel � qsort() ressemble alors � :
qsort(tab, sizeof tab, sizeof *tab, pstrcmp);
14.17 Pourquoi j'ai des erreurs sur les fonctions de la biblioth�que, alors que j'ai bien inclus les ent�tes ?
Les en-t�tes (les .h) ne contiennent que les
prototypes des fonctions.
Le code proprement-dit de ces fonctions se trouve dans des
fichiers objets. Ce code doit �tre � li� � au tien. Cela est fait
par un �diteur de liens.
Pour certaines fonctions, il faut sp�cifier explicitement �
l'�diteur de liens o� il peut les trouver (et ce particuli�rement
pour les fonctions non-standard).
Par exemple, sous Unix, pour utiliser les fonctions math�matiques,
il faut g�n�ralement lier le programme avec la biblioth�que ad�quate :
cc -lm monfic.o -o monprog
15. Styles
15.1 Comment bien programmer en C ?
La chose la plus importante est de commenter un programme.
Il ne s'agit pas de d�crire en fran�ais tout ce que fait chaque
ligne de code, mais de pr�ciser le fonctionnement des op�rations
complexes, d'expliquer le r�le des variables, de dire � quoi
servent les fonctions. Choisir des noms de variables et de
fonctions explicites est une bonne fa�on de commenter un
programme.
Tout morceau de code qui n'est pas standard doit �tre abondamment
comment� afin de rendre le portage vers d'autres cibles le moins
fastidieux possible, l'id�al �tant d'utiliser des macros.
Il est �galement important de bien structurer son programme en
modules, puis en fonctions.
Certains vont jusqu'� dire qu'une fonction ne doit pas d�passer la
taille d'un �cran.
Les d�clarations et prototypes doivent �tre regroup�s dans des
fichiers d'en-t�tes, avec les macros.
Enfin, il est tr�s important de bien pr�senter le code, avec une
indentation judicieuse et des sauts de
ligne. (cf. 15.2)
Il est g�n�ralement admis que les lignes ne doivent pas d�passer
80 caract�res.
Pour le reste, c'est une histoire de go�t.
15.2 Comment indenter proprement du code ?
L'indentation du code est une chose essentielle pour la
lisibilit�. Certaines personnes utilisent des tabulations, ce qui
est une mauvaise habitude. La largeur de ces tabulations varie
d'un �diteur � un autre. Des �diteurs remplacent les tabulation
par un nombre d'espaces fixe et d'autres encore utilisent des
tabulations de taille variable. Ne parlons pas des imprimantes ou
des lecteurs de news...
Tout ceci rend l'utilisation des tabulations difficile.
Pour �viter tout probl�me, et am�liorer la lisibilit� du code,
il faut utiliser uniquement des espaces.
Un �diteur correct doit pouvoir g�n�rer un nombre d'espaces fixe
(voire une indentation automatique) lorsqu'on appuye sur la touche
<TAB> (ou autre raccourci).
Personnellement, je r�gle � 4 espaces par tabulation.
15.3 Quel est le meilleur style de programmation ?
Comme vous vous en doutez, il n'y en a pas.
Le plus important est d'en avoir un, et de le suivre.
Quand on utilise un type d'indentation ou un style de nommage,
il faut l'utiliser dans tout le programme (voire dans tout ses
programmes). C'est la r�gularit� qui donne la lisibilit�.
Il existe des styles de programmations fr�quemment utilis�s en C,
comme les styles K&R ou GNU.
Le style K&R est le style � historique �, et
c'est pourquoi il est tr�s utilis�.
Le style GNU est utilis� pour tous les projets de la
Free Software Fundation.
Sur le site FTP <ftp://caramba.cs.tu-berlin.de>,
le repertoire /pub/doc/style contient quelques
documents int�ressants sur la question.
15.4 Qu'est-ce que la notation hongroise ?
C'est une convention de nommage des objets, invent�e par Charles
Simonyi. Le principe est de faire pr�c�der le nom des
variables par un identificateur de type.
Par exemple, une cha�ne de caract�re repr�sentant un nom sera
nomm�e szname, sz signifiant � string
zero �, ou cha�ne termin�e par un '\0'.
Personnellement, je ne trouve pas cette convention toujours
pratique. Le nom de la variable doit avant tout refl�ter son r�le.
15.5 Pourquoi certains �crivent-ils if(0==x) et non if(x==0) ?
Il arrive souvent que l'on �crive = au lieu de
==. Comme 0 n'est pas une lvalue
(cf. 10.10), cette �tourderie
provoquera une erreur, simple � d�tecter.
Dans le m�me genre, on peut �crire while (0 == x).
Certains compilateurs pr�viennent lorsque l'on fait une
affectation l� ou est attendu un test. C'est le cas de GNU
CC, avec l'option -Wall.
Lorsque l'on �crit while ( c = fct() ), certains
compilateurs r�lent en croyant que l'on s'est tromp� entre le
= et le ==. Pour �viter cela, il suffit de
rajouter un paire de parenth�ses.
while ( (c= fct()) ) {
/* ... */
}
15.6 Pourquoi faut-il mettre les '{' et '}' autour des boucles ?
C'est une pr�caution contre les erreurs du genre :
for(i = 0; i < N; i++);
tab[i] = i;
De plus, cela permet une plus grande simplicit� dans l'�volution
du code. En effet, les programmes ont tendance � s'�paissir avec
le temps.
15.7 Pourquoi certains disent-ils de ne jamais utiliser les goto ?
Le goto est une instruction qui permet de casser l'aspect
structur� d'un programme. Des goto mal utilis�s
permettent de rendre un code totalement illisible (code
spaghetti), d'autant plus qu'avec les structures de
boucles traditionnelles, on peut souvent s'en passer.
Toutefois, il arrive parfois que l'utilisation d'un goto
rende le code plus propre. C'est le cas, par exemple, des sorties
de boucles imbriqu�es en cas d'erreur.
Cela rejoint le cas plus g�n�ral des gestions d'exceptions
internes.
Poser comme r�gle de ne jamais utiliser les
goto est une absurdit�.
Par contre, avertir le programmeur de l'utiliser avec parcimonie,
et avec beaucoup de pr�cautions me semble une bonne chose.
15.8 Pourquoi commenter un #endif ?
#endif ne peut �tre suivi par autre chose qu'un
commentaire. On commente donc
pour savoir � quelle directive il correspond :
#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 diff�rents styles ?
Le document suivant contient des r�gles de base � suivre pour
programmer proprement :
<ftp://ftp.laas.fr/pub/ii/matthieu/c-superflu/c-superflu.pdf>
Sachez �galement qu'il existe un programme indent
issu de BSD qui r�indente automatiquement du code,
suivant un style donn�. Les options classiques de la version
GNU de cet utilitaire sont -kr (pour le
style d�crit dans K&R), -gnu (pour le
style utilis� dans les projets GNU) ou encore
-orig (pour le style BSD).
Sous Unix, on trouve �galement la commande
cb avec l'option -sj pour avoir le style
K&R.
15.10 Comment bien structurer son programme ?
Il n'y a pas de r�ponse d�finitive, mais voici une piste :
- Suite � l'analyse (conception) il est possible de d�couper
le projet en une multitude de syst�mes et de sous-syst�mes.
- Chaque sous-syst�me fait l'objet d'un module compos� d'un
fichier source(xxx.c) et d'un fichier d'en-t�te (xxx.h).
- Les syst�mes sont organis�s hi�rarchiquement (arbre) de
fa�on � �viter les d�pendences crois�es.
Evidement, tout �a est peu th�orique. De plus, il existes des
techniques avanc�es (ADT, callback) qui permettent les appels
crois�s propres. (c � d sans d�pendances crois�es). L'avantage est
que chaque module est testable individuellement (et le plus
souvent r�utilisable).
La phase suivante du d�veloppement est l'int�gration. Elle consiste
� mettre la colle qui va bien pour faire tenir tout les modules
ensembles et � tester le fonctionnement de l'ensemble.
Nous ne traitons pas ici des diff�rentes m�thodes d'analyse et de
conception. Rappelons toutefois les 5 phases (en 'V') de
d�veloppement d'un projet :
1 - Sp�cification 5 - Validation
\ /
2 - Conception 4 - Integration
\ /
3 - Codage et test unitaire
Voir � ce sujet les questions 9.3
et 13.5.
Merci � Emmanuel Delahaye pour cette r�ponse.
16. Autres
16.1 Comment rendre un programme plus rapide ?
Il y a deux raisons possibles � la � lenteur � d'un programme.
La premi�re vient de l'�criture du code lui-m�me,
les entr�es/sorties, les allocations dynamiques,
de nombreux appels de fonctions, des boucles, etc.
Le programmeur a g�n�ralement peu int�r�t � modifier son code,
tout au plus pourra-t-il remplacer les petites fonctions le plus
souvent appel�es par des macros et tenter de limiter les
boucles. On pourra aussi am�liorer les entr�es/sorties et les
allocations si c'est possible. Il reste enfin les options de
compilation sur lesquelles on peut jouer.
L'autre raison vient de la complexit� th�orique des algorithmes
utilis�s. Dans ce dernier cas, il faut chercher un meilleur
algorithme. Cet aspect est d�velopp� par exemple dans
<http://www.enseignement.polytechnique.fr/profs/informatique/Jean-Jacques.Levy/poly/polyx-cori-levy.ps.gz>
Il existe des outils de profilage de programmes. Il
s'agit de compiler les sources avec une biblioth�que puis de
lancer le programme. On lance alors un lociciel associ� � la
biblioth�que. Le r�sultat est un fichier o� est d�taill� le temps
pass� dans chaque fonction,
le nombre d'appels, etc. Sur Unix-like,
le projet GNU propose gprof
(cf. 4.6).
Rappelons tout de m�me que la vitesse d'ex�cution d'un programme
(hors probl�mes d'algorithmique) est peu souvent critique, et
qu'il est bien plus important de fournir un code lisible.
16.2 Quelle est la diff�rence entre byte et octet ?
L'unit� de m�moire �l�mentaire du C est le byte.
Le byte au sens anglo-saxon et donc pas
l'octet au sens francophone (caract�re).
En fait un byte correspond � un caract�re non-sign�
(unsigned char), lequel peut prendre plus (ou moins) de 8
bits. En principe, en fran�ais, on parle dans ce cas de
multiplet (et peut-�tre bient�t de
codet) comme traduction officielle de
byte dans ce sens.
(Merci � Antoine Leca).
16.3 Peut-on faire une gestion d'exceptions en C ?
Oui, c'est 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 d'arguments\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 coefficient binomial des deux
arguments ; main() appelle comb() qui
appelle fact().
Ces fonctions v�rifient un peu les arguments, et on voudrait
renvoyer une erreur en cas de probl�me ; mais :
- Je renvoie d�j� le r�sultat, il faudrait qu'il y ait des
r�sultats � impossibles � pour y coder les erreurs ; c'est le cas
ici (le r�sultat est toujours sup�rieur ou �gal � 1) mais �a
demande une analyse math�matique pas toujours facile.
- Je ne veux pas tester les codes d'erreurs � chaque
invocation d'une fonction. Cela peut devenir lourd syntaxiquement,
et �a consomme du temps CPU.
setjmp() sauvegarde l'�tat du programme au moment de
l'appel, et renvoie 0.
longjmp() remplace le contenu de la pile d'ex�cution
par la sauvegarde, et le programme se trouve � nouveau � l'endroit
de l'appel � setjmp(). Celle-ci renvoie alors une
valeur pass�e en param�tre de longjmp() (dans
l'exemple, 1 pour une erreur dans fact() et
2 pour une erreur dans comb()).
La m�thode pr�sent�e ici est assez rustique. Il existe des
m�canismes de POO [5]
en C bien plus �volu�s. Vous pouvez � ce sujet
allez voir l'excellent document :
<http://ldeniau.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.
16.4 Comment g�rer le num�ro de version de mon programme ?
Serge Paccalin propose la chose suivante :
#define STR_(a) #a
#define STR(a) STR_(a)
#define VERSION 4
printf("This is version " STR(VERSION) " of the program\n");
En effet, quelque chose comme :
#define STR(a) #a
#define VERSION a
printf("This is version " STR(VERSION) " of the program\n");
fait intervenir la concat�nation des cha�nes trop t�t ce qui fait
que le r�sultat de cette derni�re s�quence renvoie :
This is version VERSION of the program
16.5 Pourquoi ne pas mettre de `_' devant les identifiants ?
Well well well, ce n'est pas si facile.
Les vrais identificateurs r�serv�s sont :
- les mots cl�s tels que if, for,
switch, long, ...
- les identificateurs commen�ant par deux `_'
- les identificateurs commen�ant par un `_'
suivi d'une lettre majuscule.
Ensuite, il y a les headers standards et la biblioth�que. Les
headers sont libres d'utiliser des identificateurs commen�ant par
un `_' et suivis d'une lettre minuscule, comme `_liste', mais
c'est pour d�finir quelque chose qui a un � file scope �,
c'est-�-dire une port�e globale � la � translation unit � (le
fichier source C et les fichiers qu'il inclut). Donc, globalement,
on ne doit pas s'en servir pour d�finir quelque chose qui a ce �
file scope �.
�a 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 autoris�es :
void f(int _x[]) {
typedef int _foo;
struct _bar {
int x;
char *y;
};
extern void _g(void);
}
struct qux {
long _truc;
};
J'attire l'attention du public �bahi sur les quatre points
suivants :
- En d�finissant des identificateurs � port�e r�duite (bloc,
prototype, fonction), on peut masquer des identificateurs d�finis
par les headers standards, identificateurs qui pouvaient
intervenir dans des macros d�finies dans lesdits headers ; comme
toutes les fonctions standards peuvent �tre surcharg�es par des
macros, � l'int�rieur d'un bloc o� on a jou� � d�finir un
identificateur de type _foo, toute utilisation d'une
facilit� fournie par un header peut d�clencher un � undefined
behaviour � (par exemple, l'ordinateur utilise spontan�ment son
modem pour t�l�phoner � la belle-m�re du programmeur et l'inviter
� venir d�ner � la maison).
- 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 d�finie quelque part, avec
un external linkage, ce qui ne peut pas se faire en C
standard. Donc cette d�claration, quoique valide, doit avoir un
pendant dans une autre translation unit, qui ne peut pas �tre
fabriqu� de fa�on standard. Pour compl�ter, rajoutons que la
d�finition n'a besoin d'exister que si on se sert effectivement de
la fonction. Donc on est en fait autoris� � faire une d�claration
inutile qui pourrait faire planter des impl�mentations non
conformes mais courantes. How useful.
- Quand bien m�me on aurait le droit, rares sont les
impl�mentations compl�tement conformes de ce point de vue. Par pur
pragmatisme, on �vite donc de jouer avec des identificateurs
commen�ant par un `_' suivi d'une lettre minuscule.
- Chaque header apporte ses propres restrictions ; par
exemple, <ctype.h> peut d�clarer n'importe quel
identificateur qui commence par "is" ou "to" suivi d'une lettre
minuscule. Ces identificateurs sont r�serv�s pour ce qui est de
l'external linkage, ce qui veut dire que m�me si on n'inclut pas
<ctype.h>, on ne doit pas d�finir, entre autres, de
variable globale "iszoinx" qui ne soit pas prot�g� par le mot cl�
static.
16.6 � quoi peut servir un cast en (void) ?
Il y a principalement deux utilit�s � caster une expression en
(void).
La premi�re utilisation est pour indiquer explicitement au
compilateur qu'une valeur est ignor�e, comme au retour d'une
fonction.
Par exemple, il arrive souvent d'utiliser la fonction
printf() sans utiliser ni m�me tester la valeur de
retour.
Ecrire l'appel � printf() ainsi
(void)printf("%s\n", "Un message � la con") ;
est une mani�re de dire au compilateur, et aux lecteurs du code,
que je sais que printf() renvoie une valeur, mais que
j'ai d�cid� de l'ignorer. Cela peut �tre utile pour des
utilitaires de v�rification de code, comme lint.
La seconde utilisation est dans des d�finitions de macro.
Voici un exemple :
#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 :
i = (the_truc(a), 5) ;
Avec une d�finition de the_truc comme ceci,
# define the_truc(a)
il y aurait une erreur � la compilation.
17. En guise de conclusion
--- Hou l�...
--- J'ai �crit �a moi ?
--- Toujours est-il que � quelqu'un � l'a �crit.
Tant pis, maintenant tu �cris la doc'.
--- Oh non !
--- Bon alors tu d�bogues.
--- Ca va, �a va, j'�cris la doc' ...
--- OK, ensuite tu d�bogues.
--- ...
----------
1 : Application Programming Interface
i.e. interface de programmation d'applications litt�ralement ;
autrement-dit biblioth�que.
2 : � Rectificatif technique �.
3 : � Amendement �.
4 : pour GCC par exemple, l'option -Wall active -Wmissing-prototypes
-Wmissing-declarations
5 : Programmation Orient� Objet
Technical Details
Message-ID:
<fr.comp.lang.faq-c-4-1126950753.87641@calva.glou.org>Path:
arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!border1.nntp.dca.giganews.com!nntp.giganews.com!newsfeed00.sul.t-online.de!t-online.de!fr.ip.ndsoftware.net!feeder.enertel.nl!nntpfeed-01.ops.asmr-01.energis-idc.net!213.218.133.51.MISMATCH!nocworld.net!news.valadon.net!ircam.fr!fr-chartes!maintfaqShow Full Headers
Complete Article Headers
X-Google-Thread: f9822,9b264e5e2d06e49,start X-Google-Thread: 102e92,9b264e5e2d06e49,start X-Google-Attributes: gidf9822,gid102e92,public X-Google-Language: FRENCH,ASCII Path: arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!border1.nntp.dca.giganews.com!nntp.giganews.com!newsfeed00.sul.t-online.de!t-online.de!fr.ip.ndsoftware.net!feeder.enertel.nl!nntpfeed-01.ops.asmr-01.energis-idc.net!213.218.133.51.MISMATCH!nocworld.net!news.valadon.net!ircam.fr!fr-chartes!maintfaq From: Guillaume Rumeau <guillaume.rumeau@wanadoo.fr> Newsgroups: fr.comp.lang.c,fr.usenet.reponses Subject: [FAQ] fr.comp.lang.c - partie 4/4 Supersedes: <fr.comp.lang.faq-c-4-1125654432.750070@calva.glou.org> Followup-To: fr.comp.lang.c Date: Sat, 17 Sep 2005 09:52:31 GMT Organization: FCLC Approved: fr-reponses@frmug.org User-Agent: MaintFaq version 2.1b Message-ID: <fr.comp.lang.faq-c-4-1126950753.87641@calva.glou.org> Content-Type: text/plain; charset=iso-8859-15 Content-Transfer-Encoding: 8bit Summary: Faq de fr.comp.lang.c X-No-Productlink: yes Lines: 1687