Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Mauvais code] Pourquoi ?

    29 novembre 2007 à 21:18:58

    Ce post contient un exemple de ce qu'il ne faut jamais faire.


    Bonjour tout le monde !

    J'ai récemment dû "corrigé" le code d'une personne débutant en programmation. Le but de l'exercice était le maniement des chaînes mais ce n'est pas ce qui nous intéresse ici. A la fin de cet exercice se trouvait la traditionnelle question qui proposait de demander à l'utilisateur à la fin de l'exécution si il désirait recommencer l'exécution du programme.

    Or cette personne venait de suivre un cours sur la récursion et à proposer la solution suivante:

    1. #include <iostream>
    2. int main()
    3. {
    4.   //Code qui ne nous intéresse pas.
    5.   int reponse;
    6.   std:: cout<< "Voulez-vous recommencer ?[1/0]: " << std::flush;
    7.   std:: cin>> reponse;
    8.   if(reponse)
    9.     main();
    10.   return 0;
    11. }


    Un main récursif !!

    Passé la surprise, j'ai testé son code qui, à ma grande surprise, fonctionne sans problème. Il était cependant évident que ce n'était pas ce que l'auteur de la question attendait.

    La question que je me pose est donc la suivante:

    Pour quel raison ne doit pas utiliser un main récursif du moment que c'est fait correctement et qu'il existe une porte de sortie ? (Tout en précisant que je sais pertinemment que l'utilisation de la récursion dans l'exemple que j'ai présenté est une mauvaise utilisation du concept.)
    Que dire de plus à cette personne à part qu'on utilise pas la récursion pour ça ?

    Merci pour vos réponses si vous en avez.

    Nanoc
    • Partager sur Facebook
    • Partager sur Twitter
    Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
    Anonyme
      29 novembre 2007 à 21:32:16

      pour un petit programme, ça va, mais lorsque tu lance un gros programme, ça veux dire recharger tous depuis le début!
      Ce peut faire gros!
      En plus, si tu utilise un singleton, le fait d'essayer de recréer un objet échouera.

      De plus, main() est le coeur même du programme, lancer une deuxième fois cette fonction est, bien que possible, une abération qui va à l'encontre même de la programmation.
      • Partager sur Facebook
      • Partager sur Twitter
        29 novembre 2007 à 21:45:10

        Citation : Geoffroy

        En plus, si tu utilise un singleton, le fait d'essayer de recréer un objet échouera.



        Les singleton sont créés avant l'appel à la fonction main. Cela ne pose pas de problème.

        Citation : Geoffroy

        pour un petit programme, ça va, mais lorsque tu lance un gros programme, ça veux dire recharger tous depuis le début!
        Ce peut faire gros!


        C'est aussi ce que je lui ais dit. Il m'a répondu que c'était le principe même de la récursion (ce qui est vrai).

        Citation : Geoffroy

        De plus, main() est le coeur même du programme, lancer une deuxième fois cette fonction est, bien que possible, une abération qui va à l'encontre même de la programmation.


        Tout à fait d'accord, mais pourquoi ?

        En fait j'aurais dû préciser en proposant un exemple ou l'utilisation de la récursion est justifié et pas un exemple ou l'idée même de récursion est fausse.
        • Partager sur Facebook
        • Partager sur Twitter
        Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
        Anonyme
          29 novembre 2007 à 21:56:42

          Citation : Nanoc

          C'est aussi ce que je lui ais dit. Il m'a répondu que c'était le principe même de la récursion (ce qui est vrai).


          C'est le principe, mais comme tous les principes, ils ne sont pas bon partout.
          J'aimerai bien voir une telle méthode dans un programme tel qu'un photoshop, ou un autre gros programme du genre!
          En plus, il suffit d'un petit crack path pour surcharger complètement l'ordinateur très facilement!

          Pour le dernier truc, c'est le COEUR du programme, et le comble d'un coeur, ce serai bien de ne pas être unique, car il perdrai son sens!
          • Partager sur Facebook
          • Partager sur Twitter
            29 novembre 2007 à 23:21:42

            De plus, je me demande ce qui arrive lorsque l'on reinitialise des libs ou des fenêtre comme Win32 Qt SDL ou SFML... Disons que je doute que ça soit propre...
            • Partager sur Facebook
            • Partager sur Twitter
            Anonyme
              30 novembre 2007 à 6:18:01

              les libs ne sont pas réinitialisé, seulement la fonctions.
              Mais c'est vrai que dans le cas d'une musique de fond pour le programme, si tu relance main(), elle doit également se lancer une deuxième fois.
              Je vous dit pas ce queça doit donner lorsqu'on fait ça plein de fois!
              • Partager sur Facebook
              • Partager sur Twitter
                30 novembre 2007 à 7:21:00

                Puisque le rôle de main est d'être une porte d'entrée et bien il est rare qu'on entre plusieurs fois sans être sorti!
                • Partager sur Facebook
                • Partager sur Twitter
                  30 novembre 2007 à 19:07:52

                  Entièrement d'accord avec tout ce que vous avez dit. Mais dans ce cas pourquoi n'est-ce pas interdit (J'entends par le compilateur) ?
                  • Partager sur Facebook
                  • Partager sur Twitter
                  Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
                    30 novembre 2007 à 19:25:55

                    S'il n'y a pas des inits/libérations applicatives comme ici, cela ne devrait poser aucun problème.
                    J'ai souvenir de codes du OCCC qui utilisaient ce genre de construction.

                    Potentiellement attention s'il y a des variables locales trop conséquentes dans la fonction. A un moment indéterminé il pourra saturer sa pile.

                    Son principal problème, ici, viendra qu'il va partir en boucle infinie et saturer sa pile s'il rentre autre chose qu'un nombre. Mais cela sera vrai avec toute récursion -- que dis-je? boucle!--, testant mal la condition d'arrêt comme ici (correct -> FAQ C++ de developpez -> #cin_verify). Le côté appel de main() me parrait plutôt anecdotique.

                    Recursion pour un algo de calcul (ou autre) oui.
                    Récursion pour continuer d'interagir, non : cela introduit implicitement un nombre d'interactions max qui est difficilement quantifiable, d'autant qu'il variera selon les opérations réalisées derrière.

                    Citation : Nanoc

                    Citation : Geoffroy

                    En plus, si tu utilises un singleton, le fait d'essayer de recréer un objet échouera.



                    Les singleton sont créés avant l'appel à la fonction main. Cela ne pose pas de problème.


                    Cela dépend. Tous les singletons ne s'initialisent pas paresseusement sans argument.

                    PS: interdire n'est pas dans la philosophie du C ni du C++ -- contrairement à d'autres langages.
                    • Partager sur Facebook
                    • Partager sur Twitter
                    C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                      1 décembre 2007 à 14:44:22

                      Citation : lmghs

                      Potentiellement attention s'il y a des variables locales trop conséquentes dans la fonction. A un moment indéterminé il pourra saturer sa pile.


                      C'est sûr, mais c'est le principal problème de la récursion.

                      Citation : lmghs

                      Son principal problème, ici, viendra qu'il va partir en boucle infinie et saturer sa pile s'il rentre autre chose qu'un nombre. Mais cela sera vrai avec toute récursion -- que dis-je? boucle!--, testant mal la condition d'arrêt comme ici (correct -> FAQ C++ de developpez -> #cin_verify).


                      Oui, oui mais j'ai simplifié le code proposé afin de ne montrer que la partie utile pour la question qui m'intéressait.

                      Citation : lmghs

                      Recursion pour un algo de calcul (ou autre) oui.
                      Récursion pour continuer d'interagir, non : cela introduit implicitement un nombre d'interactions max qui est difficilement quantifiable, d'autant qu'il variera selon les opérations réalisées derrière.

                      C'est donc plutôt cela. On a affaire ici à une mauvaise utilisation de la récursion. Cependant dans le cas présent, si l'on crée un bloc autour de la partie principale du programme (que je n'ai pas mise ici), on ne construit qu'un int à chaque appel ce qui veut dire qu'on peut potentiellement faire quelques millions d'appel récursifs à la fonction main() avant de saturer la pile.

                      Citation : lmghs

                      J'ai souvenir de codes du OCCC qui utilisaient ce genre de construction.


                      Qu'est-ce que le OCCC ? Okhlahoma city Community college ? (Je pense pas mais j'ai rien trouvé d'autre)

                      Citation : lmghs

                      interdire n'est pas dans la philosophie du C ni du C++ -- contrairement à d'autres langages.


                      C'est vrai. Mais alors aurais-tu un exemple où ce genre de construction a une utilité ? (OCCC ?)
                      • Partager sur Facebook
                      • Partager sur Twitter
                      Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
                        2 décembre 2007 à 12:55:25

                        http://cpp.developpez.com/cours/cpp/?page=page_3#LIII-G

                        Ici le main est dit non récursif enfin qu'on ne doit pas l'utiliser en récursif.
                        • Partager sur Facebook
                        • Partager sur Twitter
                        :)
                          2 décembre 2007 à 13:29:10

                          Citation : moii

                          http://cpp.developpez.com/cours/cpp/?page=page_3#LIII-G

                          Ici le main est dit non récursif enfin qu'on ne doit pas l'utiliser en récursif.



                          D'où ma question d'une certaine manière: Pourquoi ? :)
                          • Partager sur Facebook
                          • Partager sur Twitter
                          Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
                            2 décembre 2007 à 14:00:48

                            Je pense, mais je ne suis pas sûr, que de relancer le main, c'est équivalent à relancer le programme (pas de le recharger en mémoir, juste le relancer) ce qui n'est pas corrècte, pour relancer un programme, il faut d'abord qu'il se termine, et le compilo laisse passer ce truc, car main est une fonction comme toutes autres, sauf qu'elle est spéciale (c'est la première à etre appeler, et la dernière à être libérer, un appel = une libération), en cas de récursion plusieurs appelles, une libération, c'est pas normal, et ce n'est pas dutout corrècte! enfin, de mon point de vue.
                            • Partager sur Facebook
                            • Partager sur Twitter

                            La maîtrise des fondamentaux est le fondamental de la Maîtrise.

                              2 décembre 2007 à 16:48:22

                              Mais un programme, c'est une fonction...
                              Regarde les ptits utilitaires Unix : grep, sort, etc.

                              Moi le truc pour l'instant qui pour moi empêcherait d'utiliser main sous forme récursive, c'est que son prototype est imposé...
                              • Partager sur Facebook
                              • Partager sur Twitter
                                2 décembre 2007 à 16:52:40

                                Quoi?
                                • Partager sur Facebook
                                • Partager sur Twitter

                                La maîtrise des fondamentaux est le fondamental de la Maîtrise.

                                  2 décembre 2007 à 19:44:35

                                  Citation : HanLee

                                  Moi le truc pour l'instant qui pour moi empêcherait d'utiliser main sous forme récursive, c'est que son prototype est imposé...



                                  C'est vrai qu'on peut pas en faire grand chose. Mais bon si on code un main récursif, on peut bien coder des variables à coté pour s'affranchir de ce problème.
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                  Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
                                    2 décembre 2007 à 20:16:27

                                    Citation : Lien ci-dessus

                                    La fonction main est appelée par le système d'exploitation, elle ne peut pas être appelée par le programme, c'est-à-dire qu'elle ne peut pas être récursive.



                                    apres je sais pas pourquoi le compilateur le permet et qu'à l'execution ca marche
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      2 décembre 2007 à 20:48:27

                                      Je pense que tu aurais donc du demander sur le site de développé.com sachan que c'est la haut que tu l'a vus.
                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                      :)
                                        3 décembre 2007 à 4:07:06

                                        Voyons...

                                        On a donc défini une qui sera appelée à un certain moment donné qui sera "l'entrée" du programme. Cette fonction est une parmi celles qui créer les variables globales et les détruits. Vous me direz : mais une variable automatique n'as pas besoin de fonction pour être créée elle existe au moment de ça déclaration et à la fin du bloc : erreur. Le compilateur s'occupe de transformer une déclaration de variables et d'une fin de bloc par un "push" en mémoire et un "pop". Cela a été programmé quelque part!

                                        main c'est une fonction, un sous-programme, normal qui n'as aucune spécialité autre que d'être une surcharge d'une fonction définie dans le CRT!

                                        Donc message à ceux qui font du C++ en passant d'être 100% libre de faire ce qu'ils veulent. Plus qu'en C#, en Java ou en PHP et bien : non! :p
                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          3 décembre 2007 à 12:10:08

                                          Au temps pour moi, le vrai sigle, c'est (www.) IOCCC (.org) -> The International Obfuscated C Code Contest.

                                          Et effectivement, si vous vérifiez le prochain brouillon du standard (-> n2135) §3.6.1/3 : "The function main shall not be used (3.2) within a program.", faut pas appeler main.
                                          (NB: Je pêche à voir le rapport à l'ODR (§3.2) ; -> fclc++ pour le décryptage)
                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                          C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                                            3 décembre 2007 à 21:38:58

                                            En effet il y a des perles sur ton lien lmghs.
                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                            Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
                                              4 décembre 2007 à 14:01:24

                                              main est juste une fonction, donc on peut faire un main récurcif, c'est moche, ca fait hurler mais ca marche.

                                              Le bug est dans la notion de porte de sortie, en effet si je lance 3 fois mon programme, j'aurais dans la pile d'exécution

                                              main( main(main ()))

                                              en gros je vais empiler des mains a chaque exécution, ce qui va surcharger la pile.

                                              La sortie s'effectue en fait lorsqu'on décide de quitter le dernier main lancé, alors la on dépile tout le monde.


                                              En pratique, avec une entrée utilisateur faudrait vraiment un frénétique pour remplir la pile et planter l'ordi, donc ca va marcher, mais c'est juste moche.
                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                4 décembre 2007 à 19:18:55

                                                Un petit exemple tiré du lien de lmghs, à ne surtout pas prendre comme exemple.

                                                Si vous aimez le code obscure, je vous propose ce code pour Linux:

                                                Comme vous pourrez le remarquer ;) c'est du C.
                                                1. #include                 <stdio.h>
                                                2. #include                 <sys/time.h>
                                                3. #include                 <X11/Xlib.h>
                                                4. #include                 <X11/keysym.h>
                                                5. #define                  Z(a)main(a,T)
                                                6.                          Window L;
                                                7. #define                  J (K,
                                                8.                          GC N;
                                                9. #define                  I ; for(
                                                10.                          int g,h,i,j,k,l,m,n,p,q,r,v,w,x,y,z,
                                                11. #define                  W(a)(y/v*!w+((S+1)>>a&255)*(D+y/(z|w-2))/D)<<a|
                                                12.                          o,b,f,u,s,c,a,t,e,d, C
                                                13. #define                  U (struct timeval*)H
                                                14.                          [1<<18],A=100,B=4,D=200,E=53300,F=65793,H[4];
                                                15. #define                  V(a)#a": %1d'%02d\"%02d          "
                                                16.                          Display*K; Pixmap M; char P[99]; int main
                                                17. #define                  Y(a,b)(a<b?a:b)
                                                18.                          (int S,char**T){
                                                19. #define                  X(a)\
                                                20. XDrawLine                J M,N,D+l a m,o,D+l a n,o),
                                                21. XEvent                   O I p=E/5; --S&~1?
                                                22. XSetForeground           J N,W(0)W(8)W(16)0),X(-)X(+)gettimeofday(U,0):S?z=97
                                                23.                          == ** ++ T,w=A==z[*T],B-=w[z+*T]%3,S:++S;
                                                24. XCopyArea                J M,L,N,0,0,e,D,0,0),
                                                25. XSync                    J 0),
                                                26. XFlush                   (K),select(0,0,0,0,U+1)){ I 3[H]=F; p==++r?K=
                                                27. XOpenDisplay             (0),N=
                                                28. XCreateGC                J L=
                                                29. XCreateSimpleWindow      J RootWindow(K,0),0,0,e=D*2,D,0,0,0),0,0),
                                                30. XSelectInput             J L,KeyPressMask|KeyReleaseMask),
                                                31. XMapWindow               J L),M=
                                                32. XCreatePixmap            J L,e,d=D,DefaultDepth(K,0)),u=h=q=E:r<p?a=r/D%6,r[C
                                                33.                          +p]=(l+=!a|a%2?g:a-3)/4,r[C]=2*r<=p?(n+=(i-=(i+34&&r
                                                34.                          /149%B)-(i-34&&r/353%3)))/55:C[p-r],v=4-3*z:
                                                35. XPending                 (K)?C[F+
                                                36. XLookupKeysym            (&O.xkey,0&
                                                37. XNextEvent               J&O))]^=O.type==KeyPress||O.type==KeyRelease,p:0;
                                                38.                             /* Some more code */
                                                39.  )I k=C[F+XK_Right]-C[F+XK_Left],j=k?j+v*k*(j*k<8):j*.9,u+=(s=Y(C[F+XK_space]*
                                                40.  3+s-!!s,a))/9,c=u>p?q=Y(h,q),u%=p,Z(c=0):c,t+=j*(s+A)/e-s/i*(C[u+4]-C[u]),s=t
                                                41.  *t>p?t*=.97,s*.8:g>a&&b*b<D*9?i:s,i=k=l=0,o=n=e;  i-5; Z((k/17?o++==y?m=n=D,f
                                                42.  -=A,o=30+9*i++:0,n-=2,m+=3,l=f/.7,y*F+A-i*k:k|!o?f=l=C[u]/-2-t,k+=17*w,o=k+++
                                                43.  20,n=k+k-k*k/9,F*p:o--<a?y=135,A*F|D:0)))I x=g=m=f=0,a=i=u+D,d+=9; o<f?n=2*e,
                                                44.  Z(z?r+k*F*8:E-k*A*A),n=9+x/D,Z(!k+z?r=D*F+9:A<<17),n-=x/9/D,Z(i%p>6?h=r*(3+z)
                                                45.  /5:r),n=x/13/e,Z(k?h:r),o++:(f=x/D+70+C[i+p]-C[u+p],o=x?o:f,k=2*i/41%2,l=C[--
                                                46.  i]-C[u]-t,x+=D-(y=i-u),g=(d-i)%410?g:(a=o,b=l+B*x/e,x)/A,i>u);  )I k=i=0,x=g,
                                                47.  l=b; ++i==41+!f&&k?f=k=i=l=0,x=190,a=163:i==42?i=9,k=r:!k|i-41; n=(Y(Y(Y(75,A
                                                48.  -i)/3,95-2*i),r-k+2*i)-k/r)*x/D,m=i<9?x*14/D:k?(i-31)/5*(i%12)?0:n-1:0,o=a-i*
                                                49.  x/D,y=D-o,h=Z(k*r+k*f/F))I  B=g?B:1-h%3,i=60,y=n=m,XDrawString  J M,N,30,x,P,
                                                50.  sprintf(P,V(Time)V(High)"Speed: %03dmph",h/A/i,h/A%i,h%A,q/A/i,q/A%i,q%A,s,Z(
                                                51.  A<<17))),x=h; ++f<2*z*e; l=(x*=3)%e,o=x*x%a,Z(-1)); } return A**H+1[H]/p-c; }


                                                le résultat est surprenant pour ce petit code. Pour compiler:
                                                gcc fichier.c -lX11 -o nomProgramme


                                                En tout cas si vous y comprenez quelquechose....

                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
                                                Anonyme
                                                  4 décembre 2007 à 20:57:56

                                                  :waw:

                                                  (rien à dire, c'est incroyable que qqch d'aussi illisible puisse faire ça!!!!!)

                                                  EDIT : pour ceux qui ne peuvent/veulent pas tester :
                                                  Image utilisateur
                                                  On contrôle la voiture bleu, tant dis que l'ordi contrôle la verte.
                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    4 décembre 2007 à 21:08:07

                                                    Un petit coup de "gcc -E" devrait t'aider à y voir plus clair...
                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                    C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                                                    Anonyme
                                                      4 décembre 2007 à 21:11:21

                                                      C'est un peu plus lisible, mais le main ....
                                                      C'est quoi l'intérêt de coder comme ça? Y en a pas, hein?
                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        4 décembre 2007 à 21:15:41

                                                        C'est un concours. Réaliser le code le plus obscure possible.
                                                        • Partager sur Facebook
                                                        • Partager sur Twitter
                                                        Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.

                                                        [Mauvais code] Pourquoi ?

                                                        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                        × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                        • Editeur
                                                        • Markdown