Article View: fr.comp.lang.c
Article #15318[FAQ] fr.comp.lang.c - partie 3/4
From: Guillaume Rumeau
Date: Sun, 02 Oct 2005 09:50
Date: Sun, 02 Oct 2005 09:50
574 lines
18724 bytes
18724 bytes
Archive-Name: fr/comp/lang/faq-c-3
Archive-Name: fr/comp/lang/faq-c-3
---------------------------------------------------------------------------
FAQ de fr.comp.lang.c
18 avril 2003
Partie 3/4 (sections 10 � 12)
---------------------------------------------------------------------------
10. Expressions
10.1 Le type Bool�en existe-t-il en C ?
Oui, il existe un type bool�en en C. C'est un ajout de la derni�re
version de la norme (C99). Il s'agit du type _Bool
d�fini dans <stdbool.h>. Cet en-t�te contient
�galement les d�finitions de true et false.
Une macro bool est souvent d�finie comme �quivalent �
_Bool.
Rappelons �galement qu'en C, une valeur est � fausse � si elle est
nulle (ou �quivalent), et � vraie � sinon. Les d�finitions de
true et false suivent cette r�gle. Ainsi, la
valeur enti�re de true est 1 et celle de
false est 0.
10.2 Un pointeur NULL est-il assimil� � une valeur fausse ?
Oui, la valeur NULL est apparent�e � un 0.
Les �critures if(p != NULL) et if(p)
sont donc �quivalentes. De m�me, if(p == NULL) est
�quivalent � if(!p).
Voir aussi les questions 10.1 et 7.5.
10.3 Que donne l'op�rateur ! sur un nombre n�gatif ?
L'op�rateur ! sur un nombre n�gatif donne bien ce que l'on
attend, � savoir 0.
Voir aussi la question 10.1.
10.4 Que vaut l'expression a[i] = i++ ?
Ce genre d'expression fait partie des � undefined
behaviour �, ou comportement ind�fini. Cela signifie que le
r�sultat d'une telle op�ration d�pend du compilateur.
L'op�rateur ++ modifie la valeur de i, alors que
celle-ci est utilis�e ailleurs dans l'expression.
C'est ce que l'on appelle un � effet de bords �.
10.5 Pourtant, i++ vaut i ?
Effectivement, i++ vaut i, avant
l'incr�mentation. Toutefois, rien ne dit dans quel sens est
calcul�e l'expression a[i] = i++.
Est-ce le i++ qui est �valu� avant le a[i], ou
le contraire ?
On n'en sait rien, c'est pourquoi l'on dit que c'est un
comportement ind�fini.
10.6 En est-il de m�me pour i++ * i++ ?
Oui. La norme ne pr�cise pas pour les op�rateurs binaires dans
quel ordre les op�randes sont �valu�s.
10.7 Peut-on utiliser les parenth�ses pour forcer l'ordre d'�valuation d'une expression ?
Pas en g�n�ral.
Les parenth�ses ne donnent qu'un ordre partiel d'�valuation, entre
les op�rateurs.
Voici un petit exemple :
a = f() + g() * h();
b = (f() + g()) * h();
Les parenth�ses dans la deuxi�me expression modifient l'ordre
d'�valuation de l'addition et de la multiplication.
Par contre, l'ordre dans lequel seront �valu�es les fonctions est
ind�fini, dans l'une ou l'autre des deux expressions. Cela ne
d�pend que du compilateur.
Pour forcer un ordre d'�valuation, il faut utiliser une �criture
s�quentielle, avec des variables temporaires.
tf = f();
tg = g();
th = h();
b = (tf + tg) * th;
On a alors un comportement parfaitement d�fini sur toutes les
cibles, quel que soit le compilateur.
10.8 Qu'en est-il des op�rateurs logiques && et || ?
Ces deux op�rateurs forment une exception � la r�gle.
La norme pr�voit que les op�randes de ces deux op�rateurs soient
�valu�s de gauche � droite.
De plus, l'�valuation s'arr�te d�s que le r�sultat est connu.
10.9 Comment sont �valu�es les expressions comprenant plusieurs types de variables ?
Le principe est simple.
Si les variables sont de types diff�rents, il y aura un
cast implicite vers le type le plus pr�cis. On parle
de promotion.
Voici les r�gles de base :
- Si l'un des op�randes est un long double, l'autre
est converti en un long double.
- Sinon, si l'un des op�randes est un double,
l'autre est converti en un double.
- Sinon, si l'un des op�randes est un float,
l'autre est converti en un float.
- Sinon, les op�randes de types char et
short sont convertis en int.
- Enfin, si l'un des op�randes est un long, l'autre
est converti en un long.
En C99, il faut rajouter le type long long.
Cela se complique dans le cas d'op�randes unsigned.
Les comparaisons entre valeurs sign�es et non sign�es d�pendent de
la machine et de la taille des diff�rents types.
10.10 Qu'est-ce qu'une lvalue ?
lvalue est un terme qui est utilis� pour d�finir les
expressions que l'on peut mettre � gauche d'une affection.
Toute variable modifiable est une lvalue.
Quand le compilateur pr�vient que le membre gauche d'une
affectation n'est pas une lvalue, c'est souvent parce
que l'on ne voulait pas faire une affectation, mais une
comparaison (cf. 15.5).
11. Nombres en virgule flottante
11.1 J'ai un probl�me quand j'imprime un nombre r�el.
Sur la plupart des architectures, les nombres r�els (dits
flottants) sont repr�sent�s en base 2, comme pour les
entiers. Ainsi, le nombre 3.1 ne peut s'�crire exactement en base
2. La repr�sentation binaire est un arrondi qui d�pend de la
pr�cision du codage des flottants, et des choix du compilateur.
De plus, avec une fonction comme printf(), le nombre �
imprimer est converti en base 2 puis reconverti en base 10, ce qui
augmente encore les impr�cisions.
Il est pr�f�rable d'utiliser les double, qui ont une
pr�cision sup�rieure aux float, sauf si l'�conomie de
m�moire est vraiment critique.
Voir � ce sujet la question 11.10.
11.2 Pourquoi mes extractions de racines carr�es sont erron�es ?
Assurez-vous d'avoir inclus math.h, d'avoir correctement
d�clar� les autres fonctions renvoyant des double. Une
autre fonction de la biblioth�que standard avec laquelle il faut
faire attention est atof(), dans stdlib.h.
11.3 J'ai des erreurs de compilation avec des fonctions math�matiques
Il faut s'assurer d'avoir link� (li�) son code avec
la biblioth�que math�matique. Par exemple, sous Unix,
vous devez g�n�ralement passer l'option -lm � la fin de la
ligne de commande.
11.4 Mes calculs flottants me donnent des r�sultats �tranges et/ou diff�rents selon les plateformes
Pour commencer, relisez 11.1.
Si le probl�me est plus complexe, il convient de se rappeler que
les ordinateurs utilisent des formats de repr�sentation des
flottants qui ne permettent pas des calculs exacts. Pertes de
pr�cision, accumulation d'erreurs et autres anomalies sont le lot
commun du num�ricien.
Rappelez-vous qu'aucun calcul sur des flottants n'a de chance
d'�tre exact, en particulier, n'utilisez jamais == entre
deux flottants.
Ces probl�mes ne sont pas sp�cifiques au C.
Dans certains probl�mes, une solution peut �tre d'introduire un
petit param�tre de relaxation, par exemple #define EPS
1e-10, puis de multiplier l'un des termes (judicieusement
choisi) de vos calculs par (1 + EPS).
Pour plus de renseignements, on se reportera par exemple aux
Numerical Recipes ou � Numerical Algorithms
with C (cf. 3.9).
11.5 Comment simuler == entre des flottants ?
�tant donn� qu'il y a perte de pr�cision tr�s vite, pour comparer
deux valeurs flottantes, on teste si elles sont assez
proches. Plut�t que d'�crire une horreur du genre :
double a, b;
/* ... */
if (a == b) /* HORREUR ! */
/* ... */
on �crira :
#include <math.h>
/* ... */
double a, b;
/* ... */
if (fabs (a - b) <= epsilon * fabs (a) )
/* ... */
o� l'on aura judicieusement choisi epsilon
(non-nul !).
11.6 Comment arrondir des flottants ?
La m�thode la plus simple et la plus exp�ditive est (int)(x +
0.5). Cette technique ne fonctionne pas correctement pour
les nombres n�gatifs aussi vaut-il mieux utiliser
(int)(x < 0 ? x - 0.5 : x + 0.5)
11.7 Pourquoi le C ne dispose-t-il pas d'un op�rateur d'exponentiation ?
Parce que certains processeurs ne disposent pas d'une telle
instruction. Il existe une fonction pow() d�clar�e dans
math.h bien que la multiplication soit pr�f�rable pour de
petits exposants.
11.8 Comment obtenir Pi ?
Parfois une constante pr�d�finie M_PI est d�clar�e dans
math.h mais ce n'est pas standard aussi vaut-il mieux
calculer pi soi-m�me via 4 * atan
(1.0).
11.9 Qu'est-ce qu'un NaN ?
� NaN is Not a Number �, ce qui signifie
� Ce n'est pas un nombre �.
Un NaN est un nombre flottant qui est le r�sultat d'une
op�ration non conforme, par exemple 0/0.
Lorsqu'un NaN est produit, la plupart des architectures
produisent une interruption (ou un signal) qui termine le
programme, au moment de l'utilisation de celui-ci.
Il est parfois possible de v�rifier si un nombre est
NaN. Un bon test est celui-ci :
#define isNaN(x) ((x) != (x))
Certains compilateurs fournissent des facilit�s quant � la gestion
des NaN. GCC fournit dans la biblioth�que
math�matique (math.h) les fonctions isnan(),
isinf() et finite().
11.10 Faut-il pr�f�rer les double aux float ?
La vitesse de traitement d'un double n'est pas forc�ment plus
longue qu'un float, cela d�pend du compilateur (de ses options)
et du processeur. Ainsi avec l'exemple suivant, en rempla�ant le
typedef par float ou double, on s'aper�oit
que sur un Pentium ou un PowerPC, le double est plus rapide
� calculer que le float tout en ayant une pr�cision plus grande.
#include <stdio.h>
#include <math.h>
typedef float reel; /* float ou double */
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 math�matiques pour traiter les float
directement au lieu de toujours passer par des double depuis la
derni�re norme (C99). Par exemple il existe cosf() en plus
de cos(). En faisant des essais on s'aper�oit que dans
notre exemple, le cosf() appliqu� � un float devient
aussi rapide que le cos() appliqu� � un double.
En conclusion, nous pouvons dire qu'il est pr�f�rable d'utiliser des
double � la place des float, sauf lorsque la place
m�moire devient critique.
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 � chaque fois, elle engendre une longue
discussion.
Certains intervenants pensent que caster la valeur de retour
de malloc() est inutile, voire dangereux.
En effet, malloc() renvoie un void *.
Or, en C, un pointeur void * est implicitement cast�
lors d'une affectation vers le type de la variable affect�e.
Bien s�r, expliciter le cast n'est pas interdit, et est parfois
utile.
Toutefois, caster le retour de malloc()
risque de cacher au compilateur l'oubli du prototype de
malloc().
Ce prototype se trouve dans le fichier d'en-t�te <stdlib.h>.
Sans lui, malloc() sera par d�faut une fonction
retournant un int et dont les param�tres seront du type
des arguments pass�s, ce qui peut provoquer de s�rieux bugs.
La v�ritable erreur est l'oubli du fichier d'en-t�te
<stdlib.h>, et non pas le cast de malloc() en
lui m�me.
Mais le cast de malloc() risque de cacher au compilateur
cette erreur.
� noter qu'il existe des outils de v�rification de code et des
options sur la plupart des compilateurs [4] qui permettent de d�tecter ce genre d'erreur.
D'autres intervenants jugent qu'il faille tout de m�me caster le
retour de malloc(), afin de conserver une compatibilit�
avec d'anciens compilateurs pr�-ANSI, ou pour int�grer 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
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);
Si le type de la variable change, l'allocation est toujours
valide. � noter que l'on ne caste pas le retour de
malloc()
Voir la question 12.1 � ce sujet, ainsi que la
question 12.10.
12.3 Pourquoi mettre � NULL les pointeurs apr�s un free() ?
La fonction free() lib�re l'espace m�moire point� par
le pointeur en question. Mais la valeur de celui-ci ne peut-�tre
chang�e, car en C les arguments sont pass�s par valeur aux
fonctions.
La variable pointeur contient apr�s le free() une
adresse invalide.
Son utilisation peut entra�ner de s�rieux emb�tements.
Pour �viter cela, une bonne solution consiste � affecter la valeur
NULL au pointeur apr�s l'appel � free().
Il existe aussi certaines impl�mentations de l'allocation
dynamique qui fonctionnent en Garbage Collector,
c'est-�-dire, que la m�moire n'est r�ellement lib�r�e que lorsque
le pointeur est mis � NULL.
Dans tous les cas, cela permet de tester facilement la validit�
des pointeurs.
12.4 Pourquoi free() ne met pas les pointeurs � NULL ?
Rappelons que les param�tres des fonctions sont pass�s par valeur
(ou par copie). Ainsi, pour modifier la valeur du pointeur, il
faudrait passer un pointeur sur le pointeur, ce qui compliquerait
l'utilisation de free().
Mais ce n'est pas le cas, il faut donc le faire soi-m�me.
12.5 Quelle est la diff�rence entre malloc() et calloc() ?
Pratiquement, calloc() est �quivalent � :
/* p = calloc(m, n); */
p = malloc(m * n);
memset(p, 0, m * n);
Chaque �l�ment est initialis� � 0. Ce 0
est un � tout bit � z�ro �.
La valeur des �l�ments n'est pas forc�ment valide, suivant leur type.
Voir aussi la question 5.6.
12.6 Que signifie le message � assignment of pointer from integer � quand j'utilise malloc() ?
Cela signifie que vous avez oubli� d'inclure le fichier
stdlib.h.
Voir � ce sujet la question 12.1.
12.7 Mon programme plante � cause de malloc(), cette fonction est-elle bugg�e ?
Il est assez facile de corrompre les structures de donn�es
internes de malloc(). Les sources les plus plausibles
du probl�me sont :
- l'emploi de malloc(strlen(s)) au lieu de
malloc(strlen(s)+1).
- la lib�ration d'un pointeur deux fois.
Il y en a d'autres...
Voir aussi les questions 12.2
et 12.8.
12.8 Que signifient les erreurs � segmentation fault � et � bus error � ?
Cela signifie que vous avez essay� d'acc�der � une zone m�moire
non autoris�e.
C'est souvent l'utilisation d'un pointeur non initialis� ou
NULL qui en est la cause.
Ce genre d'erreur peut aussi provenir d'une mauvaise allocation
(cf. 12.7 et
12.2)
ou de l'oubli du 0 en fin de cha�ne.
12.9 Doit-on lib�rer explicitement la m�moire avant de quitter un programme ?
Oui, car tous les syst�mes ne le font pas d'eux-m�mes.
12.10 Du bon usage de realloc()
La fonction realloc() permet de modifier la taille de
l'espace m�moire allou� � une variable.
Elle est souvent utilis�e pour augmenter cette taille.
Rappelons que la m�moire allou�e par malloc(),
calloc() et realloc() est fournie sous la
forme d'une zone continue (en un seul bloc).
Or, il peut arriver que la nouvelle taille demand�e d�passe l'espace
disponible derri�re la zone initiale.
Dans ce cas, la fonction realloc() alloue une nouvelle
zone ailleur, l� ou il y a de la place, et y recopie les donn�es
initiales. L'ancienne zone est alors lib�r�e.
C'est pourquoi realloc() renvoie un pointeur sur
la nouvelle zone m�moire, m�me si l'augmentation de taille (ou la
r�duction) a pu se faire sur place.
Bien s�r, comme malloc(), realloc() peut
�chouer.
Voici pour r�sumer une bonne mani�re d'utiliser
realloc() :
#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 l'erreur */
}
Technical Details
Message-ID:
<fr.comp.lang.faq-c-3-1128246619.953060@calva.glou.org>Path:
arc1-ca.pugleaf.net!g2news1.google.com!news4.google.com!news.glorb.com!nocworld.net!freenix!ircam.fr!fr-chartes!maintfaqShow Full Headers
Complete Article Headers
X-Google-Thread: f9822,af46c51321fc6d4c,start X-Google-Thread: 102e92,af46c51321fc6d4c,start X-Google-Attributes: gidf9822,gid102e92,public X-Google-Language: FRENCH,ASCII Path: arc1-ca.pugleaf.net!g2news1.google.com!news4.google.com!news.glorb.com!nocworld.net!freenix!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 3/4 Supersedes: <fr.comp.lang.faq-c-3-1126950731.797289@calva.glou.org> Followup-To: fr.comp.lang.c Date: Sun, 2 Oct 2005 09:50:17 GMT Organization: FCLC Approved: fr-reponses@frmug.org User-Agent: MaintFaq version 2.1b Message-ID: <fr.comp.lang.faq-c-3-1128246619.953060@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: 575