IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

comportement étrange de g++


Sujet :

C++

  1. #1
    Nouveau Candidat au Club
    Homme Profil pro
    ingénieur R&D
    Inscrit en
    Janvier 2012
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : ingénieur R&D
    Secteur : Service public

    Informations forums :
    Inscription : Janvier 2012
    Messages : 7
    Points : 0
    Points
    0
    Par défaut comportement étrange de g++
    Bonjour,

    Je viens de m'inscrire sur ce forum pour tenter d'avoir une réponse au comportement étrange d'un petit programme C++ faisant, basiquement, un tri par la méthode quicksort.

    J'utilise le compilateur g++ (version 4.6.2) sur la plateforme Fedora 16.

    Le problème est le suivant :
    • quand il est compilé sans option d'optimisation particulière, il marche bien

    g++ main.cpp && ./a.out

    • dès que j'active une optimisation O2 ou O3 (aucun problème avec O1), il donne un résultat erroné

    g++ -O2 main.cpp && ./a.out
    g++ -O3 main.cpp && ./a.out

    J'utilise une fonction template swap opérant sur des pointeurs, que j'appelle, par spécialisation, via un dynamic_cast sur un type pointeur vers un uint32_t (uint32_t étant de même taille que le float)

    c'est un peu tordu, mais ce n'est pas l'objet : je cherche juste à comprendre s'il y a dans ces lignes une erreur grossière de programmation ?

    d'autres commentaires sont dans le corps du fichier

    votre avis ?

    merci
    Fichiers attachés Fichiers attachés

  2. #2
    screetch
    Invité(e)
    Par défaut
    c'est bien l'objet; si je commente ce code tordu, ca marche. Le problème est sûrement dans le coin.
    A mon avis c'est un problème d'aliasing; pour optimiser, le compilateur fait quelques suppositions sur le code, notemment que si tu change a un endroit un "int", ca ne peut pas affecter un "float"; en gros:

    si tu as
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void doStuff(float* a, float* b) { *a = 1.0f; }
    on peut se demander si a et b ne pointent pas sur le même float; b est alors peut-être modifié quad on modifie a
    au contraire, si on a
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void doStuff(float* a, int* b) { *a = 1.0f; }
    alors a et b, étant de type différent, le compilateur suppose que si tu modifies a, b n'est pas modifié.

    or tu casses cette règle en faisant des casts et il est possible que le code optimisé croit que tes float n'ont pas changé (donc il ne les recharge pas de la mémoire) alors qu'en fait ils ont changé.

  3. #3
    Nouveau Candidat au Club
    Homme Profil pro
    ingénieur R&D
    Inscrit en
    Janvier 2012
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : ingénieur R&D
    Secteur : Service public

    Informations forums :
    Inscription : Janvier 2012
    Messages : 7
    Points : 0
    Points
    0
    Par défaut
    j'avoue ne pas comprendre pourquoi, même si effectivement un pointeur sur un int ne peut pas pointer sur un float, le reinterpret_cast ne permettrait pas quant à lui de travailler sur les adresses, permettant de manipuler le contenu pointé quel qu'il soit (avec les règles associées au type pointé après le cast), pourvu que les tailles des valeurs pointées soient identiques

    en l'occurrence, je fais juste un échange de variables en passant par une variable temporaire, et je teste, au travers du contenu pointé, que ces 2 pointeurs sont bien différents

    il n'y a donc pas de risque d'écrire par erreur l'une ou l'autre variable passée en argument du swap

    donc pas de raison pour que ça ne marche pas !

    le fait est que l'optimisation du code par le compilateur conduit le programme, seulement dans certains cas (!), à fournir des résultats erronés (voir l'exemple joint au premier message plus haut : un simple tri)

    je précise aussi que le compilateur ne génère aucun warning

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    template<typename T>
    void
    swap(T *const x, T *const y)
    {
      if (*x!=*y) {
        const T tmp = *y;
        *y = *x;
        *x = tmp;
      };
    }
     
    // spécialisation pour le type float
     
    template<>
    inline
    void
    swap<float>(float *const x, float *const y)
    {
      swap<uint32_t>(reinterpret_cast<uint32_t *const>(x),reinterpret_cast<uint32_t * const>(y));
    }

  4. #4
    screetch
    Invité(e)
    Par défaut
    http://msdn.microsoft.com/en-us/libr...(v=vs.80).aspx

    The result of a reinterpret_cast cannot safely be used for anything other than being cast back to its original type. Other uses are, at best, nonportable.
    tu mens au compilateur, tu lui dit que ta mémoire pointe sur des entiers ce qui n'est pas vrai. Comme tu ne respectes pas ta partie du contrat, le compilateur ne peut pas être blâmé.

  5. #5
    screetch
    Invité(e)
    Par défaut
    bon déjà pour confirmer, j'ai compilé avec l'option -fno-strict-aliasing (active par défaut en -O2, -O3, -Os) et le code fonctionne, c'est bien un problème d'aliasing
    ensuite j'ai regardé dans la doc, il est dit que
    an object of one type is assumed never to reside at the same address as an object of a different type, unless the types are almost the same. For example, an unsigned int can alias an int, but not a void* or a double. A character type may alias any other type.
    donc ca marcherait avec des char*

  6. #6
    Nouveau Candidat au Club
    Homme Profil pro
    ingénieur R&D
    Inscrit en
    Janvier 2012
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : ingénieur R&D
    Secteur : Service public

    Informations forums :
    Inscription : Janvier 2012
    Messages : 7
    Points : 0
    Points
    0
    Par défaut
    Citation Envoyé par screetch Voir le message
    The result of a reinterpret_cast cannot safely be used for anything other than being cast back to its original type. Other uses are, at best, nonportable.
    tu mens au compilateur, tu lui dit que ta mémoire pointe sur des entiers ce qui n'est pas vrai. Comme tu ne respectes pas ta partie du contrat, le compilateur ne peut pas être blâmé.
    je suis d'accord, je mens au compilateur, mais à dessein ; d'autre part, le type pointé ne change pas, donc au delà du swap, la valeur pointée reste un float

    Citation Envoyé par screetch Voir le message
    bon déjà pour confirmer, j'ai compilé avec l'option -fno-strict-aliasing (active par défaut en -O2, -O3, -Os) et le code fonctionne, c'est bien un problème d'aliasing
    ensuite j'ai regardé dans la doc, il est dit que
    an object of one type is assumed never to reside at the same address as an object of a different type, unless the types are almost the same. For example, an unsigned int can alias an int, but not a void* or a double. A character type may alias any other type.
    donc ca marcherait avec des char*
    je suis d'accord pour l'option d'antialiasing, mais ça ne fait que masquer le problème, et de toute façon le compilateur ne donne aucun warning, quel que soit le niveau d'optimisation

    concernant l'aliasing de n'importe quel type par un type char, c'est effectivement permis par la norme, et (miracle !) ça permet, dans ce cas, de faire disparaître le problème (voir code plus bas)

    mais, sauf erreur de ma part ça ne résout pas le problème, puisque ma fonction template voit désormais un type char (et non pas un float) : si je voulais faire un calcul sur ma variable pointée, je ne suis pas sûr que ça marcherait ... donc je perds de l'info au passage

    décidément il y a des choses qui m'échappent ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    template<typename T>
    void
    swap(T *const x, T *const y)
    {
      if (*x!=*y) {
        const T tmp = *y;
        *y = *x;
        *x = tmp;
      };
    }
     
    // specialisation pour le type T = char
     
    template<>
    inline
    void
    swap<float>(float *const x, float *const y)
    {
      swap<char>(reinterpret_cast<char *const>(x),reinterpret_cast<char * const>(y));
    }
    PS : j'ai résolu mon problème, à l'origine de ce post, autrement, mais je tiens quand même à comprendre ...

  7. #7
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    Au passage, il y a un point important sur ton code.
    Les float * const sont des pointeurs constants sur des float. Peut-être t'attendais-tu à avoir des pointeurs vers des float constants, auquel cas ce serait float const *.
    Ca n'a rien à voir avec le problème, mais il est assez rare de voir des pointeurs constants.

    Sinon, la version char ne marche probablement pas. En effet, swap<char> va échanger uniquement le premier octet des données du float. Parce qu'il croira que c'est un char, qui ne fait qu'un octet.

    Un static_cast peut également faire le travail pour passer de float à uint32_t, sinon. Mais c'est assez moche.

    Au passage, deux questions. Pourquoi la spécialisation de float veut-elle sous-traiter avec uint32_t, et donc perdre tout avantage de la spécialisation ? Pourquoi ne pas utiliser std::swap ?

  8. #8
    screetch
    Invité(e)
    Par défaut
    ben ecoutes je dirais que tu as deux choix;
    * admettre que tu as fait du code cradoc en disant au compilateur que des float etait en fait des entiers et que donc devraientetre traités comme tels (avec les avantages et les inconvénients)

    * persister a dire que c'est de la faute du compilateur, ecrire ton propre compilateur qui va gérer ce cas.

    a toi de voir evidemment.

  9. #9
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par lmaurin Voir le message
    je suis d'accord, je mens au compilateur, mais à dessein
    Je serais curieux de savoir quel est l'intérêt de lui mentir ainsi...
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  10. #10
    Nouveau Candidat au Club
    Homme Profil pro
    ingénieur R&D
    Inscrit en
    Janvier 2012
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : ingénieur R&D
    Secteur : Service public

    Informations forums :
    Inscription : Janvier 2012
    Messages : 7
    Points : 0
    Points
    0
    Par défaut
    programmation à dessein, car à l'origine, j'avais programmé le swap en utilisant le ou exlusif, qui ne fonctionne que sur des entiers (quoique je ne suis plus totalement sûr de ce dernier point désormais)

    XOR SWAP

    d'où la spécialisation du template pour le float, avec un swap classique ; mais un test de rapidité m'a fait rendre compte, quelque temps plus tard, que le swap utilisant la fonction XOR était au moins 30% plus lent (sur un CPU de type Core2) ...


    concernant float *const, sauf erreur de ma part, il s'agit bien de spécifier au compilateur C++ que l'adresse (donc le pointeur) est constant, et par conséquent ne peut pas être modifié : seule la valeur pointée doit l'être ; l'ajout de const me permet entre autre de mettre en evidence des erreurs de programmation ...

    float const * ne permet pas de signifier au compilateur que le pointeur est constant ; un petit morceau de code vous convaincra facilement :

    float const * ptr = new float[10];
    ptr++;
    passe à la compilation et incrémente le pointeur

    mais je dois avouer que je ne sais pas trop à quoi s'applique le qualificateur const dans une formulation de type float const *, sinon au contenu du pointeur, car ce code ci-dessous ne compile pas (read-only location '*ptr') :

    float const * ptr = new float[10];
    (*ptr)++;
    code qui est en fait équivalent à celui-ci (qu'il faudrait, sur la forme, lui préférer) :
    const float * ptr = new float[10];
    (*ptr)++;
    enfin, comme précisé dans mon code rédigé comme ci-dessous :
    float *const ptr = new float[10];
    ptr++;
    ne passe pas : le compilateur me précise bien que le pointeur est constant (read-only variable 'ptr'), c'est bien ce que je souhaitais, et la variable pointée reste accessible à l'écriture


    enfin, dernier point, on peut mettre des const partout (const float *const) : dans ce cas, le pointeur et son contenu ne sont pas modifiiables

    pour un tableau 2D on aurait : const float *const *const

    et on peut décliner en cascade à l'infini (ou presque ...)

  11. #11
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Pour le const, il s'applique à l'élément précédent. Seule exception, s'il est au début, il s'applique à l'élément suivant.

    Donc float const * == const float * != float * const
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  12. #12
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    Donc le const formait volontairement un pointeur constant vers un float non constant, et non un pointeur vers un float constant.

    Comme, en général, ça n'a pas grande utilité, je pensais à une erreur, mea culpa.

  13. #13
    Nouveau Candidat au Club
    Homme Profil pro
    ingénieur R&D
    Inscrit en
    Janvier 2012
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : ingénieur R&D
    Secteur : Service public

    Informations forums :
    Inscription : Janvier 2012
    Messages : 7
    Points : 0
    Points
    0
    Par défaut
    je corrige mon post précédent : en utilisant un reinterpret_cast<char *const>, ça ne passe pas non plus, ce qui est finalement assez logique puisque les mots ne sont pas de même taille (mon test portait sur des entiers pris en exemple) ...

    enfin pour répondre au static_cast cité plus haut : le problème du static_cast est qu'il change le contenu binaire de la variable

    le reinterpret_cast ne change quant à lui, dans l'utilisation que j'en fais, que le type du pointeur, mais pas le contenu (binaire) pointé

    j'ai posté le problème sur le bugzilla GCC il y a quelques jours, et on me renvoie vers un problème d'aliasing dans mon code ... je n'y crois guère

    il y a (peut-être ? mais j'en doute) un problème d'aliasing, mais ça n'explique pas tout

    bugzilla gcc

    je pense qu'ils ont d'autres chats à fouetter, vu la réponse qu'ils m'ont faite, dans la minute suivant mon post ... bref, passons ...

    merci quand même pour vos réponses

  14. #14
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par lmaurin Voir le message
    bugzilla gcc

    je pense qu'ils ont d'autres chats à fouetter, vu la réponse qu'ils m'ont faite, dans la minute suivant mon post ... bref, passons ...
    Non, ils te dissent juste la même chose qu'ici : ton code n'est pas correct et comporte un comportement indéfini. Le compilateur a alors le droit de faire ce qu'il souhaite donc il n'y a pas de bug dans gcc (mais dans ton code).

  15. #15
    Nouveau Candidat au Club
    Homme Profil pro
    ingénieur R&D
    Inscrit en
    Janvier 2012
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : ingénieur R&D
    Secteur : Service public

    Informations forums :
    Inscription : Janvier 2012
    Messages : 7
    Points : 0
    Points
    0
    Par défaut
    je suis d'accord au moins sur une chose : ils maitrisent bien mieux GCC que moi ...

    je testerai sous Visual C++ quand j'aurai le temps : ses warnings sont parfois plus éloquents

  16. #16
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par lmaurin Voir le message
    j'ai posté le problème sur le bugzilla GCC il y a quelques jours, et on me renvoie vers un problème d'aliasing dans mon code ... je n'y crois guère

    il y a (peut-être ? mais j'en doute) un problème d'aliasing, mais ça n'explique pas tout

    bugzilla gcc

    je pense qu'ils ont d'autres chats à fouetter, vu la réponse qu'ils m'ont faite, dans la minute suivant mon post ... bref, passons ...
    C'est pourtant la même (bonne) réponse que celle qu'on a fait ici. Et trois réponses concordantes par 3 personnes qui sont loin d'être des manchots, je trouve que tu as eu de la chance dans ton bug report...
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

Discussions similaires

  1. [Forms6i] Un IF-ELSIF au comportement étrange
    Par lafouine dans le forum Forms
    Réponses: 11
    Dernier message: 13/09/2005, 15h40
  2. Comportement étrange apres une désinstallation
    Par Sunchaser dans le forum Excel
    Réponses: 4
    Dernier message: 06/08/2005, 19h44
  3. comportement étrange d'une jointure ...
    Par amenis dans le forum PostgreSQL
    Réponses: 5
    Dernier message: 10/02/2005, 21h27
  4. [Système][Runtime][Exec] Comportement étrange au lancement de BeSweet
    Par divxdede dans le forum API standards et tierces
    Réponses: 1
    Dernier message: 06/06/2004, 09h54
  5. Réponses: 2
    Dernier message: 22/09/2003, 11h23

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo