Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse

NodeBB

  1. Home
  2. Programmation
  3. Développement de logiciels
  4. C
  5. Cours #4 - Récursivité et stack

Cours #4 - Récursivité et stack

Scheduled Pinned Locked Moved C
6 Posts 3 Posters 6.3k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • AlexMogA
    AlexMogA
    AlexMog
    Modérateur spécialisé
    wrote on last edited by
    #1

    <p>Bonjour à tous,<br/>
    Nous passons encore à un autre niveau aujourd'hui, et nous allons voir ensemble la Récursivité, et la notion de Stack.</p>
    <p><strong>I- La récursivité, c'est quoi?</strong><br/>
    La récursivité, c'est un autre moyen de provoquer une "boucle" dans une fonction.<br/>
    C'est totalement différent de ce que je vous ai expliqué avant. Nous avions vu la partie "itérative" du C, qui corresponds à exécuter un programme, ligne par ligne. Ici, nous allons apprendre un peux plus les fonctions de la récursivité, et comment elle réagit sur la stack.<br/>
    C'est une façon de faire, pour qu'une fonction se rappelle elle-même.</p>
    <p>Prenons l'exemple suivant:</p>
    <pre class="ipsCode prettyprint">
    int test(int a) {
    a++;
    if (a < 12)
    test(a);
    return (a);
    }
    int main(void) {
    my_putnbr(test(1));// my_putnbr est une fonction permettant d'afficher une valeur numérique. Vous devez la re-créer ou utiliser printf (ce qui est interdit par la norme! Re-créez la, ça vous apprendra pas mal de choses!)
    }</pre>
    <p>La fonction "test" est ici récursive.<br/>
    Ce code nous affichera: 12<br/>
    Vous l'aurez compris, la récursivité peut être utile dans plusieurs cas (pour annecdote, my_putnbr peut être codé en 3 lignes avec de la récursivité).<br/>
    Les fonctions récursives peuvent êtres comparées à des poupées russes s'emboitant.<br/>
    Ne vous perdez pas! Et ne vous inquiétez pas! Je vais mieux vous l'expliquer en vous expliquant le fonctionnement de la stack.</p>
    <p><strong>II- La stack? DAFUQ?</strong><br/>
    Je vais pouvoir vous expliquer une notion qui est assez floue dans le cerveau de beaucoup de développeurs: la stack.<br/>
    La stack est une mémoire assignée à votre programme pour la prise en charge de tout ce qui est "static" dans votre programme (d'où le nom "stack").<br/>
    Lors du lancement de votre programme, la stack est vide. Si vous appelez la fonction "test" celle-ci va se rajouter dans la stack. Si, de la fonction "test", vous appelez la fonction "my_putstr" celle-ci va se rajouter dans la stack, de même pour la fonction "my_putchar" contenue dans la fonction "my_putstr" qui fera elle-même appel à la fonction "write" qui se rajoutera à son tour à la stack.<br/>
    La stack a donc constitué une liste d'exécution. On peut re-définir l'ordre d'exécution précédent<br/>
    comme ceci:<br/>
     </p>
    <blockquote class="ipsQuote" data-ipsquote=""><div>write - my_putchar - my_putstr - test.
    <p> </p>
    </div></blockquote>
    <p>Il ne faut pas oublier que la stack est une mémoire, et qu'elle va stocker tout ce qui est statique dans notre programme. Donc, si nous la sur-utilisons (une boucle infinie de fonctions par exemple: surempiler les poupées russes), nous risquons de faire segfault (segmentation fault) notre programme (C'est souvent une explication pour les programme qui segfault sans raisons).<br/>
    Lorsqu'une fonction finit son exécution, elle est supprimée de la stack.<br/>
    Pour vous faire un schéma, imaginez un tas de vaisselle: à chaque fois, vous rajoutez une assiette sale sur le tat, et lorsque vous faites la vaiselle, vous enlevez vos assiettes dans l'ordre contraire de celui de l'empilation.</p>
    <p>Reprenons la théorie: Une fonction récursive est une fonction qui se rappelle elle-même. Elle se rajoute donc sur la stack, puis se rappelle. Elle se rajoute donc encore une fois sur la stack, puis se rappelle...etc...<br/>
    Et là, deux choses peuvent avoir lieu: Soit on atteint la taille maximum de la stack (définie par le système), et on provoque un segfault, sinon, et c'est ce que vous devrez faire la plupart du temps en utilisant les récursifs, vous devez prévoir une condition d'arrêt du rappel de cette fonction, donc à un moment de votre récursivité, vous dites STOP, cette fois je ne me rappelle pas, car mon rôle est terminé. A ce moment là, vous allez libérer la stack de toutes les fonctions que vous avez au préalable ajouté.</p>
    <p>Le mieux, reste encore de vous montrer un exemple de ce qu'il ne faut pas faire:<br/>
    Créons un programme qui va afficher "hello" indéfiniment:</p>
    <pre class="ipsCode prettyprint">
    void my_putchar(char c) {
    write(1, &c, 1);
    }
    void fg() {
    my_putchar('h');
    my_putchar('e');
    my_putchar('l');
    my_putchar('l');
    my_putchar('o');
    my_putchar('\n');
    fg();
    }
    int main(void) {
    fg();
    }</pre>
    <p>Effectivement, on voit "hello" s'afficher plusieurs fois, mais si on laisse tourner notre programme jusqu'à ce que la stack soit remplie, on remarque de notre programme crash, et qu'un segfault est apparu.<br/>
    C'est l'exemple typique de ce que l'on peut attendre au niveau des problèmes liés à la récursivité.<br/>
    Un autre exemple, c'est notre légendaire my_putnbr:</p>
    <pre class="ipsCode prettyprint">
    void my_put_nbr(int nb) {
    if (nb <= 9 && nb >= 0)
    my_putchar(nb + '0');
    else
    {
    my_put_nbr(nb / 10);
    my_put_nbr(nb % 10);
    }
    }</pre>
    <p>c'est typiquement la bonne utilisation de la récursivité.<br/>
    Voilà, j'espère vous avoir encore aidé au niveau de votre apprentissage avancé du C.<br/>
    Rendez-vous au prochain cours!<br/>
    Cours écrit par AlexMog. Contact: alexmog [at] live [point] fr</p>

    Multiplayer GameDev @ Unexpected
    

    Mon CV

    1 Reply Last reply
    4
    • AzadA
      AzadA
      Azad
      wrote on last edited by
      #2

      <p>Très bon tutoriel, c'est sympa que tu expliques pas à pas les différents points du C. 🙂 <br/>
      Peut-être un peu plus d'espace, sinon toujours pareil : Bien joué. 😉 <br/><br/>
      +1 point de rep.</p>

      Administrateur du forum.
      Contactez-moi par message privé ou par mail.

      1 Reply Last reply
      0
      • C
        C
        cegdd
        wrote on last edited by
        #3

        <blockquote class="ipsQuote" data-cite="AlexMog" data-ipsquote="" data-ipsquote-contentclass="forums_Topic" data-ipsquote-contentcommentid="5224" data-ipsquote-contentid="483" data-ipsquote-contenttype="forums" data-ipsquote-timestamp="1397065912" data-ipsquote-username="AlexMog"><div>
        <div>La stack est une mémoire assignée à votre programme pour la prise en charge de tout ce qui est "static" dans votre programme (d'où le nom "stack").<p></p><p>Lors du lancement de votre programme, la stack est vide. Si vous appelez la fonction "test" celle-ci va se rajouter dans la stack.</p>
        </div>
        </div></blockquote>
        <p> </p>
        <p>J'ai compris a quoi ça sert de stocker ce qui est static dans une mémoire, mais pourquoi stocker les fonctions pour les supprimer par la suite ?</p>
        <p>En quoi est-ce utile pour le programme ?</p>

        créateur de Reconquête-Salvatrice, un petit RPG 2D en ligne multi-plateforme [OpenGL / C]

        1 Reply Last reply
        0
        • AlexMogA
          AlexMogA
          AlexMog
          Modérateur spécialisé
          wrote on last edited by
          #4

          <blockquote class="ipsQuote" data-cite="cegdd" data-ipsquote="" data-ipsquote-contentclass="forums_Topic" data-ipsquote-contentcommentid="6285" data-ipsquote-contentid="483" data-ipsquote-contenttype="forums" data-ipsquote-timestamp="1398274860" data-ipsquote-username="cegdd"><div>
          <div>
          <p>J'ai compris a quoi ça sert de stocker ce qui est static dans une mémoire, mais pourquoi stocker les fonctions pour les supprimer par la suite ?</p>
          <p>En quoi est-ce utile pour le programme ?</p>
          </div>
          </div></blockquote>
          <p>En réalité, tout ce passe au niveau du code ASM.</p>
          <p>Pour éviter de modifier la stack précédente, et être sûr que ladite stack ne soit pas modifiée, toutes les fonctions font une sauvegarde temporaire de la stack (c'est ce qu'on appelle le "generic push" en ASM).</p>
          <p>La stack est donc remplie temporairement avec les anciennes valeurs.</p>
          <p>Qui plus est, lorsque tu utilise une fonction qui contient elle même une variable, ou qui passe une variable en paramètre, celle-ci est stockée en stack (c'est pour cela qu'on préfèrera envoyer un pointeur plutôt qu'une structure en dur dans une fonctions 😉 ) (pour info: un pointeur = 8 octets (une structure contenant 2 int = 8 * 2 octets. Le choix est vite fait.)</p>

          Multiplayer GameDev @ Unexpected
          

          Mon CV

          1 Reply Last reply
          0
          • AlexMogA
            AlexMogA
            AlexMog
            Modérateur spécialisé
            wrote on last edited by
            #5

            <p>Un exemple de code ASM avec un generic push:</p>
            <pre class="ipsCode prettyprint">
            ;; exemple avec la fonction "hello world" x86 asm
            global main
            extern printf

            section .text
            main:
            push rbp ;; Ici, le generic push
            mov rbp, rsp ;; Suite du generic push (on le retrouvera dans toutes les fonctions en C (sans exceptions, il est ajouté automatiquement par le compilateur)

            mov rdi, FormatStr ;; 1er param de printf
            call printf ;; call de printf

            mov rsp, rbp ;; on remet l'ancienne stack en place
            pop rbp ;; on vire la sauvegarde de l'ancienne stack

            mov rax, 60 ;; on prépare l'appel au systcall "exit"
            xor rdi, rdi ;; on passe le paramètre 0 dans rdi (xor x, x revient à mettre 0 dans x (car un xor de 2 même valeurs donne 0)
            syscall ;; appel du systcall n°60
            ret

            ;; section read only
            section .rodata
            FormatStr db 'Hello World !',0Ah,0
            </pre>

            Multiplayer GameDev @ Unexpected
            

            Mon CV

            1 Reply Last reply
            1
            • C
              C
              cegdd
              wrote on last edited by
              #6

              <p>ok merci =)</p>

              créateur de Reconquête-Salvatrice, un petit RPG 2D en ligne multi-plateforme [OpenGL / C]

              1 Reply Last reply
              0

              Hello! It looks like you're interested in this conversation, but you don't have an account yet.

              Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

              With your input, this post could be even better 💗

              Register Login
              Reply
              • Reply as topic
              Log in to reply
              • Oldest to Newest
              • Newest to Oldest
              • Most Votes


              • Login

              • Login or register to search.
              Powered by NodeBB Contributors
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • World
              • Users
              • Groups