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 :

crash étrange: bug g++?


Sujet :

C++

  1. #1
    Membre émérite
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2008
    Messages
    832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 832
    Points : 2 625
    Points
    2 625
    Par défaut crash étrange: bug g++?
    J'ai un bout de code (pondu lors d'expérimentations) qui réagit de façon assez étrange et je me demande où est l'erreur.

    Voici le bout de code incriminé:
    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
     
    #include <string>
    #include <stdio.h>
     
    int main(void)
    {
    	std::string test("test test");
    	std::string::iterator t1(test.begin());++t1;
    	std::string::iterator t2(t1);++t2;++t2;
    	std::string::iterator t3(test.end());t3--;
    	printf("%s:: %c %c %c\r\n",test.c_str(),*t1,*t2,*t3);
    	test.insert(t2,'!');
    	printf("%s:: %c %c %c\r\n",test.c_str(),*t1,*t2,*t3);
    	test.insert(t2,'!');
    	printf("%s:: %c %c %c\r\n",test.c_str(),*t1,*t2,*t3);
    	printf("wow\n");
    	return -1;
    }
    Le résultat donne:
    test test:: e t t
    tes!t test:: e t t
    :: e ! s
    wow
    *** glibc detected *** ./a.out: double free or corruption (!prev): 0x00000000014b4040 ***
    ======= Backtrace: =========
    Suivent, naturellement, la bacttrace et la memory map.
    Ce qui m'intrigue, et me fait songer à un bug de g++ plutôt qu'a une bêtise de ma part (qui est, certes, également possible vu que j'ai un doute quant au fait de jouer comme ça avec les itérateurs) c'est que test.c_str() renvoie une chaîne nulle?
    Le fait qu'il semble y avoir un double free n'est pas non plus étranger à ma pensée que j'aie pu découvrir un bug.

    Donc, si quelqu'un peut tester avec un compilo différent et/ou expliquer que c'est normal (et le pourquoi du comment tant qu'a faire) je suis preneur.

    Ah, oui, "$g++ -v" me donne:
    Citation Envoyé par g++ -v
    Using built-in specs.
    COLLECT_GCC=g++
    COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.7/lto-wrapper
    Target: x86_64-linux-gnu
    Configured with: ../src/configure -v --with-pkgversion='Debian 4.7.2-4' --with-bugurl=file:///usr/share/doc/gcc-4.7/README.Bugs --enable-languages=c,c++,go,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.7 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.7 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --with-arch-32=i586 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
    Thread model: posix
    gcc version 4.7.2 (Debian 4.7.2-4)
    PS: oui, je sais, printf c'est pas bien pour le C++ et blablabla... mais moi j'aime pas les flux, parce que je n'aime pas taper un code super long pour rien, et en plus pour moi, les opérateurs << et >> servent au décalage de données, pas à envoyer des données dans un objet complètement différent (pour ça y'a "operator+=" avec opérateurs de cast et constructeurs surchargés... mais là n'est pas le sujet)

  2. #2
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Il est possible que le string::insert invalide les itérateurs (à confirmer).

    PS:
    Les flux, avec la surchage des opérateurs << et >>, te permettent d'avoir:
    - une sécurité sur le type
    - la possibilité d'ajouter ses propres types
    Ce qui n'est pas offert (nativement) avec printf de C.
    Tu peux écrire une version de printf "correcte" en C++11 avec les variadic template.
    (Une fois de plus, C++ est typé (contrairement au C).)

  3. #3
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Effectivement, insert va invalider tous les itérateurs.

    Ceux de "derrière", très certainement, car il faut bien faire de la place pour caser le caractère qu'on ajoute, mais parfois aussi les autres, car une std::string est bien souvent représentée en interne comme un tableau de caractères contigus.

    Si, pour arriver à insérer le caractère, il a fallut agrandir la taille de ce tableau, tous tes itérateurs seront bons à jeter à la poubelle car le tableau ne se trouvera plus à l'adresse référencée par tes itérateurs

    A partir de là, bah, ben on passe dans la quatrième dimension, parce que tout ce qu'on peut dire, c'est que nous aurons un comportement indéfini (undefined behaviour), et, comme le dit si bien le terme, nul ne peut prédire ce qui se passera

    Tu as déjà de la chance de ne pas avoir provoqué une guerre atomique
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  4. #4
    Membre émérite
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2008
    Messages
    832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 832
    Points : 2 625
    Points
    2 625
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Salut,

    Effectivement, insert va invalider tous les itérateurs.

    Ceux de "derrière", très certainement, car il faut bien faire de la place pour caser le caractère qu'on ajoute, mais parfois aussi les autres, car une std::string est bien souvent représentée en interne comme un tableau de caractères contigus.

    Si, pour arriver à insérer le caractère, il a fallut agrandir la taille de ce tableau, tous tes itérateurs seront bons à jeter à la poubelle car le tableau ne se trouvera plus à l'adresse référencée par tes itérateurs

    A partir de là, bah, ben on passe dans la quatrième dimension, parce que tout ce qu'on peut dire, c'est que nous aurons un comportement indéfini (undefined behaviour), et, comme le dit si bien le terme, nul ne peut prédire ce qui se passera

    Tu as déjà de la chance de ne pas avoir provoqué une guerre atomique
    Merci pour les explications, je m'en doutait d'ailleurs un peu (sinon, je ne me serais pas amusé à faire un bout de code pour vérifier la validité jusqu'a ce que ça pète . A noter que la première insertion ne posais aucun souci avec g++ d'ailleurs.).
    Ce qui m'intrigue le plus, ce n'est pas le fait que les itérateurs se soient auto-pourris, mais le retour de c_str() qui deviens subitement nul?
    Ce serait causé par l'invalidation des itérateurs?

    Pour le coup, j'imagine qu'il va falloir que je trouve le moyen de faire utiliser un autre conteneur que le vector à basic_string, mais je devrais m'en sortir.


    Hors sujet:
    Citation Envoyé par Ehonn Voir le message
    PS:
    Les flux, avec la surchage des opérateurs << et >>, te permettent d'avoir:
    - une sécurité sur le type
    - la possibilité d'ajouter ses propres types
    Ce qui n'est pas offert (nativement) avec printf de C.
    Tu peux écrire une version de printf "correcte" en C++11 avec les variadic template.
    (Une fois de plus, C++ est typé (contrairement au C).)
    Juste parce que je ne peux pas m'empêcher, et puis, le vendredi, c'est permis:
    - une sécurité sur le type => moué... mais en fait, j'ai encore jamais eu de soucis de type avec un printf. P'tet parce que je ne m'en sers que pour afficher des chaînes de caractères et des entiers?
    - la possibilité d'ajouter ses propres types => rien ne m'empêche d'ajouter un opérateur de cast vers char*
    - les flux ne permettent pas un formatage aisé et lisible, par exemple, dans les printf utilisés, ça aurait donné std::cout << test << ":: " << *t1 << " " << *t2 << " " << *t3 << std::endl; au lieu de printf("%s:: %c %c %c\r\n",test.c_str(),*t1,*t2,*t3);. Lequel est le plus lisible?

    De manière générale, le contrôle de ce qui à été lu/écrit est tout de même plus fin avec les printf/scanf (notamment scanf qui permet l'usage de regex, entres autres). Cela dis, les flux ont des avantages également: le support de std::string, qui permet d'éviter de ne pas avoir besoin de connaître la taille de l'élément recevant une écriture.
    Pour finir sur cette parenthèse: les deux ont leurs avantages, et si je pense que cin/cout remplacent avantageusement certains trucs du C, il s'agit de puts et gets. printf/scanf ont un rôle très différent du simple affichage: l'affichage formaté, avec contrôle et tolérance d'erreur fins.

    Le C est typé, juste plus faiblement que le C++ (vu qu'en C on peut caster à l'arrache, contrairement au C++ qui... permet de le faire d'ailleurs, via reinterpret_cast<>() le honni mais parfois nécessaire ).

    Fin de la parenthèse hors sujet

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Freem Voir le message
    Merci pour les explications, je m'en doutait d'ailleurs un peu (sinon, je ne me serais pas amusé à faire un bout de code pour vérifier la validité jusqu'a ce que ça pète . A noter que la première insertion ne posais aucun souci avec g++ d'ailleurs.).
    Ce qui m'intrigue le plus, ce n'est pas le fait que les itérateurs se soient auto-pourris, mais le retour de c_str() qui deviens subitement nul?
    Ce serait causé par l'invalidation des itérateurs?
    Il faut savoir que l'augmentation d'espace dans le tableau utilisé en interne par la classe string (tout comme c'est le cas pour la classe vector d'ailleurs) est faite de manière amortie : à chaque fois que l'on doit augmenter la taille du tableau, on l'augmente de à peu près la moitié de sa taille originale.

    Cela permet d'éviter d'avoir à augmenter la taille du tableau à chaque insertion, et donc de gagner en terme de performances (en fait, le gain sera d'autant plus grand que le tableau sera gros )

    Il peut donc arriver que les itérateurs sur les éléments qui précèdent l'endroit d'insertion ne soient pas invalidés SI (et seulement si) "on a la chance" que l'insertion n'ait pas provoqué d'élargissement du tableau

    Mais comme il est plutot difficile de déterminer à quel moment le tableau sera élargi, et à quel moment il ne le sera pas (note qu'il y a moyen de le faire ), il est beaucoup plus sage de considérer que les itérateurs sont d'office invalidés lors de l'insertion (ou lors de la suppression, car le principe est foncièrement le même, si ce n'est que l'on assiste à un "amaigrissement" du tableau utilisé en interne )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Membre émérite
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2008
    Messages
    832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 832
    Points : 2 625
    Points
    2 625
    Par défaut
    Citation Envoyé par Freem Voir le message
    Merci pour les explications, je m'en doutait d'ailleurs un peu (sinon, je ne me serais pas amusé à faire un bout de code pour vérifier la validité jusqu'a ce que ça pète . A noter que la première insertion ne posais aucun souci avec g++ d'ailleurs.).
    Ce qui m'intrigue le plus, ce n'est pas le fait que les itérateurs se soient auto-pourris, mais le retour de c_str() qui deviens subitement nul?
    Ce serait causé par l'invalidation des itérateurs?
    Pour vector et string, c'est évident, ça permet d'éviter les réallocations à chaque fois que l'on ajoute/supprime un caractère, et donc un gain de performances non négligeable en vitesse.
    Mais tout cela ne me semble pas expliquer pourquoi, et comment:
    _ test.c_str() qui est censé renvoyer un pointeur volatile (dans le sens, pas stable, à ne pas écrire, à ne pas réutiliser...) sur la chaîne de caractère brute renvoie subitement une chaîne vide ""
    _ le crash de l'appli entière

    Au delà de la validité des itérateurs, ce sont ces points qui m'intrigue.

  7. #7
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Freem Voir le message
    Pour vector et string, c'est évident, ça permet d'éviter les réallocations à chaque fois que l'on ajoute/supprime un caractère, et donc un gain de performances non négligeable en vitesse.
    Mais tout cela ne me semble pas expliquer pourquoi, et comment:
    _ test.c_str() qui est censé renvoyer un pointeur volatile (dans le sens, pas stable, à ne pas écrire, à ne pas réutiliser...) sur la chaîne de caractère brute renvoie subitement une chaîne vide ""
    _ le crash de l'appli entière

    Au delà de la validité des itérateurs, ce sont ces points qui m'intrigue.
    Ce qui se passe, c'est que tu as une première insertion à la ligne 12.

    Cela signifie que, avec un peu de chance t1 est peut etre encore valide, mais t2 et t3, pouff, ils sont invalidés quoi qu'il arrive...

    Et encore : il est plus que probable que tu n'obtiennes un résultat cohérent à la ligne 13 que parce que ce sont les crasses qui trainent de la dernière utilisation de la mémoire

    Puis, à la ligne 14, tu utilises à nouveau t2 (qui est quoi qu'il invalide, faut il le rappeler !!! ) pour insérer à nouveau quelque chose dans la chaine...

    Le résultat est sans appel : undefined behaviour

    Et là, c'est au niveau de l'insertion qu'il y a un comportement indéfini

    Tu as déjà de la chance que c_str() décide de renvoyer, si pas un pointeur nul, en tout cas une chaine vide, au moins, cela t'évite de voir défiler les 12 Gb de mémoire dont tu dispose sur ta console
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  8. #8
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    HS:

    Je comptais pas répondre, mais comme il y a des questions (et qu'on est vendredi) ^^

    Citation Envoyé par Freem Voir le message
    - une sécurité sur le type => moué... mais en fait, j'ai encore jamais eu de soucis de type avec un printf. P'tet parce que je ne m'en sers que pour afficher des chaînes de caractères et des entiers?
    Parce que tu es bon
    Il me semble que gcc fait un warning si on se trompe. Mais il ne faut surtout inverser %d et %s (!)

    Citation Envoyé par Freem Voir le message
    - la possibilité d'ajouter ses propres types => rien ne m'empêche d'ajouter un opérateur de cast vers char*
    (Je ne crois pas que cela existe en C) (En C++, on préfèrera utiliser std::string)
    (Si la classe ne t'appartient pas, tu ne peux pas ajouter un tel opérateur toi-même.)

    Citation Envoyé par Freem Voir le message
    - les flux ne permettent pas un formatage aisé et lisible, par exemple, dans les printf utilisés, ça aurait donné std::cout << test << ":: " << *t1 << " " << *t2 << " " << *t3 << std::endl; au lieu de printf("%s:: %c %c %c\r\n",test.c_str(),*t1,*t2,*t3);. Lequel est le plus lisible?
    (Question de goût ) (Le std::endl de C++ est une bonne chose)
    Avec printf tu es obligé de compter les % et les arguments si tu veux en insérer au milieu. C'est une source d'erreur trop grande pour moi :s

    Citation Envoyé par Freem Voir le message
    Pour finir sur cette parenthèse: les deux ont leurs avantages, et si je pense que cin/cout remplacent avantageusement certains trucs du C, il s'agit de puts et gets. printf/scanf ont un rôle très différent du simple affichage: l'affichage formaté, avec contrôle et tolérance d'erreur fins.
    Les iostreams ont les manipulators (et autres).

    Citation Envoyé par Freem Voir le message
    Le C est typé, juste plus faiblement que le C++ (vu qu'en C on peut caster à l'arrache, contrairement au C++ qui... permet de le faire d'ailleurs, via reinterpret_cast<>() le honni mais parfois nécessaire ).
    Oui, faiblement typé est plus exact. (cast implicites et pas toujours de détection d'erreur des types)

    Je ne prétends pas que les flux sont toujours meilleurs que printf et scanf et cie (quoique ^^).
    Mais il sont plus sûrs et plus simple dans la majorité des cas

  9. #9
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Ehonn Voir le message
    (Question de goût ) (Le std::endl de C++ est une bonne chose)
    Avec printf tu es obligé de compter les % et les arguments si tu veux en insérer au milieu. C'est une source d'erreur trop grande pour moi :s
    A vrai dire, je crois que j'utiliserais cout, mais que je mettrais surtout le code un peu plus en forme.

    Quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    std::cout << test << ":: " 
              << *t1 << " " 
              << *t2 << " " 
              << *t3 << std::endl;
    Cela ne change rien, mais au moins, il suffit de compter les lignes pour savoir si on a bien affiché tout ce qu'il nous fallait

    Tu me diras que tu peux faire pareil avec printf pour ce qui en est des variables que tu transmets, mais, pour ce qui en est de la chaine de formattage et des %...

    Imagine un peu un printf proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    printf("%s %d %f %s %d %d %d %d %u",/* heu, j'ai combien de paramètres là ??? */ );
    Faut s'user les yeux pour arriver à compter
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  10. #10
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par Freem Voir le message
    Le C est typé, juste plus faiblement que le C++ (vu qu'en C on peut caster à l'arrache, contrairement au C++ qui... permet de le faire d'ailleurs, via reinterpret_cast<>() le honni mais parfois nécessaire ).
    Je ne suis pas sûr que le reinterpret_cast soit le seul cas du cast en C.
    En C++ on a 3 cast, et les cast "à l'arrache" du C sont plus proches d'un static_cast il me semble.
    Le reinterpret_cast n'est en tous cas pas honni ni honteux, il a son utilité, et c'est pour ma part celui que j'utilise au quotidien au boulot.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  11. #11
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Je ne suis pas sûr que le reinterpret_cast soit le seul cas du cast en C.
    En C++ on a 3 cast, et les cast "à l'arrache" du C sont plus proches d'un static_cast il me semble.
    Le reinterpret_cast n'est en tous cas pas honni ni honteux, il a son utilité, et c'est pour ma part celui que j'utilise au quotidien au boulot.
    Humm... Non...

    Le reinterpret_cast est bel et bien beaucoup plus proche du cast "à l'arrache" que l'on a en C que le static_cast...
    Si tu tentes le code suivant
    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
    21
    22
    23
    24
    25
    26
    struct A
    {
    };
    struct B : public A
    {
    };
    struct C
    {
     
    };
    int main()
    {
        A * a = new A;
        B* realB = static_cast<B*>(a); // accepté :types compatibles à la
                                       // compilation, mais crash au runtime si on essaye
                                       // d'accéder au contenu de realB 
       // C* maybeC = static_cast<C*>(a);// refusé :
       //                                // invalid static_cast from type 'A*' to 
                                         // type 'C*'
        C * hugglyC = reinterpret_cast<C*>(a); // accepté : tu sais ce que tu fais
                                               // mais crash au runtime si on essaye
                                               // d'accéder au contenu de hugglyC 
       C * cCast = (C*)a; //cast C style... oupsss
        delete a;
        return 0;
    }
    il compile très bien, tant que le début de la ligne 16 reste commenté.

    Si tu dé commentes le début de la ligne C pour permettre la déclaration de maybeC, tu auras l'erreur indiquée

    Tant à la compilation qu'à l'exécution, le transtypage C style et le reinterpret_cast auront le même résultat

    La seule différence entre les deux est qu'il sera plus facile de trouver les reinterpret_cast avec une commande comme grep qu'un cast C style (car (C* ) peut etre une liste d'arguments dans le prototype d'une fonction )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  12. #12
    Membre émérite
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2008
    Messages
    832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 832
    Points : 2 625
    Points
    2 625
    Par défaut
    Citation Envoyé par Ehonn Voir le message
    HS:
    Parce que tu es bon
    Il me semble que gcc fait un warning si on se trompe. Mais il ne faut surtout inverser %d et %s (!)
    Le jour ou je me considérerais bon à nouveau signifieras que j'aurai retrouvé mon niveau d'il y a 10 ans: faible

    C'est amusant de voir que quand on apprend, on apprend surtout qu'il en reste beaucoup à apprendre. C'est ce qui fait que C++ m'amuse autant, je pense. (Et puis, franchement, je me poserais toujours la question de pourquoi les langages comme java et c# conservent le new, alors qu'il n'y a pas de delete, entres autres )

    (Je ne crois pas que cela existe en C) (En C++, on préfèrera utiliser std::string)
    (Si la classe ne t'appartient pas, tu ne peux pas ajouter un tel opérateur toi-même.)
    Pas en C, non. Pour ça que j'utilise C++

    Et si la classe ne t'appartiens pas, il me semble que la syntaxe suivante doit être possible (à vérifier):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    operator NouveauType (AncienType);
    (Question de goût ) (Le std::endl de C++ est une bonne chose)
    Avec printf tu es obligé de compter les % et les arguments si tu veux en insérer au milieu. C'est une source d'erreur trop grande pour moi :s
    Les iostreams ont les manipulators (et autres).
    Connaît pas.

    Oui, faiblement typé est plus exact. (cast implicites et pas toujours de détection d'erreur des types)
    C++ permet également les cast implicites, si un opérateur adapté existe (comme pour le C, d'une certaine façon: caster implicitement un float en int fonctionne, de mémoire, avec juste un warning).
    La preuve: std::string str;str+= "hello"; compile.

    Les cast implicites ne sont pas le mal, ils réduisent le nombre de lignes de code et le rendent plus clair, intuitif. Il faut juste faire attention à ne pas définir de surcharge d'opérateur non intuitive. Ce qui est, à mon avis (qui n'est pas unique, à en juger l'article français de wikipedia) le cas de l'opérateur de décalage de bits surchargé en flux.

    Mais il sont plus sûrs et plus simple dans la majorité des cas
    Je suis d'accord, ils ont chacun leurs rôles, et si le C++ considère le C et sa lib standard comme une partie de lui-même, c'est qu'il y a une raison, je pense.

    Citation Envoyé par koala01 Voir le message
    A vrai dire, je crois que j'utiliserais cout, mais que je mettrais surtout le code un peu plus en forme.

    Quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    std::cout << test << ":: " 
              << *t1 << " " 
              << *t2 << " " 
              << *t3 << std::endl;
    Cela ne change rien, mais au moins, il suffit de compter les lignes pour savoir si on a bien affiché tout ce qu'il nous fallait

    Tu me diras que tu peux faire pareil avec printf pour ce qui en est des variables que tu transmets, mais, pour ce qui en est de la chaine de formattage et des %...

    Imagine un peu un printf proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    printf("%s %d %f %s %d %d %d %d %u",/* heu, j'ai combien de paramètres là ??? */ );
    Faut s'user les yeux pour arriver à compter
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    printf("%s::\
    %c \
    %c \
    %c \n",
    test, *t1,*t2);
    C'est moche, mais ça marche

    Citation Envoyé par Bousk Voir le message
    Je ne suis pas sûr que le reinterpret_cast soit le seul cas du cast en C.
    En C++ on a 3 cast, et les cast "à l'arrache" du C sont plus proches d'un static_cast il me semble.
    Le reinterpret_cast n'est en tous cas pas honni ni honteux, il a son utilité, et c'est pour ma part celui que j'utilise au quotidien au boulot.
    4 cast, en C++.
    _ static_cast
    _ dynamic_cast
    _ reinterpret_cast
    _ const_cast
    Et si tu utilises le reinterpret_cast tout le temps, ne viens pas me parler de type safety

    Les 2 cast C++ à éviter au maximum, mais qui ont leur rôle, sont le const_cast et reinterpret_cast. Le dynamic_cast fait quelques vérifications à la compilation et le reste à l'exécution (renvoie un pointeur nul ou une exception si le typage n'est pas compatible) et consomme donc du temps d'exécution. Le static_cast lui fait toutes les vérifications à la compilation, et assure donc un typage fort gratuit en terme de performances.

    Mais on ne peut pas l'utiliser tout le temps: il impose l'existence d'opérateurs de cast, qui sont souvent manquant dans la lib standard.


    Pour en revenir au sujet, j'ai constaté hier que, contrairement aux stack, queue et priority_stack, string ne permet pas de changer le conteneur sous-jacent: c'est vector ou rien.
    Il va donc me falloir ré-implémenter std::string sur une base de std::list, de sorte que l'insertion ne devrait plus poser de problèmes je pense. Naturellement, la suppression risque d'être nettement plus hasardeuse...
    A moins que quelqu'un ait une meilleure idée?

    Ah oui, j'oubliais, le but.
    Je suppose que tout le monde connaît scintilla et la possibilité assez limitée (pas de sauts de lignes, par exemple, ni de copier/coller) d'insérer du texte à plusieurs endroits à la fois. C'est un peu ce que je cherche à faire, un buffer permettant ce genre de manipulation, d'où la tentative foireuse


    [edit]
    Ah, non, la syntaxe ne marche pas: doit être un membre non static.
    Reste la stratégie de créer une classe qui à un constructeur qui prend en paramètre AncienType et possède un opérateur de cast vers NouveauType:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    class Proxy
    {
    public:
      Proxy(AncienType const&);
      operator NouveauType(void)const;
    };
    Ainsi que la stratégie d'héritage/surcharge:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    class Proxy: public AncienType
    {
    public:
      operator NouveauType(void)const;
    };
    Ou, tout bêtement, ajouter le constructeur nécessaire dans la classe de destination (si on la possède).

    Bref, je crois que les solutions ne manquent pas

  13. #13
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par Freem Voir le message
    C++ permet également les cast implicites, si un opérateur adapté existe (comme pour le C, d'une certaine façon: caster implicitement un float en int fonctionne, de mémoire, avec juste un warning).
    La preuve: std::string str;str+= "hello"; compile.
    (Oui, mais c'est l'auteur de la classe qui fournit cet opérateur de cast ou le constructeur non explicit qui va bien.)
    Il y a certains cast possible en C++ à cause de "l'héritage" de C. Par exemple on peut affecter le char '\0' à un entier ou une adresse (car '\0' c'est 0). Pourtant l'utilisateur s'est sûrement trompé, s'il avait voulu écrire 0, il aurait écrit 0 et non '\0'.
    En revanche ton exemple n'est pas bien choisi car std::string définit l'opérateur += sur un char const * http://www.cplusplus.com/reference/string/string/operator+=/

    Citation Envoyé par Freem Voir le message
    Les cast implicites ne sont pas le mal, ils réduisent le nombre de lignes de code et le rendent plus clair, intuitif. Il faut juste faire attention à ne pas définir de surcharge d'opérateur non intuitive. Ce qui est, à mon avis (qui n'est pas unique, à en juger l'article français de wikipedia) le cas de l'opérateur de décalage de bits surchargé en flux.
    Oui, un simple opérateur peut cacher des choses en C++. Avantages (sucre syntaxique, habitude) et/ou inconvénients (si les opérateurs sont mal choisis ou mal écrits).
    Pour la "confusion" de << (flux) avec l'opérateur de décalage. J'ai jamais eu ce souci car j'ai appris le C++ avant le C. Je ne me sert pas des décalages (et laisse le compilateur faire ce genre d'optimisation).
    Mais on peut se demander pourquoi il n'ont pas créer un nouvel opérateur. (De la même façon, pourquoi ne pas utiliser @ au lieu de & pour les références: au cas où le C introduirait @).

    Citation Envoyé par Freem Voir le message
    4 cast, en C++.
    _ static_cast
    _ dynamic_cast
    _ reinterpret_cast
    _ const_cast
    Et si tu utilises le reinterpret_cast tout le temps, ne viens pas me parler de type safety

    Les 2 cast C++ à éviter au maximum, mais qui ont leur rôle, sont le const_cast et reinterpret_cast. Le dynamic_cast fait quelques vérifications à la compilation et le reste à l'exécution (renvoie un pointeur nul ou une exception si le typage n'est pas compatible) et consomme donc du temps d'exécution. Le static_cast lui fait toutes les vérifications à la compilation, et assure donc un typage fort gratuit en terme de performances.
    http://cpp.developpez.com/faq/cpp/?page=conversions
    (const_cast est implicite (donc "inutile" (?)))
    Personnellement j'utilise les constructeurs. Si je veux un int depuis un float, je créé un nouveau int.

    Bon courage dans ton projet

  14. #14
    Membre émérite
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2008
    Messages
    832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 832
    Points : 2 625
    Points
    2 625
    Par défaut
    Citation Envoyé par Ehonn Voir le message
    http://cpp.developpez.com/faq/cpp/?page=conversions
    (const_cast est implicite (donc "inutile" (?)))
    Personnellement j'utilise les constructeurs. Si je veux un int depuis un float, je créé un nouveau int.

    Bon courage dans ton projet
    Il est implicite quand il s'agit de passer une donnée de non-const vers const. Dans l'autre sens, tu as une erreur de compilation. Il pourrait donc arriver qu'un recours au const_cast soit nécessaire (mais je n'ai jamais eu le cas pour le moment).

    Merci

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Crash étrange d'un QTimer
    Par esteban dans le forum Qt
    Réponses: 9
    Dernier message: 31/10/2012, 16h48
  2. Réponses: 6
    Dernier message: 21/08/2009, 18h20
  3. AppDomain + Generics : Crash étranges
    Par smyley dans le forum Général Dotnet
    Réponses: 8
    Dernier message: 05/10/2008, 21h38
  4. Réponses: 32
    Dernier message: 21/08/2008, 12h27
  5. Très étrange.. Bug impression etat
    Par Invité dans le forum Access
    Réponses: 2
    Dernier message: 01/08/2006, 11h44

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