🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: fr.comp.lang.c
Article #15318

[FAQ] fr.comp.lang.c - partie 3/4

#15318
From: Guillaume Rumeau
Date: Sun, 02 Oct 2005 09:50
574 lines
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!maintfaq

Show 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