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 :

[C++] Pourquoi et comment ce code fonctionne ?


Sujet :

C++

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2004
    Messages
    228
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2004
    Messages : 228
    Points : 102
    Points
    102
    Par défaut [C++] Pourquoi et comment ce code fonctionne ?
    Bonjour,

    Quelqu'un peut il m'expliquer pourquoi ce code fonctionne ?

    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
    #include <iostream>
     
    class test
    {
    public:
        void hello();
    };
     
    void test::hello()
    {
        std::cout<<"hello"<<std::endl;
    }
     
     
    int main ()
    {
        test * ptr = NULL;
        ptr->hello();
        return 0;
    }
    J'ai bien un "hello" sur la console...

  2. #2
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Points : 15 920
    Points
    15 920
    Par défaut
    La fonction test::hello() n'accède à aucune donnée de l'objet test, donc un appel de cette fonction sur une instance invalide aura de fortes chances de fonctionner.

    Plus simplement, dis toi que le compilo va traduire ta fonction membre de cette manière :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void test_hello(test* this)
    {
        std::cout<<"hello"<<std::endl;
    }
    test_hello(NULL);

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2004
    Messages
    228
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2004
    Messages : 228
    Points : 102
    Points
    102
    Par défaut .
    Je penssais à une chose dans le genre mais trop vraiment y croire. Du coup ma fonction hello n'est pas "crée" comme une fonction membre de ma classe, mais comme une bête fonction à la sauce C.

    Question supplementaire, pour eviter ce genre de comportement, le seul moyen est de faire appel à une donnée membre ?

  4. #4
    Rédacteur

    Avatar de ram-0000
    Homme Profil pro
    Consultant en sécurité
    Inscrit en
    Mai 2007
    Messages
    11 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Consultant en sécurité
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2007
    Messages : 11 517
    Points : 50 367
    Points
    50 367
    Par défaut
    Citation Envoyé par chronos Voir le message
    Question supplementaire, pour eviter ce genre de comportement, le seul moyen est de faire appel à une donnée membre ?
    Ou de tester this
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if(this == NULL)
    {
       ...
    }
    Raymond
    Vous souhaitez participer à la rubrique Réseaux ? Contactez-moi

    Cafuro Cafuro est un outil SNMP dont le but est d'aider les administrateurs système et réseau à configurer leurs équipements SNMP réseau.
    e-verbe Un logiciel de conjugaison des verbes de la langue française.

    Ma page personnelle sur DVP
    .

  5. #5
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2004
    Messages
    228
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2004
    Messages : 228
    Points : 102
    Points
    102
    Par défaut
    Effectivement, cette solution me plait bien ^^

    Merci à vous

  6. #6
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Ou rendre la fonction virtuelle...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Points : 15 920
    Points
    15 920
    Par défaut
    Sauf que c'est pas vraiment à tes fonctions membres de s'assurer que this est correct.

  8. #8
    Expert éminent

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    4 253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2007
    Messages : 4 253
    Points : 7 618
    Points
    7 618
    Billets dans le blog
    3
    Par défaut
    Citation Envoyé par chronos Voir le message
    Je penssais à une chose dans le genre mais trop vraiment y croire. Du coup ma fonction hello n'est pas "crée" comme une fonction membre de ma classe, mais comme une bête fonction à la sauce C.
    Heu... non.... tu confonds tout là...

    Le code "machine" utilisé par le CPU n'a même pas la notion de "fonction".
    Pour le CPU, il n'y a pas de fonction membre / fonction pas membre / statique ou autre... juste de la mémoire et une pile d'execution.

    Quand à l'utilisation d'une donnée membre, aucune vérification ne sera faite non plus... le CPU va juste taper dans la mémoire autour de this (null) + offset... donc tu auras une exception genre: "illegal memory access to memory at 0x000000C"
    L'utilisation d'une fonction virtuelle sera pareil, puisque le CPU ira taper dans la donnée membre à l'offset 0 (la table virtuelle).

    Il n'y a pas moyen de se prémunir contre ce genre de mauvaise programmation hors du runtime, sauf à utiliser un vrai compilateur, qui va probablement te marquer le code ainsi écrit comme erreur (déréférencement de 'NULL'). En tout cas, VC9 me met une jolie erreur sur l'appel à Hello !
    N'oubliez pas de cliquer sur mais aussi sur si un commentaire vous a été utile !
    Et surtout

  9. #9
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    En quoi l'appel d'une fonction non-virtuelle n'utilisant pas this est-il un déréférencement de pointeur nul ? Puisque le pointeur n'est justement pas déréférencé!

    Je suis étonné que VC9 mette une erreur pour un type C++ natif, alors que la bibliothèque MFC elle-même emploie ce genre de stratagème pour certaines fonctions:
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    HWND CWnd::GetSafeHwnd()
    {
    	return (this==NULL ? NULL : this->m_hWnd);
    }
    PS: Est-ce une erreur d'éxécution ou de compilation ? Dans le cas d'une erreur d'exécution, se produit-elle aussi en mode Release ?

    Edit: Notez qu'en .Net, appeler une méthode, virtuelle ou non, causera une NullReferenceException si la référence .Net est nulle.
    http://blogs.msdn.com/oldnewthing/ar...6/4407029.aspx
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  10. #10
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    En quoi l'appel d'une fonction non-virtuelle n'utilisant pas this est-il un déréférencement de pointeur nul ? Puisque le pointeur n'est justement pas déréférencé!
    Jette un coup d'oeil à http://www.developpez.net/forums/d61...uter/pointeur/

  11. #11
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Mais ici, le pointeur est peut-être nul, mais il est initialisé dans tous les cas...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  12. #12
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    A mon avis, c'est un peu le même genre d'idée. Il suffit de mettre avant d'entrer dans la méthode une vérification ecx!=NULL. Il me semble qu'il y a sous visual une option pour ajouter un bout de code avant l'entrée dans une méthode. Peut-être que VC9 utilise ce genre de mécanisme. A confirmer par un expert de VC9.

  13. #13
    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
    Appeler une fonction membre non virtuelle sur un pointeur nul, ou un pointeur invalide, tant que l'on accède pas à une donnée membre de la classe me semble tout à fait valide.

    Sinon, je vois mal comment delete this; pourrait marcher (surtout ce qui vient après).

    Mais j'avoue ne pas trouver de standardese qui supporte ce cas... J'ai juste trouvé :
    Citation Envoyé par 9.3.1/2
    If a non-static member function of a class X is called for an object that is not of type X, or of a type derived from X, the behavior is undefined.
    Mais ça parle du type de l'objet, pas de sa validité.
    Je suis un peu perplexe.
    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.

  14. #14
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    3.7.3.2/4 -> The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined
    5.2.2/1 -> For a member function call, the postfix expression shall be an implicit (9.3.1, 9.4) or explicit class member access (5.2.5)
    5.2.5/3 -> If E1 has the type “pointer to class X,” then the expression E1->E2 is converted to the equivalent form (*(E1)).E2;

    L'appel est donc considéré comme un accès à un membre. Dans le cas du delete this, tant qu'on n'a pas d'accès à un membre (même fonction appelé implicitement), qu'est-ce qui poserait problème?

    A nouveau, une des raisons pour rendre indéfinie toute utilisation de pointeurs invalides c'est que le fait de simplement les charger dans un registre peut poser problème pour certaines architectures -- faut pas aller très loin pour l'architecture, x86 suffit; il faut chercher plus pour trouver un OS qui fonctionne en mode protégé avec des segments, mais ça m'étonnerait fort qu'il n'y en ait pas.
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  15. #15
    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
    En fait, la question que je me posais était la suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class A
    {
        void f() {cout << "Hello";}
        void g() {delete this; f();}
    };
    Dans ce cas, comme this ne sert à rien pour f, on pourrait imaginer qu'il soit supprimé de l'appel, et donc que le code reste valide. Mais je pense que ce serait difficile pour le compilateur de voir ça, et donc qu'on a préféré dire qu'il s'agit d'un comportement indéfini.

    En fait, je crois que naturellement, je me serais trompé, et que j'aurais cru le code en question valide. Je suis un peu troublé par le fait que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void A::g() 
    {
        delete this;
        cout << "Hello";
    }
    Serait du code valide, alors qu'avec une fonction séparée, ce n'est plus le cas. Ce qui pourrait ressembler à un simple refactoring de type "extract method" change en fait la validité du code. Même s'il est vrai qu'un tel refactoring bien fait mettrait f en statique, et là, plus de problèmes.
    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.

  16. #16
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Faire delete this de toutes manières c'est jamais très propre...
    Boost ftw

  17. #17
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Mais pourtant nécessaire au comptage de références intrusif, dès que tu utilises des DLLs (la fonction de Release doit être membre et virtuelle)...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  18. #18
    Expert éminent

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    4 253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2007
    Messages : 4 253
    Points : 7 618
    Points
    7 618
    Billets dans le blog
    3
    Par défaut
    Ben je sais pas pour vous....
    Mais pour moi valide, ca veut pas dire "qui compile et qui marche"....

    Si je peux écrire dans le code { delete this; this->g(); } ou tout simplemenet { A* a = NULL; a->g(); } sans que le compilo me mette au moins un enorme warning (voir même l'IDE carrément en me soulignant le code en question en rouge), alors c'est que l'IDE en question a un *gros* probleme.

    Je me fiche de savoir si g() accède ou non à des membres de l'objet, c'est pas mon problême, c'est celui de l'objet... Si jamais il lui vient l'idée de le faire, alors j'aurai de gros problêmes.... Et si la fonction en question n'accèdera jamais a des membres de l'objet, alors, elle doit être STATIQUE !
    N'oubliez pas de cliquer sur mais aussi sur si un commentaire vous a été utile !
    Et surtout

  19. #19
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Le compilateur n'est pas en devoir de te signaler les comportements indéfinis que tu invoques...
    Boost ftw

  20. #20
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 377
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 377
    Points : 23 663
    Points
    23 663
    Par défaut
    Citation Envoyé par nicroman Voir le message
    Si je peux écrire dans le code { delete this; this->g(); } ou tout simplemenet { A* a = NULL; a->g(); } sans que le compilo me mette au moins un enorme warning (voir même l'IDE carrément en me soulignant le code en question en rouge), alors c'est que l'IDE en question a un *gros* probleme.
    Dans l'absolu, ta méthode pourrait toujours accéder à this (sauf trap values, mais ça ne se produit pas sur PC).

    Pour le reste, le compilateur ne peut pas te mettre un énorme avertissement car « A * a = NULL; » est séparé de « a->g(); ». Dans ton exemple, les deux instructions sont consécutives, mais elles pourraient être séparées de plusieurs lignes et dans l'intervalle, la valeur de « a » peut changer.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 5
    Dernier message: 17/02/2011, 14h45
  2. [XMLHttpRequest] Pourquoi ce code fonctionne sous IE8 et pas sur Firexox 3.0.11
    Par homeostasie dans le forum Général JavaScript
    Réponses: 12
    Dernier message: 23/07/2009, 14h50
  3. Réponses: 6
    Dernier message: 11/07/2007, 13h29
  4. Réponses: 6
    Dernier message: 12/04/2007, 13h58
  5. Réponses: 1
    Dernier message: 02/06/2005, 19h23

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